diff options
Diffstat (limited to 'lib')
28 files changed, 11842 insertions, 0 deletions
diff --git a/lib/AdminPanel/Category.pm b/lib/AdminPanel/Category.pm new file mode 100644 index 0000000..7c89fbb --- /dev/null +++ b/lib/AdminPanel/Category.pm @@ -0,0 +1,201 @@ +# vim: set et ts=4 sw=4: +#    Copyright 2012 Steven Tucker +# +#    This file is part of AdminPanel +# +#    AdminPanel is free software: you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation, either version 2 of the License, or +#    (at your option) any later version. +# +#    AdminPanel is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with AdminPanel.  If not, see <http://www.gnu.org/licenses/>. + + +#Class Category +package AdminPanel::Category; + +use strict; +use warnings; +use diagnostics; +use yui; + +## Can only add the config file data at constructor +## The Gui elements are added in setupGui inside MainDisplay +sub new { +    my ($class, $newName, $newIcon) = @_; +    my $self = { +        my $name = 0, +        my $button = 0, +        my $icon = 0, +        my $modules = 0 +    }; +    bless $self, 'AdminPanel::Category'; + +    $self->{name} = $newName; +    $self->{icon} = $newIcon; + +    return $self; +} + +## Add a new module to the list +#============================================================= + +=head2 loadModule + +=head3 INPUT + +    $self:   this object +    $module: module to add + +=head3 OUTPUT + +    1: if the module has been added +    0: otherwise + +=head3 DESCRIPTION + +    This method adds a module to the loaded +    modules if it is not already in. + +=cut + +#============================================================= +sub loadModule { +    my ($self, $module) = @_; + +    if (!$self->moduleLoaded($module->{name})) { +        push ( @{$self->{modules}}, $module ); + +        return 1; +    } +    return 0; +} + +#============================================================= + +=head2 moduleLoaded + +=head3 INPUT + +    $self:        this object +    $module_name or -CLASS => name : module/CLASS name to look for + +=head3 OUTPUT + +    $present: module present or not + +=head3 DESCRIPTION + +    This method looks for the given module and if already in +    returns true. +=cut + +#============================================================= +sub moduleLoaded { +    my $self = shift; +    my (%params) = @_; +    my ($module_name) = @_; + +    my $present = 0; + +    if (!$module_name) { +        return $present; +    } + +    foreach my $mod (@{$self->{modules}}) {  +        if (exists $params{-CLASS} && ref($mod) eq $params{-CLASS}) { +            $present = 1;  +            last; +        } +        elsif ($mod->{name} eq $module_name) { +            $present = 1;  +            last; +        } +    } + +    return $present; +} + +## Create and add buttons for each module +sub addButtons { +    my($self, $pane, $factory) = @_; +    my $count = 0; +    my $tmpButton; +    my $currLayout = 0; +    $factory->createVSpacing($pane, 2); +    foreach my $mod (@{$self->{modules}}) { +        if(($count % 2) != 1) { +            $currLayout = $factory->createHBox($pane); +            $factory->createHStretch($currLayout); +        } +        $count++; +        $tmpButton = $factory->createPushButton($currLayout, +                                                $mod->name); +        $mod->setButton($tmpButton); +        $tmpButton->setLabel($mod->name); +        $tmpButton->setIcon($mod->icon); +        $factory->createHStretch($currLayout); +        if(($count % 2) != 1) { +            $factory->createVSpacing($pane, 2);      +        } +    } +    $factory->createVStretch($pane); +} + +## Delete the module buttons +sub removeButtons { +    my($self) = @_; + +    for(@{$self->{modules}}) { +        $_->removeButton(); +    } +} + +sub setIcon { +    my($self) = @_; + +    $self->{button}->setIcon($self->{icon}); +} + +1; +__END__  + +=pod + +=head1 NAME + +       Category - add new category to window + +=head1 SYNOPSIS +        +       $category = new Category('Category Name'); + + +=head1 USAGE + +       my $display = new MainDisplay(); + +       my $category = new Category('Network'); +       $display->loadCategory($category); + +       $display->start(); + +=head1 FUNCTIONS + +=head2 new (name) + +       Constructor: creates a new category named Name  + +       $category = new Category('Name');  + +=head3 name (String) + +       The name of the category + +=cut diff --git a/lib/AdminPanel/ConfigReader.pm b/lib/AdminPanel/ConfigReader.pm new file mode 100644 index 0000000..718a381 --- /dev/null +++ b/lib/AdminPanel/ConfigReader.pm @@ -0,0 +1,119 @@ +# vim: set et ts=4 sw=4: +#    Copyright 2012 Steven Tucker +# +#    This file is part of AdminPanel +# +#    AdminPanel is free software: you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation, either version 2 of the License, or +#    (at your option) any later version. +# +#    AdminPanel is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with AdminPanel.  If not, see <http://www.gnu.org/licenses/>. + + +#Class ConfigReader +package AdminPanel::ConfigReader; + +use strict; +use warnings; +use diagnostics; +use XML::Simple; +use Data::Dumper; + +sub new { +    my ($class, $fileName) = @_; +     +    my $self = { +        my $data = 0, +        my $catLen = 0, +        my $currCat = 0, +        my $modLen = 0, +        my $currMod = 0, +        my $placeHolder = 0 +    }; +    bless $self, 'AdminPanel::ConfigReader'; +     +    my $xml = new XML::Simple (KeyAttr=>[]); +    $self->{data} = $xml->XMLin($fileName); +    if (ref($self->{data}->{category}) eq "HASH") { +        # one element alone +        my @categories; +        push @categories, $self->{data}->{category}; +        $self->{data}->{category} = undef; +        push @{$self->{data}->{category}}, @categories; +    } +    $self->{catLen} = scalar(@{$self->{data}->{category}}); +    $self->{currCat} = -1; +     +    if(ref(@{$self->{data}->{category}}[0]->{module}) eq "ARRAY") { +        $self->{modLen} = scalar(@{@{$self->{data}->{category}}[0]->{module}}); +    } else { +        $self->{modLen} = 1; +    } +    $self->{currMod} = -1; + +    return $self; +} + +sub hasNextCat { +    my ($self) = @_; +     +    if($self->{currCat} + 1 >= $self->{catLen}) { +        return 0; +    } +    return 1; +} + +sub getNextCat { +    my ($self) = @_; +     +    $self->{currCat}++; +    if($self->{currCat} >= $self->{catLen}) { +        return 0; +    } +     +    # Reset the Module Count and Mod length for new Category +    $self->{currMod} = -1; +    if(ref(@{$self->{data}->{category}}[$self->{currCat}]->{module}) eq "ARRAY") { +        $self->{modLen} = scalar(@{@{$self->{data}->{category}}[$self->{currCat}]->{module}}); +    } else { +        $self->{modLen} = 1; +    } + +    my $tmp = @{$self->{data}->{category}}[$self->{currCat}]; +     +    return $tmp; +} + +sub hasNextMod { +    my ($self) = @_; + +    if($self->{currMod} + 1 >= $self->{modLen}) { +        return 0; +    } +    return 1; +} + +sub getNextMod { +    my ($self) = @_; + +    my $ret = 0; + +    $self->{currMod}++; + +    if($self->{modLen} == 1) { +        $ret = @{$self->{data}->{category}}[$self->{currCat}]->{module}; +    } else { +        $ret = @{@{$self->{data}->{category} }[$self->{currCat}]->{module}}[$self->{currMod}]; +    } + +    return $ret; +} + +1; diff --git a/lib/AdminPanel/LogViewer/init.pm b/lib/AdminPanel/LogViewer/init.pm new file mode 100644 index 0000000..1c96171 --- /dev/null +++ b/lib/AdminPanel/LogViewer/init.pm @@ -0,0 +1,31 @@ +package AdminPanel::LogViewer::init; + +use strict; +use warnings; +use diagnostics; +use English; +use lib qw(/usr/lib/libDrakX); +use common; +use AdminPanel::Shared; +use base qw(Exporter); + +our @EXPORT = qw(warn_about_user_mode +		interactive_msg); + +sub interactive_msg { +	my ($title, $contents) = @_; +	return ask_YesOrNo($title, $contents); +} + +sub warn_about_user_mode() { +	my $title = N("Running in user mode"); +	my $msg = N("You are launching this program as a normal user.\n". +		    "You will not be able to read system logs which you do not have rights to,\n". +		    "but you may still browse all the others."); +	if(($EUID != 0) and (!interactive_msg($title, $msg))) { +		return 0; +	} +	return 1; +} + +1; diff --git a/lib/AdminPanel/MainDisplay.pm b/lib/AdminPanel/MainDisplay.pm new file mode 100644 index 0000000..8792bb2 --- /dev/null +++ b/lib/AdminPanel/MainDisplay.pm @@ -0,0 +1,505 @@ +# vim: set et ts=4 sw=4: +#    Copyright 2012 Steven Tucker +# +#    This file is part of AdminPanel +# +#    AdminPanel is free software: you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation, either version 2 of the License, or +#    (at your option) any later version. +# +#    AdminPanel is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with AdminPanel.  If not, see <http://www.gnu.org/licenses/>. + + +package AdminPanel::MainDisplay; +#============================================================= -*-perl-*- + +=head1 NAME + +AdminPanel::MainDisplay - class for AdminPaneol main window + +=head1 SYNOPSIS + +       $mainDisplay = new AdminPanel::MainDisplay(); + +=head1 METHODS + +=head1 DESCRIPTION + +Long_description + +=head1 EXPORT + +exported + +=head1 SUPPORT + +You can find documentation for this module with the perldoc command: + +perldoc AdminPanel::MainDisplay + +=head1 SEE ALSO + +SEE_ALSO + +=head1 AUTHOR + +Steven Tucker  + +=head1 COPYRIGHT and LICENSE + +Copyright (C) 2012, Steven Tucker +Copyright (C) 2014, Angelo Naselli. + +AdminPanel is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +AdminPanel is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with AdminPanel.  If not, see <http://www.gnu.org/licenses/>. + +=head1 FUNCTIONS + +=cut + + + +=head1 VERSION + +Version 0.01 + +=cut + +our $VERSION = '1.0.0'; + +use strict; +use warnings; +use diagnostics; +use AdminPanel::SettingsReader; +use AdminPanel::ConfigReader; +use AdminPanel::Category; +use AdminPanel::Module; +use Data::Dumper; +use yui; + +sub new { + +    my $self = { +        my $categories = 0, +        my $event = 0, +        my $factory = 0, +        my $mainWin = 0, +        my $mainLayout = 0, +        my $menuLayout = 0, +        my $menus = { +            my $file = 0, +            my $help = 0 +        }, +        my $layout = 0, +        my $leftPane = 0, +        my $rightPane = 0, +        my $currCategory = 0, +        my $confDir = 0, +        my $title   = 0, +        my $settings = 0, +        my $exitButton = 0, +#        my $justToGetRidOfERROR = 0, +        my $replacePoint = 0 +    }; +    bless $self, 'AdminPanel::MainDisplay'; +     +## Default values +    $self->{name} =     "Administration panel"; +    $self->{categories} = []; +    $self->{confDir}    = "/etc/apanel", +    $self->{title}      = "apanel", +     +    my $cmdline = new yui::YCommandLine; + +    ## TODO add parameter check +    my $pos       = $cmdline->find("--name"); +    if ($pos > 0) +    { +        $self->{title} = $cmdline->arg($pos+1); +    } +    $pos       = $cmdline->find("--conf_dir"); +    if ($pos > 0) +    { +        $self->{confDir} = $cmdline->arg($pos+1); +    } +    else +    { +        $self->{confDir} = "/etc/$self->{title}"; +    } +#     print "name     = ".$self->{title}."\n"; +#     print "conf dir = ".$self->{confDir}."\n"; + +    $self->setupGui(); + +    return $self; +} + +## Begin the program event loop +sub start { +    my ($self) = shift; +    my $reqExit = 0; + +    ##Default category selection +    if (!$self->{currCategory}) { +        $self->{currCategory} = @{$self->{categories}}[0];  +    } +    $self->{currCategory}->addButtons($self->{rightPane}, $self->{factory}); +    $self->{rightPaneFrame}->setLabel($self->{currCategory}->{name}); +    $self->{factory}->createSpacing($self->{rightPane}, 1, 1, 1.0 ); +    my $launch = 0; +    while(!$launch) { + +        ## Grab Event +        $self->{event} = $self->{mainWin}->waitForEvent(); + +        ## Check for window close +        if ($self->{event}->eventType() == $yui::YEvent::CancelEvent) +        { +            last; +        } + +## why i can't test item() with $self->{menus}->{file}[0]? +        ## Check for Exit button push or menu +        if($self->{event}->widget() == $self->{exitButton} ||  +           ($self->{event}->item() && ($self->{event}->item()->label() eq $self->{menus}->{file}[0]->label() ))) { +            last; +        }; + +        ## Discover if a menu button was selected. +        ## If menu button selected, set right panel to display +        ## selected Category Modules +        for(@{$self->{categories}}){ +            if( $_->{button} == $self->{event}->widget() ){ +                ## Menu item selected, set right pane +                $self->{mainWin}->startMultipleChanges(); +                ## Remove existing modules +                $self->{replacePoint}->deleteChildren(); +                $self->{rightPane} = $self->{factory}->createVBox($self->{replacePoint}); + +                ## Change Current Category to the selected one +                $self->{currCategory} = $_; +                ## Add new Module Buttons to Right Pane +                $self->{currCategory}->addButtons($self->{rightPane}, $self->{factory}); +                $self->{rightPaneFrame}->setLabel($self->{currCategory}->{name}); +                $self->{factory}->createSpacing($self->{rightPane}, 1, 1, 1.0 ); +                $self->{replacePoint}->showChild(); +                $self->{mainWin}->recalcLayout(); +                $self->{mainWin}->doneMultipleChanges(); + +                last; +            } +        } + +        ## Check if event is from current Category View +        ## If icon click, launch the Module +        for(@{$self->{currCategory}->{modules}}) { +            if( $_->{button} == $self->{event}->widget() ){ +                $launch = $_; +                last; +            } +        } +    } + +    return $launch; +} + +sub destroy { +    my ($self) = shift; +    $self->{mainWin}->destroy(); +    for (my $cat=0; $cat < scalar(@{$self->{categories}}); $cat++ ) { +        @{$self->{categories}}[$cat]->{button} = 0; +        @{$self->{categories}}[$cat]->removeButtons(); +    } +} + +sub setupGui { +    my ($self) = shift; + +    $self->loadSettings(); +    yui::YUILog::setLogFileName($self->{settings}->{log}); +    $self->{name} = $self->{settings}->{title}; +    yui::YUI::app()->setApplicationTitle($self->{name}); +    yui::YUI::app()->setApplicationIcon($self->{settings}->{icon}); + +    $self->{factory} = yui::YUI::widgetFactory; +    $self->{mainWin} = $self->{factory}->createMainDialog; +#print "Title:  ".yui::YUI::app()->applicationTitle()."\n"; + +    $self->{mainLayout} = $self->{factory}->createVBox($self->{mainWin}); +    $self->{menuLayout} = $self->{factory}->createHBox($self->{mainLayout}); +    +    ## Menu file +    ## TODO i8n  +    my $align = $self->{factory}->createAlignment($self->{menuLayout}, 1, 0); +    my $menu            = $self->{factory}->createMenuButton($align, "File"); +    my $item = new yui::YMenuItem("Exit"); + +    push(@{$self->{menus}->{file}}, $item); +    $menu->addItem($item); +    $menu->rebuildMenuTree(); + +    $align = $self->{factory}->createAlignment($self->{menuLayout}, 2, 0); +    $menu           = $self->{factory}->createMenuButton($align, "Help"); +    $item = new yui::YMenuItem("Help"); +    $menu->addItem($item); +    push(@{$self->{menus}->{help}}, $item); +    $item = new yui::YMenuItem("About"); +    $menu->addItem($item); +    push(@{$self->{menus}->{help}}, $item); +    $menu->rebuildMenuTree(); + +    $self->{layout}     = $self->{factory}->createHBox($self->{mainLayout}); + +    #create left Panel Frame no need to add a label for title  +    $self->{leftPaneFrame} = $self->{factory}->createFrame($self->{layout}, $self->{settings}->{category_title}); +    #create right Panel Frame no need to add a label for title (use setLabel when module changes)  +    $self->{rightPaneFrame} = $self->{factory}->createFrame($self->{layout}, ""); +    #create replace point for dynamically created widgets +    $self->{replacePoint} = $self->{factory}->createReplacePoint($self->{rightPaneFrame}); + +    $self->{rightPane} = $self->{factory}->createVBox($self->{replacePoint}); +    $self->{leftPane} = $self->{factory}->createVBox($self->{leftPaneFrame}); + +    #logo from settings +    my $logo = $self->{factory}->createImage($self->{leftPane}, $self->{settings}->{logo}); +    $logo->setAutoScale(1); + +#     $self->{leftPaneFrame}->setWeight(0, 1); +    $self->{rightPaneFrame}->setWeight(0, 2); + +    $self->loadCategories(); +    $self->{factory}->createVStretch($self->{leftPane}); + +    $self->{exitButton} = $self->{factory}->createPushButton($self->{leftPane}, "Exit"); +    $self->{exitButton}->setIcon("$self->{settings}->{images_dir}/quit.png");     +    $self->{exitButton}->setStretchable(0, 1); +#     $self->{exitButton}->setStretchable(1, 1); +} + +## adpanel settings +sub loadSettings { +    my ($self, $force_load) = @_; +    # configuration file name +    my $fileName = "$self->{confDir}/settings.conf"; +    if (!$self->{settings} || $force_load) { +        $self->{settings} = new AdminPanel::SettingsReader($fileName); +    } +} + +#============================================================= + +=head2 categoryLoaded + +=head3 INPUT + +    $self:     this object +    $category: category to look for + +=head3 OUTPUT + +    $present: category is present or not + +=head3 DESCRIPTION + +    This method looks for the given category and if already in +    returns true. +=cut + +#============================================================= +sub categoryLoaded { +    my ($self, $category) = @_; +    my $present = 0; + +    if (!$category) { +        return $present; +    } + +    foreach my $cat (@{$self->{categories}}) {  +        if ($cat->{name} eq $category->{name}) { +            $present = 1;  +            last; +        } +    } + +    return $present; +} + +#============================================================= + +=head2 getCategory + +=head3 INPUT + +    $self:     this object +    $name:     category name + +=head3 OUTPUT + +    $category: category object if exists + +=head3 DESCRIPTION + +    This method looks for the given category name and returns +    the realte object. +=cut + +#============================================================= +sub getCategory { +    my ($self, $name) = @_; +    my $category = undef; + +    foreach $category (@{$self->{categories}}) {  +        if ($category->{name} eq $name) { +            return $category; +        } +    } + +    return $category; +} + +sub loadCategory { +    my ($self, $category) = @_; + +    if (!$self->categoryLoaded($category)) { +        push ( @{$self->{categories}}, $category ); + +        @{$self->{categories}}[-1]->{button} = $self->{factory}->createPushButton( +                                                                    $self->{leftPane}, +                                                                    $self->{categories}[-1]->{name} +                                                                    ); +        @{$self->{categories}}[-1]->setIcon(); + +        @{$self->{categories}}[-1]->{button}->setStretchable(0, 1); +    } +    else { +        for (my $cat=0; $cat < scalar(@{$self->{categories}}); $cat++ ) { +            if( @{$self->{categories}}[$cat]->{name} eq $category->{name} && +                !@{$self->{categories}}[$cat]->{button})  { +                    @{$self->{categories}}[$cat]->{button} = $self->{factory}->createPushButton( +                                                                    $self->{leftPane}, +                                                                    $self->{categories}[$cat]->{name} +                                                                    ); +                    @{$self->{categories}}[$cat]->setIcon(); +                    @{$self->{categories}}[$cat]->{button}->setStretchable(0, 1); +                    last; + +            } +        } +    } +} + +sub loadCategories { +    my ($self) = @_; + +    # category files  +    my @categoryFiles; +    my $fileName = "$self->{confDir}/categories.conf"; +     +     +    # configuration file dir +    my $directory = "$self->{confDir}/categories.conf.d"; +     +    push(@categoryFiles, $fileName); +    push(@categoryFiles, <$directory/*.conf>); +    my $currCategory; +     +    foreach $fileName (@categoryFiles) { +        my $inFile = new AdminPanel::ConfigReader($fileName); +        my $tmpCat; +        my $tmp; +        my $hasNextCat = $inFile->hasNextCat(); +        while( $hasNextCat ) { +            $tmp = $inFile->getNextCat(); +            $tmpCat = $self->getCategory($tmp->{title}); +            if (!$tmpCat) { +                $tmpCat = new AdminPanel::Category($tmp->{title}, $tmp->{icon}); +            } +            $self->loadCategory($tmpCat); +            $hasNextCat  = $inFile->hasNextCat(); +            $currCategory = $tmpCat; +         +            my $hasNextMod = $inFile->hasNextMod(); +            while( $hasNextMod ) { +                $tmp = $inFile->getNextMod(); +                my $tmpMod; +                my $loaded = 0; +                if (exists $tmp->{title}) { +                    if (not $currCategory->moduleLoaded($tmp->{title})) { +                        $tmpMod = AdminPanel::Module->create(name => $tmp->{title},  +                                                             icon => $tmp->{icon}, +                                                             launch => $tmp->{launcher} +                        ); +                    } +                }  +                elsif (exists $tmp->{class}) { +                    if (not $currCategory->moduleLoaded(-CLASS => $tmp->{class})) { +                        $tmpMod = AdminPanel::Module->create(-CLASS => $tmp->{class}); +                    } +                } +                if ($tmpMod) { +                    $loaded = $currCategory->loadModule($tmpMod); +                    undef $tmpMod if !$loaded; +                } +                $hasNextMod = $inFile->hasNextMod(); +            } +        } +    } +} + +sub menuEventIndex { +    my ($self) = shift; +     +    my $index = -1; + +    for(my $i = 0; $i < scalar(@{$self->{categories}} ); ++$i) +    { +        print "Current Index = ".$index."\n"; +        if($self->{event}->widget() == @{$self->{categories}}[$i]->{button}) +        { +            $index = $i; +            print "Index found is : ".$index; +            last; +        } +    } +    return $index; +} + +1; + +=pod + +=head2 start + +       contains the main loop of the application +              where we can check for events + +=head2 setupGui + +       creates a popupDialog using a YUI::WidgetFactory +              and then populate this dialog with some components + +=head2 loadCategory + +       creates a new button representing a category + +=head3 category (String) + +=cut + diff --git a/lib/AdminPanel/Module.pm b/lib/AdminPanel/Module.pm new file mode 100644 index 0000000..1488e95 --- /dev/null +++ b/lib/AdminPanel/Module.pm @@ -0,0 +1,121 @@ +#!/usr/bin/perl + +# vim: set et ts=4 sw=4: +#    Copyright 2012 Steven Tucker +# +#    This file is part of AdminPanel +# +#    AdminPanel is free software: you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation, either version 2 of the License, or +#    (at your option) any later version. +# +#    AdminPanel is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with AdminPanel.  If not, see <http://www.gnu.org/licenses/>. + + +#Class Module +package AdminPanel::Module; + +use Moose; + +=head1 VERSION + +Version 0.01 + +=cut + +our $VERSION = '1.0.0'; + +use strict; +use warnings; +use diagnostics; +use yui; + +=head1 SUBROUTINES/METHODS + +=head2 create - returns a Module object such as a module +                launcher (this object) or an extension of +                this class + +=cut + +sub create { +    my $class = shift; +    $class = ref $class || $class; +    my (%params) = @_; + +    my $obj; +    if ( exists $params{-CLASS} ) { +        my $driver = $params{-CLASS}; +         +        eval { +            my $pkg = $driver; +            $pkg =~ s/::/\//g; +            $pkg .= '.pm'; +            require $pkg; +            $obj=$driver->new(); +        }; +        if ( $@ ) { +            die "Error getting obj for driver $params{-CLASS}: $@"; +            return undef; +        } +    } +    else { +        $obj = new AdminPanel::Module(@_); +    } +    return $obj; +} + +has 'icon' => ( +    is      => 'rw', +    isa     => 'Str', +); + +has 'name' => ( +    is      => 'rw', +    isa     => 'Str', +); + +has 'launch' => ( +    is      => 'rw', +    isa     => 'Str', +); + +has 'button' => ( +    is      => 'rw', +   init_arg => undef, +); + + +sub setButton { +    my ($self, $button) = @_; +    $self->{button} = $button; +} + +sub removeButton { +    my($self) = @_; + +    undef($self->{button}); +} + +# base class launcher +sub start { +    my $self = shift; + +    if ($self->{launch}) { +        my $err = yui::YUI::app()->runInTerminal( $self->{launch} . " --ncurses"); +        if ($err == -1) { +            system($self->{launch}); +        }    +    } +} + + +no Moose; +1; diff --git a/lib/AdminPanel/Module/AdminMouse.pm b/lib/AdminPanel/Module/AdminMouse.pm new file mode 100644 index 0000000..9e4e37c --- /dev/null +++ b/lib/AdminPanel/Module/AdminMouse.pm @@ -0,0 +1,278 @@ +# vim: set et ts=4 sw=4: +#***************************************************************************** +#  +#  Copyright (c) 2013 Angelo Naselli <anaselli@linux.it> +#  from drakx services +#  +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +#  +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +#  +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#  +#***************************************************************************** + +package AdminPanel::Module::AdminMouse; + +#leaving them atm +use lib qw(/usr/lib/libDrakX); + +# i18n: IMPORTANT: to get correct namespace (drakx-kbd-mouse-x11 instead of libDrakX) +BEGIN { unshift @::textdomains, 'drakx-kbd-mouse-x11' } + +use common; +use modules; +use mouse; +use c; + +use AdminPanel::Shared; + +use yui; +use Moose; + +extends qw( AdminPanel::Module ); + +has '+icon' => ( +    default => "/usr/share/mcc/themes/default/mousedrake-mdk.png", +); + +has '+name' => ( +    default => N("AdminMouse"),  +); + +sub start { +    my $self = shift; + +    $self->_adminMouseDialog(); +} + +sub _getUntranslatedName { +    my ($self, $name, $list) = @_; +     +    foreach my $n (@{$list}) { +        my @names  = split(/\|/, $n); +        for (my $lev=0; $lev < scalar(@names); $lev++) { +            if (translate($names[$lev]) eq $name) { +                return $names[$lev]; +            } +        } +    } + +    return undef; +} + + +sub _adminMouseDialog { +    my $self = shift; +      +    my $datavalue = "TEST"; +     +    my $appTitle = yui::YUI::app()->applicationTitle(); +     +    ## set new title to get it in dialog +    yui::YUI::app()->setApplicationTitle($self->name); +    ## set icon if not already set by external launcher +    yui::YUI::app()->setApplicationIcon($self->icon); + +    my $factory      = yui::YUI::widgetFactory; +     +    my $dialog     = $factory->createMainDialog; +    my $vbox       = $factory->createVBox( $dialog ); +    my $frame      = $factory->createFrame ($vbox, N("Please choose your type of mouse.")); +    my $treeWidget = $factory->createTree($frame, ""); +     +    my $modules_conf = modules::any_conf->read; + +    my $mouse = mouse::read(); + +    if (!$::noauto) { +        my $probed_mouse = mouse::detect($modules_conf); +        $mouse = $probed_mouse if !$mouse->{Protocol} || !$probed_mouse->{unsafe}; +    } +     +    if (!$mouse || !$::auto) { +        $mouse ||= mouse::fullname2mouse('Universal|Any PS/2 & USB mice'); +         +        my $prev = my $fullname = $mouse->{type} . '|' . $mouse->{name}; +        my $selected = $mouse->{name}; +         +        my $fullList = { list => [ mouse::_fullnames() ], items => [], separator => '|', val => \$fullname,  +                     format => sub { join('|', map { translate($_) } split('\|', $_[0])) } } ; +        my $i;              +         +        my $itemColl = new yui::YItemCollection; +        my @items; +        for ($i=0; $i<scalar(@{$fullList->{list}}); $i++) { +            my @names  = split(/\|/, $fullList->{list}[$i]); +            for (my $lev=0; $lev < scalar(@names); $lev++) { +                $names[$lev] = N($names[$lev]); +            } +            if ($i == 0 || $names[0] ne $items[0]->{label}) { +                if ($i != 0) { +                    $itemColl->push($items[0]->{item});  +                    push @{$fullList->{items}}, $items[-1]->{item};; +                } +                @items = undef;                 +                my $item = new yui::YTreeItem ($names[0]); +          +                if ($selected eq $self->_getUntranslatedName($item->label(), $fullList->{list})) { +                    $item->setSelected(1) ; +                    $item->setOpen(1); +                    my $parent = $item;   +                    while($parent = $parent->parent()) { +                        $parent->setOpen(1);                                 +                    } +                } +                $item->DISOWN(); +                @items = ({item => $item, label => $names[0], level => 0}); +                for (my $lev=1; $lev < scalar(@names); $lev++) { +                    $item = new yui::YTreeItem ($items[$lev-1]->{item}, $names[$lev]); +                     +                    if ($selected eq $self->_getUntranslatedName($item->label(), $fullList->{list})) { +                        $item->setSelected(1) ; +                        $item->setOpen(1); +                        my $parent = $item;   +                        while($parent = $parent->parent()) { +                            $parent->setOpen(1);                                 +                        } +                    } +                    $item->DISOWN(); +                    if ($lev < scalar(@names)-1) { +                        push @items, {item => $item, label => $names[$lev], level => $lev}; +                    }                     +                } +            } +            else { +                my $prevItem = 0; +                for (my $lev=1; $lev < scalar(@names); $lev++) { +                    my $it; +                    for ($it=1; $it < scalar(@items); $it++){ +                        if ($items[$it]->{label} eq $names[$lev] && $items[$it]->{level} == $lev) { +                            $prevItem = $it; +                            last; +                        }                         +                    } +                    if ($it == scalar(@items)) { +                        my $item = new yui::YTreeItem ($items[$prevItem]->{item}, $names[$lev]); + +                        if ($selected eq $self->_getUntranslatedName($item->label(), $fullList->{list})) { +                            $item->setSelected(1) ; +                            $item->setOpen(1); +                            my $parent = $item;   +                            while($parent = $parent->parent()) { +                                $parent->setOpen(1);                                 +                            } +                        } +                        $item->DISOWN(); +                        push @items, {item => $item, label => $names[$lev], level => $lev};                         +                    } +                } +            } +        } +        $itemColl->push($items[0]->{item}); +        push @{$fullList->{items}}, $items[-1]->{item}; + +        $treeWidget->addItems($itemColl); +        my $align        = $factory->createLeft($vbox); +        my $hbox         = $factory->createHBox($align); +        my $aboutButton  = $factory->createPushButton($hbox, N("About") ); +        $align = $factory->createRight($hbox); +        $hbox     = $factory->createHBox($align); +        my $cancelButton = $factory->createPushButton($hbox, N("Cancel") ); +        my $okButton = $factory->createPushButton($hbox, N("Ok") ); + +        while(1) { +            my $event     = $dialog->waitForEvent(); +            my $eventType = $event->eventType(); +             +            #event type checking +            if ($eventType == $yui::YEvent::CancelEvent) { +                last; +            } +            elsif ($eventType == $yui::YEvent::WidgetEvent) { +                # widget selected +                my $widget = $event->widget(); + +                if ($widget == $cancelButton) { +                    last; +                } +                elsif ($widget == $aboutButton) { +                    my $license = translate($AdminPanel::Shared::License); +                    AdminPanel::Shared::AboutDialog( +                          { name => N("AdminMouse"), +                            version => $self->VERSION,  +                            copyright => N("Copyright (C) %s Mageia community", '2014'), +                            license => $license,  +                            comments => N("AdminMouse is the Mageia mouse management tool \n(from the original idea of Mandriva mousedrake)."), +                            website => 'http://www.mageia.org', +                            website_label => N("Mageia"), +                            authors => "Angelo Naselli <anaselli\@linux.it>\nMatteo Pasotti <matteo.pasotti\@gmail.com>", +                            translator_credits => +                            #-PO: put here name(s) and email(s) of translator(s) (eg: "John Smith <jsmith@nowhere.com>") +                            N("_: Translator(s) name(s) & email(s)\n")} +                    ); +                } +                elsif ($widget == $okButton) { +                    my $continue = 1; +                    my $selectedItem = $treeWidget->selectedItem(); +                     +                    my $it=$selectedItem; +                    my $fullname = $self->_getUntranslatedName($it->label(), $fullList->{list}); +                    while($it = yui::toYTreeItem($it)->parent()) { +                        $fullname = join("|", $self->_getUntranslatedName($it->label(), $fullList->{list}), $fullname); +                    } +                     +                    if ($fullname ne $prev) { +                        my $mouse_ = mouse::fullname2mouse($fullname, device => $mouse->{device}); +                        if ($fullname =~ /evdev/) { +                            $mouse_->{evdev_mice} = $mouse_->{evdev_mice_all} = $mouse->{evdev_mice_all}; +                        } +                        %$mouse = %$mouse_; +                    } +                     +                    if ($mouse->{nbuttons} < 3 ) { +                        $mouse->{Emulate3Buttons} = AdminPanel::Shared::ask_YesOrNo('', N("Emulate third button?")); +                    } +                    if ($mouse->{type} eq 'serial') { +                        my @list = (); +                        foreach (detect_devices::serialPorts()) { +                            push @list, detect_devices::serialPort2text($_); +                        } +                        my $choice = AdminPanel::Shared::ask_fromList(N("Mouse Port"),  +                                                         N("Please choose which serial port your mouse is connected to."), +                                                         \@list); +                        if ( !$choice ) { +                            $continue = 0; +                        } +                        else { +                            $mouse->{device} = $choice; +                        } +                    } +                     +                    if ($continue) { +                        last; +                    } +                } +            } +        } +         +    } + + #  TODO manage write conf without interactive things + #  mouse::write_conf($in->do_pkgs, $modules_conf, $mouse, 1); +    system('systemctl', 'try-restart', 'gpm.service') if -e '/usr/lib/systemd/system/gpm.service'; +    +    $dialog->destroy(); +     +    #restore old application title +    yui::YUI::app()->setApplicationTitle($appTitle); +} + +1; diff --git a/lib/AdminPanel/Module/Hosts.pm b/lib/AdminPanel/Module/Hosts.pm new file mode 100644 index 0000000..fe6e011 --- /dev/null +++ b/lib/AdminPanel/Module/Hosts.pm @@ -0,0 +1,355 @@ +# vim: set et ts=4 sw=4: +#***************************************************************************** +#  +#  Copyright (c) 2013-2014 Matteo Pasotti <matteo.pasotti@gmail.com> +#  +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +#  +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +#  +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#  +#***************************************************************************** + +package AdminPanel::Module::Hosts; + +use Modern::Perl 2011; +use autodie; +use Moose; +use POSIX qw(ceil); +use utf8; + +use Glib; +use yui; +use AdminPanel::Shared; +use AdminPanel::Shared::Hosts; + +extends qw( AdminPanel::Module ); + + +has '+icon' => ( +    default => "/usr/lib/libDrakX/icons/IC-Dhost-48.png" +); + +has '+name' => ( +    default => "Hostmanager", +); + +=head1 VERSION + +Version 1.0.0 + +=cut + +our $VERSION = '1.0.0'; + +has 'dialog' => ( +    is => 'rw', +    init_arg => undef +); + +has 'table' => ( +    is => 'rw', +    init_arg => undef +); + +has 'cfgHosts' => ( +    is => 'rw', +    init_arg => undef +); + +sub start { +    my $self = shift; + +    $self->manageHostsDialog(); +}; + + +#============================================================= + +=head2 _addHostDialog + +=head3 INPUT + +    $self: this object + +=head3 DESCRIPTION + +This subroutine creates the Host dialog to add host definitions  + +=cut + +#============================================================= +sub _manipulateHostDialog { +    my $self = shift; + +    my $headerString = shift(); +    my $boolEdit = shift(); + +    my $hostIpString = ""; +    my $hostNameString = ""; +    my $hostAliasesString = ""; + +    if($boolEdit == 1){ +        $hostIpString = shift(); +        $hostNameString = shift(); +        $hostAliasesString = shift(); +    } + +    my $factory  = yui::YUI::widgetFactory; +    my $dlg = $factory->createPopupDialog(); +    my $layout = $factory->createVBox($dlg); + +    my $hbox_header = $factory->createHBox($layout); +    my $vbox_content = $factory->createVBox($layout); +    my $hbox_footer = $factory->createHBox($layout); + +    # header +    my $labelDescription = $factory->createLabel($hbox_header,$headerString); + +    # content +    my $firstHbox = $factory->createHBox($vbox_content); +    my $secondHbox = $factory->createHBox($vbox_content); +    my $thirdHbox = $factory->createHBox($vbox_content); + +    my $labelIPAddress = $factory->createLabel($firstHbox,"IP Address"); +    my $labelHostName  = $factory->createLabel($secondHbox,"Hostname"); +    my $labelHostAlias = $factory->createLabel($thirdHbox,"Host aliases"); +    $labelIPAddress->setWeight($yui::YD_HORIZ, 10); +    $labelHostName->setWeight($yui::YD_HORIZ, 10); +    $labelHostAlias->setWeight($yui::YD_HORIZ, 10); + +    my $textIPAddress = $factory->createInputField($firstHbox,""); +    my $textHostName = $factory->createInputField($secondHbox,""); +    my $textHostAlias = $factory->createInputField($thirdHbox,""); +    $textIPAddress->setWeight($yui::YD_HORIZ, 30); +    $textHostName->setWeight($yui::YD_HORIZ, 30); +    $textHostAlias->setWeight($yui::YD_HORIZ, 30); + +    if($boolEdit == 1){ +        $textIPAddress->setValue($hostIpString); +        $textHostName->setValue($hostNameString); +        $textHostAlias->setValue($hostAliasesString); +    } + +    # footer +    my $cancelButton = $factory->createPushButton($factory->createLeft($hbox_footer),"Cancel"); +    my $okButton = $factory->createPushButton($factory->createRight($hbox_footer),"OK"); + +    while(1){ +        my $event     = $dlg->waitForEvent(); +        my $eventType = $event->eventType(); + +        #event type checking +        if ($eventType == $yui::YEvent::CancelEvent) { +            last; +        } +        elsif ($eventType == $yui::YEvent::WidgetEvent) { +            # widget selected +            my $widget = $event->widget(); +            if ($widget == $cancelButton) { +                last; +            } +            elsif($widget == $okButton) { +                my $res = undef; +                my @hosts_toadd; +                push @hosts_toadd, $textHostName->value(); +                if(trim($textHostAlias->value()) ne ""){ +                    push @hosts_toadd, $textHostAlias->value(); +                } +                print "@hosts_toadd\n"; +                if($boolEdit == 0){ +                    $res = $self->cfgHosts->_insertHost($textIPAddress->value(),[@hosts_toadd]); +                }else{ +                    $res = $self->cfgHosts->_modifyHost($textIPAddress->value(),[@hosts_toadd]); +                } +                $res = $self->cfgHosts->_writeHosts(); +                print "Write result: $res\n"; +                last; +            } +        } +    } + +    destroy $dlg; +} + +sub _addHostDialog { +    my $self = shift(); +    return $self->_manipulateHostDialog("Add the information",0); +} + +sub _edtHostDialog { +    my $self = shift(); +    my $hostIp = shift(); +    my $hostName = shift(); +    my $hostAliases = shift(); +    return $self->_manipulateHostDialog("Modify the information",1,$hostIp,$hostName,$hostAliases); +} + +#============================================================= + +=head2 setupTable + +=head3 INPUT + +    $self: this object + +    $data: reference to the array containaing the host data to show into the table + +=head3 DESCRIPTION + +This subroutine populates a previously created YTable with the hosts data +retrieved by the Config::Hosts module + +=cut + +#============================================================= +sub setupTable { +    my $self = shift(); +     +    my @hosts = $self->cfgHosts->_getHosts(); +    # clear table +    $self->table->deleteAllItems(); +    foreach my $host (@hosts){ +        my $tblItem; +        my $aliases = join(',',@{$host->{'hosts'}}); +        if(scalar(@{$host->{'hosts'}}) > 1){ +            $aliases =~s/^$host->{'hosts'}[0]\,*//g; +        }elsif(scalar(@{$host->{'hosts'}}) == 1){ +            $aliases = ""; +        } +        $tblItem = new yui::YTableItem($host->{'ip'},$host->{'hosts'}[0],$aliases); +        $self->table->addItem($tblItem); +    } +} + +sub manageHostsDialog { +    my $self = shift; + +    ## TODO fix for adminpanel +    my $appTitle = yui::YUI::app()->applicationTitle(); +    my $appIcon = yui::YUI::app()->applicationIcon(); +    ## set new title to get it in dialog +    my $newTitle = "Manage hosts definitions"; +    yui::YUI::app()->setApplicationTitle($newTitle); + +    my $factory  = yui::YUI::widgetFactory; +    my $optional = yui::YUI::optionalWidgetFactory; +     + +    $self->dialog($factory->createMainDialog()); +    my $layout    = $factory->createVBox($self->dialog); + +    my $hbox_header = $factory->createHBox($layout); +    my $headLeft = $factory->createHBox($factory->createLeft($hbox_header)); +    my $headRight = $factory->createHBox($factory->createRight($hbox_header)); + +    my $logoImage = $factory->createImage($headLeft, $appIcon); +    my $labelAppDescription = $factory->createLabel($headRight,$newTitle);  +    $logoImage->setWeight($yui::YD_HORIZ,0); +    $labelAppDescription->setWeight($yui::YD_HORIZ,3); + +    my $hbox_content = $factory->createHBox($layout); + +    my $tableHeader = new yui::YTableHeader(); +    $tableHeader->addColumn("IP Address"); +    $tableHeader->addColumn("Hostname"); +    $tableHeader->addColumn("Host Aliases"); +    my $leftContent = $factory->createLeft($hbox_content); +    $leftContent->setWeight($yui::YD_HORIZ,45); +    $self->table($factory->createTable($leftContent,$tableHeader)); + +    # initialize Config::Hosts +    $self->cfgHosts(AdminPanel::Shared::Hosts->new()); +    $self->setupTable(); +     +    my $rightContent = $factory->createRight($hbox_content); +    $rightContent->setWeight($yui::YD_HORIZ,10); +    my $topContent = $factory->createTop($rightContent); +    my $vbox_commands = $factory->createVBox($topContent); +    my $addButton = $factory->createPushButton($factory->createHBox($vbox_commands),"Add"); +    my $edtButton = $factory->createPushButton($factory->createHBox($vbox_commands),"Edit"); +    my $remButton = $factory->createPushButton($factory->createHBox($vbox_commands),"Remove"); +    $addButton->setWeight($yui::YD_HORIZ,1); +    $edtButton->setWeight($yui::YD_HORIZ,1); +    $remButton->setWeight($yui::YD_HORIZ,1); + +    my $hbox_foot = $factory->createHBox($layout); +    my $vbox_foot_left = $factory->createVBox($factory->createLeft($hbox_foot)); +    my $vbox_foot_right = $factory->createVBox($factory->createRight($hbox_foot)); +    my $aboutButton = $factory->createPushButton($vbox_foot_left,"About"); +    my $cancelButton = $factory->createPushButton($vbox_foot_right,"Cancel"); +    my $okButton = $factory->createPushButton($vbox_foot_right,"OK"); + +    # main loop +    while(1) { +        my $event     = $self->dialog->waitForEvent(); +        my $eventType = $event->eventType(); +         +        #event type checking +        if ($eventType == $yui::YEvent::CancelEvent) { +            last; +        } +        elsif ($eventType == $yui::YEvent::WidgetEvent) { +### Buttons and widgets ### +            my $widget = $event->widget(); +            if ($widget == $cancelButton) { +                last; +            } +            elsif ($widget == $addButton) { +                $self->_addHostDialog(); +                $self->setupTable(); +            } +            elsif ($widget == $edtButton) { +                my $tblItem = yui::toYTableItem($self->table->selectedItem()); +                if($tblItem->cellCount() >= 3){ +                    $self->_edtHostDialog($tblItem->cell(0)->label(),$tblItem->cell(1)->label(),$tblItem->cell(2)->label()); +                }else{ +                    $self->_edtHostDialog($tblItem->cell(0)->label(),$tblItem->cell(1)->label(),""); +                } +                $self->setupTable(); +            } +            elsif ($widget == $remButton) { +                # implement deletion dialog +                if(AdminPanel::Shared::ask_YesOrNo("Confirmation","Are you sure to drop this host?") == 1){ +                    my $tblItem = yui::toYTableItem($self->table->selectedItem()); +                    # drop the host using the ip +                    $self->cfgHosts->_dropHost($tblItem->cell(0)->label()); +                    # write changes +                    $self->cfgHosts->_writeHosts(); +                    $self->setupTable(); +                } +            }elsif ($widget == $aboutButton) { +                AdminPanel::Shared::AboutDialog({ +                    name => $appTitle, +                    version => $VERSION, +                    copyright => "Copyright (c) 2013-2014 by Matteo Pasotti", +                    license => $AdminPanel::Shared::License, +                    comments => "Graphical manager for hosts definitions", +                    website => "http://gitweb.mageia.org/software/adminpanel", +                    website_label => "WebSite", +                    authors => "Matteo Pasotti <matteo.pasotti\@gmail.com>" +                    } +                ); +            }elsif ($widget == $okButton) { +                # write changes +                $self->cfgHosts->_writeHosts(); +                last; +            } +        } +    } + +    $self->dialog->destroy() ; + +    #restore old application title +    yui::YUI::app()->setApplicationTitle($appTitle); +} + +1; diff --git a/lib/AdminPanel/Module/Services.pm b/lib/AdminPanel/Module/Services.pm new file mode 100644 index 0000000..3332637 --- /dev/null +++ b/lib/AdminPanel/Module/Services.pm @@ -0,0 +1,559 @@ +# vim: set et ts=4 sw=4: +#***************************************************************************** +#  +#  Copyright (c) 2013 Angelo Naselli <anaselli@linux.it> +#  from drakx services +#  +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +#  +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +#  +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#  +#***************************************************************************** + +package AdminPanel::Module::Services; + +#-###################################################################################### +#- misc imports +#-###################################################################################### + +use strict; + +# TODO same translation atm +use lib qw(/usr/lib/libDrakX); +use common qw(N +              N_ +              cat_  +              formatAlaTeX  +              translate  +              find); +use run_program; + +use Moose; + +use yui; +use AdminPanel::Shared; +use AdminPanel::Shared::Services qw( +                                    services +                                    xinetd_services +                                    is_service_running +                                    restart_or_start +                                    stop +                                    set_service +                    ); + +use File::Basename; + +extends qw( AdminPanel::Module ); + +has '+icon' => ( +    default => "/usr/share/mcc/themes/default/service-mdk.png", +); + +has '+name' => ( +    default => N("AdminService"),  +); + +has 'services' => ( +    traits  => ['Array'], +    is      => 'rw', +    isa     => 'ArrayRef[Str]', +    default => sub { [] }, +    init_arg  => undef, +    handles => { +        all_services    => 'elements', +        add_service     => 'push', +        map_service     => 'map', +        service_count   => 'count', +        sorted_services => 'sort', +    }, +); + +has 'xinetd_services' => ( +    traits  => ['Array'], +    is      => 'rw', +    isa     => 'ArrayRef[Str]', +    default => sub { [] }, +    init_arg  => undef, +    handles => { +        all_xinetd_services    => 'elements', +        add_xinetd_service     => 'push', +        map_xinetd_service     => 'map', +        xinetd_service_count   => 'count', +        sorted_xinetd_services => 'sort', +    }, +); + +has 'on_services' => ( +    traits  => ['Array'], +    is      => 'rw', +    isa     => 'ArrayRef[Str]', +    default => sub { [] }, +    init_arg  => undef, +    handles => { +        all_on_services    => 'elements', +        add_on_service     => 'push', +        map_on_service     => 'map', +        on_service_count   => 'count', +        sorted_on_services => 'sort', +    }, +); + + +has 'running_services' => ( +    traits  => ['Array'], +    is      => 'rw', +    isa     => 'ArrayRef[Str]', +    default => sub { [] }, +    init_arg  => undef, +    handles => { +        all_running_services    => 'elements', +        add_running_service     => 'push', +        map_running_service     => 'map', +        running_service_count   => 'count', +        sorted_running_services => 'sort', +    }, +); +=head1 VERSION + +Version 1.0.0 + +=cut + +our $VERSION = '1.0.0'; + + +sub description { +    my %services = ( +acpid => N_("Listen and dispatch ACPI events from the kernel"),      +alsa => N_("Launch the ALSA (Advanced Linux Sound Architecture) sound system"), +anacron => N_("Anacron is a periodic command scheduler."), +apmd => N_("apmd is used for monitoring battery status and logging it via syslog. +It can also be used for shutting down the machine when the battery is low."), +atd => N_("Runs commands scheduled by the at command at the time specified when +at was run, and runs batch commands when the load average is low enough."), +'avahi-deamon' => N_("Avahi is a ZeroConf daemon which implements an mDNS stack"), +chronyd => N_("An NTP client/server"), +cpufreq => N_("Set CPU frequency settings"), +crond => N_("cron is a standard UNIX program that runs user-specified programs +at periodic scheduled times. vixie cron adds a number of features to the basic +UNIX cron, including better security and more powerful configuration options."), +cups => N_("Common UNIX Printing System (CUPS) is an advanced printer spooling system"), +dm => N_("Launches the graphical display manager"), +fam => N_("FAM is a file monitoring daemon. It is used to get reports when files change. +It is used by GNOME and KDE"), +g15daemon => N_("G15Daemon allows users access to all extra keys by decoding them and  +pushing them back into the kernel via the linux UINPUT driver. This driver must be loaded  +before g15daemon can be used for keyboard access. The G15 LCD is also supported. By default,  +with no other clients active, g15daemon will display a clock. Client applications and  +scripts can access the LCD via a simple API."), +gpm => N_("GPM adds mouse support to text-based Linux applications such the +Midnight Commander. It also allows mouse-based console cut-and-paste operations, +and includes support for pop-up menus on the console."), +haldaemon => N_("HAL is a daemon that collects and maintains information about hardware"), +harddrake => N_("HardDrake runs a hardware probe, and optionally configures +new/changed hardware."), +httpd => N_("Apache is a World Wide Web server. It is used to serve HTML files and CGI."), +inet => N_("The internet superserver daemon (commonly called inetd) starts a +variety of other internet services as needed. It is responsible for starting +many services, including telnet, ftp, rsh, and rlogin. Disabling inetd disables +all of the services it is responsible for."), +ip6tables => N_("Automates a packet filtering firewall with ip6tables"), +iptables => N_("Automates a packet filtering firewall with iptables"), +irqbalance => N_("Evenly distributes IRQ load across multiple CPUs for enhanced performance"), +keytable => N_("This package loads the selected keyboard map as set in +/etc/sysconfig/keyboard.  This can be selected using the kbdconfig utility. +You should leave this enabled for most machines."), +kheader => N_("Automatic regeneration of kernel header in /boot for +/usr/include/linux/{autoconf,version}.h"), +kudzu => N_("Automatic detection and configuration of hardware at boot."), +'laptop-mode' => N_("Tweaks system behavior to extend battery life"), +linuxconf => N_("Linuxconf will sometimes arrange to perform various tasks +at boot-time to maintain the system configuration."), +lpd => N_("lpd is the print daemon required for lpr to work properly. It is +basically a server that arbitrates print jobs to printer(s)."), +lvs => N_("Linux Virtual Server, used to build a high-performance and highly +available server."), +mandi => N_("Monitors the network (Interactive Firewall and wireless"), +mdadm => N_("Software RAID monitoring and management"), +messagebus => N_("DBUS is a daemon which broadcasts notifications of system events and other messages"), +msec => N_("Enables MSEC security policy on system startup"), +named => N_("named (BIND) is a Domain Name Server (DNS) that is used to resolve host names to IP addresses."), +netconsole => N_("Initializes network console logging"), +netfs => N_("Mounts and unmounts all Network File System (NFS), SMB (Lan +Manager/Windows), and NCP (NetWare) mount points."), +network => N_("Activates/Deactivates all network interfaces configured to start +at boot time."), +'network-auth' => N_("Requires network to be up if enabled"), +'network-up' => N_("Wait for the hotplugged network to be up"), +nfs => N_("NFS is a popular protocol for file sharing across TCP/IP networks. +This service provides NFS server functionality, which is configured via the +/etc/exports file."), +nfslock => N_("NFS is a popular protocol for file sharing across TCP/IP +networks. This service provides NFS file locking functionality."), +ntpd => N_("Synchronizes system time using the Network Time Protocol (NTP)"), +numlock => N_("Automatically switch on numlock key locker under console +and Xorg at boot."), +oki4daemon => N_("Support the OKI 4w and compatible winprinters."), +partmon => N_("Checks if a partition is close to full up"), +pcmcia => N_("PCMCIA support is usually to support things like ethernet and +modems in laptops.  It will not get started unless configured so it is safe to have +it installed on machines that do not need it."), +portmap => N_("The portmapper manages RPC connections, which are used by +protocols such as NFS and NIS. The portmap server must be running on machines +which act as servers for protocols which make use of the RPC mechanism."), +portreserve => N_("Reserves some TCP ports"), +postfix => N_("Postfix is a Mail Transport Agent, which is the program that moves mail from one machine to another."), +random => N_("Saves and restores system entropy pool for higher quality random +number generation."), +rawdevices => N_("Assign raw devices to block devices (such as hard disk drive +partitions), for the use of applications such as Oracle or DVD players"), +resolvconf => N_("Nameserver information manager"), +routed => N_("The routed daemon allows for automatic IP router table updated via +the RIP protocol. While RIP is widely used on small networks, more complex +routing protocols are needed for complex networks."), +rstatd => N_("The rstat protocol allows users on a network to retrieve +performance metrics for any machine on that network."), +rsyslog => N_("Syslog is the facility by which many daemons use to log messages to various system log files.  It is a good idea to always run rsyslog."), +rusersd => N_("The rusers protocol allows users on a network to identify who is +logged in on other responding machines."), +rwhod => N_("The rwho protocol lets remote users get a list of all of the users +logged into a machine running the rwho daemon (similar to finger)."), +saned => N_("SANE (Scanner Access Now Easy) enables to access scanners, video cameras, ..."), +shorewall => N_("Packet filtering firewall"), +smb => N_("The SMB/CIFS protocol enables to share access to files & printers and also integrates with a Windows Server domain"), +sound => N_("Launch the sound system on your machine"), +'speech-dispatcherd' => N_("layer for speech analysis"), +sshd => N_("Secure Shell is a network protocol that allows data to be exchanged over a secure channel between two computers"), +syslog => N_("Syslog is the facility by which many daemons use to log messages +to various system log files.  It is a good idea to always run syslog."), +'udev-post' => N_("Moves the generated persistent udev rules to /etc/udev/rules.d"), +usb => N_("Load the drivers for your usb devices."), +vnStat => N_("A lightweight network traffic monitor"), +xfs => N_("Starts the X Font Server."), +xinetd => N_("Starts other deamons on demand."), +    ); +    my ($name) = @_; +    my $s = $services{$name}; +    if ($s) { +        $s = translate($s); +    } else { +        my $file = "$::prefix/usr/lib/systemd/system/$name.service"; +        if (-e $file) { +                $s = cat_($file); +                $s = $s =~ /^Description=(.*)/mg ? $1 : ''; +        } else { +                $file = find { -e $_ } map { "$::prefix$_/$name" } '/etc/rc.d/init.d', '/etc/init.d', '/etc/xinetd.d'; +                $s = cat_($file); +                $s =~ s/\\\s*\n#\s*//mg; +                $s = +                        $s =~ /^#\s+(?:Short-)?[dD]escription:\s+(.*?)^(?:[^#]|# {0,2}\S)/sm ? $1 : +                        $s =~ /^#\s*(.*?)^[^#]/sm ? $1 : ''; + +                $s =~ s/#\s*//mg; +        } +    } +    $s =~ s/\n/ /gm; $s =~ s/\s+$//; +    $s; +} + +sub BUILD { +    my $self = shift; + +    $self->loadServices(); +} + + +#============================================================= + +=head2 start + +=head3 INPUT + +    $self: this object + +=head3 DESCRIPTION + +    This method extends Module::start and is invoked to +    start  adminService + +=cut + +#============================================================= +sub start { +    my $self = shift; + +    $self->servicePanel(); +}; + + +#============================================================= + +=head2 loadServices + +=head3 INPUT + +    $self: this object + +=head3 DESCRIPTION + +   This methonds load service info into local attributes such +   as xinetd_services, on_services and all the available,  +   services + +=cut + +#============================================================= +sub loadServices { +    my $self = shift; + +    my ($l, $on_services) = AdminPanel::Shared::Services::services(); +    my @xinetd_services = map { $_->[0] } AdminPanel::Shared::Services::xinetd_services(); + +    $self->xinetd_services(); +    $self->xinetd_services(\@xinetd_services); +    $self->services(\@$l); +    $self->on_services(\@$on_services); + +    $self->refreshRunningServices(); +} + +sub refreshRunningServices { +    my $self = shift; + +    my @running; +    foreach ($self->all_services) { + +        my $serviceName = $_; +        push @running, $serviceName if is_service_running($serviceName); +    } +    $self->running_services(\@running); +} + +## serviceInfo sets widgets accordingly to selected service status  +## param +##   'service'     service name  +##   'infoPanel'   service information widget  +sub serviceInfo { +    my ($self, $service, $infoPanel) = @_; + +    yui::YUI::ui()->blockEvents(); +    ## infoPanel +    $infoPanel->setValue(formatAlaTeX(description($service))); +    yui::YUI::ui()->unblockEvents(); +} + +sub serviceStatus { +    my ($self, $tbl, $item) = @_; + +    my $started; + +    if (member($item->label(), $self->all_xinetd_services)) { +        $started = N("Start when requested"); +    } +    else { +        $started = (member($item->label(), $self->all_running_services)? N("running") : N("stopped")); +    } +# TODO add icon green/red led   +    my $cell   = $tbl->toCBYTableItem($item)->cell(1); +    if ($cell) { +        $cell->setLabel($started); +        $tbl->cellChanged($cell); +    } +} + +## draw service panel and manage it  +sub servicePanel { +    my $self = shift; + +    my $appTitle = yui::YUI::app()->applicationTitle(); + +    ## set new title to get it in dialog +    yui::YUI::app()->setApplicationTitle($self->name); +    ## set icon if not already set by external launcher +    yui::YUI::app()->setApplicationIcon($self->icon); + +#    my ($l, $on_services) = services(); +#    my @xinetd_services = map { $_->[0] } xinetd_services(); + +    my $mageiaPlugin = "mga"; +    my $factory      = yui::YUI::widgetFactory; +    my $mgaFactory   = yui::YExternalWidgets::externalWidgetFactory($mageiaPlugin); +    $mgaFactory      = yui::YMGAWidgetFactory::getYMGAWidgetFactory($mgaFactory); +     +    my $dialog  = $factory->createMainDialog; +    my $vbox    = $factory->createVBox( $dialog ); +    my $frame   = $factory->createFrame ($vbox, N("Services")); + +    my $frmVbox = $factory->createVBox( $frame ); +    my $hbox = $factory->createHBox( $frmVbox ); + +    my $yTableHeader = new yui::YTableHeader(); +    $yTableHeader->addColumn(N("Service"), $yui::YAlignBegin); +    $yTableHeader->addColumn(N("Status"),  $yui::YAlignCenter); +    $yTableHeader->addColumn(N("On boot"), $yui::YAlignBegin); + +    ## service list (serviceBox) +    my $serviceTbl = $mgaFactory->createCBTable($hbox, $yTableHeader, $yui::YCBTableCheckBoxOnLastColumn); +    my $itemCollection = new yui::YItemCollection; +    foreach ($self->all_services) { + +        my $serviceName = $_; +         +        my $item = new yui::YCBTableItem($serviceName); +        my $started; +        if (member($serviceName, $self->all_xinetd_services)) { +            $started = N("Start when requested"); +        } +        else { +            $started = (member($serviceName, $self->all_running_services)? N("running") : N("stopped")); +        } + +# TODO add icon green/red led   +        my $cell   = new yui::YTableCell($started); +        $item->addCell($cell); + +        $item->check(member($serviceName, $self->all_on_services)); +        $item->setLabel($serviceName); +        $itemCollection->push($item); +        $item->DISOWN(); +    } +    $serviceTbl->addItems($itemCollection); +    $serviceTbl->setImmediateMode(1); +    $serviceTbl->setWeight(0, 50); + +    ## info panel (infoPanel) +    $frame   = $factory->createFrame ($hbox, N("Information")); +    $frame->setWeight(0, 30); +    $frmVbox = $factory->createVBox( $frame ); +    my $infoPanel = $factory->createRichText($frmVbox, "--------------"); #, 0, 0); +    $infoPanel->setAutoScrollDown(); + +    ### Service Start button ($startButton) +    $hbox = $factory->createHBox( $frmVbox ); +    my $startButton = $factory->createPushButton($hbox, N("Start")); +     +    ### Service Stop button ($stopButton) +    my $stopButton  = $factory->createPushButton($hbox, N("Stop")); + +    # dialog buttons +    $factory->createVSpacing($vbox, 1.0); +    ## Window push buttons +    $hbox = $factory->createHBox( $vbox ); +    my $align = $factory->createLeft($hbox); +    $hbox     = $factory->createHBox($align); +    my $aboutButton = $factory->createPushButton($hbox, N("About") ); +    $align = $factory->createRight($hbox); +    $hbox     = $factory->createHBox($align); +    my $closeButton = $factory->createPushButton($hbox, N("Close") ); + +    #first item status +    my $item = $serviceTbl->selectedItem(); +    if ($item) { +        $self->serviceInfo($item->label(), $infoPanel); +        if (member($item->label(), $self->all_xinetd_services)) { +            $stopButton->setDisabled(); +            $startButton->setDisabled(); +        } +        else { +            $stopButton->setEnabled(1); +            $startButton->setEnabled(1); +        } +    } + +    while(1) { +        my $event       = $dialog->waitForEvent(); +        my $eventType   = $event->eventType(); + +        #event type checking +        if ($eventType == $yui::YEvent::CancelEvent) { +            last; +        } +        elsif ($eventType == $yui::YEvent::WidgetEvent) { +            # widget selected +            my $widget = $event->widget(); +            my $wEvent = yui::toYWidgetEvent($event); +             +            if ($widget == $closeButton) { +                last; +            } +            elsif ($widget == $aboutButton) { +                my $license = translate($AdminPanel::Shared::License); +                # TODO fix version value +                AboutDialog({ name => N("AdminService"), +                    version => $self->VERSION,  +                    copyright => N("Copyright (C) %s Mageia community", '2013-2014'), +                    license => $license,  +                    comments => N("Service Manager is the Mageia service and daemon management tool \n(from the original idea of Mandriva draxservice)."), +                    website => 'http://www.mageia.org', +                    website_label => N("Mageia"), +                    authors => "Angelo Naselli <anaselli\@linux.it>\nMatteo Pasotti <matteo.pasotti\@gmail.com>", +                    translator_credits => +                        #-PO: put here name(s) and email(s) of translator(s) (eg: "John Smith <jsmith@nowhere.com>") +                        N("_: Translator(s) name(s) & email(s)\n")} +                ); +            } +            elsif ($widget == $serviceTbl) { +                 +                # service selection changed +                $item = $serviceTbl->selectedItem(); +                if ($item) { +                    $self->serviceInfo($item->label(), $infoPanel); +                    if (member($item->label(), $self->all_xinetd_services)) { +                        $stopButton->setDisabled(); +                        $startButton->setDisabled(); +                    } +                    else { +                        $stopButton->setEnabled(1); +                        $startButton->setEnabled(1); +                    } +                } +# TODO fix libyui-mga-XXX item will always be changed after first one +                if ($wEvent->reason() == $yui::YEvent::ValueChanged) { +                    $item = $serviceTbl->changedItem(); +                    if ($item) { + +                        set_service($item->label(), $item->checked()); +                        # we can push/pop service, but this (slower) should return real situation +                        $self->refreshRunningServices(); +                    } +                } +            } +            elsif ($widget == $startButton) { +                $item = $serviceTbl->selectedItem(); +                if ($item) { +                    restart_or_start($item->label()); +                    # we can push/pop service, but this (slower) should return real situation +                    $self->refreshRunningServices(); +                    $self->serviceStatus($serviceTbl, $item); +                } +            } +            elsif ($widget == $stopButton) { +                $item = $serviceTbl->selectedItem(); +                if ($item) { +                    stop($item->label()); +                    # we can push/pop service, but this (slower) should return real situation +                    $self->refreshRunningServices(); +                    $self->serviceStatus($serviceTbl, $item); +                } +            } +        } +    } +    $dialog->destroy(); +     +    #restore old application title +    yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle; +} + +no Moose; +__PACKAGE__->meta->make_immutable; + +1; diff --git a/lib/AdminPanel/Module/Users.pm b/lib/AdminPanel/Module/Users.pm new file mode 100644 index 0000000..16ea058 --- /dev/null +++ b/lib/AdminPanel/Module/Users.pm @@ -0,0 +1,2570 @@ +# vim: set et ts=4 sw=4: +#***************************************************************************** +#  +#  Copyright (c) 2013 Angelo Naselli <anaselli@linux.it> +#  from adduserdrake and userdrake +#  +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +#  +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +#  +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#  +#***************************************************************************** + +package AdminPanel::Module::Users; + +############################################### +## +## graphic related routines for managing user +## +############################################### + + +use strict; +# TODO evaluate if Moose is too heavy and use Moo  +# instead +use POSIX qw(ceil); +# use Time::localtime; + +# TODO same translation atm +use lib qw(/usr/lib/libDrakX); +# i18n: IMPORTANT: to get correct namespace (userdrake instead of libDrakX) +BEGIN { unshift @::textdomains, 'userdrake', 'libuser', 'drakconf' } + +use common qw(N +              translate); +use security::level; +use run_program; +## USER is from userdrake +use USER; +use utf8; +use log; + +use Glib; +use yui; +use AdminPanel::Shared; +use AdminPanel::Shared::Users; +use Moose; +extends qw( AdminPanel::Module ); + +has '+icon' => ( +    default => "/usr/share/icons/userdrake.png", +); + +has '+name' => ( +    default => N("AdminUser"),  +); + + +=head1 VERSION + +Version 1.0.0 + +=cut + +our $VERSION = '1.0.0'; + +# main dialog +has 'dialog'     => ( +    is        => 'rw', +    init_arg  => undef, +); + +has 'widgets' => (  +    traits    => ['Hash'], +    default   => sub { {} }, +    is        => 'rw', +    isa       => 'HashRef', +    handles   => { +        set_widget     => 'set', +        get_widget     => 'get', +        widget_pairs   => 'kv', +    }, +    init_arg  => undef, +); + +has 'action_menu' => ( +    traits    => ['Hash'], +    default   => sub { {} }, +    is        => 'rw', +    isa       => 'HashRef', +    handles   => { +        set_action_menu     => 'set', +        get_action_menu     => 'get', +        action_menu_pairs   => 'kv', +    }, +    init_arg  => undef,  +); + +## Used by USER (for getting values? TODO need explanations, where?) +has 'USER_GetValue' => ( +    default   => -65533, +    is        => 'ro', +    isa       => 'Int', +    init_arg  => undef, +); + +## Used by USER (for getting values? TODO need explanations, where?) +has 'ctx' => ( +    default   => sub {USER::ADMIN->new}, +    is        => 'ro', +    init_arg  => undef, +); + + +has 'edit_tab_widgets' => (  +    traits    => ['Hash'], +    default   => sub { {} }, +    is        => 'rw', +    isa       => 'HashRef', +    handles   => { +        set_edit_tab_widget => 'set', +        get_edit_tab_widget => 'get', +        edit_tab_pairs      => 'kv', +    }, +    init_arg  => undef, +); + +sub start { +    my $self = shift; + +    $self->manageUsersDialog(); +}; + +# TODO move to Shared? +sub labeledFrameBox { +    my ($parent, $label) = @_; + +    my $factory  = yui::YUI::widgetFactory; + +    my $frame    = $factory->createFrame($parent, $label); +    $frame->setWeight( $yui::YD_HORIZ, 1); +    $frame->setWeight( $yui::YD_VERT, 2); +    $frame       = $factory->createHVCenter( $frame ); +    $frame       = $factory->createVBox( $frame ); +  return $frame; +} + +# usefull local variable to avoid duplicating +# translation point for user edit labels +my %userEditLabel = ( +    user_data     => N("User Data"), +    account_info  => N("Account Info"), +    password_info => N("Password Info"), +    groups        => N("Groups"), +); +# usefull local variable to avoid duplicating +# translation point for group edit labels +my %groupEditLabel = ( +    group_data    => N("Group Data"), +    group_users   => N("Group Users"), +); +#============================================================= + +=head2 ChooseGroup + +=head3 INPUT + +    $self: this object + +=head3 OUTPUT + +    $choice: 0 or 1 (choice) +            -1 cancel or exit + +=head3 DESCRIPTION + +creates a popup dialog to ask if adding user to an existing  +group or to the 'users' group + +=cut + +#============================================================= +sub ChooseGroup { +    my $self = shift; + +    my $choice = -1; + +    ## push application title +    my $appTitle = yui::YUI::app()->applicationTitle(); +    ## set new title to get it in dialog +    yui::YUI::app()->setApplicationTitle(N("Choose group")); +     +    my $factory  = yui::YUI::widgetFactory; + +    my $dlg      = $factory->createPopupDialog(); +    my $layout   = $factory->createVBox($dlg); + + +    my $frame    = labeledFrameBox($layout,  N("A group with this name already exists.  What would you like to do?")); + +    my $rbg      = $factory->createRadioButtonGroup( $frame ); +    $frame       = $factory->createVBox( $rbg ); +    my $align    = $factory->createLeft($frame); + +    my $rb1      = $factory->createRadioButton( $align, N("Add to the existing group"), 1); +    $rb1->setNotify(1); +    $rbg->addRadioButton( $rb1 ); +    $align        = $factory->createLeft($frame); +    my $rb2 = $factory->createRadioButton( $align, N("Add to the 'users' group"), 0); +    $rb2->setNotify(1); +    $rbg->addRadioButton( $rb2 ); + +    my $hbox            = $factory->createHBox($layout); +    $align           = $factory->createRight($hbox); +    my $cancelButton = $factory->createPushButton($align, N("Cancel")); +    my $okButton     = $factory->createPushButton($hbox,  N("Ok")); +    while(1) { +        my $event     = $dlg->waitForEvent(); +        my $eventType = $event->eventType(); +         +        #event type checking +        if ($eventType == $yui::YEvent::CancelEvent) { +            last; +        } +        elsif ($eventType == $yui::YEvent::WidgetEvent) { +            # widget selected +            my $widget = $event->widget(); +            if ($widget == $cancelButton) { +                last; +            } +            if ($widget == $okButton) { +                $choice = $rb1->value() ? 0 : 1 ; +                last; +            } +        } +    } + +    destroy $dlg; +     +    #restore old application title +    yui::YUI::app()->setApplicationTitle($appTitle); + +    return $choice; +} + +#============================================================= + +=head2 _updateOrDelUsersInGroup + +=head3 INPUT + +    $name:   username + +=head3 DESCRIPTION + +    Fixes user deletion into groups. + +=cut + +#============================================================= +sub _updateOrDelUserInGroup { +    my ($self, $name) = @_; +    my $groups = $self->ctx->GroupsEnumerateFull; +    foreach my $g (@$groups) { +        my $members = $g->MemberName(1, 0); +        if ($self->_inArray($name, $members)) {  +            eval { $g->MemberName($name, 2) }; +            eval { $self->ctx->GroupModify($g) }; +        } +    } +} + +#============================================================= + +=head2 _deleteGroupDialog + +=head3 INPUT + +    $self: this object + +=head3 DESCRIPTION + +    This method open a dialog to delete the selected group. + +=cut + +#============================================================= +sub _deleteGroupDialog { +    my $self = shift; + +    my $item = $self->get_widget('table')->selectedItem(); +    if (! $item) { +       return; +    } + +    my $groupname = $item->label();  +    ## push application title +    my $appTitle = yui::YUI::app()->applicationTitle(); +    ## set new title to get it in dialog +    yui::YUI::app()->setApplicationTitle(N("Warning")); + +    my $factory  = yui::YUI::widgetFactory; +    my $dlg      = $factory->createPopupDialog(); +    my $layout   = $factory->createVBox($dlg); + +    my $align    = $factory->createLeft($layout); + +    $factory->createLabel($align, N("Do you really want to delete the group %s?",  +                                    $groupname)); + +    $align    = $factory->createRight($layout); +    my $hbox  = $factory->createHBox($align); +    my $cancelButton = $factory->createPushButton($hbox, N("Cancel")); +    my $deleteButton = $factory->createPushButton($hbox,  N("Delete")); + +    while(1) { +        my $event     = $dlg->waitForEvent(); +        my $eventType = $event->eventType(); +         +        #event type checking +        if ($eventType == $yui::YEvent::CancelEvent) { +            last; +        } +        elsif ($eventType == $yui::YEvent::WidgetEvent) { +            # widget selected +            my $widget = $event->widget(); +            if ($widget == $cancelButton) { +                last; +            } +            elsif ($widget == $deleteButton) { +                my $groupEnt = $self->ctx->LookupGroupByName($groupname); +                my $members  = $self->ctx->EnumerateUsersByGroup($groupname); +                my $continue = 1; +                GLOOP: foreach my $username (@$members) { +                    my $userEnt = $self->ctx->LookupUserByName($username); +                    if ($userEnt && $userEnt->Gid($self->USER_GetValue) == $groupEnt->Gid($self->USER_GetValue)) { +                        AdminPanel::Shared::msgBox(N("%s is a primary group for user %s\n Remove the user first",  +                                                     $groupname, $username)); +                        $continue = 0; +                        last GLOOP; +                    } +                } +                if ($continue) {  +                    log::explanations(N("Removing group: %s", $groupname)); +                    eval { $self->ctx->GroupDel($groupEnt) };  +                    $self->_refresh(); +                } +                last; +            } +        } +    } + +    destroy $dlg; +     +    #restore old application title +    yui::YUI::app()->setApplicationTitle($appTitle); +} + +#============================================================= + +=head2 _deleteUserDialog + +=head3 INPUT + +    $self: this object + +=head3 DESCRIPTION + +    This method open a dialog to delete the selected user. +    It also asks for additional information to be removed. + +=cut + +#============================================================= +sub _deleteUserDialog { +    my $self = shift; + +    my $item = $self->get_widget('table')->selectedItem(); +    if (! $item) { +       return; +    }  +    my $username = $item->label();  + +    my $userEnt = $self->ctx->LookupUserByName($username); +    my $homedir = $userEnt->HomeDir($self->USER_GetValue); + +    ## push application title +    my $appTitle = yui::YUI::app()->applicationTitle(); +    ## set new title to get it in dialog +    yui::YUI::app()->setApplicationTitle(N("Delete files or not?")); + +    my $factory  = yui::YUI::widgetFactory; +    my $dlg      = $factory->createPopupDialog(); +    my $layout   = $factory->createVBox($dlg); + +    my $align    = $factory->createLeft($layout); +    $factory->createLabel($align, N("Deleting user %s\nAlso perform the following actions\n", +                                   $username)); +    $align    = $factory->createLeft($layout); +    my $checkhome  = $factory->createCheckBox($align, N("Delete Home Directory: %s", $homedir, 0)); +    $align    = $factory->createLeft($layout); +    my $checkspool = $factory->createCheckBox($align, N("Delete Mailbox: /var/spool/mail/%s", +                                                        $username), 0); +    $align    = $factory->createRight($layout); +    my $hbox  = $factory->createHBox($align); +    my $cancelButton = $factory->createPushButton($hbox, N("Cancel")); +    my $deleteButton = $factory->createPushButton($hbox,  N("Delete")); +     +    if ($homedir !~ m!(?:/home|/var/spool)!) {  +        $checkhome->setDisabled();  +        $checkspool->setDisabled();  +    } + +     +    while(1) { +        my $event     = $dlg->waitForEvent(); +        my $eventType = $event->eventType(); +         +        #event type checking +        if ($eventType == $yui::YEvent::CancelEvent) { +            last; +        } +        elsif ($eventType == $yui::YEvent::WidgetEvent) { +            # widget selected +            my $widget = $event->widget(); +            if ($widget == $cancelButton) { +                last; +            } +            elsif ($widget == $deleteButton) { +                log::explanations(N("Removing user: %s", $username)); +                $self->ctx->UserDel($userEnt); +                $self->_updateOrDelUserInGroup($username); +                #Let's check out the user's primary group +                my $usergid = $userEnt->Gid($self->USER_GetValue); +                my $groupEnt = $self->ctx->LookupGroupById($usergid); +                if ($groupEnt) { +                    my $member = $groupEnt->MemberName(1, 0); +                    if (scalar(@$member) == 0 && $groupEnt->Gid($self->USER_GetValue) > 499) { +                        $self->ctx->GroupDel($groupEnt); +                    } +                } +                if ($checkhome->isChecked()) {  +                    eval { $self->ctx->CleanHome($userEnt) }; +                    $@ and AdminPanel::Shared::msgBox($@) and last; +                } +                if ($checkspool->isChecked()) { +                    eval { $self->ctx->CleanSpool($userEnt) }; +                    $@ and AdminPanel::Shared::msgBox($@) and last; +                } +                $self->_refresh(); +                last; +            } +        } +    } + +    destroy $dlg; +     +    #restore old application title +    yui::YUI::app()->setApplicationTitle($appTitle); + +} + + +sub _addGroupDialog { +    my $self = shift; + +    my $is_system = 0; + +    ## push application title +    my $appTitle = yui::YUI::app()->applicationTitle(); +    ## set new title to get it in dialog +    yui::YUI::app()->setApplicationTitle(N("Create New Group")); +    my $factory  = yui::YUI::widgetFactory; +    my $optional = yui::YUI::optionalWidgetFactory; + +    my $dlg      = $factory->createPopupDialog(); +    my $layout   = $factory->createVBox($dlg); + +    ## 'group name' +    my $align        = $factory->createRight($layout); +    my $hbox         = $factory->createHBox($align); +    my $label        = $factory->createLabel($hbox, N("Group Name:") ); +    my $groupName    = $factory->createInputField($hbox, "", 0); +    $label->setWeight($yui::YD_HORIZ, 1); +    $groupName->setWeight($yui::YD_HORIZ, 2); + +    $factory->createVSpacing($layout, 1); + +    # Specify group id manually +    $align           = $factory->createLeft($layout); +    $hbox            = $factory->createHBox($align); +    my $gidManually  = $factory->createCheckBox($hbox, N("Specify group ID manually"), 0); +    $factory->createHSpacing($hbox, 2); +    my $GID = $factory->createIntField($hbox, N("GID"), 1, 65000, 500); +    $GID->setEnabled($gidManually->value()); +    $gidManually->setNotify(1); + +    $hbox            = $factory->createHBox($layout); +    $align           = $factory->createRight($hbox); +    my $cancelButton = $factory->createPushButton($align, N("Cancel")); +    my $okButton     = $factory->createPushButton($hbox,  N("Ok")); +    while(1) { +        my $event     = $dlg->waitForEvent(); +        my $eventType = $event->eventType(); +         +        #event type checking +        if ($eventType == $yui::YEvent::CancelEvent) { +            last; +        } +        elsif ($eventType == $yui::YEvent::WidgetEvent) { +            # widget selected +            my $widget = $event->widget(); +            if ($widget == $cancelButton) { +                last; +            } +            elsif ($widget == $gidManually) { +                # GID inserction enabled? +                $GID->setEnabled($gidManually->value()); +            } +            elsif ($widget == $okButton) { +                ## check data +                my $groupname = $groupName->value(); +                my ($continue, $errorString) = valid_groupname($groupname); +                my $nm = $continue && $self->ctx->LookupGroupByName($groupname); +                if ($nm) { +                    $groupName->setValue(""); +                    $errorString = N("Group already exists, please choose another Group Name"); +                    $continue = 0; +                } +                my $groupEnt = $self->ctx->InitGroup($groupname, $is_system); +         +                my $gid = 0; +                if ($continue && $gidManually->value()) { +                    if (($gid = $GID->value()) < 500) { +                        $errorString = ""; +                        my $gidchoice = AdminPanel::Shared::ask_YesOrNo(N(" Group Gid is < 500"), +                                        N("Creating a group with a GID less than 500 is not recommended.\n Are you sure you want to do this?\n\n")); +                        $continue = $gidchoice and $groupEnt->Gid($gid); +                    } else {  +                        my $g = $self->ctx->LookupGroupById($gid); +                        if ($g) { +                            $errorString = ""; +                            my $gidchoice = AdminPanel::Shared::ask_YesOrNo(N(" Group ID is already used "), +                                        N("Creating a group with a non unique GID?\n\n")); +                            $continue = $gidchoice and $groupEnt->Gid($gid); +                        } +                        else { +                            $groupEnt and $groupEnt->Gid($gid); +                        } +                    } +                } + + +                if (!$continue) { +                    #--- raise error +                    AdminPanel::Shared::msgBox($errorString) if ($errorString); +                } +                else { +                    log::explanations(N("Adding group: %s ", $groupname)); +                    $self->ctx->GroupAdd($groupEnt); +                    $self->_refresh(); +                    last; +                } +            } +        } +    } +    destroy $dlg; +     +    #restore old application title +    yui::YUI::app()->setApplicationTitle($appTitle); +} + + +#============================================================= + +=head2 _buildUserData + +=head3 INPUT + +    $self:    this object +    $layout : layout in wich drawing graphic user data +  +=head3 OUTPUT + +    %userData: hash containing reference to graphical object  +               such as: +               full_name, login_name, password, password1, +               login_shell + +=head3 DESCRIPTION + +    This method is used by addUserDialog and _editUserDialog +    to create User Data dialog +=cut + +#============================================================= +sub _buildUserData { +    my ($self, $layout, $selected_shell) = @_; + + +    my @shells = @{$self->ctx->GetUserShells}; + +    my $factory  = yui::YUI::widgetFactory; + +    ## user 'full name' +    my $align        = $factory->createRight($layout); +    my $hbox         = $factory->createHBox($align); +    my $label        = $factory->createLabel($hbox, N("Full Name:") ); +    my $fullName     = $factory->createInputField($hbox, "", 0); +    $label->setWeight($yui::YD_HORIZ, 1); +    $fullName->setWeight($yui::YD_HORIZ, 2); + +    ## user 'login name' +    $align           = $factory->createRight($layout); +    $hbox            = $factory->createHBox($align); +    $label           = $factory->createLabel($hbox, N("Login:") ); +    my $loginName    = $factory->createInputField($hbox, "", 0); +    $label->setWeight($yui::YD_HORIZ, 1); +    $loginName->setWeight($yui::YD_HORIZ, 2); +    $loginName->setNotify(1); + +    ## user 'Password' +    $align           = $factory->createRight($layout); +    $hbox            = $factory->createHBox($align); +    $label           = $factory->createLabel($hbox, N("Password:") ); +    my $password     = $factory->createInputField($hbox, "", 1); +    $label->setWeight($yui::YD_HORIZ, 1); +    $password->setWeight($yui::YD_HORIZ, 2); +     +    ## user 'confirm Password' +    $align           = $factory->createRight($layout); +    $hbox            = $factory->createHBox($align); +    $label           = $factory->createLabel($hbox, N("Confirm Password:") ); +    my $password1    = $factory->createInputField($hbox, "", 1); +    $label->setWeight($yui::YD_HORIZ, 1); +    $password1->setWeight($yui::YD_HORIZ, 2); +     +    ## user 'Login Shell' +    $align           = $factory->createRight($layout); +    $hbox            = $factory->createHBox($align); +    $label           = $factory->createLabel($hbox, N("Login Shell:") ); +    my $loginShell   = $factory->createComboBox($hbox, "", 0); +    my $itemColl = new yui::YItemCollection; +    foreach my $shell (@shells) { +            my $item = new yui::YItem ($shell, 0); +            $item->setSelected(1) if ($selected_shell && $selected_shell eq $shell); +            $itemColl->push($item); +            $item->DISOWN(); +    } +    $loginShell->addItems($itemColl); +    $label->setWeight($yui::YD_HORIZ, 1); +    $loginShell->setWeight($yui::YD_HORIZ, 2); +     +    my %userData = ( +        full_name   => $fullName, +        login_name  => $loginName, +        password    => $password, +        password1   => $password1, +        login_shell => $loginShell, +    ); +     +    return ( %userData ); +} + +#============================================================= + +=head2 addUserDialog + +=head3 INPUT + +    $self:       this object +    $standalone: if set the application title is set +                 from the name set in costructor + +=head3 DESCRIPTION + +    This method creates and manages the dialog to add a new +    user. + +=cut + +#============================================================= +sub addUserDialog { +    my $self = shift; +    my $standalone = shift; + +    my $dontcreatehomedir = 0;  +    my $is_system = 0; + +    ## push application title +    my $appTitle = yui::YUI::app()->applicationTitle(); +    ## set new title to get it in dialog +    if ($standalone) { +        yui::YUI::app()->setApplicationTitle($self->name); +    } +    else { +        yui::YUI::app()->setApplicationTitle(N("Create New User")); +    } +     +    my $factory  = yui::YUI::widgetFactory; +    my $optional = yui::YUI::optionalWidgetFactory; + +    my $dlg      = $factory->createPopupDialog(); +    my $layout   = $factory->createVBox($dlg); +     +    my %userData = $self->_buildUserData($layout); +     +    ##### add a separator +    ## Create Home directory +    my $align           = $factory->createLeft($layout); +    my $hbox            = $factory->createHBox($align); +    my $createHome = $factory->createCheckBox($hbox, N("Create Home Directory"), 1); +    ## Home directory +    $align           = $factory->createLeft($layout); +    $hbox            = $factory->createHBox($align); +    my $label        = $factory->createLabel($hbox, N("Home Directory:") ); +    my $homeDir      = $factory->createInputField($hbox, "", 0); +    $label->setWeight($yui::YD_HORIZ, 1); +    $homeDir->setWeight($yui::YD_HORIZ, 2); + +    # Create private group +    $align           = $factory->createLeft($layout); +    $hbox            = $factory->createHBox($align); +    my $createGroup  = $factory->createCheckBox($hbox, N("Create a private group for the user"), 1); +     +    # Specify user id manually +    $align           = $factory->createRight($layout); +    $hbox            = $factory->createHBox($align); +    my $uidManually  = $factory->createCheckBox($hbox, N("Specify user ID manually"), 0); +    my $UID = $factory->createIntField($hbox, N("UID"), 1, 65000, 500); +    $UID->setEnabled($uidManually->value()); +    $uidManually->setNotify(1); +    $uidManually->setWeight($yui::YD_HORIZ, 2); +    $UID->setWeight($yui::YD_HORIZ, 1); + +    ## user 'icon' +    $hbox        = $factory->createHBox($layout); +    $factory->createLabel($hbox, N("Click on icon to change it") ); +    my $iconFace = AdminPanel::Shared::Users::GetFaceIcon(); +    my $icon = $factory->createPushButton($hbox, ""); +    $icon->setIcon(AdminPanel::Shared::Users::face2png($iconFace));  +    $icon->setLabel($iconFace); + +    $hbox            = $factory->createHBox($layout); +    $align           = $factory->createRight($hbox); +    my $cancelButton = $factory->createPushButton($align, N("Cancel")); +    my $okButton     = $factory->createPushButton($hbox,  N("Ok")); +    while(1) { +        my $event     = $dlg->waitForEvent(); +        my $eventType = $event->eventType(); +         +        #event type checking +        if ($eventType == $yui::YEvent::CancelEvent) { +            last; +        } +        elsif ($eventType == $yui::YEvent::WidgetEvent) { +            # widget selected +            my $widget = $event->widget(); +            if ($widget == $cancelButton) { +                last; +            } +            elsif ($widget == $icon) { +                #remove shortcut from label +                my $iconLabel = $self->_skipShortcut($icon->label()); + +                my $nextIcon = GetFaceIcon($icon->label(), 1); +                $icon->setLabel($nextIcon); +                $icon->setIcon(AdminPanel::Shared::Users::face2png($nextIcon)); +            } +            elsif ($widget == $uidManually) { +                # UID inserction enabled? +                $UID->setEnabled($uidManually->value()); +            } +            elsif ($widget == $userData{ login_name }) { +                my $username = $userData{ login_name }->value(); +                $homeDir->setValue("/home/$username"); +            } +            elsif ($widget == $okButton) { +                ## check data +                my $username = $userData{ login_name }->value(); +                my ($continue, $errorString) = valid_username($username); +                my $nm = $continue && $self->ctx->LookupUserByName($username); +                if ($nm) { +                    $userData{ login_name }->setValue(""); +                    $homeDir->setValue(""); +                    $errorString = N("User already exists, please choose another User Name"); +                    $continue = 0; +                } +                my $passwd = $continue && $userData{ password }->value(); +                if ($continue && $passwd ne $userData{ password1 }->value()) { +                    $errorString = N("Password Mismatch"); +                    $continue = 0; +                } +                my $sec = security::level::get(); +                if ($sec > 3 && length($passwd) < 6) { +                    $errorString = N("This password is too simple. \n Good passwords should be > 6 characters"); +                    $continue = 0; +                } +                my $userEnt = $continue && $self->ctx->InitUser($username, $is_system); +                if ($continue && $createHome->value()) { +                    $dontcreatehomedir = 0; +                    my $homedir = $homeDir->value(); +                    $userEnt and $userEnt->HomeDir($homedir); +                } else { +                    $dontcreatehomedir = 1; +                } +                my $uid = 0; +                if ($continue && $uidManually->value()) { +                    if (($uid = $UID->value()) < 500) { +                        $errorString = ""; +                        my $uidchoice = AdminPanel::Shared::ask_YesOrNo(N("User Uid is < 500"), +                                        N("Creating a user with a UID less than 500 is not recommended.\nAre you sure you want to do this?\n\n"));  +                        $continue = $uidchoice and $userEnt->Uid($uid); +                    } else {  +                        $userEnt and $userEnt->Uid($uid); +                    } +                } +                my $gid = 0; +                if ($createGroup->value()) { +                    if ($continue) { +                        #Check if group exist +                        my $gr = $self->ctx->LookupGroupByName($username); +                        if ($gr) {  +                            my $groupchoice = $self->ChooseGroup(); +                            if ($groupchoice == 0 ) { +                                #You choose to put it in the existing group +                                $gid = $gr->Gid($self->USER_GetValue); +                            } elsif ($groupchoice == 1) { +                                # Put it in 'users' group +                                log::explanations(N("Putting %s to 'users' group", +                                                    $username)); +                                $gid = AdminPanel::Shared::Users::Add2UsersGroup($username, $self->ctx); +                            } +                            else { +                                $errorString = ""; +                                $continue = 0; +                            } +                        } else {  +                            #it's a new group: Add it +                            my $newgroup = $self->ctx->InitGroup($username,$is_system); +                            log::explanations(N("Creating new group: %s", $username)); +                            $gid = $newgroup->Gid($self->USER_GetValue); +                            $self->ctx->GroupAdd($newgroup); +                        } +                    } +                } else { +                    $continue and $gid = AdminPanel::Shared::Users::Add2UsersGroup($username, $self->ctx); +                } + +                if (!$continue) { +                    #---rasie error +                    AdminPanel::Shared::msgBox($errorString) if ($errorString); +                } +                else { +                    ## OK let's create the user +                    print N("Adding user: ") . $username . " \n"; +                    log::explanations(N("Adding user: %s"), $username); +                    my $loginshell = $userData{ login_shell }->value(); +                    my $fullname   = $userData{ full_name }->value(); +                    $userEnt->Gecos($fullname);  $userEnt->LoginShell($loginshell); +                    $userEnt->Gid($gid); +                    $userEnt->ShadowMin(-1); $userEnt->ShadowMax(99999); +                    $userEnt->ShadowWarn(-1); $userEnt->ShadowInact(-1); +                    $self->ctx->UserAdd($userEnt, $is_system, $dontcreatehomedir); +                    $self->ctx->UserSetPass($userEnt, $passwd); +                    defined $icon->label() and +                         AdminPanel::Shared::Users::addKdmIcon($username, $icon->label()); +###  TODO Migration wizard +#                      +#                     Refresh($sysfilter, $stringsearch); +#                     transfugdrake::get_windows_disk() +#                         and $in->ask_yesorno(N("Migration wizard"), +#                                             N("Do you want to run the migration wizard in order to import Windows documents and settings in your Mageia distribution?")) +#                             and run_program::raw({ detach => 1 }, 'transfugdrake'); + + +                    last; +                } +            } +        } +    } + +    destroy $dlg; +     +    #restore old application title +    yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;  +} + +#============================================================= + +=head2 _createUserTable + +=head3 INPUT + +    $self: this object + +=head3 DESCRIPTION + +This function create the User table to be added to the replace  +point of the tab widget. Note this function is meant for internal  +use only + +=cut + +#============================================================= +sub _createUserTable { +    my $self = shift; + +    $self->dialog->startMultipleChanges(); +    $self->get_widget('replace_pnt')->deleteChildren(); +    my $parent = $self->get_widget('replace_pnt'); +    my $factory      = yui::YUI::widgetFactory; +    my $yTableHeader = new yui::YTableHeader(); +    $yTableHeader->addColumn(N("User Name"),      $yui::YAlignBegin); +    $yTableHeader->addColumn(N("User ID"),        $yui::YAlignBegin); +    $yTableHeader->addColumn(N("Primary Group"),  $yui::YAlignBegin); +    $yTableHeader->addColumn(N("Full Name"),      $yui::YAlignBegin); +    $yTableHeader->addColumn(N("Login Shell"),    $yui::YAlignBegin); +    $yTableHeader->addColumn(N("Home Directory"), $yui::YAlignBegin); +    $yTableHeader->DISOWN(); +     +    $self->set_widget(table => $factory->createTable($parent, $yTableHeader)); + +    $self->get_widget('table')->setImmediateMode(1); +    $self->get_widget('table')->DISOWN(); +    $self->get_widget('replace_pnt')->showChild(); +    $self->dialog->recalcLayout(); +    $self->dialog->doneMultipleChanges(); +    $self->_refreshUsers(); +} + +#============================================================= + +=head2 _createGroupTable + +=head3 INPUT + +    $self: this object + +=head3 DESCRIPTION + +This function create the Group table to be added to the replace  +point of the tab widget. Note this function is meant for internal  +use only + + +=cut + +#============================================================= +sub _createGroupTable { +    my $self = shift; + + +    $self->dialog->startMultipleChanges(); +    $self->get_widget('replace_pnt')->deleteChildren(); +    my $parent = $self->get_widget('replace_pnt'); +    my $factory      = yui::YUI::widgetFactory; +    my $yTableHeader = new yui::YTableHeader(); +    $yTableHeader->addColumn(N("Group Name"),     $yui::YAlignBegin); +    $yTableHeader->addColumn(N("Group ID"),       $yui::YAlignBegin); +    $yTableHeader->addColumn(N("Group Members"),  $yui::YAlignBegin); +    $yTableHeader->DISOWN(); + +    $self->set_widget(table => $factory->createTable($parent, $yTableHeader)); +    +    $self->get_widget('table')->setImmediateMode(1); +    $self->get_widget('table')->DISOWN(); +    $self->get_widget('replace_pnt')->showChild(); +    $self->dialog->recalcLayout(); +    $self->dialog->doneMultipleChanges();  +    $self->_refreshGroups(); +} + + +#============================================================= + +=head2 _computeLockExpire + +=head3 INPUT + +    $l: login user info + +=head3 OUTPUT + +    $status: Locked, Expired, or empty string + +=head3 DESCRIPTION + +    This method returns if the login is Locked, Expired or ok. +    Note this function is meant for internal use only + +=cut + +#============================================================= +sub _computeLockExpire { +    my ( $self, $l ) = @_; +    my $ep = $l->ShadowExpire($self->USER_GetValue); +    my $tm = ceil(time()/(24*60*60)); +    $ep = -1 if int($tm) <= $ep; +    my $status = $self->ctx->IsLocked($l) ? N("Locked") : ($ep != -1 ? N("Expired") : ''); +    $status; +} + +#============================================================= + +=head2 _refreshUsers + +=head3 INPUT + +    $self: this object + +=head3 DESCRIPTION + +    This method refresh user info into User tab widget. +    Note this function is meant for internal use only + +=cut + +#============================================================= +sub _refreshUsers { +    my $self = shift; + +    my $strfilt = $self->get_widget('filter')->value(); +    my $filterusers = $self->get_widget('filter_system')->isChecked(); +     +    my ($users, $group, $groupnm, $expr);  +    defined $self->ctx and $users = $self->ctx->UsersEnumerateFull; + +    $self->dialog->startMultipleChanges(); +    #for some reasons QT send an event using table->selectItem() +    # WA remove notification immediate +    $self->get_widget('table')->setImmediateMode(0); +    $self->get_widget('table')->deleteAllItems(); + +    my @UserReal; +  LOOP: foreach my $l (@$users) { +        next LOOP if $filterusers && $l->Uid($self->USER_GetValue) <= 499 || $l->Uid($self->USER_GetValue) == 65534; +        push @UserReal, $l if $l->UserName($self->USER_GetValue) =~ /^\Q$strfilt/; +    } +    my $i; +    my $itemColl = new yui::YItemCollection; +    foreach my $l (@UserReal) { +        $i++; +        my $uid = $l->Uid($self->USER_GetValue); +        if (!defined $uid) { +         warn "bogus user at line $i\n"; +         next; +        } +        my $a = $l->Gid($self->USER_GetValue); +        $group = $self->ctx->LookupGroupById($a); +        $groupnm = ''; +        $expr = $self->_computeLockExpire($l); +        $group and $groupnm = $group->GroupName($self->USER_GetValue);  +        my $s = $l->Gecos($self->USER_GetValue); +        c::set_tagged_utf8($s); +        my $username = $l->UserName($self->USER_GetValue); +        my $Uid      = $l->Uid($self->USER_GetValue); +        my $shell    = $l->LoginShell($self->USER_GetValue); +        my $homedir  = $l->HomeDir($self->USER_GetValue);  +        my $item = new yui::YTableItem ("$username", +                                        "$Uid", +                                        "$groupnm", +                                        "$s", +                                        "$shell", +                                        "$homedir", +                                        "$expr"); +        # TODO workaround to get first cell at least until we don't +        # a cast from YItem +        $item->setLabel( $username ); +        $itemColl->push($item); +        $item->DISOWN(); +    } +    $self->get_widget('table')->addItems($itemColl); +    my $item = $self->get_widget('table')->selectedItem(); +    $self->get_widget('table')->selectItem($item, 0) if $item; +    $self->dialog->recalcLayout(); +    $self->dialog->doneMultipleChanges();  +    $self->_refreshActions(); +    $self->get_widget('table')->setImmediateMode(1); +} + +#============================================================= + +=head2 _refreshGroups + +=head3 INPUT + +    $self: this object + +=head3 DESCRIPTION + +    This method refresh group info into Group tab widget. +    Note this function is meant for internal use only + +=cut + +#============================================================= +sub _refreshGroups { +    my $self = shift; + +    my $strfilt = $self->get_widget('filter')->value(); +    my $filtergroups = $self->get_widget('filter_system')->isChecked(); + +    my $groups; +    defined $self->ctx and $groups = $self->ctx->GroupsEnumerateFull; + +    $self->dialog->startMultipleChanges(); +    #for some reasons QT send an event using table->selectItem() +    # WA remove notification immediate +    $self->get_widget('table')->setImmediateMode(0); +    $self->get_widget('table')->deleteAllItems();     +    my @GroupReal; +  LOOP: foreach my $g (@$groups) { +        next LOOP if $filtergroups && $g->Gid($self->USER_GetValue) <= 499 || $g->Gid($self->USER_GetValue) == 65534; +        push @GroupReal, $g if $g->GroupName($self->USER_GetValue) =~ /^\Q$strfilt/; +    } + +    my $itemColl = new yui::YItemCollection; +    foreach my $g (@GroupReal) { +     my $a = $g->GroupName($self->USER_GetValue); +        #my $group = $ctx->LookupGroupById($a); +        my $u_b_g = $a && $self->ctx->EnumerateUsersByGroup($a); +        my $listUbyG  = join(',', @$u_b_g); +        my $group_id  = $g->Gid($self->USER_GetValue); +        my $groupname = $g->GroupName($self->USER_GetValue); +        my $item      = new yui::YTableItem ("$groupname", +                                             "$group_id", +                                             "$listUbyG"); +        $item->setLabel( $groupname ); +        $itemColl->push($item); +        $item->DISOWN(); +    } + +    $self->get_widget('table')->addItems($itemColl); +    my $item = $self->get_widget('table')->selectedItem(); +    $self->get_widget('table')->selectItem($item, 0) if $item; +    $self->dialog->recalcLayout(); +    $self->dialog->doneMultipleChanges();  +    $self->_refreshActions(); +    $self->get_widget('table')->setImmediateMode(1); +} + + +#============================================================= + +=head2 _getUserInfo + +=head3 INPUT + +    $self: this object + +=head3 OUTPUT + +    %userData:  selected user info as: +                username:      username +                full_name:      full name of user +                shell:         shell used   +                homedir:       home dir path +                UID:           User identifier +                acc_check_exp: account expiration enabling +                acc_expy:      account expiration year +                acc_expm:      account expiration month +                acc_expd:      account expiration day +                lockuser:      account locked +                pwd_check_exp: password expiration enabling +                pwd_exp_min:   days before changing password  +                               is allowed +                pwd_exp_max:   days before changing password  +                               is required +                pwd_exp_warn:  warning days before changing +                pwd_exp_inact: days before account becomes  +                               inact +                members:       Array containing groups the user +                               belongs to. +                primary_group: primary group ID for the user + +=head3 DESCRIPTION + +    Retrieves the selected user info from the system +    Note that acc_expy,  acc_expm and acc_expd are valid if  +    acc_check_exp is enabled. +    Note that pwd_exp_min, pwd_exp_max, pwd_exp_warn, +    pwd_exp_inact are valid if pwd_check_exp is enabled. + +=cut + +#============================================================= + +sub _getUserInfo { +    my $self = shift; + +    my $label = $self->_skipShortcut($self->get_widget('tabs')->selectedItem()->label()); +    if ($label ne N("Users") ) { +        return undef; +    } + +    my $item = $self->get_widget('table')->selectedItem(); +    if (! $item) { +       return undef; +    } +     +    my %userData; +    $userData{username}  = $item->label();  +    my $userEnt = $self->ctx->LookupUserByName($userData{username});  + +    my $s                = $userEnt->Gecos($self->USER_GetValue); +    c::set_tagged_utf8($s); +    $userData{full_name} = $s; +    $userData{shell}     = $userEnt->LoginShell($self->USER_GetValue); +    $userData{homedir}   = $userEnt->HomeDir($self->USER_GetValue); +    $userData{UID}       = $userEnt->Uid($self->USER_GetValue); + +    # default expiration time +    my ($day, $mo, $ye)      = (localtime())[3, 4, 5]; +    $userData{acc_expy}      = $ye+1900; +    $userData{acc_expm}      = $mo+1; +    $userData{acc_expd}      = $day; +    $userData{acc_check_exp} = 0; +    my $expire               = $userEnt->ShadowExpire($self->USER_GetValue); +    if ($expire && $expire != -1) { +        my $times                = TimeOfArray($expire, 1);  +        $userData{acc_expy}      = $times->{year}; +        $userData{acc_expm}      = $times->{month}; +        $userData{acc_expd}      = $times->{dayint}; +        $userData{acc_check_exp} = 1; +    } + +    # user password are not retrieved if admin wants +    # to change it has to insert a new one +    $userData{password}      = undef; +    $userData{password1}     = undef; +    # Check if user account is locked  + +    $userData{lockuser}      = $self->ctx->IsLocked($userEnt); + +    $userData{icon_face}     = AdminPanel::Shared::Users::GetFaceIcon($userData{username}); +    $userData{pwd_check_exp} = 0; +    $userData{pwd_exp_min}   = $userEnt->ShadowMin($self->USER_GetValue);  +    $userData{pwd_exp_max}   = $userEnt->ShadowMax($self->USER_GetValue);  +    $userData{pwd_exp_warn}  = $userEnt->ShadowWarn($self->USER_GetValue); +    $userData{pwd_exp_inact} = $userEnt->ShadowInact($self->USER_GetValue); +  +    if ($userData{pwd_exp_min} && $userData{pwd_exp_min} != -1 ||  +        $userData{pwd_exp_max} && $userData{pwd_exp_max} != 99999 ||  +        $userData{pwd_exp_warn} && $userData{pwd_exp_warn} != 7 && $userData{pwd_exp_warn} != -1 ||  +        $userData{pwd_exp_inact} && $userData{pwd_exp_inact} != -1) { +        $userData{pwd_check_exp} = 1; +    } + +    $userData{members}       = $self->ctx->EnumerateGroupsByUser($userData{username}); +    $userData{primary_group} = $userEnt->Gid($self->USER_GetValue); +     +    return %userData; + +} + +#============================================================= + +=head2 _getUserInfo + +=head3 INPUT + +    $self: this object + +=head3 OUTPUT + +    %groupData:  selected group info as: +    $groupname:  group name +    $members:    users that are members of this group + +=head3 DESCRIPTION + +    Retrieves the selected group info from the system + +=cut + +#============================================================= + +sub _getGroupInfo { +    my $self = shift; + +    my $label = $self->_skipShortcut($self->get_widget('tabs')->selectedItem()->label()); +    if ($label ne N("Groups") ) { +        return undef; +    } + +    my $item = $self->get_widget('table')->selectedItem(); +    if (! $item) { +       return undef; +    } +     +    my %groupData; +    $groupData{start_groupname} = $item->label(); +    $groupData{groupname}       = $item->label(); + +    my $groupEnt = $self->ctx->LookupGroupByName($groupData{groupname});  +    $groupData{members} = $self->ctx->EnumerateUsersByGroup($groupData{groupname}); +     +    return %groupData; + +} + +sub _storeDataFromGroupEditPreviousTab { +    my ($self, %groupData) = @_; + +    my $previus_tab = $self->get_edit_tab_widget('edit_tab_label'); +    if (!$previus_tab) { +        return %groupData; +    } +    elsif ($previus_tab eq $groupEditLabel{group_data}) { +        $groupData{groupname} = $self->get_edit_tab_widget('groupname')->value(); +    } +    elsif ($previus_tab eq $groupEditLabel{group_users}) { +        my $tbl = $self->get_edit_tab_widget('members'); +        $groupData{members} = undef; +        my @members;  +        my $i; +        for($i=0;$i<$tbl->itemsCount();$i++) { +            push (@members, $tbl->item($i)->label()) if $tbl->toCBYTableItem($tbl->item($i))->checked(); +        } +        $groupData{members} = [ @members ]; +    } + +    return %groupData;        +} + + +sub _storeDataFromUserEditPreviousTab { +    my ($self, %userData) = @_; + +    my $previus_tab = $self->get_edit_tab_widget('edit_tab_label'); +    if (!$previus_tab) { +        return %userData; +    } +    elsif ($previus_tab eq $userEditLabel{user_data}) { +        $userData{full_name} = $self->get_edit_tab_widget('full_name')->value(); +        $userData{username}  = $self->get_edit_tab_widget('login_name')->value() ;  +        $userData{shell}     = $self->get_edit_tab_widget('login_shell')->value(); +        $userData{homedir}   = $self->get_edit_tab_widget('homedir')->value(); +        my $passwd           = $self->get_edit_tab_widget('password')->value(); +        $userData{password}  = $passwd; +        $passwd              = $self->get_edit_tab_widget('password1')->value(); +        $userData{password1} = $passwd; +    } +    elsif ($previus_tab eq $userEditLabel{account_info}) { +        $userData{acc_check_exp} = $self->get_edit_tab_widget('acc_check_exp')->value(); +        $userData{acc_expy}      = $self->get_edit_tab_widget('acc_expy')->value(); +        $userData{acc_expm}      = $self->get_edit_tab_widget('acc_expm')->value(); +        $userData{acc_expd}      = $self->get_edit_tab_widget('acc_expd')->value(); +        $userData{lockuser}      = $self->get_edit_tab_widget('lockuser')->value(); +        $userData{icon_face}     = $self->get_edit_tab_widget('icon_face')->label(); +    } +    elsif ($previus_tab eq $userEditLabel{password_info}) { +        $userData{pwd_check_exp} = $self->get_edit_tab_widget('pwd_check_exp')->value(); +        $userData{pwd_exp_min}   = $self->get_edit_tab_widget('pwd_exp_min')->value();  +        $userData{pwd_exp_max}   = $self->get_edit_tab_widget('pwd_exp_max')->value(); +        $userData{pwd_exp_warn}  = $self->get_edit_tab_widget('pwd_exp_warn')->value(); +        $userData{pwd_exp_inact} = $self->get_edit_tab_widget('pwd_exp_inact')->value(); +    } +    elsif ($previus_tab eq $userEditLabel{groups}) { +        my $tbl = $self->get_edit_tab_widget('members'); +        $userData{members} = undef; +        my @members;  +        my $i; +        for($i=0;$i<$tbl->itemsCount();$i++) { +            push (@members, $tbl->item($i)->label()) if $tbl->toCBYTableItem($tbl->item($i))->checked(); +        } +        $userData{members} = [ @members ]; + +        if ($self->get_edit_tab_widget('primary_group')->selectedItem()) { +            my $Gent      = $self->ctx->LookupGroupByName($self->get_edit_tab_widget('primary_group')->selectedItem()->label()); +            my $primgroup = $Gent->Gid($self->USER_GetValue); + +            $userData{primary_group} = $primgroup; +        } +        else { +            $userData{primary_group} = -1; +        } +    } + +    return %userData;        +} + +#============================================================= + +=head2 _userDataTabWidget + +=head3 INPUT + +    $self:        this object +    $dialog:      YUI dialog that owns the YUI replace point +    $replace_pnt: YUI replace point, needed to add a new tab +                  widget +    %userData:    hash containing user data info, tabs are  +                  removed and added again on selection, so +                  data must be saved outside of widgets. +    $previus_tab: previous tab widget label, needed to store +                  user data from the old tab before removing +                  it, if user changed something.  + +=head3 OUTPUT + +    %userDataWidget: hash containing new YUI widget objects +                     such as: +                     retunred onject from _buildUserData and +                     homedir.  + +=head3 DESCRIPTION + +    This internal method removes old tab widget saving its +    relevant data into userData and creates new selected table +    to be shown. + +=cut + +#============================================================= +sub _userDataTabWidget { +    my ($self, $dialog, $replace_pnt, %userData) = @_; +      +    my $factory  = yui::YUI::widgetFactory; + +    $dialog->startMultipleChanges(); + +    $replace_pnt->deleteChildren(); +    my $layout         = $factory->createVBox($replace_pnt); +    my %userDataWidget = $self->_buildUserData($layout, $userData{shell}); + +    ## user 'login name' +    my $align                = $factory->createRight($layout); +    my $hbox                 = $factory->createHBox($align); +    my $label                = $factory->createLabel($hbox, N("Home:") ); +    $userDataWidget{homedir} = $factory->createInputField($hbox, "", 0); +    $label->setWeight($yui::YD_HORIZ, 1); +    $userDataWidget{homedir}->setWeight($yui::YD_HORIZ, 2); + +    # fill data into widgets +    ## +    # full_name, login_name, password, password1, +    #                login_shell +    $userDataWidget{full_name}->setValue($userData{full_name}); +    $userDataWidget{login_name}->setValue($userData{username}); +    $userDataWidget{password}->setValue($userData{password})  if $userData{password};  +    $userDataWidget{password1}->setValue($userData{password1}) if $userData{password1}; +    $userDataWidget{homedir}->setValue($userData{homedir}); + +    $replace_pnt->showChild(); +    $dialog->recalcLayout(); +    $dialog->doneMultipleChanges(); +     +    return %userDataWidget; +} + + +#============================================================= + +=head2 _groupDataTabWidget + +=head3 INPUT + +    $self:        this object +    $dialog:      YUI dialog that owns the YUI replace point +    $replace_pnt: YUI replace point, needed to add a new tab +                  widget +    %groupData:   hash containing group data info, tabs are  +                  removed and added again on selection, so +                  data must be saved outside of widgets. +    $previus_tab: previous tab widget label, needed to store +                  group data from the old tab before removing +                  it, if user changed something.  + +=head3 OUTPUT + +    %groupDataWidget: hash containing new YUI widget objects +                      such as: +                       groupname.  + +=head3 DESCRIPTION + +    This internal method removes old tab widget saving its +    relevant data into groupData and creates new selected table +    to be shown. + +=cut + +#============================================================= +sub _groupDataTabWidget { +    my ($self, $dialog, $replace_pnt, %groupData) = @_; +      +    my $factory  = yui::YUI::widgetFactory; + +    $dialog->startMultipleChanges(); + +    $replace_pnt->deleteChildren(); +    my $layout               = $factory->createVBox($replace_pnt); + +    my %groupDataWidget; + +    ## user 'login name' +    my $align                = $factory->createRight($layout); +    my $hbox                 = $factory->createHBox($align); +    my $label                = $factory->createLabel($hbox, N("Group Name:") ); +    $groupDataWidget{groupname} = $factory->createInputField($hbox, "", 0); +    $label->setWeight($yui::YD_HORIZ, 1); +    $groupDataWidget{groupname}->setWeight($yui::YD_HORIZ, 2); + +    $groupDataWidget{groupname}->setValue($groupData{groupname}); + +    $replace_pnt->showChild(); +    $dialog->recalcLayout(); +    $dialog->doneMultipleChanges(); +     +    return %groupDataWidget; +} + + +sub _userAccountInfoTabWidget { +    my ($self, $dialog, $replace_pnt, %userData) = @_; + +    my $factory  = yui::YUI::widgetFactory; +     +    $dialog->startMultipleChanges(); + +    $replace_pnt->deleteChildren(); +    my $layout         = $factory->createVBox($replace_pnt); + +    my %userAccountWidget; +    $userAccountWidget{acc_check_exp} = $factory->createCheckBoxFrame($layout, N("Enable account expiration"), 1); +    my $align                         = $factory->createRight($userAccountWidget{acc_check_exp}); +    my $hbox                          = $factory->createHBox($align);     +    my $label                         = $factory->createLabel($hbox, N("Account expires (YYYY-MM-DD):")); +    $userAccountWidget{acc_expy}      = $factory->createIntField($hbox, "", 1970, 9999, $userData{acc_expy}); +    $userAccountWidget{acc_expm}      = $factory->createIntField($hbox, "", 1, 12, $userData{acc_expm}); +    $userAccountWidget{acc_expd}      = $factory->createIntField($hbox, "", 1, 31, $userData{acc_expd}); +    $userAccountWidget{acc_check_exp}->setValue($userData{acc_check_exp}); +    $label->setWeight($yui::YD_HORIZ, 2); +    $align                            = $factory->createLeft($layout); +    $userAccountWidget{lockuser}      = $factory->createCheckBox($align, N("Lock User Account"), $userData{lockuser}); +     +    $align                            = $factory->createLeft($layout); +    $hbox                             = $factory->createHBox($align);  +    $label                            = $factory->createLabel($hbox, N("Click on the icon to change it")); +    $userAccountWidget{icon_face}     = $factory->createPushButton($hbox, ""); +    $userAccountWidget{icon_face}->setIcon(AdminPanel::Shared::Users::face2png($userData{icon_face}));  +    $userAccountWidget{icon_face}->setLabel($userData{icon_face}); +     +    $replace_pnt->showChild(); +    $dialog->recalcLayout(); +    $dialog->doneMultipleChanges(); +     +    return %userAccountWidget; +} + + +sub _userPasswordInfoTabWidget { +    my ($self, $dialog, $replace_pnt, %userData) = @_; + +    my $factory  = yui::YUI::widgetFactory; +     +    $dialog->startMultipleChanges(); + +    $replace_pnt->deleteChildren(); +    my $layout  = $factory->createVBox($replace_pnt); + +    my %userPasswordWidget; +    my $userEnt = $self->ctx->LookupUserByName($userData{username});  +    my $lastchg = $userEnt->ShadowLastChange($self->USER_GetValue); + +    my $align   = $factory->createLeft($layout); +    my $hbox    = $factory->createHBox($align);     +    my $label   = $factory->createLabel($hbox, N("User last changed password on: ")); +    my $dayStr  = $factory->createLabel($hbox, ""); +    my $month   = $factory->createLabel($hbox, ""); +    my $dayInt  = $factory->createLabel($hbox, ""); +    my $year    = $factory->createLabel($hbox, ""); +    if ($lastchg) { +        my $times = TimeOfArray($lastchg, 0);  +        $dayStr->setValue($times->{daystr}); +        $month->setValue($times->{month}); +        $dayInt->setValue($times->{dayint}); +        $year->setValue($times->{year}); +    } +     +    $userPasswordWidget{pwd_check_exp} = $factory->createCheckBoxFrame($layout, N("Enable Password Expiration"), 1); +    $layout  = $factory->createVBox($userPasswordWidget{pwd_check_exp}); +    $align   = $factory->createLeft($layout); +    $hbox    = $factory->createHBox($align); +    $label   = $factory->createLabel($hbox, N("Days before change allowed:")); +    $userPasswordWidget{pwd_exp_min} = $factory->createInputField($hbox, "", 0); +    $userPasswordWidget{pwd_exp_min}->setValue("$userData{pwd_exp_min}"); +    $label->setWeight($yui::YD_HORIZ, 1); +    $userPasswordWidget{pwd_exp_min}->setWeight($yui::YD_HORIZ, 2); +     +    $align   = $factory->createLeft($layout); +    $hbox    = $factory->createHBox($align); +    $label   = $factory->createLabel($hbox, N("Days before change required:")); +    $userPasswordWidget{pwd_exp_max} = $factory->createInputField($hbox, "", 0); +    $userPasswordWidget{pwd_exp_max}->setValue("$userData{pwd_exp_max}"); +    $label->setWeight($yui::YD_HORIZ, 1); +    $userPasswordWidget{pwd_exp_max}->setWeight($yui::YD_HORIZ, 2); + +    $align   = $factory->createLeft($layout); +    $hbox    = $factory->createHBox($align); +    $label   = $factory->createLabel($hbox, N("Days warning before change:")); +    $userPasswordWidget{pwd_exp_warn} = $factory->createInputField($hbox, "", 0); +    $userPasswordWidget{pwd_exp_warn}->setValue("$userData{pwd_exp_warn}"); +    $label->setWeight($yui::YD_HORIZ, 1); +    $userPasswordWidget{pwd_exp_warn}->setWeight($yui::YD_HORIZ, 2); + +    $align   = $factory->createLeft($layout); +    $hbox    = $factory->createHBox($align); +    $label   = $factory->createLabel($hbox, N("Days before account inactive:")); +    $userPasswordWidget{pwd_exp_inact} = $factory->createInputField($hbox, "", 0); +    $userPasswordWidget{pwd_exp_inact}->setValue("$userData{pwd_exp_inact}"); +    $label->setWeight($yui::YD_HORIZ, 1); +    $userPasswordWidget{pwd_exp_inact}->setWeight($yui::YD_HORIZ, 2); + +    $userPasswordWidget{pwd_check_exp}->setValue($userData{pwd_check_exp}); + +    $replace_pnt->showChild(); +    $dialog->recalcLayout(); +    $dialog->doneMultipleChanges(); +     +    return %userPasswordWidget; +} + +sub _groupUsersTabWidget { +    my ($self, $dialog, $replace_pnt, %groupData) = @_; + +    my $factory  = yui::YUI::widgetFactory; +    my $mageiaPlugin = "mga"; +    my $mgaFactory   = yui::YExternalWidgets::externalWidgetFactory($mageiaPlugin); +    $mgaFactory      = yui::YMGAWidgetFactory::getYMGAWidgetFactory($mgaFactory); +     +    $dialog->startMultipleChanges(); + +    $replace_pnt->deleteChildren(); + +    my %groupUsersWidget; + +    my $layout   = labeledFrameBox($replace_pnt, N("Select the users to join this group:")); + +    my $yTableHeader = new yui::YTableHeader(); +    $yTableHeader->addColumn("", $yui::YAlignBegin); +    $yTableHeader->addColumn(N("User"), $yui::YAlignBegin); + +    $groupUsersWidget{members} = $mgaFactory->createCBTable($layout, $yTableHeader, $yui::YCBTableCheckBoxOnFirstColumn); + +    my $groupEnt = $self->ctx->LookupGroupByName($groupData{groupname});  +    my $users  = $self->ctx->UsersEnumerate; +    my @susers = sort(@$users); + +    my $itemCollection = new yui::YItemCollection; +    my $members = $groupData{members}; +    foreach my $user (@susers) { +        my $item = new yui::YCBTableItem($user); +        $item->check(member($user, @$members)); +        $item->setLabel($user); +        $itemCollection->push($item); +        $item->DISOWN();     +    }     +    $groupUsersWidget{members}->addItems($itemCollection); + +    $replace_pnt->showChild(); +    $dialog->recalcLayout(); +    $dialog->doneMultipleChanges(); +     +    return %groupUsersWidget; +} + +sub _userGroupsTabWidget { +    my ($self, $dialog, $replace_pnt, %userData) = @_; + +    my $factory  = yui::YUI::widgetFactory; +    my $mageiaPlugin = "mga"; +    my $mgaFactory   = yui::YExternalWidgets::externalWidgetFactory($mageiaPlugin); +    $mgaFactory      = yui::YMGAWidgetFactory::getYMGAWidgetFactory($mgaFactory); +     +    $dialog->startMultipleChanges(); + +    $replace_pnt->deleteChildren(); + +    my %userGroupsWidget; +    my $userEnt = $self->ctx->LookupUserByName($userData{username});  +    my $lastchg = $userEnt->ShadowLastChange($self->USER_GetValue); + +    my $layout   = labeledFrameBox($replace_pnt, N("Select groups that the user will be member of:")); + +    my $yTableHeader = new yui::YTableHeader(); +    $yTableHeader->addColumn("", $yui::YAlignBegin); +    $yTableHeader->addColumn(N("Group"), $yui::YAlignBegin); + +    $userGroupsWidget{members} = $mgaFactory->createCBTable($layout, $yTableHeader, $yui::YCBTableCheckBoxOnFirstColumn); + +    my $grps = $self->ctx->GroupsEnumerate; +    my @sgroups = sort @$grps; +  +    my $itemCollection = new yui::YItemCollection; +    my $members = $userData{members}; +    foreach my $group (@sgroups) { +        my $item = new yui::YCBTableItem($group); +        $item->check(member($group, @$members)); +        $item->setLabel($group); +        $itemCollection->push($item); +        $item->DISOWN();     +    }     +    $userGroupsWidget{members}->addItems($itemCollection); +    $userGroupsWidget{members}->setNotify(1); +    my $primgroup = ''; +    if ($userData{primary_group} != -1) { +        my $Gent      = $self->ctx->LookupGroupById($userData{primary_group}); +        $primgroup    = $Gent->GroupName($self->USER_GetValue); +    } + +    my $align   = $factory->createLeft($layout); +    my $hbox    = $factory->createHBox($align);     +    my $label   = $factory->createLabel($hbox, N("Primary Group")); +    $userGroupsWidget{primary_group} = $factory->createComboBox($hbox, "", 0); +    my $itemColl = new yui::YItemCollection; +    foreach my $member (@$members) { +            my $item = new yui::YItem ($member, 0); +            $item->setSelected(1) if ($item->label() eq $primgroup); +            $itemColl->push($item); +            $item->DISOWN(); +    } +    $userGroupsWidget{primary_group}->addItems($itemColl); +    $label->setWeight($yui::YD_HORIZ, 1); +    $userGroupsWidget{primary_group}->setWeight($yui::YD_HORIZ, 2); + +    $replace_pnt->showChild(); +    $dialog->recalcLayout(); +    $dialog->doneMultipleChanges(); +     +    return %userGroupsWidget; +} + +sub _groupEdit_Ok { +    my ($self, %groupData) = @_; + +    # update last changes if any  +    %groupData = $self->_storeDataFromGroupEditPreviousTab(%groupData); +     +    my ($continue, $errorString) = valid_groupname($groupData{groupname}); +    if (!$continue) { +        AdminPanel::Shared::msgBox($errorString) if ($errorString); +        return $continue; +    } +    my $groupEnt = $self->ctx->LookupGroupByName($groupData{start_groupname});  +    if ($groupData{start_groupname} ne $groupData{groupname}) {  +        $groupEnt->GroupName($groupData{groupname});  +    } + +    my $members = $groupData{members}; +    my $gid     = $groupEnt->Gid($self->USER_GetValue); +    my $users   = $self->ctx->UsersEnumerate; +    my @susers  = sort(@$users); + +    foreach my $user (@susers) { +        my $uEnt = $self->ctx->LookupGroupByName($user); +        if ($uEnt) { +            my $ugid = $uEnt->Gid($self->USER_GetValue); +            my $m    = $self->ctx->EnumerateUsersByGroup($groupData{start_groupname}); +            if (member($user, @$members)) { +                if (!$self->_inArray($user, $m)) { +                    if ($ugid != $gid) { +                        eval { $groupEnt->MemberName($user,1) }; +                    } +                } +            } +            else { +                if ($self->_inArray($user, $m)) { +                    if ($ugid == $gid) { +                        AdminPanel::Shared::msgBox(N("You cannot remove user '%s' from their primary group", $user)); +                        return 0; +                    } +                    else { +                        eval { $groupEnt->MemberName($user,2) }; +                    } +                } +            } +        } +    }     + +    $self->ctx->GroupModify($groupEnt); +    $self->_refresh(); + +    return 1; +} + +sub _userEdit_Ok { +    my ($self, %userData) = @_; + +    # update last changes if any  +    %userData = $self->_storeDataFromUserEditPreviousTab(%userData); +     +    my ($continue, $errorString) = valid_username($userData{username}); +    if (!$continue) { +        AdminPanel::Shared::msgBox($errorString) if ($errorString); +        return $continue; +    } + +    if ( $userData{password} ne $userData{password1}) { +        AdminPanel::Shared::msgBox(N("Password Mismatch")); +        return 0; +    } +    my $userEnt = $self->ctx->LookupUserByName($userData{username});  +    if ($userData{password} ne '') { +        my $sec = security::level::get(); +        if ($sec > 3 && length($userData{password}) < 6) { +            AdminPanel::Shared::msgBox(N("This password is too simple. \n Good passwords should be > 6 characters")); +            return 0; +        } +        $self->ctx->UserSetPass($userEnt, $userData{password}); +    } + +    $userEnt->UserName($userData{username}); +    $userEnt->Gecos($userData{full_name}); +    $userEnt->HomeDir($userData{homedir}); +    $userEnt->LoginShell($userData{shell}); +    my $username = $userEnt->UserName($self->USER_GetValue); +    my $grps = $self->ctx->GroupsEnumerate; +    my @sgroups = sort @$grps; +  +    my $members = $userData{members}; +    foreach my $group (@sgroups) { + +        my $gEnt = $self->ctx->LookupGroupByName($group); +        my $ugid = $gEnt->Gid($self->USER_GetValue); +        my $m    = $gEnt->MemberName(1,0); +        if (member($group, @$members)) { +            if (!$self->_inArray($username, $m) && $userData{primary_group} != $ugid) { +                eval { $gEnt->MemberName($username, 1) }; +                $self->ctx->GroupModify($gEnt); +            } +        } +        else { +            if ($self->_inArray($username, $m)) { +                eval { $gEnt->MemberName($username, 2) }; +                $self->ctx->GroupModify($gEnt); +            } +        } +    } +    if ($userData{primary_group} == -1) { +        AdminPanel::Shared::msgBox(N("Please select at least one group for the user")); +        return 0; +    } +    $userEnt->Gid($userData{primary_group}); + +    if ($userData{acc_check_exp}) { +        my $yr = $userData{acc_expy};  +        my $mo = $userData{acc_expm}; +        my $dy = $userData{acc_expd}; +        if (!ValidInt($yr, $dy, $mo)) { +            AdminPanel::Shared::msgBox(N("Please specify Year, Month and Day \n for Account Expiration ")); +            return 0; +        } +        my $Exp = ConvTime($dy, $mo, $yr); +        $userEnt->ShadowExpire($Exp); +    } +    else {  +        $userEnt->ShadowExpire(ceil(-1))  +    } + +    if ($userData{pwd_check_exp}) { +        my $allowed = int($userData{pwd_exp_min}); +        my $required = int($userData{pwd_exp_max}); +        my $warning = int($userData{pwd_exp_warn}); +        my $inactive = int($userData{pwd_exp_inact}); +        if ($allowed && $required && $warning && $inactive) { +            $userEnt->ShadowMin($allowed); +            $userEnt->ShadowMax($required); +            $userEnt->ShadowWarn($warning); +            $userEnt->ShadowInact($inactive); +        } +        else { +            AdminPanel::Shared::msgBox(N("Please fill up all fields in password aging\n")); +            return 0; +        } +    } +    else { +        $userEnt->ShadowMin(-1); +        $userEnt->ShadowMax(99999); +        $userEnt->ShadowWarn(-1); +        $userEnt->ShadowInact(-1);  +    } +    +    $self->ctx->UserModify($userEnt); + +    if ($userData{lockuser}) { +        !$self->ctx->IsLocked($userEnt) and $self->ctx->Lock($userEnt); +    }  +    else {  +        $self->ctx->IsLocked($userEnt) and $self->ctx->UnLock($userEnt);  +    } +             +    defined $userData{icon_face} and AdminPanel::Shared::Users::addKdmIcon($userData{username}, $userData{icon_face}); +    $self->_refresh(); + +    return 1; +} + + + +sub _editUserDialog { +    my $self = shift; + +    my $dontcreatehomedir = 0;  +    my $is_system = 0; + +    ## push application title +    my $appTitle = yui::YUI::app()->applicationTitle(); +    ## set new title to get it in dialog +    yui::YUI::app()->setApplicationTitle(N("Edit User")); +     +    my $factory  = yui::YUI::widgetFactory; +    my $optional = yui::YUI::optionalWidgetFactory; + +    my $dlg      = $factory->createPopupDialog(); +    my $layout   = $factory->createVBox($dlg); +     +    my %tabs; +    if ($optional->hasDumbTab()) { +        my $hbox = $factory->createHBox($layout); +        my $align = $factory->createHCenter($hbox); +        $tabs{widget} = $optional->createDumbTab($align); + +        $tabs{user_data} = new yui::YItem($userEditLabel{user_data}); +        $tabs{user_data}->setSelected(); +        $tabs{used}      = $tabs{user_data}->label(); +        $tabs{widget}->addItem( $tabs{user_data} ); +        $tabs{user_data}->DISOWN(); + +        $tabs{account_info} = new yui::YItem($userEditLabel{account_info}); +        $tabs{widget}->addItem( $tabs{account_info} ); +        $tabs{account_info}->DISOWN(); + +        $tabs{password_info} = new yui::YItem($userEditLabel{password_info}); +        $tabs{widget}->addItem( $tabs{password_info} ); +        $tabs{password_info}->DISOWN(); + +        $tabs{groups} = new yui::YItem($userEditLabel{groups}); +        $tabs{widget}->addItem( $tabs{groups} ); +        $tabs{groups}->DISOWN(); + +        my $vbox           = $factory->createVBox($tabs{widget}); +        $align             = $factory->createLeft($vbox); +        $tabs{replace_pnt} = $factory->createReplacePoint($align); +         +        $hbox            = $factory->createHBox($vbox); +        $align           = $factory->createRight($hbox); +        my $cancelButton = $factory->createPushButton($align, N("Cancel")); +        my $okButton     = $factory->createPushButton($hbox,  N("Ok")); +         +        my %userData        = $self->_getUserInfo(); +        # userData here should be tested because it could be undef +         +        # Useful entry point for the current edit user/group tab widget  +        $self->set_edit_tab_widget( $self->_userDataTabWidget($dlg, $tabs{replace_pnt}, %userData) ); +        $self->set_edit_tab_widget( edit_tab_label => $userEditLabel{user_data}); + +        while(1) { +            my $event     = $dlg->waitForEvent(); +            my $eventType = $event->eventType(); +             +            #event type checking +            if ($eventType == $yui::YEvent::CancelEvent) { +                last; +            } +            elsif ($eventType == $yui::YEvent::MenuEvent) { +                ### MENU ### +                my $item = $event->item(); +                if ($item->label() eq $tabs{user_data}->label()) { +                    %userData = $self->_storeDataFromUserEditPreviousTab(%userData); +                    my %edit_tab = $self->_userDataTabWidget($dlg, $tabs{replace_pnt}, %userData ); +                    $self->edit_tab_widgets( {} ); +                    $self->set_edit_tab_widget(%edit_tab); +                    $self->set_edit_tab_widget( edit_tab_label => $userEditLabel{user_data}); +                } +                elsif ($item->label() eq $tabs{account_info}->label()) { +                    %userData = $self->_storeDataFromUserEditPreviousTab(%userData); +                    my %edit_tab = $self->_userAccountInfoTabWidget($dlg, $tabs{replace_pnt}, %userData ); +                    $self->edit_tab_widgets( {} ); +                    $self->set_edit_tab_widget(%edit_tab); +                    $self->set_edit_tab_widget( edit_tab_label => $userEditLabel{account_info}); +                } +                elsif ($item->label() eq $tabs{password_info}->label()) { +                    %userData = $self->_storeDataFromUserEditPreviousTab(%userData); +                    my %edit_tab = $self->_userPasswordInfoTabWidget($dlg, $tabs{replace_pnt}, %userData ); +                    $self->edit_tab_widgets( {} ); +                    $self->set_edit_tab_widget(%edit_tab); +                    $self->set_edit_tab_widget( edit_tab_label => $userEditLabel{password_info}); +                } +                elsif ($item->label() eq $tabs{groups}->label()) { +                    %userData = $self->_storeDataFromUserEditPreviousTab(%userData); +                    my %edit_tab = $self->_userGroupsTabWidget($dlg, $tabs{replace_pnt}, %userData ); +                    $self->edit_tab_widgets( {} ); +                    $self->set_edit_tab_widget(%edit_tab); +                    $self->set_edit_tab_widget( edit_tab_label => $userEditLabel{groups}); +                } +            } +            elsif ($eventType == $yui::YEvent::WidgetEvent) { +                ### widget  +                my $widget = $event->widget(); +                if ($widget == $cancelButton) { +                    last; +                } +                elsif ($widget == $okButton) { +                    ## save changes +                    if ($self->_userEdit_Ok(%userData)) { +                        last; +                    } +                } +# last: managing tab widget events +                else { +                    my $current_tab = $self->get_edit_tab_widget('edit_tab_label'); +                    if ($current_tab && $current_tab eq $userEditLabel{account_info}) { +                        if ($widget == $self->get_edit_tab_widget('icon_face')) { +                            my $iconLabel = $self->_skipShortcut($self->get_edit_tab_widget('icon_face')->label()); +                            my $nextIcon = GetFaceIcon($iconLabel, 1); +                            $self->get_edit_tab_widget('icon_face')->setLabel($nextIcon); +                            $self->get_edit_tab_widget('icon_face')->setIcon(AdminPanel::Shared::Users::face2png($nextIcon)); +                        } +                    }                     +                    elsif ($current_tab && $current_tab eq $userEditLabel{groups}) { +                        if ($widget == $self->get_edit_tab_widget('members')) { +                            my $item = $self->get_edit_tab_widget('members')->changedItem(); +                            if ($item) { +                                if ($item->checked()) { +                                    # add it to possible primary groups +                                    my $pgItem = new yui::YItem ($item->label(), 0); +                                    $self->get_edit_tab_widget('primary_group')->addItem($pgItem); +                                } +                                else { +                                    # remove it to possible primary groups +                                    $dlg->startMultipleChanges(); +                                    my $itemColl = new yui::YItemCollection; +                                    my $tbl = $self->get_edit_tab_widget('members'); +                                    for(my $i=0;$i < $tbl->itemsCount();$i++) { +                                        if ($tbl->toCBYTableItem($tbl->item($i))->checked()) { +                                            my $pgItem = new yui::YItem ($tbl->item($i)->label(), 0); +                                            my $Gent   = $self->ctx->LookupGroupById($userData{primary_group}); +                                            my $primgroup = $Gent->GroupName($self->USER_GetValue); +                                            $pgItem->setSelected(1) if ($pgItem->label() eq $primgroup); + +                                            $itemColl->push($pgItem); +                                            $pgItem->DISOWN(); +                                        }  +                                    } +                                    $self->get_edit_tab_widget('primary_group')->deleteAllItems(); +                                    $self->get_edit_tab_widget('primary_group')->addItems($itemColl); +                                    $dlg->recalcLayout(); +                                    $dlg->doneMultipleChanges(); +                                } +                            } +                        }                         +                    } +                } +            } +        } + +    } +    else { +        AdminPanel::Shared::warningMsgBox(N("Cannot create tab widgets")); +    } + +    destroy $dlg; +     +    #restore old application title +    yui::YUI::app()->setApplicationTitle($appTitle); + +} + +sub _editGroupDialog { +    my $self = shift; +  +    ## push application title +    my $appTitle = yui::YUI::app()->applicationTitle(); +    ## set new title to get it in dialog +    yui::YUI::app()->setApplicationTitle(N("Edit Group")); +     +    my $factory  = yui::YUI::widgetFactory; +    my $optional = yui::YUI::optionalWidgetFactory; + +    my $dlg      = $factory->createPopupDialog(); +    my $layout   = $factory->createVBox($dlg); +     +    my %tabs; +    if ($optional->hasDumbTab()) { +        my $hbox = $factory->createHBox($layout); +        my $align = $factory->createHCenter($hbox); +        $tabs{widget} = $optional->createDumbTab($align); + +        $tabs{group_data} = new yui::YItem($groupEditLabel{group_data}); +        $tabs{group_data}->setSelected(); +        $tabs{widget}->addItem( $tabs{group_data} ); +        $tabs{group_data}->DISOWN(); + +        $tabs{group_users} = new yui::YItem($groupEditLabel{group_users}); +        $tabs{widget}->addItem( $tabs{group_users} ); +        $tabs{group_users}->DISOWN(); + +        my $vbox           = $factory->createVBox($tabs{widget}); +        $align             = $factory->createLeft($vbox); +        $tabs{replace_pnt} = $factory->createReplacePoint($align); +         +        $hbox            = $factory->createHBox($vbox); +        $align           = $factory->createRight($hbox); +        my $cancelButton = $factory->createPushButton($align, N("Cancel")); +        my $okButton     = $factory->createPushButton($hbox,  N("Ok")); +         +        my %groupData        = $self->_getGroupInfo(); +        # groupData here should be tested because it could be undef + +# %groupData:  selected group info as: +# $groupname:  group name +# $members:    users that are members of this group + + +        # Useful entry point for the current edit user/group tab widget  +        $self->set_edit_tab_widget( $self->_groupDataTabWidget($dlg, $tabs{replace_pnt}, %groupData) ); +        $self->set_edit_tab_widget( edit_tab_label => $groupEditLabel{group_data}); + +        while(1) { +            my $event     = $dlg->waitForEvent(); +            my $eventType = $event->eventType(); +             +            #event type checking +            if ($eventType == $yui::YEvent::CancelEvent) { +                last; +            } +            elsif ($eventType == $yui::YEvent::MenuEvent) { +                ### MENU ### +                my $item = $event->item(); +                if ($item->label() eq $tabs{group_data}->label()) { +                    %groupData = $self->_storeDataFromGroupEditPreviousTab(%groupData); +                    my %edit_tab = $self->_groupDataTabWidget($dlg, $tabs{replace_pnt}, %groupData ); +                    $self->edit_tab_widgets( {} ); +                    $self->set_edit_tab_widget(%edit_tab); +                    $self->set_edit_tab_widget( edit_tab_label => $groupEditLabel{group_data}); +                } +                elsif ($item->label() eq $tabs{group_users}->label()) { +                    %groupData = $self->_storeDataFromGroupEditPreviousTab(%groupData); +                    my %edit_tab = $self->_groupUsersTabWidget($dlg, $tabs{replace_pnt}, %groupData ); +                    $self->edit_tab_widgets( {} ); +                    $self->set_edit_tab_widget(%edit_tab); +                    $self->set_edit_tab_widget( edit_tab_label => $groupEditLabel{group_users}); +                } +            } +            elsif ($eventType == $yui::YEvent::WidgetEvent) { +                ### widget  +                my $widget = $event->widget(); +                if ($widget == $cancelButton) { +                    last; +                } +                elsif ($widget == $okButton) { +                    ## save changes +                    if ($self->_groupEdit_Ok(%groupData)) { +                        last; +                    } +                } +            } +        } + +    } +    else { +        AdminPanel::Shared::warningMsgBox(N("Cannot create tab widgets")); +    } + +    destroy $dlg; +     +    #restore old application title +    yui::YUI::app()->setApplicationTitle($appTitle); + +} + +sub _editUserOrGroup { +    my $self = shift; + +    # TODO item management avoid label if possible +    my $label = $self->_skipShortcut($self->get_widget('tabs')->selectedItem()->label()); +    if ($label eq N("Users") ) { +        $self->_editUserDialog();  +    } +    else { +        $self->_editGroupDialog(); +    } +    $self->_refresh(); +} + + +sub _deleteUserOrGroup { +    my $self = shift; + +    # TODO item management avoid label if possible +    my $label = $self->_skipShortcut($self->get_widget('tabs')->selectedItem()->label()); +    if ($label eq N("Users") ) { +        $self->_deleteUserDialog(); +        $self->_refresh(); +    } +    else { +        $self->_deleteGroupDialog(); +        $self->_refresh(); +    } +} + + +sub _refresh { +    my $self = shift; + +    # TODO item management avoid label if possible +    my $label = $self->_skipShortcut($self->get_widget('tabs')->selectedItem()->label()); +    if ($label eq N("Users") ) { +        $self->_refreshUsers();  +    } +    else { +        $self->_refreshGroups(); +    } +# TODO xguest +#     RefreshXguest(1); +} + +# TODO context menu creation is missed in libyui  +sub _contextMenuActions { +    my $self = shift; + +    my $item = $self->get_widget('table')->selectedItem(); +    if ($item) { +    } +} + +sub _refreshActions { +    my $self = shift; +     +    my $item = $self->get_widget('table')->selectedItem(); +    $self->dialog->startMultipleChanges(); +    $self->get_widget('action_menu')->deleteAllItems(); +     +    # do we need to undef them first? +    $self->set_action_menu( +            add_user  => undef,  +            add_group => undef, +            edit      => undef, +            del       => undef, +            inst      => undef, +    ); +    $self->set_action_menu( +            add_user  => new yui::YMenuItem(N("Add User")),  +            add_group => new yui::YMenuItem(N("Add Group")), +            edit      => new yui::YMenuItem(N("&Edit")), +            del       => new yui::YMenuItem(N("&Delete")), +            inst      => new yui::YMenuItem(N("Install guest account")), +    ); + +    my $itemColl = new yui::YItemCollection; +    for my $pair ( $self->action_menu_pairs ) { +        my $menuItem = $pair->[1]; +        if ($pair->[0] eq 'edit' || $pair->[0] eq 'del') { +            if ($item) { +                $itemColl->push($menuItem); +            } +        } +        else { +            $itemColl->push($menuItem);            +        } +        $menuItem->DISOWN(); +    } +    $self->get_widget('action_menu')->addItems($itemColl); +    $self->get_widget('action_menu')->rebuildMenuTree(); +    if ($item) { +        $self->get_widget('edit')->setEnabled(); +        $self->get_widget('del')->setEnabled(); +    } +    else { +        $self->get_widget('edit')->setDisabled(); +        $self->get_widget('del')->setDisabled(); +    } + +    $self->dialog->doneMultipleChanges(); +} + + +sub manageUsersDialog { +    my $self = shift; + +    ## TODO fix for adminpanel +    my $pixdir = '/usr/share/userdrake/pixmaps/'; +    ## push application title +    my $appTitle = yui::YUI::app()->applicationTitle(); + +    ## set new title to get it in dialog +    yui::YUI::app()->setApplicationTitle($self->name); +    ## set icon if not already set by external launcher +    yui::YUI::app()->setApplicationIcon($self->icon); + + +    my $factory  = yui::YUI::widgetFactory; +    my $optional = yui::YUI::optionalWidgetFactory; +     + +    $self->dialog($factory->createMainDialog()); +    my $layout    = $factory->createVBox($self->dialog); + +    my $hbox_headbar = $factory->createHBox($layout); +    my $head_align_left = $factory->createLeft($hbox_headbar); +    my $head_align_right = $factory->createRight($hbox_headbar); +    my $headbar = $factory->createHBox($head_align_left); +    my $headRight = $factory->createHBox($head_align_right); + +    my %fileMenu = ( +            widget  => $factory->createMenuButton($headbar,N("File")), +            refresh => new yui::YMenuItem(N("Refresh")),  +            quit    => new yui::YMenuItem(N("&Quit")), +    ); + +    $fileMenu{ widget }->addItem($fileMenu{ refresh }); +    $fileMenu{ widget }->addItem($fileMenu{ quit }); +    $fileMenu{ widget }->rebuildMenuTree(); +    +    my $actionMenu = $factory->createMenuButton($headbar, N("Actions")); +    $actionMenu->DISOWN(); +     +    my %helpMenu = ( +            widget     => $factory->createMenuButton($headRight, N("&Help")), +            help       => new yui::YMenuItem(N("Help")),  +            report_bug => new yui::YMenuItem(N("Report Bug")), +            about      => new yui::YMenuItem(N("&About")), +    ); + +    while ( my ($key, $value) = each(%helpMenu) ) { +        if ($key ne 'widget' ) { +            $helpMenu{ widget }->addItem($value); +        } +    } +    $helpMenu{ widget }->rebuildMenuTree(); + +    my $hbox    = $factory->createHBox($layout); +    $hbox       = $factory->createHBox($factory->createLeft($hbox)); +    $self->set_widget( +        add_user    => $factory->createIconButton($hbox, $pixdir . 'user_add.png', N("Add User")), +        add_group   => $factory->createIconButton($hbox, $pixdir . 'group_add.png', N("Add Group")), +        edit        => $factory->createIconButton($hbox, $pixdir . 'user_conf.png', N("Edit")), +        del         => $factory->createIconButton($hbox, $pixdir . 'user_del.png', N("Delete")), +        refresh     => $factory->createIconButton($hbox, $pixdir . 'refresh.png', N("Refresh")), +        action_menu => $actionMenu, +    ); +     + +    $hbox                   = $factory->createHBox($layout); +    $head_align_left        = $factory->createLeft($hbox); +    $self->set_widget(filter_system => $factory->createCheckBox($head_align_left, N("Filter system users"), 1)); +                              $factory->createHSpacing($hbox, 3); +    $head_align_right       = $factory->createRight($hbox); +    $headRight              = $factory->createHBox($head_align_right); +                              $factory->createLabel($headRight, N("Search:")); +    $self->set_widget(filter         => $factory->createInputField($headRight, "", 0)); +    $self->set_widget(apply_filter  => $factory->createPushButton($headRight, N("Apply filter"))); +    $self->get_widget('filter')->setWeight($yui::YD_HORIZ, 2); +    $self->get_widget('apply_filter')->setWeight($yui::YD_HORIZ, 1); +    $self->get_widget('filter_system')->setNotify(1); + +    my %tabs; +    if ($optional->hasDumbTab()) { +        $hbox = $factory->createHBox($layout); +        my $align = $factory->createHCenter($hbox); +        $self->set_widget(tabs => $optional->createDumbTab($align)); +        $tabs{users} = new yui::YItem(N("Users")); +        $tabs{users}->setSelected(); +        $self->get_widget('tabs')->addItem( $tabs{users} ); +        $tabs{users}->DISOWN(); +        $tabs{groups} = new yui::YItem(N("Groups")); +        $self->get_widget('tabs')->addItem( $tabs{groups} ); +        $tabs{groups}->DISOWN(); +        my $vbox        = $factory->createVBox($self->get_widget('tabs')); +        $align          = $factory->createLeft($vbox); +        $self->set_widget(replace_pnt =>  $factory->createReplacePoint($align)); +        $self->_createUserTable(); +        $self->get_widget('table')->setImmediateMode(1); +        $self->get_widget('table')->DISOWN(); +    } +     +    $self->_refreshActions(); +     +    # main loop +    while(1) { +        my $event     = $self->dialog->waitForEvent(); +        my $eventType = $event->eventType(); +         +        #event type checking +        if ($eventType == $yui::YEvent::CancelEvent) { +            last; +        } +        elsif ($eventType == $yui::YEvent::MenuEvent) { +### MENU ### +            my $item = $event->item(); +            my $menuLabel = $item->label(); +            if ($menuLabel eq $fileMenu{ quit }->label())  { +                last; +            } +            elsif ($menuLabel eq $helpMenu{about}->label())  { +                my $license = translate($AdminPanel::Shared::License); +                AboutDialog({ name => N("AdminUser"), +                    version => $self->VERSION, +                    copyright => N("Copyright (C) %s Mageia community", '2013-2014'), +                    license => $license,  +                    comments => N("AdminUser is a Mageia user management tool \n(from the original idea of Mandriva userdrake)."), +                    website => 'http://www.mageia.org', +                    website_label => N("Mageia"), +                    authors => "Angelo Naselli <anaselli\@linux.it>\nMatteo Pasotti <matteo.pasotti\@gmail.com>", +                    translator_credits => +                        #-PO: put here name(s) and email(s) of translator(s) (eg: "John Smith <jsmith@nowhere.com>") +                        N("_: Translator(s) name(s) & email(s)\n")} +                ); +            } +            elsif ($menuLabel eq $self->get_action_menu('add_user')->label())  { +                $self->addUserDialog(); +                $self->_refresh(); +            } +            elsif ($menuLabel eq $self->get_action_menu('add_group')->label()) { +                $self->_addGroupDialog(); +                $self->_refresh(); +            } +            elsif ($menuLabel eq $self->get_action_menu('del')->label())  { +                $self->_deleteUserOrGroup(); +            } +            elsif ($menuLabel eq $self->get_action_menu('edit')->label())  { +                $self->_editUserOrGroup(); +            } +            elsif ($self->get_widget('tabs') && $menuLabel eq  $tabs{groups}->label()) { +                $self->_createGroupTable(); +            } +            elsif ($self->get_widget('tabs') && $menuLabel eq  $tabs{users}->label()) { +                $self->_createUserTable(); +            } +            elsif ($menuLabel eq  $fileMenu{refresh}->label()) { +                $self->_refresh(); +            } +        } +        elsif ($eventType == $yui::YEvent::WidgetEvent) { +### Buttons and widgets ### +            my $widget = $event->widget(); +            if ($widget == $self->get_widget('add_user')) { +                $self->addUserDialog(); +                $self->_refresh(); +            } +            elsif ($widget == $self->get_widget('del')) { +                $self->_deleteUserOrGroup(); +            } +            elsif ($widget == $self->get_widget('table')) { +                $self->_refreshActions(); +                my $wEvent = yui::YMGAWidgetFactory::getYWidgetEvent($event); +                if ($wEvent && $wEvent->reason() == $yui::YEvent::Activated) { +                    $self->_editUserOrGroup(); +                } +            } +            elsif ($widget == $self->get_widget('add_group')) { +                $self->_addGroupDialog(); +                $self->_refresh(); +            } +            elsif ($widget == $self->get_widget('edit')) { +                $self->_editUserOrGroup();                 +            } +            elsif ( $widget == $self->get_widget('filter_system') ||  +                    $widget == $self->get_widget('refresh') ||  +                    $widget == $self->get_widget('apply_filter') ) { +                $self->_refresh(); +            } +        } +    } + +    $self->dialog->destroy() ; + +    #restore old application title +    yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle; +} + +#============================================================= + +=head2 _skipShortcut + +=head3 INPUT + +    $self:  this object +    $label: an item label to be cleaned by keyboard shortcut "&" + +=head3 OUTPUT + +    $label: cleaned label  + +=head3 DESCRIPTION + +    This internal method is a workaround to label that are +    changed by "&" due to keyborad shortcut. + +=cut + +#============================================================= +sub _skipShortcut { +    my ($self, $label) = @_; + +    $label =~ s/&// if ($label); + +    return ($label); +} + +#============================================================= + +=head2 _inArray + +=head3 INPUT + +    $self: this object +    $item: item to search +    $arr:  array container + +=head3 OUTPUT + +    true: if the array contains the item + +=head3 DESCRIPTION + +This method returns if an item is into the array container + +=cut + +#============================================================= +sub _inArray { +    my ($self, $item, $arr) = @_; +     +    return grep( /^$item$/, @$arr );  +} + + +sub ValidInt { +    foreach my $i (@_) { $i =~ /\d+/ or return 0 } +    return 1; +} + +sub ConvTime { +    my ($day, $month, $year) = @_; +    my ($tm, $days, $mon, $yr); +    $mon = $month - 1; $yr = $year - 1900; +    $tm = POSIX::mktime(0, 0, 0, $day, $mon, $yr); +    $days = ceil($tm / (24 * 60 * 60)); +    return $days; +} + +sub TimeOfArray { +    my ($reltime, $cm) = @_; +    my $h; my %mth = (Jan => 1, Feb => 2, Mar => 3, Apr => 4, May => 5, Jun => 6, Jul => 7, Aug => 8, Sep => 9, Oct => 10, Nov => 11, Dec => 12); +    my $_t = localtime($reltime * 24 * 60 * 60) =~ /(\S+)\s+(\S+)\s+(\d+)\s+(\S+)\s+(\d+)/; +    $h->{daystr} = $1; +    $h->{month} = $2; +    $h->{dayint} = $3; +    $h->{year} = $5; +    $cm and $h->{month} = $mth{$2};  +    $h; +} + + +no Moose; +__PACKAGE__->meta->make_immutable; + +1; diff --git a/lib/AdminPanel/Privileges.pm b/lib/AdminPanel/Privileges.pm new file mode 100644 index 0000000..73f2098 --- /dev/null +++ b/lib/AdminPanel/Privileges.pm @@ -0,0 +1,61 @@ +# vim: set et ts=4 sw=4: +#    Copyright 2012-2013 Matteo Pasotti +# +#    This file is part of AdminPanel +# +#    AdminPanel is free software: you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation, either version 2 of the License, or +#    (at your option) any later version. +# +#    AdminPanel is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with AdminPanel.  If not, see <http://www.gnu.org/licenses/>. + +package AdminPanel::Privileges; + +use strict; +use warnings; +use diagnostics; +require Exporter; +use base qw(Exporter); +use English qw(-no_match_vars); + +our @EXPORT = qw(require_root_capability +         ask_for_authentication); + +my $wrappers = { "sudo" => "sudo", +                 "pkit" => "pkexec", +                 "chlp" => "consolehelper" +               }; + +my $wrapper = 0; + +sub require_root_capability { +    return $EUID != 0; +} + +sub ask_for_authentication { +    my $wrapper_id = shift; +    $wrapper = $wrappers->{$wrapper_id} if(defined($wrappers->{$wrapper_id})); +    my ($command, @args) = wrap_command($0, @ARGV); +    unshift(@args,$command->[1]); +    unshift(@args, '-n') if($wrapper_id eq "sudo"); # let sudo die if password is needed +    exec { $command->[0] } $command->[1], @args or die ("command %s missing", $command->[0]); +} + +sub wrap_command { +    my ($app, @args) = @_; +    return ([$wrapper, $app], @args); +} + +sub get_wrapper { +    my $id = shift; +    return $wrappers->{$id} if(defined($wrappers->{$id})); +} + +1; diff --git a/lib/AdminPanel/Rpmdragora/.perl_checker b/lib/AdminPanel/Rpmdragora/.perl_checker new file mode 100644 index 0000000..202e053 --- /dev/null +++ b/lib/AdminPanel/Rpmdragora/.perl_checker @@ -0,0 +1 @@ +Basedir .. diff --git a/lib/AdminPanel/Rpmdragora/edit_urpm_sources.pm b/lib/AdminPanel/Rpmdragora/edit_urpm_sources.pm new file mode 100644 index 0000000..9ef5d00 --- /dev/null +++ b/lib/AdminPanel/Rpmdragora/edit_urpm_sources.pm @@ -0,0 +1,1216 @@ +# vim: set et ts=4 sw=4: +package AdminPanel::Rpmdragora::edit_urpm_sources; +#***************************************************************************** +#  +#  Copyright (c) 2002 Guillaume Cottenceau +#  Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com> +#  Copyright (c) 2002-2007 Mandriva Linux +#  +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +#  +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +#  +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#  +#***************************************************************************** +# +# $Id: edit_urpm_sources.pm 266598 2010-03-03 12:00:58Z tv $ + + +use strict; +use lib qw(/usr/lib/libDrakX); +use common; +use AdminPanel::rpmdragora; +use AdminPanel::Rpmdragora::open_db; +use AdminPanel::Rpmdragora::formatting; +use URPM::Signature; +use MDK::Common::Math qw(max); +use urpm::media; +use urpm::download; +use urpm::lock; + +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw(run); + +#use mygtk2 qw(gtknew gtkset); +#use ugtk2 qw(:all); + +my $urpm; +my ($mainw, $list_tv, $something_changed); + +my %col = ( +    mainw => { +        is_enabled => 0, +        is_update  => 1, +        type       => 2, +        name       => 3, +        activatable => 4 +    }, +); + + +sub get_medium_type { +    my ($medium) = @_; +    my %medium_type = ( +        cdrom     => N("CD-ROM"), +        ftp       => N("FTP"), +        file      => N("Local"), +        http      => N("HTTP"), +        https     => N("HTTPS"), +        nfs       => N("NFS"), +        removable => N("Removable"), +        rsync     => N("rsync"), +        ssh       => N("NFS"), +    ); +    return N("Mirror list") if $medium->{mirrorlist}; +    return $medium_type{$1} if $medium->{url} =~ m!^([^:]*)://!; +    return N("Local"); +} + +sub selrow { +    my ($o_list_tv) = @_; +    defined $o_list_tv or $o_list_tv = $list_tv; +    my ($model, $iter) = $o_list_tv->get_selection->get_selected; +    $model && $iter or return -1; +    my $path = $model->get_path($iter); +    my $row = $path->to_string; +    return $row; +} + +sub selected_rows { +    my ($o_list_tv) = @_; +    defined $o_list_tv or $o_list_tv = $list_tv; +    my (@rows) = $o_list_tv->get_selection->get_selected_rows; +    return -1 if @rows == 0; +    map { $_->to_string } @rows; +} + +sub remove_row { +    my ($model, $path_str) = @_; +    my $iter = $model->get_iter_from_string($path_str); +    $iter or return; +    $model->remove($iter); +} + +sub remove_from_list { +    my ($list, $list_ref, $model) = @_; +    my $row = selrow($list); +    if ($row != -1) { +        splice @$list_ref, $row, 1; +        remove_row($model, $row); +    } + +} + +sub _want_base_distro() { +    distro_type(0) eq 'updates' ? interactive_msg( +	N("Choose media type"), +N("In order to keep your system secure and stable, you must at a minimum set up +sources for official security and stability updates. You can also choose to set +up a fuller set of sources which includes the complete official Mageia +repositories, giving you access to more software than can fit on the Mageia +discs. Please choose whether to configure update sources only, or the full set +of sources."), +	 transient => $::main_window, +	yesno => 1, text => { yes => N("Full set of sources"), no => N("Update sources only") }, +    ) : 1; +} + +sub easy_add_callback_with_mirror() { +    # when called on early init by rpmdragora +    $urpm ||= fast_open_urpmi_db(); + +    #- cooker and community don't have update sources +    my $want_base_distro = _want_base_distro(); +    defined $want_base_distro or return; +    my $distro = $rpmdragora::mandrake_release; +    my ($mirror) = choose_mirror($urpm, message => +N("This will attempt to install all official sources corresponding to your +distribution (%s). + +I need to contact the Mageia website to get the mirror list. +Please check that your network is currently running. + +Is it ok to continue?", $distro), +     transient => $::main_window, +    ) or return 0; +    ref $mirror or return; +    my $wait = wait_msg(N("Please wait, adding media...")); +    add_distrib_update_media($urpm, $mirror, if_(!$want_base_distro, only_updates => 1)); +    $offered_to_add_sources->[0] = 1; +    remove_wait_msg($wait); +    return 1; +} + +sub easy_add_callback() { +    # when called on early init by rpmdragora +    $urpm ||= fast_open_urpmi_db(); + +    #- cooker and community don't have update sources +    my $want_base_distro = _want_base_distro(); +    defined $want_base_distro or return; +    warn_for_network_need(undef, transient => $::main_window) or return; +    my $wait = wait_msg(N("Please wait, adding media...")); +    add_distrib_update_media($urpm, undef, if_(!$want_base_distro, only_updates => 1)); +    $offered_to_add_sources->[0] = 1; +    remove_wait_msg($wait); +    return 1; +} + +sub add_callback() { +    my $w = ugtk2->new(N("Add a medium"), grab => 1, center => 1,  transient => $::main_window); +    my $prev_main_window = $::main_window; +    local $::main_window = $w->{real_window}; +    my %radios_infos = ( +	local => { name => N("Local files"), url => N("Medium path:"), dirsel => 1 }, +	ftp => { name => N("FTP server"), url => N("URL:"), loginpass => 1 }, +	rsync => { name => N("RSYNC server"), url => N("URL:") }, +	http => { name => N("HTTP server"), url => N("URL:") }, +	removable => { name => N("Removable device (CD-ROM, DVD, ...)"), url => N("Path or mount point:"), dirsel => 1 }, +    ); +    my @radios_names_ordered = qw(local ftp rsync http removable); +    # TODO: replace NoteBook by sensitive widgets and Label->set() +    my $notebook = gtknew('Notebook'); +    $notebook->set_show_tabs(0); $notebook->set_show_border(0); +    my ($count_nbs, %pages); +    my $size_group = Gtk2::SizeGroup->new('horizontal'); +    my ($cb1, $cb2); +    foreach (@radios_names_ordered) { +	my $info = $radios_infos{$_}; +	my $url_entry = sub { +	    gtkpack_( +		gtknew('HBox'), +		1, $info->{url_entry} = gtkentry(), +		if_( +		    $info->{dirsel}, +		    0, gtksignal_connect( +			gtknew('Button', text => but(N("Browse..."))), +			clicked => sub { $info->{url_entry}->set_text(ask_dir()) }, +		    ) +		), +	    ); +	}; +        my $checkbut_entry = sub { +            my ($name, $label, $visibility, $callback, $tip) = @_; +            my $w = [ gtksignal_connect( +		    $info->{$name . '_check'} = gtkset(gtknew('CheckButton', text => $label), tip => $tip), +		    clicked => sub { +			$info->{$name . '_entry'}->set_sensitive($_[0]->get_active); +			$callback and $callback->(@_); +		    }, +	    ), +	    gtkset_visibility(gtkset_sensitive($info->{$name . '_entry'} = gtkentry(), 0), $visibility) ]; +	    $size_group->add_widget($info->{$name . '_check'}); +	    $w; +        }; +	my $loginpass_entries = sub { +	    map { +		$checkbut_entry->( +		    @$_, sub { +			$info->{pass_check}->set_active($_[0]->get_active); +			$info->{login_check}->set_active($_[0]->get_active); +		    } +		); +	    } ([ 'login', N("Login:"), 1 ], [ 'pass', N("Password:"), 0 ]); +	}; +	$pages{$info->{name}} = $count_nbs++; +	$notebook->append_page( +	    gtkshow(create_packtable( +		{ xpadding => 0, ypadding => 0 }, +		[ gtkset_alignment(gtknew('Label', text => N("Medium name:")), 0, 0.5), +		    $info->{name_entry} = gtkentry('') ], +		[ gtkset_alignment(gtknew('Label', text => $info->{url}), 0, 0.5), +		    $url_entry->() ], +		if_($info->{loginpass}, $loginpass_entries->()), +		sub { +		    [ $info->{distrib_check} = $cb1 = gtknew('CheckButton', text => N("Create media for a whole distribution"), +                                                         toggled => sub { +                                                             return if !$cb2; +                                                             my ($w) = @_; +                                                             $info->{update_check}->set_sensitive(!$w->get_active); +                                                         }) +		    ]; +		}->(), +		sub { +		    [ $info->{update_check} = $cb2 = gtknew('CheckButton', text => N("Tag this medium as an update medium")) ]; +		}->(), +	    )) +	); +    } +    $size_group->add_widget($_) foreach $cb1, $cb2; + +    my $checkok = sub { +	my $info = $radios_infos{$radios_names_ordered[$notebook->get_current_page]}; +	my ($name, $url) = map { $info->{$_ . '_entry'}->get_text } qw(name url); +	$name eq '' || $url eq '' and interactive_msg('rpmdragora', N("You need to fill up at least the two first entries.")), return 0; +	if (member($name, map { $_->{name} } @{$urpm->{media}})) { +	    $info->{name_entry}->select_region(0, -1); +	    interactive_msg('rpmdragora', +N("There is already a medium by that name, do you +really want to replace it?"), yesno => 1) or return 0; +	} +	1; +    }; + +    my $type = 'local'; +    my (%i, %make_url); +    gtkadd( +	$w->{window}, +	gtkpack( +	    gtknew('VBox', spacing => 5), +	    gtknew('Title2', label => N("Adding a medium:")), +	    gtknew('HBox', children_tight => [ +                      Gtk2::Label->new(but(N("Type of medium:"))), +                      gtknew('ComboBox', text_ref => \$type,  +                             list => \@radios_names_ordered, +                             format => sub { $radios_infos{$_[0]}{name} }, +                             changed => sub { $notebook->set_current_page($pages{$_[0]->get_text}) }) +                     ]), +	    $notebook, +	    gtknew('HSeparator'), +	    gtkpack( +		gtknew('HButtonBox'), +		gtknew('Button', text => N("Cancel"), clicked => sub { $w->{retval} = 0; Gtk2->main_quit }), +		gtksignal_connect( +		    gtknew('Button', text => N("Ok")), clicked => sub { +			if ($checkok->()) { +			    $w->{retval} = { nb => $notebook->get_current_page }; +			    my $info = $radios_infos{$type}; +			    %i = ( +				name => $info->{name_entry}->get_text, +				url => $info->{url_entry}->get_text, +				distrib => $info->{distrib_check} ? $info->{distrib_check}->get_active : 0, +				update => $info->{update_check}->get_active ? 1 : undef, +			    ); +			    %make_url = ( +				local => "file:/$i{url}", +				http => $i{url}, +				rsync => $i{url}, +				removable => "removable:/$i{url}", +			    ); +			    $i{url} =~ s|^ftp://||; +			    $make_url{ftp} = sprintf "ftp://%s%s", +				$info->{login_check}->get_active +				    ? ($info->{login_entry}->get_text . ':' . $info->{pass_entry}->get_text . '@') +				    : '', +				$i{url}; +			    Gtk2->main_quit; +			} +		    }, +		), +	    ), +	), +    ); + +    if ($w->main) { +	$::main_window = $prev_main_window; +	if ($i{distrib}) { +	    add_medium_and_check( +		$urpm, +		{ nolock => 1, distrib => 1 }, +		$i{name}, $make_url{$type}, probe_with => 'synthesis', update => $i{update}, +	    ); +	} else { +	    if (member($i{name}, map { $_->{name} } @{$urpm->{media}})) { +		urpm::media::select_media($urpm, $i{name}); +		urpm::media::remove_selected_media($urpm); +	    } +	    add_medium_and_check( +		$urpm, +		{ nolock => 1 }, +		$i{name}, $make_url{$type}, $i{hdlist}, update => $i{update}, +	    ); +	} +	return 1; +    } +    return 0; +} + +sub options_callback() { +    my $w = ugtk2->new(N("Global options for package installation"), grab => 1, center => 1,  transient => $::main_window); +    local $::main_window = $w->{real_window}; +    my %verif = (0 => N("never"), 1 => N("always")); +    my $verify_rpm = $urpm->{global_config}{'verify-rpm'}; +    my @avail_downloaders = urpm::download::available_ftp_http_downloaders(); +    my $downloader = $urpm->{global_config}{downloader} || $avail_downloaders[0]; +    my %xml_info_policies = ( +        'never'       => N("Never"),  +        'on-demand'   => N("On-demand"),  +        'update-only' => N("Update-only"),  +        'always'      => N("Always"),  +    ); +    my $xml_info_policy = $urpm->{global_config}{'xml-info'}; + +    gtkadd( +	$w->{window}, +	gtkpack( +	    gtknew('VBox', spacing => 5), +	    gtknew('HBox', children_loose => [ gtknew('Label', text => N("Verify RPMs to be installed:")), +                                               gtknew('ComboBox', list => [ keys %verif ], text_ref => \$verify_rpm, +                                                      format => sub { $verif{$_[0]} || $_[0] }, +                                                  ) +                                           ]), +	    gtknew('HBox', children_loose => [ gtknew('Label', text => N("Download program to use:")), +                                               gtknew('ComboBox', list => \@avail_downloaders, text_ref => \$downloader, +                                                      format => sub { $verif{$_[0]} || $_[0] }, +                                                  ) +                                           ]), +	    gtknew('HBox', +                   children_loose => +                     [ gtknew('Label', text => N("XML meta-data download policy:")), +                       gtknew('ComboBox', +                              list => [ keys %xml_info_policies ], text_ref => \$xml_info_policy, + +                              format => sub { $xml_info_policies{$_[0]} || $_[0] }, +                              tip =>  +                                join("\n", +                                     N("For remote media, specify when XML meta-data (file lists, changelogs & information) are downloaded."), +                                     '', +                                     N("Never"), +                                     N("For remote media, XML meta-data are never downloaded."), +                                     '', +                                     N("On-demand"), +                                     N("(This is the default)"), +                                     N("The specific XML info file is downloaded when clicking on package."), +                                     '', +                                     N("Update-only"),  +                                     N("Updating media implies updating XML info files already required at least once."), +                                     '', +                                     N("Always"), +                                     N("All XML info files are downloaded when adding or updating media."), +                                 ), +                          ), +                   ]), + +	    gtkpack( +		gtknew('HButtonBox'), +		gtknew('Button', text => N("Cancel"), clicked => sub { Gtk2->main_quit }), +		gtksignal_connect( +		    gtknew('Button', text => N("Ok")), clicked => sub { +                        $urpm->{global_config}{'verify-rpm'} = $verify_rpm; +                        $urpm->{global_config}{downloader} = $downloader; +                        $urpm->{global_config}{'xml-info'} = $xml_info_policy; +                        $something_changed = 1; +			urpm::media::write_config($urpm); +			$urpm = fast_open_urpmi_db(); +			Gtk2->main_quit; +		    }, +		), +	    ), +	), +    ); +    $w->main; +} + +sub remove_callback() { +    my @rows = selected_rows(); +    @rows == 0 and return; +    interactive_msg( +	N("Source Removal"), +	@rows == 1 ? +	  N("Are you sure you want to remove source \"%s\"?", $urpm->{media}[$rows[0]]{name}) : +	    N("Are you sure you want to remove the following sources?") . "\n\n" . +	      format_list(map { $urpm->{media}[$_]{name} } @rows), +	yesno => 1, scroll => 1, +	 transient => $::main_window, +    ) or return; + +    my $wait = wait_msg(N("Please wait, removing medium...")); +    foreach my $row (reverse(@rows)) { +     $something_changed = 1; +	urpm::media::remove_media($urpm, [ $urpm->{media}[$row] ]); +	urpm::media::write_urpmi_cfg($urpm); +	remove_wait_msg($wait); +    } +    return 1; +} + +sub renum_media ($$$) { +    my ($model, @iters) = @_; +    my @rows = map { $model->get_path($_)->to_string } @iters; +    my @media = map { $urpm->{media}[$_] } @rows; +    $urpm->{media}[$rows[$_]] = $media[1 - $_] foreach 0, 1; +    $model->swap(@iters); +    $something_changed = 1; +    urpm::media::write_config($urpm); +    $urpm = fast_open_urpmi_db(); +} + +sub upwards_callback() { +    my @rows = selected_rows(); +    @rows == 0 and return; +    my $model = $list_tv->get_model; +    my $prev = $model->get_iter_from_string($rows[0] - 1); +    defined $prev and renum_media($model, $model->get_iter_from_string($rows[0]), $prev); +    $list_tv->get_selection->signal_emit('changed'); +} + +sub downwards_callback() { +    my @rows = selected_rows(); +    @rows == 0 and return; +    my $model = $list_tv->get_model; +    my $iter = $model->get_iter_from_string($rows[0]); +    my $next = $model->iter_next($iter); +    defined $next and renum_media($model, $iter, $next); +    $list_tv->get_selection->signal_emit('changed'); +} + +#- returns the name of the media for which edition failed, or undef on success +sub edit_callback() { +    my ($row) = selected_rows(); +    $row == -1 and return; +    my $medium = $urpm->{media}[$row]; +    my $config = urpm::cfg::load_config_raw($urpm->{config}, 1); +    my ($verbatim_medium) = grep { $medium->{name} eq $_->{name} } @$config; +    my $old_main_window = $::main_window; +    my $w = ugtk2->new(N("Edit a medium"), grab => 1, center => 1,  transient => $::main_window); +    local $::main_window = $w->{real_window}; +    my ($url_entry, $downloader_entry, $url, $downloader); +    gtkadd( +	$w->{window}, +	gtkpack_( +	    gtknew('VBox', spacing => 5), +	    0, gtknew('Title2', label => N("Editing medium \"%s\":", $medium->{name})), +	    0, create_packtable( +		{}, +		[ gtknew('Label_Left', text => N("URL:")), $url_entry = gtkentry($verbatim_medium->{url} || $verbatim_medium->{mirrorlist}) ], +		[ gtknew('Label_Left', text => N("Downloader:")), +            my $download_combo = Gtk2::ComboBox->new_with_strings([ urpm::download::available_ftp_http_downloaders() ], +                                                                  $verbatim_medium->{downloader} || '') ], +	    ), +	    0, gtknew('HSeparator'), +	    0, gtkpack( +		gtknew('HButtonBox'), +		gtksignal_connect( +		    gtknew('Button', text => N("Cancel")), +		    clicked => sub { $w->{retval} = 0; Gtk2->main_quit }, +		), +		gtksignal_connect( +		    gtknew('Button', text => N("Save changes")), +		    clicked => sub { +			$w->{retval} = 1; +			$url = $url_entry->get_text; +			$downloader = $downloader_entry->get_text; +			Gtk2->main_quit; +		    }, +		), +		gtksignal_connect( +		    gtknew('Button', text => N("Proxy...")), +		    clicked => sub { proxy_callback($medium) }, +		), +	    ) +	) +    ); +    $downloader_entry = $download_combo->entry; +    $w->{rwindow}->set_size_request(600, -1); +    if ($w->main) { +	my ($name, $update) = map { $medium->{$_} } qw(name update); +	$url =~ m|^removable://| and ( +	    interactive_msg( +		N("You need to insert the medium to continue"), +		N("In order to save the changes, you need to insert the medium in the drive."), +		yesno => 1, text => { yes => N("Ok"), no => N("Cancel") } +	    ) or return 0 +	); +	my $saved_proxy = urpm::download::get_proxy($name); +	undef $saved_proxy if !defined $saved_proxy->{http_proxy} && !defined $saved_proxy->{ftp_proxy}; +	urpm::media::select_media($urpm, $name); +     if (my ($media) = grep { $_->{name} eq $name } @{$urpm->{media}}) { +         put_in_hash($media, { +             ($verbatim_medium->{mirrorlist} ? 'mirrorlist' : 'url') => $url, +             name => $name, +             if_($update ne $media->{update} || $update, update => $update), +             if_($saved_proxy ne $media->{proxy} || $saved_proxy, proxy => $saved_proxy), +             if_($downloader ne $media->{downloader} || $downloader, downloader => $downloader), +             modified => 1, +         }); +         urpm::media::write_config($urpm); +         local $::main_window = $old_main_window; +         update_sources_noninteractive($urpm, [ $name ], transient => $::main_window, nolock => 1); +     } else { +         urpm::media::remove_selected_media($urpm); +         add_medium_and_check($urpm, { nolock => 1, proxy => $saved_proxy }, $name, $url, undef, update => $update, if_($downloader, downloader => $downloader)); +     } +	return $name; +    } +    return undef; +} + +sub update_callback() { +    update_sources_interactive($urpm,  transient => $::main_window, nolock => 1); +} + +sub proxy_callback { +    my ($medium) = @_; +    my $medium_name = $medium ? $medium->{name} : ''; +    my $w = ugtk2->new(N("Configure proxies"), grab => 1, center => 1,  transient => $::main_window); +    local $::main_window = $w->{real_window}; +    require curl_download; +    my ($proxy, $proxy_user) = curl_download::readproxy($medium_name); +    my ($user, $pass) = $proxy_user =~ /^([^:]*):(.*)$/; +    my ($proxybutton, $proxyentry, $proxyuserbutton, $proxyuserentry, $proxypasswordentry); +    my $sg = Gtk2::SizeGroup->new('horizontal'); +    gtkadd( +	$w->{window}, +	gtkpack__( +	    gtknew('VBox', spacing => 5), +	    gtknew('Title2', label => +		$medium_name +		    ? N("Proxy settings for media \"%s\"", $medium_name) +		    : N("Global proxy settings") +	    ), +	    gtknew('Label_Left', text => N("If you need a proxy, enter the hostname and an optional port (syntax: <proxyhost[:port]>):")), +	    gtkpack_( +		gtknew('HBox', spacing => 10), +		1, gtkset_active($proxybutton = gtknew('CheckButton', text => N("Proxy hostname:")), to_bool($proxy)), +		0, gtkadd_widget($sg, gtkset_sensitive($proxyentry = gtkentry($proxy), to_bool($proxy))), +	    ), +         gtkset_active($proxyuserbutton = gtknew('CheckButton', text => N("You may specify a username/password for the proxy authentication:")), to_bool($proxy_user)), +	    gtkpack_( +		my $hb_user = gtknew('HBox', spacing => 10, sensitive => to_bool($proxy_user)), +		1, gtknew('Label_Left', text => N("User:")), +		0, gtkadd_widget($sg, $proxyuserentry = gtkentry($user)), +      ), +	    gtkpack_( +		my $hb_pswd = gtknew('HBox', spacing => 10, sensitive => to_bool($proxy_user)), +		1, gtknew('Label_Left', text => N("Password:")), +		0, gtkadd_widget($sg, gtkset_visibility($proxypasswordentry = gtkentry($pass), 0)), +	    ), +	    gtknew('HSeparator'), +	    gtkpack( +		gtknew('HButtonBox'), +		gtksignal_connect( +		    gtknew('Button', text => N("Ok")), +		    clicked => sub { +			$w->{retval} = 1; +			$proxy = $proxybutton->get_active ? $proxyentry->get_text : ''; +			$proxy_user = $proxyuserbutton->get_active +			    ? ($proxyuserentry->get_text . ':' . $proxypasswordentry->get_text) : ''; +			Gtk2->main_quit; +		    }, +		), +		gtksignal_connect( +		    gtknew('Button', text => N("Cancel")), +		    clicked => sub { $w->{retval} = 0; Gtk2->main_quit }, +		) +	    ) +	) +    ); +    $sg->add_widget($_) foreach $proxyentry, $proxyuserentry, $proxypasswordentry; +    $proxybutton->signal_connect( +	clicked => sub { +	    $proxyentry->set_sensitive($_[0]->get_active); +	    $_[0]->get_active and return; +	    $proxyuserbutton->set_active(0); +	    $hb_user->set_sensitive(0); +	    $hb_pswd->set_sensitive(0); +	} +    ); +    $proxyuserbutton->signal_connect(clicked => sub { $_->set_sensitive($_[0]->get_active) foreach $hb_user, $hb_pswd; +    $proxypasswordentry->set_sensitive($_[0]->get_active) }); + +    $w->main and do { +        $something_changed = 1; +        curl_download::writeproxy($proxy, $proxy_user, $medium_name); +    }; +} + +sub parallel_read_sysconf() { +    my @conf; +    foreach (cat_('/etc/urpmi/parallel.cfg')) { +        my ($name, $protocol, $command) = /([^:]+):([^:]+):(.*)/ or print STDERR "Warning, unrecognized line in /etc/urpmi/parallel.cfg:\n$_"; +        my $medias = $protocol =~ s/\(([^\)]+)\)$// ? [ split /,/, $1 ] : []; +        push @conf, { name => $name, protocol => $protocol, medias => $medias, command => $command }; +    } +    \@conf; +} + +sub parallel_write_sysconf { +    my ($conf) = @_; +    output '/etc/urpmi/parallel.cfg', +           map { my $m = @{$_->{medias}} ? '(' . join(',', @{$_->{medias}}) . ')' : ''; +                 "$_->{name}:$_->{protocol}$m:$_->{command}\n" } @$conf; +} + +sub remove_parallel { +    my ($num, $conf) = @_; +    if ($num != -1) { +        splice @$conf, $num, 1; +        parallel_write_sysconf($conf); +    } +} + +sub add_callback_ { +    my ($title, $label, $mainw, $widget, $get_value, $check) = @_; +    my $w = ugtk2->new($title, grab => 1,  transient => $mainw->{real_window}); +    local $::main_window = $w->{real_window}; +    gtkadd( +        $w->{window}, +        gtkpack__( +            gtknew('VBox', spacing => 5), +            gtknew('Label', text => $label), +            $widget, +            gtknew('HSeparator'), +            gtkpack( +                gtknew('HButtonBox'), +                gtknew('Button', text => N("Ok"), clicked => sub { $w->{retval} = 1; $get_value->(); Gtk2->main_quit }), +                gtknew('Button', text => N("Cancel"), clicked => sub { $w->{retval} = 0; Gtk2->main_quit }) +            ) +        ) +    ); +    $check->() if $w->main; +} + +sub edit_parallel { +    my ($num, $conf) = @_; +    my $edited = $num == -1 ? {} : $conf->[$num]; +    my $w = ugtk2->new($num == -1 ? N("Add a parallel group") : N("Edit a parallel group"), grab => 1, center => 1,  transient => $::main_window); +    local $::main_window = $w->{real_window}; +    my $name_entry; + +    my ($medias_ls, $hosts_ls) = (Gtk2::ListStore->new("Glib::String"), Gtk2::ListStore->new("Glib::String")); + +    my ($medias, $hosts) = map { +        my $list = Gtk2::TreeView->new_with_model($_); +        $list->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0)); +        $list->set_headers_visible(0); +        $list->get_selection->set_mode('browse'); +        $list; +    } $medias_ls, $hosts_ls; + +    $medias_ls->append_set([ 0 => $_ ]) foreach @{$edited->{medias}}; + +    my $add_media = sub { +        my $medias_list_ls = Gtk2::ListStore->new("Glib::String"); +        my $medias_list = Gtk2::TreeView->new_with_model($medias_list_ls); +        $medias_list->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0)); +        $medias_list->set_headers_visible(0); +        $medias_list->get_selection->set_mode('browse'); +        $medias_list_ls->append_set([ 0 => $_->{name} ]) foreach @{$urpm->{media}}; +        my $sel; +        add_callback_(N("Add a medium limit"), N("Choose a medium to add to the media limit:"), +                      $w, $medias_list, sub { $sel = selrow($medias_list) }, +                      sub { +                          return if $sel == -1; +                          my $media = ${$urpm->{media}}[$sel]{name}; +                          $medias_ls->append_set([ 0 => $media ]); +                          push @{$edited->{medias}}, $media; +                      } +                  ); +    }; + +    my $hosts_list; +    if    ($edited->{protocol} eq 'ssh')    { $hosts_list = [ split /:/, $edited->{command} ] } +    elsif ($edited->{protocol} eq 'ka-run') { push @$hosts_list, $1 while $edited->{command} =~ /-m (\S+)/g } +    $hosts_ls->append_set([ 0 => $_ ]) foreach @$hosts_list; +    my $add_host = sub { +        my ($entry, $value); +        add_callback_(N("Add a host"), N("Type in the hostname or IP address of the host to add:"), +                      $mainw, $entry = gtkentry(), sub { $value = $entry->get_text }, +                      sub { $hosts_ls->append_set([ 0 => $value ]); push @$hosts_list, $value } +                  ); +    }; + +    my @protocols_names = qw(ka-run ssh); +    my @protocols; +    gtkadd( +	$w->{window}, +	gtkpack_( +	    gtknew('VBox', spacing => 5), +	    if_( +		$num != -1, +		0, gtknew('Label', text => N("Editing parallel group \"%s\":", $edited->{name})) +	    ), +	    1, create_packtable( +		{}, +		[ N("Group name:"), $name_entry = gtkentry($edited->{name}) ], +		[ N("Protocol:"), gtknew('HBox', children_tight => [ +		    @protocols = gtkradio($edited->{protocol}, @protocols_names) ]) ], +		[ N("Media limit:"), +		gtknew('HBox', spacing => 5, children => [ +		    1, gtknew('Frame', shadow_type => 'in', child =>  +			gtknew('ScrolledWindow', h_policy => 'never', child => $medias)), +		    0, gtknew('VBox', children_tight => [ +			gtksignal_connect(Gtk2::Button->new(but(N("Add"))),    clicked => sub { $add_media->() }), +			gtksignal_connect(Gtk2::Button->new(but(N("Remove"))), clicked => sub { +                                              remove_from_list($medias, $edited->{medias}, $medias_ls); +                                          }) ]) ]) ], +		[ N("Hosts:"), +		gtknew('HBox', spacing => 5, children => [ +		    1, gtknew('Frame', shadow_type => 'in', child =>  +			gtknew('ScrolledWindow', h_policy => 'never', child => $hosts)), +		    0, gtknew('VBox', children_tight => [ +			gtksignal_connect(Gtk2::Button->new(but(N("Add"))),    clicked => sub { $add_host->() }), +			gtksignal_connect(Gtk2::Button->new(but(N("Remove"))), clicked => sub { +                                              remove_from_list($hosts, $hosts_list, $hosts_ls); +                                          }) ]) ]) ] +	    ), +	    0, gtknew('HSeparator'), +	    0, gtkpack( +		gtknew('HButtonBox'), +		gtksignal_connect( +		    gtknew('Button', text => N("Ok")), clicked => sub { +			$w->{retval} = 1; +			$edited->{name} = $name_entry->get_text; +			mapn { $_[0]->get_active and $edited->{protocol} = $_[1] } \@protocols, \@protocols_names; +			Gtk2->main_quit; +		    } +		), +		gtknew('Button', text => N("Cancel"), clicked => sub { $w->{retval} = 0; Gtk2->main_quit })) +	) +    ); +    $w->{rwindow}->set_size_request(600, -1); +    if ($w->main) { +        $num == -1 and push @$conf, $edited; +        if ($edited->{protocol} eq 'ssh')    { $edited->{command} = join(':', @$hosts_list) } +        if ($edited->{protocol} eq 'ka-run') { $edited->{command} = "-c ssh " . join(' ', map { "-m $_" } @$hosts_list) } +        parallel_write_sysconf($conf); +	return 1; +    } +    return 0; +} + +sub parallel_callback() { +    my $w = ugtk2->new(N("Configure parallel urpmi (distributed execution of urpmi)"), grab => 1, center => 1,  transient => $mainw->{real_window}); +    local $::main_window = $w->{real_window}; +    my $list_ls = Gtk2::ListStore->new("Glib::String", "Glib::String", "Glib::String", "Glib::String"); +    my $list = Gtk2::TreeView->new_with_model($list_ls); +    each_index { $list->append_column(Gtk2::TreeViewColumn->new_with_attributes($_, Gtk2::CellRendererText->new, 'text' => $::i)) } N("Group"), N("Protocol"), N("Media limit"); +    $list->append_column(my $commandcol = Gtk2::TreeViewColumn->new_with_attributes(N("Command"), Gtk2::CellRendererText->new, 'text' => 3)); +    $commandcol->set_max_width(200); + +    my $conf; +    my $reread = sub { +	$list_ls->clear; +        $conf = parallel_read_sysconf(); +	foreach (@$conf) { +            $list_ls->append_set([ 0 => $_->{name}, +                                   1 => $_->{protocol}, +                                   2 => @{$_->{medias}} ? join(', ', @{$_->{medias}}) : N("(none)"), +                                   3 => $_->{command} ]); +	} +    }; +    $reread->(); + +    gtkadd( +	$w->{window}, +	gtkpack_( +	    gtknew('VBox', spacing => 5), +	    1, gtkpack_( +		gtknew('HBox', spacing => 10), +		1, $list, +		0, gtkpack__( +		    gtknew('VBox', spacing => 5), +		    gtksignal_connect( +			Gtk2::Button->new(but(N("Remove"))), +			clicked => sub { remove_parallel(selrow($list), $conf); $reread->() }, +		    ), +		    gtksignal_connect( +			Gtk2::Button->new(but(N("Edit..."))), +			clicked => sub { +			    my $row = selrow($list); +			    $row != -1 and edit_parallel($row, $conf); +			    $reread->(); +			}, +		    ), +		    gtksignal_connect( +			Gtk2::Button->new(but(N("Add..."))), +			clicked => sub { edit_parallel(-1, $conf) and $reread->() }, +		    ) +		) +	    ), +	    0, gtknew('HSeparator'), +	    0, gtkpack( +		gtknew('HButtonBox'), +		gtknew('Button', text => N("Ok"), clicked => sub { Gtk2->main_quit }) +	    ) +	) +    ); +    $w->main; +} + +sub keys_callback() { +    my $w = ugtk2->new(N("Manage keys for digital signatures of packages"), grab => 1, center => 1,  transient => $mainw->{real_window}); +    local $::main_window = $w->{real_window}; +    $w->{real_window}->set_size_request(600, 300); + +    my $media_list_ls = Gtk2::ListStore->new("Glib::String"); +    my $media_list = Gtk2::TreeView->new_with_model($media_list_ls); +    $media_list->append_column(Gtk2::TreeViewColumn->new_with_attributes(N("Medium"), Gtk2::CellRendererText->new, 'text' => 0)); +    $media_list->get_selection->set_mode('browse'); + +    my $key_col_size = 200; +    my $keys_list_ls = Gtk2::ListStore->new("Glib::String", "Glib::String"); +    my $keys_list = Gtk2::TreeView->new_with_model($keys_list_ls); +    $keys_list->set_rules_hint(1); +    $keys_list->append_column(my $col = Gtk2::TreeViewColumn->new_with_attributes(N("_:cryptographic keys\nKeys"), my $renderer = Gtk2::CellRendererText->new, 'text' => 0)); +    $col->set_sizing('fixed'); +    $col->set_fixed_width($key_col_size); +    $renderer->set_property('width' => 1); +    $renderer->set_property('wrap-width', $key_col_size); +    $keys_list->get_selection->set_mode('browse'); + +    my ($current_medium, $current_medium_nb, @keys); + +    my $read_conf = sub { +        $urpm->parse_pubkeys(root => $urpm->{root}); +        @keys = map { [ split /[,\s]+/, $_->{'key-ids'} ] } @{$urpm->{media}}; +    }; +    my $write = sub { +        $something_changed = 1; +        urpm::media::write_config($urpm); +        $urpm = fast_open_urpmi_db(); +        $read_conf->(); +        $media_list->get_selection->signal_emit('changed'); +    }; +    $read_conf->(); +    my $key_name = sub { +        exists $urpm->{keys}{$_[0]} ? $urpm->{keys}{$_[0]}{name} +                                    : N("no name found, key doesn't exist in rpm keyring!"); +    }; +    $media_list_ls->append_set([ 0 => $_->{name} ]) foreach @{$urpm->{media}}; +    $media_list->get_selection->signal_connect(changed => sub { +        my ($model, $iter) = $_[0]->get_selected; +        $model && $iter or return; +        $current_medium = $model->get($iter, 0); +        $current_medium_nb = $model->get_path($iter)->to_string; +        $keys_list_ls->clear; +        $keys_list_ls->append_set([ 0 => sprintf("%s (%s)", $_, $key_name->($_)), 1 => $_ ]) foreach @{$keys[$current_medium_nb]}; +    }); + +    my $add_key = sub { +        my $available_keyz_ls = Gtk2::ListStore->new("Glib::String", "Glib::String"); +        my $available_keyz = Gtk2::TreeView->new_with_model($available_keyz_ls); +        $available_keyz->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0)); +        $available_keyz->set_headers_visible(0); +        $available_keyz->get_selection->set_mode('browse'); +        $available_keyz_ls->append_set([ 0 => sprintf("%s (%s)", $_, $key_name->($_)), 1 => $_ ]) foreach keys %{$urpm->{keys}}; +        my $key; +        add_callback_(N("Add a key"), N("Choose a key to add to the medium %s", $current_medium), $w, $available_keyz, +                      sub { +                          my ($model, $iter) = $available_keyz->get_selection->get_selected; +                          $model && $iter and $key = $model->get($iter, 1); +                      }, +                      sub { +                          return if !defined $key; +                          $urpm->{media}[$current_medium_nb]{'key-ids'} = join(',', sort(uniq(@{$keys[$current_medium_nb]}, $key))); +                          $write->(); +                      } +                  ); + + +    }; + +    my $remove_key = sub { +        my ($model, $iter) = $keys_list->get_selection->get_selected; +        $model && $iter or return; +        my $key = $model->get($iter, 1); +	interactive_msg(N("Remove a key"), +                        N("Are you sure you want to remove the key %s from medium %s?\n(name of the key: %s)", +                          $key, $current_medium, $key_name->($key)), +                        yesno => 1,  transient => $w->{real_window}) or return; +        $urpm->{media}[$current_medium_nb]{'key-ids'} = join(',', difference2(\@{$keys[$current_medium_nb]}, [ $key ])); +        $write->(); +    }; + +    gtkadd( +	$w->{window}, +	gtkpack_( +	    gtknew('VBox', spacing => 5), +	    1, gtkpack_( +		gtknew('HBox', spacing => 10), +		1, create_scrolled_window($media_list), +		1, create_scrolled_window($keys_list), +		0, gtkpack__( +		    gtknew('VBox', spacing => 5), +		    gtksignal_connect( +			Gtk2::Button->new(but(N("Add"))), +			clicked => \&$add_key, +		    ), +		    gtksignal_connect( +			Gtk2::Button->new(but(N("Remove"))), +			clicked => \&$remove_key, +		    ) +		) +	    ), +	    0, gtknew('HSeparator'), +	    0, gtkpack( +		gtknew('HButtonBox'), +		gtknew('Button', text => N("Ok"), clicked => sub { Gtk2->main_quit }) +	    ), +	), +    ); +    $w->main; +} + +sub mainwindow() { +    undef $something_changed; +    $mainw = ugtk2->new(N("Configure media"), center => 1, transient => $::main_window, modal => 1); +    local $::main_window = $mainw->{real_window}; + +    my $reread_media; + +    my ($menu, $_factory) = create_factory_menu( +	$mainw->{real_window}, +	[ N("/_File"), undef, undef, undef, '<Branch>' ], +	[ N("/_File") . N("/_Update"), N("<control>U"), sub { update_callback() and $reread_media->() }, undef, '<Item>', ], +        [ N("/_File") . N("/Add a specific _media mirror"), N("<control>M"), sub { easy_add_callback_with_mirror() and $reread_media->() }, undef, '<Item>' ], +        [ N("/_File") . N("/_Add a custom medium"), N("<control>A"), sub { add_callback() and $reread_media->() }, undef, '<Item>' ], +	[ N("/_File") . N("/Close"), N("<control>W"), sub { Gtk2->main_quit }, undef, '<Item>', ], +     [ N("/_Options"), undef, undef, undef, '<Branch>' ], +     [ N("/_Options") . N("/_Global options"), N("<control>G"), \&options_callback, undef, '<Item>' ], +     [ N("/_Options") . N("/Manage _keys"), N("<control>K"), \&keys_callback, undef, '<Item>' ], +     [ N("/_Options") . N("/_Parallel"), N("<control>P"), \¶llel_callback, undef, '<Item>' ], +     [ N("/_Options") . N("/P_roxy"), N("<control>R"), \&proxy_callback, undef, '<Item>' ], +     if_($0 =~ /edit-urpm-sources/, +         [ N("/_Help"), undef, undef, undef, '<Branch>' ], +         [ N("/_Help") . N("/_Report Bug"), undef, sub { run_drakbug('edit-urpm-sources.pl') }, undef, '<Item>' ], +         [ N("/_Help") . N("/_Help"), undef, sub { rpmdragora::open_help('sources') }, undef, '<Item>' ], +         [ N("/_Help") . N("/_About..."), undef, sub { +               my $license = formatAlaTeX(translate($::license)); +               $license =~ s/\n/\n\n/sg; # nicer formatting +               my $w = gtknew('AboutDialog', name => N("Rpmdragora"), +                              version => $rpmdragora::distro_version, +                              copyright => N("Copyright (C) %s by Mandriva", '2002-2008'), +                              license => $license, wrap_license => 1, +                              comments => N("Rpmdragora is the Mageia package management tool."), +                              website => 'http://www.mageia.org/', +                              website_label => N("Mageia"), +                              authors => 'Thierry Vignaud <vignaud@mandriva.com>', +                              artists => 'Hรฉlรจne Durosini <ln@mandriva.com>', +                              translator_credits => +                                #-PO: put here name(s) and email(s) of translator(s) (eg: "John Smith <jsmith@nowhere.com>") +                                N("_: Translator(s) name(s) & email(s)\n"), +                              transient_for => $::main_window, modal => 1, position_policy => 'center-on-parent', +                          ); +               $w->show_all; +               $w->run; +           }, undef, '<Item>' +       ] +     ), +    ); + +    my $list = Gtk2::ListStore->new("Glib::Boolean", "Glib::Boolean", "Glib::String", "Glib::String", "Glib::Boolean"); +    $list_tv = Gtk2::TreeView->new_with_model($list); +    $list_tv->get_selection->set_mode('multiple'); +    my ($dw_button, $edit_button, $remove_button, $up_button); +    $list_tv->get_selection->signal_connect(changed => sub { +        my ($selection) = @_; +        my @rows = $selection->get_selected_rows; +        my $model = $list; +        # we can delete several medium at a time: +        $remove_button and $remove_button->set_sensitive($#rows != -1); +        # we can only edit/move one item at a time: +        $_ and $_->set_sensitive(@rows == 1) foreach $up_button, $dw_button, $edit_button; + +        # we can only up/down one item if not at begin/end: +        return if @rows != 1; +	 +        my $curr_path = $rows[0]; +        my $first_path = $model->get_path($model->get_iter_first); +        $up_button->set_sensitive($first_path && $first_path->compare($curr_path)); + +        $curr_path->next; +        my $next_item = $model->get_iter($curr_path); +        $dw_button->set_sensitive($next_item); # && !$model->get($next_item, 0) +    }); + +    $list_tv->set_rules_hint(1); +    $list_tv->set_reorderable(1); + +    my $reorder_ok = 1; +    $list->signal_connect( +	row_deleted => sub { +	    $reorder_ok or return; +	    my ($model) = @_; +	    my @media; +	    $model->foreach( +		sub { +		    my (undef, undef, $iter) = @_; +		    my $name = $model->get($iter, $col{mainw}{name}); +		    push @media, urpm::media::name2medium($urpm, $name); +		    0; +		}, undef); +	    @{$urpm->{media}} = @media; +	}, +    ); + +    $list_tv->append_column(Gtk2::TreeViewColumn->new_with_attributes(N("Enabled"), +                                                                      my $tr = Gtk2::CellRendererToggle->new, +                                                                      'active' => $col{mainw}{is_enabled})); +    $list_tv->append_column(Gtk2::TreeViewColumn->new_with_attributes(N("Updates"), +                                                                      my $cu = Gtk2::CellRendererToggle->new, +                                                                      'active' => $col{mainw}{is_update}, +                                                                      activatable => $col{mainw}{activatable})); +    $list_tv->append_column(Gtk2::TreeViewColumn->new_with_attributes(N("Type"), +                                                                      Gtk2::CellRendererText->new, +                                                                      'text' => $col{mainw}{type})); +    $list_tv->append_column(Gtk2::TreeViewColumn->new_with_attributes(N("Medium"), +                                                                      Gtk2::CellRendererText->new, +                                                                      'text' => $col{mainw}{name})); +    my $id; +    $id = $tr->signal_connect( +	toggled => sub { +	    my (undef, $path) = @_; +	    $tr->signal_handler_block($id); +	    my $_guard = before_leaving { $tr->signal_handler_unblock($id) }; +	    my $iter = $list->get_iter_from_string($path); +	    $urpm->{media}[$path]{ignore} = !$urpm->{media}[$path]{ignore} || undef; +	    $list->set($iter, $col{mainw}{is_enabled}, !$urpm->{media}[$path]{ignore}); +	    urpm::media::write_config($urpm); +	    my $ignored = $urpm->{media}[$path]{ignore}; +	    $reread_media->(); +	    if (!$ignored && $urpm->{media}[$path]{ignore}) { +		# reread media failed to un-ignore an ignored medium +		# probably because urpm::media::check_existing_medium() complains +		# about missing synthesis when the medium never was enabled before; +		# thus it restored the ignore bit +		$urpm->{media}[$path]{ignore} = !$urpm->{media}[$path]{ignore} || undef; +		urpm::media::write_config($urpm); +		#- Enabling this media failed, force update +		interactive_msg('rpmdragora', +		    N("This medium needs to be updated to be usable. Update it now?"), +		    yesno => 1, +		) and $reread_media->($urpm->{media}[$path]{name}); +	    } +	}, +    ); + +    $cu->signal_connect( +	toggled => sub { +	    my (undef, $path) = @_; +	    my $iter = $list->get_iter_from_string($path); +	    $urpm->{media}[$path]{update} = !$urpm->{media}[$path]{update} || undef; +	    $list->set($iter, $col{mainw}{is_update}, ! !$urpm->{media}[$path]{update}); +         $something_changed = 1; +	}, +    ); + +    $reread_media = sub { +	my ($name) = @_; +        $reorder_ok = 0; +     $something_changed = 1; +	if (defined $name) { +	    urpm::media::select_media($urpm, $name); +	    update_sources_check( +		$urpm, +		{ nolock => 1 }, +		N_("Unable to update medium, errors reported:\n\n%s"), +		$name, +	    ); +	} +	# reread configuration after updating media else ignore bit will be restored +	# by urpm::media::check_existing_medium(): +	$urpm = fast_open_urpmi_db(); +	$list->clear; +     foreach (grep { ! $_->{external} } @{$urpm->{media}}) { +         my $name = $_->{name}; +         $list->append_set($col{mainw}{is_enabled} => !$_->{ignore}, +                           $col{mainw}{is_update} => ! !$_->{update}, +                           $col{mainw}{type} => get_medium_type($_), +                           $col{mainw}{name} => $name, +                           $col{mainw}{activatable} => to_bool($::expert), +                       ); +     } +        $reorder_ok = 1; +    }; +    $reread_media->(); +    $something_changed = 0; + +    gtkadd( +	$mainw->{window}, +	gtkpack_( +	    gtknew('VBox', spacing => 5), +	    0, $menu, +	    ($0 =~ /rpm-edit-media|edit-urpm-sources/ ? (0, Gtk2::Banner->new($ugtk2::wm_icon, N("Configure media"))) : ()), +	    1, gtkpack_( +		gtknew('HBox', spacing => 10), +		1, gtknew('ScrolledWindow', child => $list_tv), +		0, gtkpack__( +		    gtknew('VBox', spacing => 5), +		    gtksignal_connect( +			$remove_button = Gtk2::Button->new(but(N("Remove"))), +			clicked => sub { remove_callback() and $reread_media->() }, +		    ), +		    gtksignal_connect( +			$edit_button = Gtk2::Button->new(but(N("Edit"))), +			clicked => sub { +			    my $name = edit_callback(); defined $name and $reread_media->($name); +			} +		    ), +		    gtksignal_connect( +			Gtk2::Button->new(but(N("Add"))), +			clicked => sub { easy_add_callback() and $reread_media->() }, +		    ), +		    gtkpack( +			gtknew('HBox'), +			gtksignal_connect( +                            $up_button = gtknew('Button', +                                                image => gtknew('Image', stock => 'gtk-go-up')), +                            clicked => \&upwards_callback), + +			gtksignal_connect( +                            $dw_button = gtknew('Button', +                                                image => gtknew('Image', stock => 'gtk-go-down')), +                            clicked => \&downwards_callback), +		    ), +		) +	    ), +	    0, gtknew('HSeparator'), +	    0, gtknew('HButtonBox', layout => 'edge', children_loose => [ +		gtksignal_connect(Gtk2::Button->new(but(N("Help"))), clicked => sub { rpmdragora::open_help('sources') }), +		gtksignal_connect(Gtk2::Button->new(but(N("Ok"))), clicked => sub { Gtk2->main_quit }) +	    ]) +	) +    ); +    $_->set_sensitive(0) foreach $dw_button, $edit_button, $remove_button, $up_button; + +    $mainw->{rwindow}->set_size_request(600, 400); +    $mainw->main; +    return $something_changed; +} + + +sub run() { +    # ignore rpmdragora's option regarding ignoring debug media: +    local $ignore_debug_media = [ 0 ]; +    local $ugtk2::wm_icon = get_icon('rpmdragora-mdk', 'title-media'); +    my $lock; +    { +        $urpm = fast_open_urpmi_db(); +        my $err_msg = "urpmdb locked\n"; +        local $urpm->{fatal} = sub { +            interactive_msg('rpmdragora', +                            N("The Package Database is locked. Please close other applications +working with the Package Database. Do you have another media +manager on another desktop, or are you currently installing +packages as well?")); +            die $err_msg; +        }; +        # lock urpmi DB +        eval { $lock = urpm::lock::urpmi_db($urpm, 'exclusive', wait => $urpm->{options}{wait_lock}) }; +        if (my $err = $@) { +            return if $err eq $err_msg; +            die $err; +        } +    } + +    my $res = mainwindow(); +    urpm::media::write_config($urpm); + +    writeconf(); + +    undef $lock; +    $res; +} + + +1; diff --git a/lib/AdminPanel/Rpmdragora/formatting.pm b/lib/AdminPanel/Rpmdragora/formatting.pm new file mode 100644 index 0000000..ccbdf0c --- /dev/null +++ b/lib/AdminPanel/Rpmdragora/formatting.pm @@ -0,0 +1,192 @@ +# vim: set et ts=4 sw=4: +package AdminPanel::Rpmdragora::formatting; +#***************************************************************************** +# +#  Copyright (c) 2002 Guillaume Cottenceau +#  Copyright (c) 2002-2006 Thierry Vignaud <tvignaud@mandriva.com> +#  Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +#  Copyright (c) 2005, 2006 Mandriva SA +# +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +# +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +# +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +#***************************************************************************** +# +# $Id: formatting.pm 261189 2009-10-01 14:44:39Z tv $ + +use strict; +use utf8; +use POSIX qw(strftime); +use AdminPanel::rpmdragora; +use lib qw(/usr/lib/libDrakX); +use MDK::Common::Various; # included for internal_error subroutine +use common; +#use ugtk2 qw(escape_text_for_TextView_markup_format); + +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw( +                    $spacing +                    ensure_utf8 +                    format_changelog_changelogs +                    format_changelog_string +                    format_field +                    format_header +                    format_list +                    format_name_n_summary +                    format_size +                    format_filesize +                    format_update_field +                    my_fullname +                    pkg2medium +                    rpm_description +                    split_fullname +                    urpm_name +            ); + + +sub escape_text_for_TextView_markup_format { +    my ($str) = @_; +    my %rules = ('&' => '&', +                 '<' => '<', +                 '>' => '>', +    ); +    eval { $str =~ s!([&<>])!$rules{$1}!g }; #^(&(amp|lt|gt);)!!) { +    if (my $err = $@) { +        internal_error("$err\n$str"); +    } +    $str; +} + +# from rpmtools, #37482: +sub ensure_utf8 { +    if (utf8::is_utf8($_[0])) { +	utf8::valid($_[0]) and return; + +	utf8::encode($_[0]); #- disable utf8 flag +	utf8::upgrade($_[0]); +    } else { +	utf8::decode($_[0]); #- try to set utf8 flag +	utf8::valid($_[0]) and return; +	warn "do not know what to with $_[0]\n"; +    } +} + +sub rpm_description { +    my ($description) = @_; +    ensure_utf8($description); +    my ($t, $tmp); +    foreach (split "\n", $description) { +	s/^\s*//; +        if (/^$/ || /^\s*(-|\*|\+|o)\s/) { +            $t || $tmp and $t .= "$tmp\n"; +            $tmp = $_; +        } else { +            $tmp = ($tmp ? "$tmp " : ($t && "\n") . $tmp) . $_; +        } +    } +    "$t$tmp\n"; +} + +sub split_fullname { $_[0] =~ /^(.*)-([^-]+)-([^-]+)\.([^.-]+)$/ } + +sub my_fullname { +    return '?-?-?' unless ref $_[0]; +    my ($name, $version, $release) = $_[0]->fullname; +    "$name-$version-$release"; +} + +sub urpm_name { +    return '?-?-?.?' unless ref $_[0]; +    scalar $_[0]->fullname; +} + +sub pkg2medium { +    my ($p, $urpm) = @_; +    return if !ref $p; +    return { name => N("None (installed)") } if !defined($p->id); # if installed +    URPM::pkg2media($urpm->{media}, $p) || { name => N("Unknown"), fake => 1 }; +} + +# [ duplicate urpmi's urpm::msg::localtime2changelog() ] +#- strftime returns a string in the locale charset encoding; +#- but gtk2 requires UTF-8, so we use to_utf8() to ensure the +#- output of localtime2changelog() is always in UTF-8 +#- as to_utf8() uses LC_CTYPE for locale encoding and strftime() uses LC_TIME, +#- it doesn't work if those two variables have values with different +#- encodings; but if a user has a so broken setup we can't do much anyway +sub localtime2changelog { to_utf8(POSIX::strftime("%c", localtime($_[0]))) } + +our $spacing = "        "; +sub format_changelog_string { +    my ($installed_version, $string) = @_; +    #- preprocess changelog for faster TextView insert reaction +    require Gtk2::Pango; +    my %date_attr = ('weight' => Gtk2::Pango->PANGO_WEIGHT_BOLD); +    my %update_attr = ('style' => 'italic'); +    my $version; +    my $highlight; +    [ map { +        my %attrs; +        if (/^\*/) { +            add2hash(\%attrs, \%date_attr); +            ($version) = /(\S*-\S*)\s*$/; +            $highlight = $installed_version ne N("(none)") && 0 < URPM::rpmvercmp($version, $installed_version); +        } +        add2hash(\%attrs, \%update_attr) if $highlight; +        [ "$spacing$_\n", if_(%attrs, \%attrs) ]; +    } split("\n", $string) ]; +} + +sub format_changelog_changelogs { +    my ($installed_version, @changelogs) = @_; +    format_changelog_string($installed_version, join("\n", map { +        "* " . localtime2changelog($_->{time}) . " $_->{name}\n\n$_->{text}\n"; +    } @changelogs)); +} + +sub format_update_field { +    my ($name) = @_; +    '<i>' . eval { escape_text_for_TextView_markup_format($name) } . '</i>'; +} + +sub format_name_n_summary { +    my ($name, $summary) = @_; +    join("\n", '<b>' . $name . '</b>', escape_text_for_TextView_markup_format($summary)); +} + +sub format_header { +    my ($str) = @_; +    '<big>' . escape_text_for_TextView_markup_format($str) . '</big>'; +} + +sub format_field { +    my ($str) = @_; +    '<b>' . escape_text_for_TextView_markup_format($str) . '</b>'; +} + +sub format_size { +    my ($size) = @_; +    $size >= 0 ?  +      N("%s of additional disk space will be used.", formatXiB($size)) : +        N("%s of disk space will be freed.", formatXiB(-$size)); +} + +sub format_filesize { +    my ($filesize) = @_; +    $filesize ? N("%s of packages will be retrieved.", formatXiB($filesize)) : (); +} + +sub format_list { join("\n", map { s/^(\s)/  $1/mg; "- $_" } sort { uc($a) cmp uc($b) } @_) } + +1; diff --git a/lib/AdminPanel/Rpmdragora/gui.pm b/lib/AdminPanel/Rpmdragora/gui.pm new file mode 100644 index 0000000..66d41fd --- /dev/null +++ b/lib/AdminPanel/Rpmdragora/gui.pm @@ -0,0 +1,1220 @@ +# vim: set et ts=4 sw=4: +package AdminPanel::Rpmdragora::gui; +#***************************************************************************** +# +#  Copyright (c) 2002 Guillaume Cottenceau +#  Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com> +#  Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +#  Copyright (c) 2005-2007 Mandriva SA +#  Copyright (c) 2013 Matteo Pasotti <matteo.pasotti@gmail.com> +# +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +# +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +# +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +#***************************************************************************** +# +# $Id$ + +############################################################ +# WARNING: do not modify before asking matteo or anaselli +############################################################ + +use strict; +our @ISA = qw(Exporter); +use lib qw(/usr/lib/libDrakX); +use common; + +# TO WORKAROUND LOCALIZATION ISSUE +use AdminPanel::Rpmdragora::localization; + +use AdminPanel::rpmdragora; +use AdminPanel::Rpmdragora::open_db; +use AdminPanel::Rpmdragora::formatting; +use AdminPanel::Rpmdragora::init; +use AdminPanel::Rpmdragora::icon; +use AdminPanel::Rpmdragora::pkg; +use AdminPanel::Shared; +use yui; +use feature 'state'; + +our @EXPORT = qw( +                    $descriptions +                    $find_entry +                    $force_displaying_group +                    $force_rebuild +                    $pkgs +                    $results_ok +                    $results_none +                    $size_free +                    $size_selected +                    $urpm +                    %grp_columns +                    %pkg_columns +                    @filtered_pkgs +                    @initial_selection +                    ask_browse_tree_given_widgets_for_rpmdragora +                    build_tree +                    callback_choices +                    compute_main_window_size +                    do_action +                    get_info +                    get_summary +                    group_has_parent +                    group_parent +                    groups_tree +                    is_locale_available +                    node_state +                    pkgs_provider +                    real_quit +                    reset_search +                    set_node_state +                    sort_callback +                    switch_pkg_list_mode +                    toggle_all +                    toggle_nodes +                    fast_toggle +            ); + +our ($descriptions, %filters, @filtered_pkgs, %filter_methods, $force_displaying_group, $force_rebuild, @initial_selection, $pkgs, $size_free, $size_selected, $urpm); +our ($results_ok, $results_none) = (N("Search results"), N("Search results (none)")); + +our %grp_columns = ( +    label => 0, +    icon => 2, +); + +our %pkg_columns = ( +    text => 0, +    state_icon => 1, +    state => 2, +    selected => 3, +    short_name => 4, +    version => 5, +    release => 6, +    'arch' => 7, +    selectable => 8, +); + +sub compute_main_window_size { +    my ($w) = @_; +    ($typical_width) = string_size($w->{real_window}, translate("Graphical Environment") . "xmms-more-vis-plugins"); +    $typical_width > 600 and $typical_width = 600;  #- try to not being crazy with a too large value +    $typical_width < 150 and $typical_width = 150; +} + +sub get_summary { +    my ($key) = @_; +    my $summary = translate($pkgs->{$key}{pkg}->summary); +    require utf8; +    utf8::valid($summary) ? $summary : @{[]}; +} + +sub build_expander { +    my ($pkg, $label, $type, $get_data, $o_installed_version) = @_; +    my $textview; +    gtkadd( +        gtkshow(my $exp = gtksignal_connect( +            Gtk2::Expander->new(format_field($label)), +            activate => sub { +                state $first; +                return if $first; +                $first = 1; +                slow_func($::main_window->window, sub { +                              extract_header($pkg, $urpm, $type, $o_installed_version); +                              gtktext_insert($textview, $get_data->() || [ [  N("(Not available)") ] ]); +                          }); +            })), +        $textview = gtknew('TextView') +    ); +    $exp->set_use_markup(1); +    $exp; +} + + +sub get_advisory_link { +    my ($update_descr) = @_; +    my $link = gtkshow(Gtk2::LinkButton->new($update_descr->{URL}, N("Security advisory"))); +    $link->set_uri_hook(\&run_help_callback); +    [ $link ]; +} + +sub get_description { +    my ($pkg, $update_descr) = @_; + +    join("<br />", +        (eval { +            $pkg->{description} || $update_descr->{description}; +            } || '<i>'. N("No description").'</i>')); +} + +sub get_string_from_keywords { +    my ($medium, $name) = @_; +    my @media_types; +    if ($medium->{mediacfg}) { +        my ($distribconf, $medium_path) = @{$medium->{mediacfg}}; +        @media_types = split(':', $distribconf->getvalue($medium_path, 'media_type')) if $distribconf; +    } + +    my $unsupported = N("It is <b>not supported</b> by Mageia."); +    my $dangerous = N("It may <b>break</b> your system."); +    my $s; +    $s .= N("This package is not free software") . "\n" if member('non-free', @media_types); +    if ($pkgs->{$name}{is_backport} || member('backport', @media_types)) { +        return join("\n", +                    N("This package contains a new version that was backported."), +                    $unsupported, $dangerous, $s); +    } elsif (member('testing', @media_types)) { +        return join("\n", +                    N("This package is a potential candidate for an update."), +                    $unsupported, $dangerous, $s); +    } elsif (member('updates', @media_types)) { +        return join("\n", +                    (member('official', @media_types) ? +                       N("This is an official update which is supported by Mageia.") +                       : (N("This is an unofficial update."), $unsupported)) +                    , +                    $s); +    } else { +        $s .= N("This is an official package supported by Mageia") . "\n" if member('official', @media_types); +        return $s; +    } +} + +sub get_main_text { +    my ($medium, $fullname, $name, $summary, $is_update, $update_descr) = @_; + +    my $txt = get_string_from_keywords($medium, $fullname); +  +    join("<br />", +        format_header(join(' - ', $name, $summary)) . +            if_($txt, format_field(N("Notice: ")) . $txt), +            if_($is_update, # is it an update? +            format_field(N("Importance: ")) . format_update_field($update_descr->{importance}), +            format_field(N("Reason for update: ")) . format_update_field(rpm_description($update_descr->{pre})), +            ), +            ''  # extra empty line +        ); +} + +sub get_details { +    my ($pkg, $upkg, $installed_version, $raw_medium) = @_; +    my @details = (); +    push @details, format_field(N("Version: ")) . $upkg->EVR; +    push @details, format_field(N("Currently installed version: ")) . $installed_version if($upkg->flag_installed); +    push @details, format_field(N("Group: ")) . translate_group($upkg->group); +    push @details, format_field(N("Architecture: ")) . $upkg->arch; +    push @details, format_field(N("Size: ")) . N("%s KB", int($upkg->size/1024)); +    push @details, eval { format_field(N("Medium: ")) . $raw_medium->{name} }; + +    my @link = get_url_link($upkg, $pkg); +    push @details, join("<br />   ",@link) if(@link); +    unshift @details, "<br />   "; +    join("<br />   ", @details); +} + +sub get_new_deps { +    my ($urpm, $upkg) = @_; +    my $deps_textview; +    my @a = [ gtkadd( +        gtksignal_connect( +            gtkshow(my $dependencies = Gtk2::Expander->new(format_field(N("New dependencies:")))),  +            activate => sub {  +                slow_func($::main_window->window, sub { +                              my $state = {}; +                              my $db = open_rpm_db(); +                              my @requested = $urpm->resolve_requested__no_suggests_( +                                  $db, $state, +                                  { $upkg->id => 1 }, +                              ); +                              @requested = $urpm->resolve_requested_suggests($db, $state, \@requested); +                              undef $db; +                              my @nodes_with_deps = map { urpm_name($_) } @requested; +                              my @deps = sort { $a cmp $b } difference2(\@nodes_with_deps, [ urpm_name($upkg) ]); +                              @deps = N("All dependencies installed.") if !@deps; +                              gtktext_insert($deps_textview, join("\n", @deps)); +                          }); +            } +        ), +        $deps_textview = gtknew('TextView') +    ) ]; +    $dependencies->set_use_markup(1); +    @a; +} + +sub get_url_link { +    my ($upkg, $pkg) = @_; + +    my $url = $upkg->url || $pkg->{url}; + +    if (!$url) { +        open_rpm_db()->traverse_tag_find('name', $upkg->name, sub { $url = $_[0]->url }); +    } + +    return if !$url; + +    my @a; +    push @a, format_field(N("URL: "))."${spacing}$url"; +    @a; +} + +sub files_format { +    my ($files) = @_; +    ugtk2::markup_to_TextView_format( +        '<tt>' . $spacing #- to highlight information +          . join("\n$spacing", map { "\x{200e}$_" } @$files) +            . '</tt>'); +} + +sub format_pkg_simplifiedinfo { +    my ($pkgs, $key, $urpm, $descriptions) = @_; +    my ($name) = split_fullname($key); +    my $pkg = $pkgs->{$key}; +    my $upkg = $pkg->{pkg}; +    return if !$upkg; +    my $raw_medium = pkg2medium($upkg, $urpm); +    my $medium = !$raw_medium->{fake} ? $raw_medium->{name} : undef; +    my $update_descr = $descriptions->{$medium}{$name}; +    # discard update fields if not matching: +    my $is_update = ($upkg->flag_upgrade && $update_descr && $update_descr->{pre}); +    my $summary = get_summary($key); +    my $dummy_string = get_main_text($raw_medium, $key, $name, $summary, $is_update, $update_descr); +    my $s; +    push @$s, $dummy_string; +    push @$s, get_advisory_link($update_descr) if $is_update; + +    push @$s, get_description($pkg, $update_descr); +    push @$s, [ "\n" ]; +    my $installed_version = eval { find_installed_version($upkg) }; + +    #push @$s, [ gtkadd(gtkshow(my $details_exp = Gtk2::Expander->new(format_field(N("Details:")))), +    #                   gtknew('TextView', text => get_details($pkg, $upkg, $installed_version, $raw_medium))) ]; +    push @$s, join("\n", format_field(N("Details:"))."\n".get_details($pkg, $upkg, $installed_version, $raw_medium)); +    #$details_exp->set_use_markup(1); +    push @$s, [ "\n\n" ]; +    #push @$s, [ build_expander($pkg, N("Files:"), 'files', sub { files_format($pkg->{files}) }) ]; +    push @$s, [ "\n\n" ]; +    #push @$s, [ build_expander($pkg, N("Changelog:"), 'changelog',  sub { $pkg->{changelog} }, $installed_version) ]; + +    push @$s, [ "\n\n" ]; +    if ($upkg->id) { # If not installed +        #    push @$s, get_new_deps($urpm, $upkg); +    } +    $s; +} + +sub format_pkg_info { +    my ($pkgs, $key, $urpm, $descriptions) = @_; +    my $pkg = $pkgs->{$key}; +    my $upkg = $pkg->{pkg}; +    my ($name, $version) = split_fullname($key); +    my @files = ( +	format_field(N("Files:\n")), +	exists $pkg->{files} +	    ? '<tt>' . join("\n", map { "\x{200e}$_" } @{$pkg->{files}}) . '</tt>' #- to highlight information +	    : N("(Not available)"), +    ); +    my @chglo = (format_field(N("Changelog:\n")), ($pkg->{changelog} ? @{$pkg->{changelog}} : N("(Not available)"))); +    my @source_info = ( +	$MODE eq 'remove' || !@$max_info_in_descr +	    ? () +	    : ( +		format_field(N("Medium: ")) . pkg2medium($upkg, $urpm)->{name}, +		format_field(N("Currently installed version: ")) . find_installed_version($upkg), +	    ) +    ); +    my @max_info = @$max_info_in_descr && $changelog_first ? (@chglo, @files) : (@files, '', @chglo); +    ugtk2::markup_to_TextView_format(join("\n", format_field(N("Name: ")) . $name, +      format_field(N("Version: ")) . $version, +      format_field(N("Architecture: ")) . $upkg->arch, +      format_field(N("Size: ")) . N("%s KB", int($upkg->size/1024)), +      if_( +	  $MODE eq 'update', +	  format_field(N("Importance: ")) . $descriptions->{$name}{importance} +      ), +      @source_info, +      '', # extra empty line +      format_field(N("Summary: ")) . $upkg->summary, +      '', # extra empty line +      if_( +	  $MODE eq 'update', +	  format_field(N("Reason for update: ")) . rpm_description($descriptions->{$name}{pre}), +      ), +      format_field(N("Description: ")), ($pkg->{description} || $descriptions->{$name}{description} || N("No description")), +      @max_info, +    )); +} + +sub warn_if_no_pkg { +    my ($name) = @_; +    my ($short_name) = split_fullname($name); +    state $warned; +    if (!$warned) { +        $warned = 1; +        interactive_msg(N("Warning"), +                        join("\n", +                             N("The package \"%s\" was found.", $name), +                             N("However this package is not in the package list."), +                             N("You may want to update your urpmi database."), +                             '', +                             N("Matching packages:"), +                             '', +                             join("\n", sort map { +                                 #-PO: this is list fomatting: "- <package_name> (medium: <medium_name>)" +                                 #-PO: eg: "- rpmdragora (medium: "Main Release" +                                 N("- %s (medium: %s)", $_, pkg2medium($pkgs->{$_}{pkg}, $urpm)->{name}); +                             } grep { /^$short_name/ } keys %$pkgs), +                         ), +                        scroll => 1, +                    ); +    } +    return 'XXX'; +} + +sub node_state { +    my ($name) = @_; +    #- checks $_[0] -> hack for partial tree displaying +    return 'XXX' if !$name; +    my $pkg = $pkgs->{$name}; +    my $urpm_obj = $pkg->{pkg}; +    return warn_if_no_pkg($name) if !$urpm_obj; +    $pkg->{selected} ? +      ($urpm_obj->flag_installed ? +         ($urpm_obj->flag_upgrade ? 'to_install' : 'to_remove') +           : 'to_install') +        : ($urpm_obj->flag_installed ?  +            ($pkgs->{$name}{is_backport} ? 'backport' : +             ($urpm_obj->flag_upgrade ? 'to_update' +                : ($urpm_obj->flag_base ? 'base' : 'installed'))) +               : 'uninstalled'); +} + +my ($common, $w, %wtree, %ptree, %pix, @table_item_list); + +sub set_node_state { +    my ($tblItem, $state, $detail_list) = @_; +    return if $state eq 'XXX' || !$state; +    $detail_list->parent()->parent()->startMultipleChanges(); +    $tblItem->addCell($state,"/usr/share/rpmdrake/icons/state_$state.png") if(ref $tblItem eq "yui::YCBTableItem"); +    if(to_bool(member($state, qw(base installed to_install)))){ +        # it should be parent()->setChecked(1) +        $detail_list->checkItem($tblItem, 1); +        # $tblItem->setSelected(1); +    }else{ +        $detail_list->checkItem($tblItem, 0); +        # $tblItem->setSelected(0); +    } +    if(!to_bool($state ne 'base')){ +        #$iter->cell(0)->setLabel('-'); +        $tblItem->cell(0)->setLabel('-'); +    } +    $detail_list->parent()->parent()->doneMultipleChanges(); +} + +sub set_leaf_state { +    my ($leaf, $state, $detail_list) = @_; +    # %ptree is a hash using the pkg name as key and a monodimensional array (?) as value +    # were it is stored the index of the item into the table +    my $nodeIndex = $ptree{$leaf}[0]; +    my $node = itemAt($detail_list,$nodeIndex); +    set_node_state($node, $state, $detail_list); +} + +sub grep_unselected {  +    my @l = shift(); +    my @result = grep { exists $pkgs->{$_} && !$pkgs->{$_}{selected} } @l ; +    return @result; +} + +my %groups_tree = (); + +sub add_parent { +    my ($tree, $root, $state) = @_; +    $tree or return undef; +    #$root or return undef; +    my $parent = 0; +    my @items = split('\|', $root); +    my $i = 0; +    for my $item (@items) { +        chomp $item; +        $item = trim($item); +		my $treeItem;  +		if($i == 0){ +			$parent = $item; +		    $treeItem = new yui::YTreeItem($item,get_icon_path($item,0),0); +			if(!defined($groups_tree{$parent})) { +				$groups_tree{$parent}{parent} = $treeItem; +				$groups_tree{$parent}{children} = (); +				$tree->addItem($groups_tree{$parent}{'parent'}); +			} +		}else{ +            #if(any { $_ ne $item } @{$groups_tree{$parent}{'children'}}){ +            #    push @{$groups_tree{$parent}{'children'}}, $item; +            #} +            if(!defined($groups_tree{$parent}{'children'}{$item})){ +		        $treeItem = new yui::YTreeItem($item,get_icon_path($item,$parent),0); +                $groups_tree{$parent}{'children'}{$item} = $treeItem; +			    $groups_tree{$parent}{'parent'}->addChild($treeItem); +            } +		} +		$i++; +	} +    $tree->rebuildTree(); +} + +sub add_node { +    my ($leaf, $root, $o_options) = @_; +    my $state = node_state($leaf) or return; +    if ($leaf) { +        my $iter; +        if (is_a_package($leaf)) { +            my ($name, $version, $release, $arch) = split_fullname($leaf); +            #OLD $iter = $w->{detail_list_model}->append_set([ $pkg_columns{text} => $leaf, +            #                                              $pkg_columns{short_name} => format_name_n_summary($name, get_summary($leaf)), +            #                                              $pkg_columns{version} => $version, +            #                                              $pkg_columns{release} => $release, +            #                                              $pkg_columns{arch} => $arch, +            #                                          ]); +            $name = "" if(!defined($name)); +            $version = "" if(!defined($version)); +            $release = "" if(!defined($release)); +            $arch = "" if(!defined($arch)); +            #my $newTableItem = new yui::YTableItem(format_name_n_summary($name, get_summary($leaf)), +            my $newTableItem = new yui::YCBTableItem($name."\n".get_summary($leaf), +                                                     $version, +                                                     $release, +                                                     $arch); +            $w->{detail_list}->addItem($newTableItem); +            set_node_state($newTableItem, $state, $w->{detail_list}); +            # $ptree{$leaf} = [ $newTableItem->label() ]; +            $ptree{$leaf} = [ $newTableItem->index() ]; +            $table_item_list[$newTableItem->index()] = $leaf; +            $newTableItem->DISOWN(); +        } else { +            $iter = $w->{tree_model}->append_set(add_parent($w->{tree},$root, $state), [ $grp_columns{label} => $leaf ]); +            #push @{$wtree{$leaf}}, $iter; +        } +    } else { +        my $parent = add_parent($w->{tree}, $root, $state); +        #- hackery for partial displaying of trees, used in rpmdragora: +        #- if leaf is void, we may create the parent and one child (to have the [+] in front of the parent in the ctree) +        #- though we use '' as the label of the child; then rpmdragora will connect on tree_expand, and whenever +        #- the first child has '' as the label, it will remove the child and add all the "right" children +        $o_options->{nochild} or $w->{tree_model}->append_set($parent, [ $grp_columns{label} => '' ]); # test $leaf? +    } +} + +my ($prev_label); +sub update_size { +    my ($common) = shift @_; +    if ($w->{status}) { +        my $new_label = $common->{get_status}(); +        $prev_label="" if(!defined($prev_label)); +        $prev_label ne $new_label and $w->{status}->setText($prev_label = $new_label); +    } +} + +sub treeview_children { +    my($tbl) = @_; +    my $it; +    my @l; +    my $i=0; +    # using iterators +    for ($it = $tbl->itemsBegin(); $it != $tbl->itemsEnd(); ) { +       my $item  = $tbl->YItemIteratorToYItem($it); +       push @l, $item; +       $it = $tbl->nextItem($it); +       $i++; +       if ($i == $tbl->itemsCount()) { +            last; +       } +    } +    # using items +    #for($i=0;$i<$tbl->itemsCount();$i++) { +    #    print " item label " .  $tbl->item($i)->cell(0)->label() . "\n"; +    #    push @l, $tbl->item($i); +    #} +    return @l; +} + +sub children { +    my ($w, @table_item_list) = @_; +    # map { $w->{detail_list}->get($_, $pkg_columns{text}) } treeview_children($w->{detail_list}); +    # map { $table_item_list[$_->index()] } treeview_children($w->{detail_list}); +    my @children = treeview_children($w->{detail_list}); +    my @result; +    for my $child(@children){ +        push @result, $table_item_list[$child->index()]; +    } +    return @result; +} + +sub itemAt { +    my ($table, $index) = @_; +    return $table->item($index); +    #return bless ($table->item($index),'yui::YTableItem'); +    #foreach my $item(treeview_children($table)){ +    #    if($item->index() == $index){ +    #        print "\n== item label ".$item->label()."\n"; +    #        return bless ($item, 'yui::YTableItem'); +    #    } +    #} +} + +sub toggle_all { +    my ($common, $_val) = @_; +    my $w = $common->{widgets}; +    my @l = children($w, $common->{table_item_list}) or return; + +    my @unsel = grep_unselected(@l); +    my @p = @unsel ? +      #- not all is selected, select all if no option to potentially override +      (exists $common->{partialsel_unsel} && $common->{partialsel_unsel}->(\@unsel, \@l) ? difference2(\@l, \@unsel) : @unsel) +        : @l; +    # toggle_nodes($w->{detail_list}, $w->{detail_list_model}, \&set_leaf_state, node_state($p[0]), @p); +    toggle_nodes($w->{detail_list}, $w->{detail_list}, \&set_leaf_state, node_state($p[0][0]), @{$p[0]}); +    update_size($common); +} + +sub fast_toggle { +    my ($item) = @_; +    #gtkset_mousecursor_wait($w->{w}{rwindow}->window); +    #my $_cleaner = before_leaving { gtkset_mousecursor_normal($w->{w}{rwindow}->window) }; +    my $name = $common->{table_item_list}[$item->index()]; +    my $urpm_obj = $pkgs->{$name}{pkg}; +    if ($urpm_obj->flag_base) { +        interactive_msg(N("Warning"), N("Removing package %s would break your system", $name)); +        return ''; +    }  +    if ($urpm_obj->flag_skip) {  +        interactive_msg(N("Warning"), N("The \"%s\" package is in urpmi skip list.\nDo you want to select it anyway?", $name), yesno => 1) or return '';  +        $urpm_obj->set_flag_skip(0); +    }  +    if ($Rpmdragora::pkg::need_restart && !$priority_up_alread_warned) {  +        $priority_up_alread_warned = 1; +        interactive_msg(N("Warning"), '<b>' . N("Rpmdragora or one of its priority dependencies needs to be updated first. Rpmdragora will then restart.") . '</b>' . "\n\n"); +    }  +    # toggle_nodes($w->{tree}->window, $w->{detail_list_model}, \&set_leaf_state, $w->{detail_list_model}->get($iter, $pkg_columns{state}), +    my $state; +#pasmatt checked should be to install no? +    if($item->checked()){ +        $state = "to_install"; +    }else{ +        $state = "to_remove"; +    } +    toggle_nodes($w->{tree}, $w->{detail_list}, \&set_leaf_state, $state, $name); +    update_size($common); +}; + +# ask_browse_tree_given_widgets_for_rpmdragora will run gtk+ loop. its main parameter "common" is a hash containing: +# - a "widgets" subhash which holds: +#   o a "w" reference on a ugtk2 object +#   o "tree" & "info" references a TreeView +#   o "info" is a TextView +#   o "tree_model" is the associated model of "tree" +#   o "status" references a Label +# - some methods: get_info, node_state, build_tree, partialsel_unsel, grep_unselected, rebuild_tree, toggle_nodes, get_status +# - "tree_submode": the default mode (by group, ...), ... +# - "state": a hash of misc flags: => { flat => '0' }, +#   o "flat": is the tree flat or not +# - "tree_mode": mode of the tree ("gui_pkgs", "by_group", ...) (mainly used by rpmdragora) +           +sub ask_browse_tree_given_widgets_for_rpmdragora { +    ($common) = @_; +    $w = $common->{widgets}; + +    $common->{table_item_list} = \@table_item_list; + +    $w->{detail_list} ||= $w->{tree}; +    #$w->{detail_list_model} ||= $w->{tree_model}; + +    $common->{add_parent} = \&add_parent; +    my $clear_all_caches = sub { +	%ptree = %wtree = (); +    @table_item_list = (); +    }; +    $common->{clear_all_caches} = $clear_all_caches; +    $common->{delete_all} = sub { +	    $clear_all_caches->(); +        $w->{detail_list}->deleteAllItems() if($w->{detail_list}->hasItems()); +	    $w->{tree}->deleteAllItems() if($w->{tree}->hasItems()); +        %groups_tree = (); +    }; +    $common->{rebuild_tree} = sub { +	    $common->{delete_all}->(); +	    $common->{build_tree}($common->{state}{flat}, $common->{tree_mode}); +	    update_size($common); +    }; +    $common->{delete_category} = sub { +	my ($cat) = @_; +	exists $wtree{$cat} or return; +	%ptree = (); + +	if (exists $wtree{$cat}) { +	    my $_iter_str = $w->{tree_model}->get_path_str($wtree{$cat}); +	    $w->{tree_model}->remove($wtree{$cat}); +	    delete $wtree{$cat}; +	} +	update_size($common); +    }; +    $common->{add_nodes} = sub { +	my (@nodes) = @_; +	$w->{detail_list}->deleteAllItems(); +	#$w->{detail_list}->scroll_to_point(0, 0); +    foreach(@nodes){ +	    add_node($_->[0], $_->[1], $_->[2]); +    } +	update_size($common); +    }; +     +    $common->{display_info} = sub { +        gtktext_insert($w->{info}, get_info($_[0], $w->{tree}->window)); +        $w->{info}->scroll_to_iter($w->{info}->get_buffer->get_start_iter, 0, 0, 0, 0); +        0; +    }; + +    my $fast_toggle = sub { +        my ($item) = @_; +        #gtkset_mousecursor_wait($w->{w}{rwindow}->window); +        #my $_cleaner = before_leaving { gtkset_mousecursor_normal($w->{w}{rwindow}->window) }; +        my $name = $common->{table_item_list}[$item->index()]; +        my $urpm_obj = $pkgs->{$name}{pkg}; + +        if ($urpm_obj->flag_base) { +            interactive_msg(N("Warning"), +                            N("Removing package %s would break your system", $name)); +            return ''; +        } + +        if ($urpm_obj->flag_skip) { +            interactive_msg(N("Warning"), N("The \"%s\" package is in urpmi skip list.\nDo you want to select it anyway?", $name), yesno => 1) or return ''; +            $urpm_obj->set_flag_skip(0); +        } + +        if ($Rpmdragora::pkg::need_restart && !$priority_up_alread_warned) { +            $priority_up_alread_warned = 1; +            interactive_msg(N("Warning"), '<b>' . N("Rpmdragora or one of its priority dependencies needs to be updated first. Rpmdragora will then restart.") . '</b>' . "\n\n"); +        } + +        # toggle_nodes($w->{tree}->window, $w->{detail_list_model}, \&set_leaf_state, $w->{detail_list_model}->get($iter, $pkg_columns{state}), +    toggle_nodes($w->{tree}->window, $w->{detail_list_model}, \&set_leaf_state, $item->selected, $common->{table_item_list}[$item->index()]); +	    update_size($common); +    }; +    #$w->{detail_list}->get_selection->signal_connect(changed => sub { +	#my ($model, $iter) = $_[0]->get_selected; +	#$model && $iter or return; +    # $common->{display_info}($model->get($iter, $pkg_columns{text})); +	#}); +    # WARNING: รจ interessante! +    #($w->{detail_list}->get_column(0)->get_cell_renderers)[0]->signal_connect(toggled => sub { +    #    my ($_cell, $path) = @_; #text_ +    #    my $iter = $w->{detail_list_model}->get_iter_from_string($path); +    #    $fast_toggle->($iter) if $iter; +    # 1; +    #}); +    $common->{rebuild_tree}->(); +    update_size($common); +    $common->{initial_selection} and toggle_nodes($w->{tree}->window, $w->{detail_list}, \&set_leaf_state, undef, @{$common->{initial_selection}}); +    my $_b = before_leaving { $clear_all_caches->() }; +    $common->{init_callback}->() if $common->{init_callback}; +    #OLD $w->{w}->main; +    $w->{w}; +} + +our $find_entry; + +sub reset_search() { +    return if !$common; +    $common->{delete_category}->($_) foreach $results_ok, $results_none; +    # clear package list: +    $common->{add_nodes}->(); +} + +sub is_a_package { +    my ($pkg) = @_; +    return exists $pkgs->{$pkg}; +} + +sub switch_pkg_list_mode { +    my ($mode) = @_; +    return if !$mode; +    return if !$filter_methods{$mode}; +    $force_displaying_group = 1; +    $filter_methods{$mode}->(); +} + +sub is_updatable { +    my $p = $pkgs->{$_[0]}; +    $p->{pkg} && !$p->{selected} && $p->{pkg}->flag_installed && $p->{pkg}->flag_upgrade; +} + +sub pkgs_provider { +    my ($mode, %options) = @_; +    return if !$mode; +    my $h = &get_pkgs(%options); +    ($urpm, $descriptions) = @$h{qw(urpm update_descr)}; +    $pkgs = $h->{all_pkgs}; +    %filters = ( +        non_installed => $h->{installable}, +        installed => $h->{installed}, +        all => [ keys %$pkgs ], +    ); +    my %tmp_filter_methods = ( +        all => sub {  +            [ difference2([ keys %$pkgs ], $h->{inactive_backports}) ]; +        }, +        all_updates => sub { +            # potential "updates" from media not tagged as updates: +            if (!$options{pure_updates} && !$Rpmdragora::pkg::need_restart) { +                [ @{$h->{updates}}, +                  difference2([ grep { is_updatable($_) } @{$h->{installable}} ], $h->{backports}) ]; +            } else { +                [ difference2($h->{updates}, $h->{inactive_backports}) ]; +            } +        }, +        backports => sub { $h->{backports} }, +        meta_pkgs => sub {  +            [ difference2($h->{meta_pkgs}, $h->{inactive_backports}) ]; +        }, +        gui_pkgs => sub {  +            [ difference2($h->{gui_pkgs}, $h->{inactive_backports}) ]; +        }, +    ); +    foreach my $importance (qw(bugfix security normal)) { +        $tmp_filter_methods{$importance} = sub { +            my @media = keys %$descriptions; +            [ grep {  +                my ($name) = split_fullname($_); +                my $medium = find { $descriptions->{$_}{$name} } @media; +                $medium && $descriptions->{$medium}{$name}{importance} eq $importance } @{$h->{updates}} ]; +        }; +    } + +    undef %filter_methods; +    foreach my $type (keys %tmp_filter_methods) { +        $filter_methods{$type} = sub { +            $force_rebuild = 1; # force rebuilding tree since we changed filter (FIXME: switch to SortModel) +            @filtered_pkgs = intersection($filters{$filter->[0]}, $tmp_filter_methods{$type}->()); +        }; +    } + +    switch_pkg_list_mode($mode); +} + +sub closure_removal { +    local $urpm->{state} = {}; +    urpm::select::find_packages_to_remove($urpm, $urpm->{state}, \@_); +} + +sub is_locale_available { +    my ($name) = @_; +    any { $urpm->{depslist}[$_]->flag_selected } keys %{$urpm->{provides}{$name} || {}} and return 1; +    my $found; +    open_rpm_db()->traverse_tag_find('name', $name, sub { $found = 1 }); +    return $found; +} + +sub callback_choices { +    my (undef, undef, undef, $choices) = @_; +    return $choices->[0] if $::rpmdragora_options{auto}; +    foreach my $pkg (@$choices) { +        foreach ($pkg->requires_nosense) { +            /locales-/ or next; +            is_locale_available($_) and return $pkg; +        } +    } +    my $callback = sub { interactive_msg(N("More information on package..."), get_info($_[0]), scroll => 1) }; +    $choices = [ sort { $a->name cmp $b->name } @$choices ]; +    my @choices = interactive_list_(N("Please choose"), (scalar(@$choices) == 1 ?  +    N("The following package is needed:") : N("One of the following packages is needed:")), +                                    [ map { urpm_name($_) } @$choices ], $callback, nocancel => 1); +    defined $choices[0] ? $choices->[$choices[0]] : undef; +} + +sub deps_msg { +        return 1 if $dont_show_selections->[0]; +        my ($title, $msg, $nodes, $nodes_with_deps) = @_; +        my @deps = sort { $a cmp $b } difference2($nodes_with_deps, $nodes); +        @deps > 0 or return 1; +      deps_msg_again: +        my $results = interactive_msg( +            $title, $msg . +              format_list(map { scalar(urpm::select::translate_why_removed_one($urpm, $urpm->{state}, $_)) } @deps) +                . "\n\n" . format_size($urpm->selected_size($urpm->{state})), +            yesno => [ N("Cancel"), N("More info"), N("Ok") ], +            scroll => 1, +        ); +        if ($results eq +		    #-PO: Keep it short, this is gonna be on a button +		    N("More info")) { +            interactive_packtable( +                N("Information on packages"), +                $::main_window, +                undef, +                [ map { my $pkg = $_; +                        [ gtknew('HBox', children_tight => [ gtkset_selectable(gtknew('Label', text => $pkg), 1) ]), +                          gtknew('Button', text => N("More information on package..."),  +                                 clicked => sub { +                                     interactive_msg(N("More information on package..."), get_info($pkg), scroll => 1); +                                 }) ] } @deps ], +                [ gtknew('Button', text => N("Ok"),  +                         clicked => sub { Gtk2->main_quit }) ] +            ); +            goto deps_msg_again; +        } else { +            return $results eq N("Ok"); +        } +} + +sub toggle_nodes { +    my ($widget, $detail_list, $set_state, $old_state, @nodes) = @_; +    @nodes = grep { exists $pkgs->{$_} } @nodes +      or return; +    #- avoid selecting too many packages at once +    return if !$dont_show_selections->[0] && @nodes > 2000; +    my $new_state = !$pkgs->{$nodes[0]}{selected}; + +    my @nodes_with_deps; + +    my $bar_id = statusbar_msg(N("Checking dependencies of package..."), 0); + +    my $warn_about_additional_packages_to_remove = sub { +        my ($msg) = @_; +        statusbar_msg_remove($bar_id); +        deps_msg(N("Some additional packages need to be removed"), +                 formatAlaTeX($msg) . "\n\n", +                 \@nodes, \@nodes_with_deps) or @nodes_with_deps = (); +    }; + +    if (member($old_state, qw(to_remove installed))) { # remove pacckages +        if ($new_state) { +            my @remove; +            slow_func($widget, sub { @remove = closure_removal(@nodes) }); +            @nodes_with_deps = grep { !$pkgs->{$_}{selected} && !/^basesystem/ } @remove; +            $warn_about_additional_packages_to_remove->( +                N("Because of their dependencies, the following package(s) also need to be removed:")); +            my @impossible_to_remove; +            foreach (grep { exists $pkgs->{$_}{base} } @remove) { +                ${$pkgs->{$_}{base}} == 1 ? push @impossible_to_remove, $_ : ${$pkgs->{$_}{base}}--; +            } +            @impossible_to_remove and interactive_msg(N("Some packages cannot be removed"), +                                                      N("Removing these packages would break your system, sorry:\n\n") . +                                                        format_list(@impossible_to_remove)); +            @nodes_with_deps = difference2(\@nodes_with_deps, \@impossible_to_remove); +        } else { +            @nodes_with_deps = grep { intersection(\@nodes, [ closure_removal($_) ]) } +                              grep { $pkgs->{$_}{selected} && !member($_, @nodes) } keys %$pkgs; +            push @nodes_with_deps, @nodes; +            $warn_about_additional_packages_to_remove->( +                N("Because of their dependencies, the following package(s) must be unselected now:\n\n")); +            $pkgs->{$_}{base} && ${$pkgs->{$_}{base}}++ foreach @nodes_with_deps; +        } +    } else { +        if ($new_state) { +            if (@nodes > 1) { +                #- unselect i18n packages of which locales is not already present (happens when user clicks on KDE group) +                my @bad_i18n_pkgs; +                foreach my $sel (@nodes) { +                    foreach ($pkgs->{$sel}{pkg}->requires_nosense) { +                        /locales-([^-]+)/ or next; +                        $sel =~ /-$1[-_]/ && !is_locale_available($_) and push @bad_i18n_pkgs, $sel; +                    } +                } +                @nodes = difference2(\@nodes, \@bad_i18n_pkgs); +            } +            my @requested; +            @requested = $urpm->resolve_requested( +                    open_rpm_db(), $urpm->{state}, +                    { map { $pkgs->{$_}{pkg}->id => 1 } @nodes }, +                    callback_choices => \&callback_choices, +            ); +            @nodes_with_deps = map { urpm_name($_) } @requested; +            statusbar_msg_remove($bar_id); +            if (!deps_msg(N("Additional packages needed"), +                             formatAlaTeX(N("To satisfy dependencies, the following package(s) also need to be installed:\n\n")) . "\n\n", +                             \@nodes, \@nodes_with_deps)) { +                @nodes_with_deps = (); +                $urpm->disable_selected(open_rpm_db(), $urpm->{state}, @requested); +                goto packages_selection_ok; +            } + +	    if (my $conflicting_msg = urpm::select::conflicting_packages_msg($urpm, $urpm->{state})) { +                if (!interactive_msg(N("Conflicting Packages"), $conflicting_msg, yesno => 1, scroll => 1)) { +		    @nodes_with_deps = (); +		    $urpm->disable_selected(open_rpm_db(), $urpm->{state}, @requested); +		    goto packages_selection_ok; +		} +	    } + +            if (my @cant = sort(difference2(\@nodes, \@nodes_with_deps))) { +                my @ask_unselect = urpm::select::unselected_packages($urpm->{state}); +                my @reasons = map { +                    my $cant = $_; +                    my $unsel = find { $_ eq $cant } @ask_unselect; +                    $unsel +                      ? join("\n", urpm::select::translate_why_unselected($urpm, $urpm->{state}, $unsel)) +                        : ($pkgs->{$_}{pkg}->flag_skip ? N("%s (belongs to the skip list)", $cant) : $cant); +                } @cant; +                my $count = @reasons; +                interactive_msg( +                    ($count == 1 ? N("One package cannot be installed") : N("Some packages cannot be installed")), +		    ($count == 1 ?  +                 N("Sorry, the following package cannot be selected:\n\n%s", format_list(@reasons)) +                   : N("Sorry, the following packages cannot be selected:\n\n%s", format_list(@reasons))), +                    scroll => 1, +                ); +                foreach (@cant) { +                    next unless $pkgs->{$_}{pkg}; +                    $pkgs->{$_}{pkg}->set_flag_requested(0); +                    $pkgs->{$_}{pkg}->set_flag_required(0); +                } +            } +          packages_selection_ok: +        } else { +            my @unrequested; +            @unrequested = $urpm->disable_selected(open_rpm_db(), $urpm->{state}, +                                                                   map { $pkgs->{$_}{pkg} } @nodes);  +            @nodes_with_deps = map { urpm_name($_) } @unrequested; +            statusbar_msg_remove($bar_id); +            if (!deps_msg(N("Some packages need to be removed"), +                             N("Because of their dependencies, the following package(s) must be unselected now:\n\n"), +                             \@nodes, \@nodes_with_deps)) { +                @nodes_with_deps = (); +                $urpm->resolve_requested(open_rpm_db(), $urpm->{state}, { map { $_->id => 1 } @unrequested }); +                goto packages_unselection_ok; +            } +          packages_unselection_ok: +        } +    } + +    foreach (@nodes_with_deps) { +        #- some deps may exist on some packages which aren't listed because +        #- not upgradable (older than what currently installed) +        exists $pkgs->{$_} or next; +        if (!$pkgs->{$_}{pkg}) { #- can't be removed  # FIXME; what about next packages in the loop? +            undef $pkgs->{$_}{selected}; +            log::explanations("can't be removed: $_"); +        } else { +            $pkgs->{$_}{selected} = $new_state; +        } +        # invoke set_leaf_state($pkgname, node_state, ) +        # node_state = {to_install, to_remove,...} +        $set_state->($_, node_state($_), $detail_list); +        if (my $pkg = $pkgs->{$_}{pkg}) { +            # FIXME: shouldn't we threat all of them as POSITIVE (as selected size) +            $size_selected += $pkg->size * ($pkg->flag_installed && !$pkg->flag_upgrade ? ($new_state ? -1 : 1) : ($new_state ? 1 : -1)); +        } +    } +} + +sub is_there_selected_packages() { +    int(grep { $pkgs->{$_}{selected} } keys %$pkgs); +} + +sub real_quit() { +    if (is_there_selected_packages()) { +        interactive_msg(N("Some packages are selected."), N("Some packages are selected.") . "\n" . N("Do you really want to quit?"), yesno => 1) or return; +    } +    Gtk2->main_quit; +} + +sub do_action__real { +    my ($options, $callback_action, $o_info) = @_; +    require urpm::sys; +    if (!urpm::sys::check_fs_writable()) { +        $urpm->{fatal}(1, N("Error: %s appears to be mounted read-only.", $urpm::sys::mountpoint)); +        return 1; +    } +    if (!$Rpmdragora::pkg::need_restart && !is_there_selected_packages()) { +        interactive_msg(N("You need to select some packages first."), N("You need to select some packages first.")); +        return 1; +    } +    my $size_added = sum(map { if_($_->flag_selected && !$_->flag_installed, $_->size) } @{$urpm->{depslist}}); +    if ($MODE eq 'install' && $size_free - $size_added/1024 < 50*1024) { +        interactive_msg(N("Too many packages are selected"), +                        N("Warning: it seems that you are attempting to add so many +packages that your filesystem may run out of free diskspace, +during or after package installation ; this is particularly +dangerous and should be considered with care. + +Do you really want to install all the selected packages?"), yesno => 1) +          or return 1; +    } +    my $res = $callback_action->($urpm, $pkgs); +    if (!$res) { +        $force_rebuild = 1; +        pkgs_provider($options->{tree_mode}, if_($Rpmdragora::pkg::probe_only_for_updates, pure_updates => 1), skip_updating_mu => 1); +        reset_search(); +        $size_selected = 0; +        (undef, $size_free) = MDK::Common::System::df('/usr'); +        $options->{rebuild_tree}->() if $options->{rebuild_tree}; +        gtktext_insert($o_info, '') if $o_info; +    } +    $res; +} + +sub do_action { +    my ($options, $callback_action, $o_info) = @_; +    my $res = eval { do_action__real($options, $callback_action, $o_info) }; +    my $err = $@; +    # FIXME: offer to report the problem into bugzilla: +    if ($err && $err !~ /cancel_perform/) { +        interactive_msg(N("Fatal error"), +                        N("A fatal error occurred: %s.", $err)); +    } +    $res; +} + +sub translate_group { +    join('/', map { translate($_) } split m|/|, $_[0]); +} + +sub ctreefy { +    join('|', map { translate($_) } split m|/|, $_[0]); +} + +sub _build_tree { +    my ($tree, $elems, @elems) = @_; +    #- we populate all the groups tree at first +    %$elems = (); +    # better loop on packages, create groups tree and push packages in the proper place: +    foreach my $pkg (@elems) { +        my $grp = $pkg->[1]; +        # no state for groups (they're not packages and thus have no state) +        add_parent($tree, $grp, undef); +        $elems->{$grp} ||= []; +        push @{$elems->{$grp}}, $pkg; +    } +} + + +sub build_tree { +    my ($tree, $tree_model, $elems, $options, $force_rebuild, $flat, $mode) = @_; +    state $old_mode; +    $mode = $options->{rmodes}{$mode} || $mode; +    $old_mode = '' if(!defined($old_mode)); +    return if $old_mode eq $mode && !$force_rebuild; +    $old_mode = $mode; +    undef $force_rebuild; +    my @elems; +    my $wait; $wait = statusbar_msg(N("Please wait, listing packages...")) if $MODE ne 'update'; +    { +        my @keys = @filtered_pkgs; +        if (member($mode, qw(all_updates security bugfix normal))) { +            @keys = grep { +                my ($name) = split_fullname($_); +                member($descriptions->{$name}{importance}, @$mandrakeupdate_wanted_categories) +                  || ! $descriptions->{$name}{importance}; +            } @keys; +            if (@keys == 0) { +                add_node('', N("(none)"), { nochild => 1 }); +                state $explanation_only_once; +                $explanation_only_once or interactive_msg(N("No update"), +                                                          N("The list of updates is empty. This means that either there is +no available update for the packages installed on your computer, +or you already installed all of them.")); +                $explanation_only_once = 1; +            } +        } +        # FIXME: better do this on first group access for faster startup... +        @elems = map { [ $_, !$flat && ctreefy($pkgs->{$_}{pkg}->group) ] } sort_packages(@keys); +    } +    my %sortmethods = ( +        by_size => sub { sort { $pkgs->{$b->[0]}{pkg}->size <=> $pkgs->{$a->[0]}{pkg}->size } @_ }, +        by_selection => sub { sort { $pkgs->{$b->[0]}{selected} <=> $pkgs->{$a->[0]}{selected} +                                       || uc($a->[0]) cmp uc($b->[0]) } @_ }, +        by_leaves => sub { +            # inlining part of MDK::Common::Data::difference2(): +            my %l; @l{map { $_->[0] } @_} = (); +            my @pkgs_times = ('rpm', '-q', '--qf', '%{name}-%{version}-%{release}.%{arch} %{installtime}\n', +		    map { chomp_($_) } run_program::get_stdout('urpmi_rpm-find-leaves')); +            sort { $b->[1] <=> $a->[1] } grep { exists $l{$_->[0]} } map { chomp; [ split ] } run_rpm(@pkgs_times); +        }, +        flat => sub { no locale; sort { uc($a->[0]) cmp uc($b->[0]) } @_ }, +        by_medium => sub { sort { $a->[2] <=> $b->[2] || uc($a->[0]) cmp uc($b->[0]) } @_ }, +    ); +    if ($flat) { +        add_node($tree->currentItem()->label(), '') foreach $sortmethods{$::mode->[0] || 'flat'}->(@elems); +    } else { +        if (0 && $MODE eq 'update') { +            foreach ($sortmethods{flat}->(@elems)){ +                add_node($tree->currentItem()->label(), $_->[0], N("All"))  +            } +            $tree->expand_row($tree_model->get_path($tree_model->get_iter_first), 0); +        } elsif ($::mode->[0] eq 'by_source') { +             _build_tree($tree, $elems, $sortmethods{by_medium}->(map { +                my $m = pkg2medium($pkgs->{$_->[0]}{pkg}, $urpm); [ $_->[0], $m->{name}, $m->{priority} ]; +            } @elems)); +        } elsif ($::mode->[0] eq 'by_presence') { +            _build_tree($tree, $elems, map { +                my $pkg = $pkgs->{$_->[0]}{pkg}; +                [ $_->[0], $pkg->flag_installed ? +                    (!$pkg->flag_skip && $pkg->flag_upgrade ? N("Upgradable") : N("Installed")) +                      : N("Addable") ]; +              } $sortmethods{flat}->(@elems)); +        } else { +            _build_tree($tree, $elems, @elems); +            # INFO: $elems contains references to the packages of the group, see _build_tree +        } +    } +    statusbar_msg_remove($wait) if defined $wait; +} + +sub get_info { +    my ($key, $widget) = @_; +    #- the package information hasn't been loaded. Instead of rescanning the media, just give up. +    exists $pkgs->{$key} or return [ [ N("Description not available for this package\n") ] ]; +    #- get the description if needed: +    exists $pkgs->{$key}{description} or slow_func($widget, sub { extract_header($pkgs->{$key}, $urpm, 'info', find_installed_version($pkgs->{$key}{pkg})) }); +    format_pkg_simplifiedinfo($pkgs, $key, $urpm, $descriptions); +} + +sub sort_callback { +    my ($store, $treeiter1, $treeiter2) = @_; +    URPM::rpmvercmp(map { $store->get_value($_, $pkg_columns{version}) } $treeiter1, $treeiter2); +} + +sub run_help_callback { +    my (undef, $url) = @_; +    my ($user) = grep { $_->[2] eq $ENV{USERHELPER_UID} } list_passwd(); +    local $ENV{HOME} = $user->[7] if $user && $ENV{USERHELPER_UID}; +    run_program::raw({ detach => 1, as_user => 1 }, 'www-browser', $url); +} + +sub groups_tree { +    return %groups_tree; +} + +sub group_has_parent { +    my ($group) = shift; +    return 0 if(!defined($group)); +    return defined($groups_tree{$group}{parent}); +} + +sub group_parent { +    my ($group) = shift; +    # if group is a parent itself return it +    # who use group_parent have to take care of the comparison +    # between a group and its parent +    # e.g. group System has groups_tree{'System'}{parent}->label() = 'System' +    return $groups_tree{$group}{parent} if(group_has_parent($group)); +    for my $sup (keys %groups_tree){ +        for my $item(keys %{$groups_tree{$sup}{children}}){ +            if(defined($group) && ($item eq $group)){ +                return $groups_tree{$sup}{parent}; +            } +        } +    } +    return undef; +} + +1; diff --git a/lib/AdminPanel/Rpmdragora/gurpm.pm b/lib/AdminPanel/Rpmdragora/gurpm.pm new file mode 100644 index 0000000..983f055 --- /dev/null +++ b/lib/AdminPanel/Rpmdragora/gurpm.pm @@ -0,0 +1,138 @@ +# vim: set et ts=4 sw=4: +package AdminPanel::Rpmdragora::gurpm; +#***************************************************************************** +# +#  Copyright (c) 2002 Guillaume Cottenceau +#  Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com> +#  Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +#  Copyright (c) 2005-2007 Mandriva SA +#  Copyright (c) 2013 Matteo Pasotti <matteo.pasotti@gmail.com> +# +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +# +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +# +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +#***************************************************************************** +# +# $Id: gurpm.pm 255450 2009-04-03 16:00:16Z tv $ + +use strict; +use lib qw(/usr/lib/libDrakX); +use yui; +use Time::HiRes; +use feature 'state'; + +sub new { +    my ($class, $title, $initializing, %options) = @_; +    my $self = { +		my $label = 0, +		my $factory = 0, +		my $mainw = 0, +		my $vbox = 0, +		my $progressbar = 0, +		my $cancel = 0 +	}; +    bless $self, 'AdminPanel::Rpmdragora::gurpm'; +    #my $mainw = bless(ugtk2->new($title, %options, default_width => 600, width => 600), $self); +    $self->{factory} = yui::YUI::widgetFactory; +    $self->{mainw} = $self->{factory}->createPopupDialog(); +    #$::main_window = $self->{mainw}; +    $self->{vbox} = $self->{factory}->createVBox($self->{mainw}); +    #OLD $mainw->{label} = gtknew('Label', text => $initializing, alignment => [ 0.5, 0 ]); +    $self->{label} = $self->{factory}->createLabel($self->{vbox}, $initializing); +    # size label's heigh to 2 lines in order to prevent dummy vertical resizing: +    #my $context = $mainw->{label}->get_layout->get_context; +    #my $metrics = $context->get_metrics($mainw->{label}->style->font_desc, $context->get_language); +    #$mainw->{label}->set_size_request(-1, 2 * Gtk2::Pango->PANGO_PIXELS($metrics->get_ascent + $metrics->get_descent)); + +    #OLD $mainw->{progressbar} = gtknew('ProgressBar'); +    $self->{progressbar} = $self->{factory}->createProgressBar($self->{vbox}, ""); +    #gtkadd($mainw->{window}, $mainw->{vbox} = gtknew('VBox', spacing => 5, border_width => 6, children_tight => [ +    #    $mainw->{label}, +    #    $mainw->{progressbar} +    #])); +    #$mainw->{rwindow}->set_position('center-on-parent'); +    #$mainw->{real_window}->show_all; +    #select(undef, undef, undef, 0.1);  #- hackish :-( +    #$mainw->SUPER::sync; +    $self->{mainw}->pollEvent(); +    $self->flush(); +    $self; +} + +sub flush { +    my ($self) = @_; +    $self->{mainw}->recalcLayout(); +    $self->{mainw}->doneMultipleChanges(); +} + +sub label { +    my ($self, $label) = @_; +    $self->{label} = $self->{factory}->createLabel($self->{vbox},$label); +    #select(undef, undef, undef, 0.1);  #- hackish :-( +    $self->flush(); +} + +sub progress { +    my ($self, $value) = @_; +    state $time; +    $time = 0 if(!defined($time)); +    $value = 0 if $value < 0; +    $value = 100 if 1 < $value; +    $self->{progressbar}->setValue($value); +    return if Time::HiRes::clock_gettime() - $time < 0.333; +    $time = Time::HiRes::clock_gettime(); +    $self->flush(); +} + +sub DESTROY { +    my ($self) = @_; +    #mygtk2::may_destroy($self); +    $self and $self->{mainw}->destroy; +    #$self = undef; +    $self->{cancel} = undef;  #- in case we'll do another one later +} + +sub validate_cancel { +    my ($self, $cancel_msg, $cancel_cb) = @_; +    if (!$self->{cancel}) { +		$self->{cancel} = $self->{factory}->createIconButton($self->{vbox},"",$cancel_msg); +        #gtkpack__( +	    #$self->{vbox}, +	    #$self->{hbox_cancel} = gtkpack__( +		#gtknew('HButtonBox'), +		#$self->{cancel} = gtknew('Button', text => $cancel_msg, clicked => \&$cancel_cb), +	    #), +	#); +    } +    #$self->{cancel}->set_sensitive(1); +    #$self->{cancel}->show; +    $self->flush(); +} + +sub invalidate_cancel { +    my ($self) = @_; +    $self->{cancel} and $self->{cancel}->setEnabled(0); +} + +sub invalidate_cancel_forever { +    my ($self) = @_; +    #$self->{hbox_cancel} or return; +    #$self->{hbox_cancel}->destroy; +    # FIXME: temporary workaround that prevents +    # Gtk2::Label::set_text() set_text_internal() -> queue_resize() -> +    # size_allocate() call chain to mess up when ->shrink_topwindow() +    # has been called (#32613): +    #$self->shrink_topwindow; +} + +1; diff --git a/lib/AdminPanel/Rpmdragora/icon.pm b/lib/AdminPanel/Rpmdragora/icon.pm new file mode 100644 index 0000000..1dee4e5 --- /dev/null +++ b/lib/AdminPanel/Rpmdragora/icon.pm @@ -0,0 +1,236 @@ +# vim: set et ts=4 sw=4: +package AdminPanel::Rpmdragora::icon; +#***************************************************************************** +# +#  Copyright (c) 2002 Guillaume Cottenceau +#  Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com> +#  Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +#  Copyright (c) 2005-2007 Mandriva SA +# +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +# +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +# +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +#***************************************************************************** +# +# $Id: icon.pm 237459 2008-02-26 14:20:47Z tv $ + +use strict; +our @ISA = qw(Exporter); +use lib qw(/usr/lib/libDrakX); +use POSIX; +use common; + +# TO WORKAROUND LOCALIZATION ISSUE +use AdminPanel::Rpmdragora::localization; + +our @EXPORT = qw(get_icon_path); +#- /usr/share/rpmlint/config (duplicates are normal, so that we are not too far away from .py) +my %group_icons = ( +	N("All") => 'system_section', +	N("Accessibility") => 'accessibility_section', +	N("Archiving") => 'archiving_section', +	join('|', N("Archiving"), N("Backup")) => 'backup_section', +	join('|', N("Archiving"), N("Cd burning")) => 'cd_burning_section', +	join('|', N("Archiving"), N("Compression")) => 'compression_section', +	join('|', N("Archiving"), N("Other")) => 'other_archiving', +	N("Communications") => 'communications_section', +	join('|', N("Communications"), N("Bluetooth")) => 'communications_bluetooth_section', +	join('|', N("Communications"), N("Dial-Up")) => 'communications_dialup_section', +	join('|', N("Communications"), N("Fax")) => 'communications_fax_section', +	join('|', N("Communications"), N("Mobile")) => 'communications_mobile_section', +	join('|', N("Communications"), N("Radio")) => 'communications_radio_section', +	join('|', N("Communications"), N("Serial")) => 'communications_serial_section', +	join('|', N("Communications"), N("Telephony")) => 'communications_phone_section', +	N("Databases") => 'databases_section', +	N("Development") => 'development_section', +	join('|', N("Development"), N("Basic")) => '', +	join('|', N("Development"), N("C")) => '', +	join('|', N("Development"), N("C++")) => '', +	join('|', N("Development"), N("C#")) => '', +	join('|', N("Development"), N("Databases")) => 'databases_section', +	join('|', N("Development"), N("Debug")) => '', +	join('|', N("Development"), N("Erlang")) => '', +	join('|', N("Development"), N("GNOME and GTK+")) => 'gnome_section', +	join('|', N("Development"), N("Java")) => '', +	join('|', N("Development"), N("KDE and Qt")) => 'kde_section', +	join('|', N("Development"), N("Kernel")) => '', +	join('|', N("Development"), N("OCaml")) => '', +	join('|', N("Development"), N("Other")) => '', +	join('|', N("Development"), N("Perl")) => '', +	join('|', N("Development"), N("PHP")) => '', +	join('|', N("Development"), N("Python")) => '', +	join('|', N("Development"), N("Tools")) => 'development_tools_section', +	join('|', N("Development"), N("X11")) => '', +	N("Documentation") => 'documentation_section', +	N("Editors") => 'editors_section', +	N("Education") => 'education_section', +	N("Emulators") => 'emulators_section', +	N("File tools") => 'file_tools_section', +	N("Games") => 'amusement_section', +	join('|', N("Games"), N("Adventure")) => 'adventure_section', +	join('|', N("Games"), N("Arcade")) => 'arcade_section', +	join('|', N("Games"), N("Boards")) => 'boards_section', +	join('|', N("Games"), N("Cards")) => 'cards_section', +	join('|', N("Games"), N("Other")) => 'other_amusement', +	join('|', N("Games"), N("Puzzles")) => 'puzzle_section', +	join('|', N("Games"), N("Shooter")) => 'shooter_section', +	join('|', N("Games"), N("Simulation")) => 'simulation_section', +	join('|', N("Games"), N("Sports")) => 'sport_section', +	join('|', N("Games"), N("Strategy")) => 'strategy_section', +	N("Geography") => 'geography_section', +	N("Graphical desktop") => 'graphical_desktop_section', +	join('|', N("Graphical desktop"), +          #-PO: This is a package/product name. Only translate it if needed: +          N("Enlightenment")) => 'enlightment_section', +	join('|', N("Graphical desktop"), +          #-PO: This is a package/product name. Only translate it if needed: +          N("GNOME")) => 'gnome_section', +	join('|', N("Graphical desktop"), +          #-PO: This is a package/product name. Only translate it if needed: +          N("Icewm")) => 'icewm_section', +	join('|', N("Graphical desktop"), +          #-PO: This is a package/product name. Only translate it if needed: +          N("KDE")) => 'kde_section', +	join('|', N("Graphical desktop"), N("Other")) => 'more_applications_other_section', +	join('|', N("Graphical desktop"), +          #-PO: This is a package/product name. Only translate it if needed: +          N("WindowMaker")) => 'windowmaker_section', +	join('|', N("Graphical desktop"), +          #-PO: This is a package/product name. Only translate it if needed: +          N("Xfce")) => 'xfce_section', +	N("Graphics") => 'graphics_section', +	join('|', N("Graphics"), N("3D")) => 'graphics_3d_section', +	join('|', N("Graphics"), N("Editors and Converters")) => 'graphics_editors_section', +	join('|', N("Graphics"), N("Utilities")) => 'graphics_utilities_section', +	join('|', N("Graphics"), N("Photography")) => 'graphics_photography_section', +	join('|', N("Graphics"), N("Scanning")) => 'graphics_scanning_section', +	join('|', N("Graphics"), N("Viewers")) => 'graphics_viewers_section', +	N("Monitoring") => 'monitoring_section', +	N("Networking") => 'networking_section', +	join('|', N("Networking"), N("File transfer")) => 'file_transfer_section', +	join('|', N("Networking"), N("IRC")) => 'irc_section', +	join('|', N("Networking"), N("Instant messaging")) => 'instant_messaging_section', +	join('|', N("Networking"), N("Mail")) => 'mail_section', +	join('|', N("Networking"), N("News")) => 'news_section', +	join('|', N("Networking"), N("Other")) => 'other_networking', +	join('|', N("Networking"), N("Remote access")) => 'remote_access_section', +	join('|', N("Networking"), N("WWW")) => 'networking_www_section', +	N("Office") => 'office_section', +	join('|', N("Office"), N("Dictionary")) => 'office_dictionary_section', +	join('|', N("Office"), N("Finance")) => 'finances_section', +	join('|', N("Office"), N("Management")) => 'timemanagement_section', +	join('|', N("Office"), N("Organizer")) => 'timemanagement_section', +	join('|', N("Office"), N("Utilities")) => 'office_accessories_section', +	join('|', N("Office"), N("Spreadsheet")) => 'spreadsheet_section', +	join('|', N("Office"), N("Suite")) => 'office_suite', +	join('|', N("Office"), N("Word processor")) => 'wordprocessor_section', +	N("Publishing") => 'publishing_section', +	N("Sciences") => 'sciences_section', +	join('|', N("Sciences"), N("Astronomy")) => 'astronomy_section', +	join('|', N("Sciences"), N("Biology")) => 'biology_section', +	join('|', N("Sciences"), N("Chemistry")) => 'chemistry_section', +	join('|', N("Sciences"), N("Computer science")) => 'computer_science_section', +	join('|', N("Sciences"), N("Geosciences")) => 'geosciences_section', +	join('|', N("Sciences"), N("Mathematics")) => 'mathematics_section', +	join('|', N("Sciences"), N("Other")) => 'other_sciences', +	join('|', N("Sciences"), N("Physics")) => 'physics_section', +	N("Security") => 'security_section', +	N("Shells") => 'shells_section', +	N("Sound") => 'sound_section', +	join('|', N("Sound"), N("Editors and Converters")) => 'sound_editors_section', +	join('|', N("Sound"), N("Midi")) => 'sound_midi_section', +	join('|', N("Sound"), N("Mixers")) => 'sound_mixers_section', +	join('|', N("Sound"), N("Players")) => 'sound_players_section', +	join('|', N("Sound"), N("Utilities")) => 'sound_utilities_section', +	N("System") => 'system_section', +	join('|', N("System"), N("Base")) => 'system_section', +	join('|', N("System"), N("Boot and Init")) => 'boot_init_section', +	join('|', N("System"), N("Cluster")) => 'parallel_computing_section', +	join('|', N("System"), N("Configuration")) => 'configuration_section', +	join('|', N("System"), N("Fonts")) => 'chinese_section', +	join('|', N("System"), N("Fonts"), N("True type")) => '', +	join('|', N("System"), N("Fonts"), N("Type1")) => '', +	join('|', N("System"), N("Fonts"), N("X11 bitmap")) => '', +	join('|', N("System"), N("Internationalization")) => 'chinese_section', +	join('|', N("System"), N("Kernel and hardware")) => 'hardware_configuration_section', +	join('|', N("System"), N("Libraries")) => 'system_section', +	join('|', N("System"), N("Networking")) => 'networking_configuration_section', +	join('|', N("System"), N("Packaging")) => 'packaging_section', +	join('|', N("System"), N("Printing")) => 'printing_section', +	join('|', N("System"), N("Servers")) => 'servers_section', +	join('|', N("System"), +          #-PO: This is a package/product name. Only translate it if needed: +          N("X11")) => 'x11_section', +	N("Terminals") => 'terminals_section', +	N("Text tools") => 'text_tools_section', +	N("Toys") => 'toys_section', +	N("Video") => 'video_section', +	join('|', N("Video"), N("Editors and Converters")) => 'video_editors_section', +	join('|', N("Video"), N("Players")) => 'video_players_section', +	join('|', N("Video"), N("Television")) => 'video_television_section', +	join('|', N("Video"), N("Utilities")) => 'video_utilities_section', + +     # for Mageia Choice: +	N("Workstation") => 'system_section', +	join('|', N("Workstation"), N("Configuration")) => 'configuration_section', +	join('|', N("Workstation"), N("Console Tools")) => 'interpreters_section', +	join('|', N("Workstation"), N("Documentation")) => 'documentation_section', +	join('|', N("Workstation"), N("Game station")) => 'amusement_section', +	join('|', N("Workstation"), N("Internet station")) => 'networking_section', +	join('|', N("Workstation"), N("Multimedia station")) => 'multimedia_section', +	join('|', N("Workstation"), N("Network Computer (client)")) => 'other_networking', +	join('|', N("Workstation"), N("Office Workstation")) => 'office_section', +	join('|', N("Workstation"), N("Scientific Workstation")) => 'sciences_section', +	N("Graphical Environment") => 'graphical_desktop_section', + +	join('|', N("Graphical Environment"), N("GNOME Workstation")) => 'gnome_section', +	join('|', N("Graphical Environment"), N("IceWm Desktop")) => 'icewm_section', +	join('|', N("Graphical Environment"), N("KDE Workstation")) => 'kde_section', +	join('|', N("Graphical Environment"), N("Other Graphical Desktops")) => 'more_applications_other_section', +	N("Development") => 'development_section', +	join('|', N("Development"), N("Development")) => 'development_section', +	join('|', N("Development"), N("Documentation")) => 'documentation_section', +	N("Server") => 'servers_section', +	join('|', N("Server"), N("DNS/NIS")) => 'networking_section', +	join('|', N("Server"), N("Database")) => 'databases_section', +	join('|', N("Server"), N("Firewall/Router")) => 'networking_section', +	join('|', N("Server"), N("Mail")) => 'mail_section', +	join('|', N("Server"), N("Mail/Groupware/News")) => 'mail_section', +	join('|', N("Server"), N("Network Computer server")) => 'networking_section', +	join('|', N("Server"), N("Web/FTP")) => 'networking_www_section', + +    ); + +sub get_icon_path { +    my ($group, $parent) = @_; +    my $path; +    if(isdigit($parent) && $parent == 0){ +        $path = '/usr/share/icons/'; +    }else{ +        $path = '/usr/share/icons/mini/'; +    } +    my $icon_path = ""; +    if(defined($group_icons{$group})){ +        $icon_path = join('', $path, $group_icons{$group}, '.png'); +    }elsif(defined($group_icons{$parent."\|".$group})){ +        $icon_path = join('', $path, $group_icons{$parent."\|".$group}, '.png'); +    }else{ +        $icon_path = join('', $path, 'applications_section', '.png'); +    } +    unless(-e $icon_path){ +        $icon_path = join('', $path, 'applications_section', '.png'); +    } +    return $icon_path; +} + +1; diff --git a/lib/AdminPanel/Rpmdragora/init.pm b/lib/AdminPanel/Rpmdragora/init.pm new file mode 100644 index 0000000..34581bf --- /dev/null +++ b/lib/AdminPanel/Rpmdragora/init.pm @@ -0,0 +1,174 @@ +# vim: set et ts=4 sw=4: +package AdminPanel::Rpmdragora::init; +#***************************************************************************** +# +#  Copyright (c) 2002 Guillaume Cottenceau +#  Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com> +#  Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +#  Copyright (c) 2005-2007 Mandriva SA +#  Copyright (c) 2013 Matteo Pasotti <matteo.pasotti@gmail.com> +# +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +# +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +# +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +#***************************************************************************** +# +# $Id: init.pm 263915 2009-12-03 17:41:04Z tv $ + +use strict; +use MDK::Common::Func 'any'; +use lib qw(/usr/lib/libDrakX); +use common; +use English; +BEGIN { $::no_global_argv_parsing = 1 } +require urpm::args; + +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw(init +                 warn_about_user_mode +                 $MODE +                 $changelog_first +                 $default_list_mode +                 %rpmdragora_options +                 @ARGV_copy +                 ); + +our @ARGV_copy =  @ARGV; + +BEGIN {  #- we want to run this code before the Gtk->init of the use-my_gtk +    my $basename = sub { local $_ = shift; s|/*\s*$||; s|.*/||; $_ }; +    any { /^--?h/ } @ARGV and do { +	printf join("\n", N("Usage: %s [OPTION]...", $basename->($0)), +N("  --auto                 assume default answers to questions"), +N("  --changelog-first      display changelog before filelist in the description window"), +N("  --media=medium1,..     limit to given media"), +N("  --merge-all-rpmnew     propose to merge all .rpmnew/.rpmsave files found"), +N("  --mode=MODE            set mode (install (default), remove, update)"), +N("  --justdb               update the database, but do not modify the filesystem"), +N("  --no-confirmation      don't ask first confirmation question in update mode"), +N("  --no-media-update      don't update media at startup"), +N("  --no-verify-rpm        don't verify package signatures"), +if_($0 !~ /MageiaUpdate/, N("  --parallel=alias,host  be in parallel mode, use \"alias\" group, use \"host\" machine to show needed deps")), +N("  --rpm-root=path        use another root for rpm installation"), +N("  --urpmi-root           use another root for urpmi db & rpm installation"), +N("  --run-as-root          force to run as root"), +N("  --search=pkg           run search for \"pkg\""), +N("  --test                 only verify if the installation can be achieved correctly"), +chomp_(N("  --version              print this tool's version number +")), +"" +); +	exit 0; +    }; +} + +BEGIN { #- for mcc +    if ("@ARGV" =~ /--embedded (\w+)/) { +	$::XID = $1; +	$::isEmbedded = 1; +    } +} + + +#- This is needed because text printed by Gtk2 will always be encoded +#- in UTF-8; we first check if LC_ALL is defined, because if it is, +#- changing only LC_COLLATE will have no effect. +use POSIX qw(setlocale LC_ALL LC_COLLATE strftime); +use locale; +my $collation_locale = $ENV{LC_ALL}; +if ($collation_locale) { +  $collation_locale =~ /UTF-8/ or setlocale(LC_ALL, "$collation_locale.UTF-8"); +} else { +  $collation_locale = setlocale(LC_COLLATE); +  $collation_locale =~ /UTF-8/ or setlocale(LC_COLLATE, "$collation_locale.UTF-8"); +} + +our $version = 1; +our %rpmdragora_options; + +my $i; +foreach (@ARGV) { +    $i++; +    /^-?-(\S+)$/ or next; +    my $val = $1; +    if ($val =~ /=/) { +        my ($name, $values) = split /=/, $val; +        my @values = split /,/, $values; +        $rpmdragora_options{$name} = \@values if @values; +    } else { +        if ($val eq 'version') { +            print "$0 $version\n"; +            exit(0); +       } elsif ($val =~ /^(test|expert)$/) { +           eval "\$::$1 = 1"; +       } elsif ($val =~ /^(q|quiet)$/) { +           urpm::args::set_verbose(-1); +       } elsif ($val =~ /^(v|verbose)$/) { +           urpm::args::set_verbose(1); +       } else { +           $rpmdragora_options{$val} = 1; +       } +    } +} + +foreach my $option (qw(media mode parallel rpm-root search)) { +    if (defined $rpmdragora_options{$option} && !ref($rpmdragora_options{$option})) { +        warn qq(wrong usage of "$option" option!\n); +        exit(-1); # too early for my_exit() +    } +} + +$urpm::args::options{basename} = 1; + +our $MODE = ref $rpmdragora_options{mode} ? $rpmdragora_options{mode}[0] : undef; +our $overriding_config = defined $MODE; +unless ($MODE) { +    $MODE = 'install'; +    $0 =~ m|remove$|  and $MODE = 'remove'; +    $0 =~ m|update$|i and $MODE = 'update'; +} + +our $default_list_mode; +$default_list_mode = 'gui_pkgs' if $MODE eq 'install'; +if ($MODE eq 'remove') { +    $default_list_mode = 'installed'; +} elsif ($MODE eq 'update') { +    $default_list_mode = 'all_updates'; +} + +$MODE eq 'update' || $rpmdragora_options{'run-as-root'} and require_root_capability(); +$::noborderWhenEmbedded = 1; + +require AdminPanel::rpmdragora; + +our $changelog_first = $rpmdragora::changelog_first_config->[0]; +$changelog_first = 1 if $rpmdragora_options{'changelog-first'}; + +sub warn_about_user_mode() { +    my $title = N("Running in user mode"); +    my $msg = N("You are launching this program as a normal user.\n". +                "You will not be able to perform modifications on the system,\n". +                "but you may still browse the existing database."); + +    if(($EUID != 0) and (!AdminPanel::rpmdragora::interactive_msg($title, $msg))) { +        return 0; +    } +    return 1; +} + +sub init() { +    URPM::bind_rpm_textdomain_codeset(); +} + +1; diff --git a/lib/AdminPanel/Rpmdragora/localization.pm b/lib/AdminPanel/Rpmdragora/localization.pm new file mode 100644 index 0000000..fb199d9 --- /dev/null +++ b/lib/AdminPanel/Rpmdragora/localization.pm @@ -0,0 +1,35 @@ +#!/usr/bin/perl +# vim: set et ts=4 sw=4: +package AdminPanel::Rpmdragora::localization; +#***************************************************************************** +# +#  Copyright (c) 2013 Matteo Pasotti <matteo.pasotti@gmail.com> +# +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +# +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +# +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +#***************************************************************************** +use strict; +use warnings; +use diagnostics; +use lib qw(/usr/lib/libDrakX); +use common; + +Locale::gettext::bind_textdomain_codeset($_, 'UTF8') foreach 'libDrakX', if_(!$::isInstall, 'libDrakX-standalone'), +	if_($::isRestore, 'draksnapshot'), if_($::isInstall, 'urpmi'), +	'drakx-net', 'drakx-kbd-mouse-x11', # shared translation +	@::textdomains; + +#========= UGLY WORKAROUND ============ +push @::textdomains, 'rpmdrake'; +#========= UGLY WORKAROUND ============ diff --git a/lib/AdminPanel/Rpmdragora/open_db.pm b/lib/AdminPanel/Rpmdragora/open_db.pm new file mode 100644 index 0000000..aa1fd14 --- /dev/null +++ b/lib/AdminPanel/Rpmdragora/open_db.pm @@ -0,0 +1,162 @@ +# vim: set et ts=4 sw=4: +package AdminPanel::Rpmdragora::open_db; +#***************************************************************************** +# +#  Copyright (c) 2002 Guillaume Cottenceau +#  Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com> +#  Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +#  Copyright (c) 2005-2007 Mandriva SA +# +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +# +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +# +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +#***************************************************************************** +# +# $Id: open_db.pm 268344 2010-05-06 13:06:08Z jvictor $ + +use strict; +use common; +use AdminPanel::rpmdragora; +use URPM; +use urpm; +use urpm::args; +use urpm::select; +use urpm::media; +use feature 'state'; + +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw(fast_open_urpmi_db +                 get_backport_media +                 get_inactive_backport_media +                 get_update_medias +                 is_it_a_devel_distro +                 open_rpm_db +                 open_urpmi_db +            ); + + +# because rpm blocks some signals when rpm DB is opened, we don't keep open around: +sub open_rpm_db { +    my ($o_force) = @_; +    my $host; +    log::explanations("opening the RPM database"); +    if ($::rpmdragora_options{parallel} && ((undef, $host) = @{$::rpmdragora_options{parallel}})) { +        state $done; +        my $dblocation = "/var/cache/urpmi/distantdb/$host"; +        if (!$done || $o_force) { +            print "syncing db from $host to $dblocation..."; +            mkdir_p "$dblocation/var/lib/rpm"; +            system "rsync -Sauz -e ssh $host:/var/lib/rpm/ $dblocation/var/lib/rpm"; +            $? == 0 or die "Couldn't sync db from $host to $dblocation"; +            $done = 1; +            print "done.\n"; +        } +        URPM::DB::open($dblocation) or die "Couldn't open RPM DB"; +    } else { +        my $db; +        if ($::env) { +	    #- URPM has same methods as URPM::DB and empty URPM will be seen as empty URPM::DB. +	    $db = new URPM; +            $db->parse_synthesis("$::env/rpmdb.cz"); +	} else { +            $db = URPM::DB::open($::rpmdragora_options{'rpm-root'}[0]); +        } +        $db or die "Couldn't open RPM DB (" . ($::env ? "$::env/rpmdb.cz" : $::rpmdragora_options{'rpm-root'}[0]) . ")"; +    } +} + +# do not pay the urpm::media::configure() heavy cost: +sub fast_open_urpmi_db() { +    my $urpm = urpm->new; +    my $error_happened; +    $urpm->{fatal} = sub { +        $error_happened = 1; +        interactive_msg(N("Fatal error"), +                         N("A fatal error occurred: %s.", $_[1])); +    }; + +    urpm::set_files($urpm, $::rpmdragora_options{'urpmi-root'}[0]) if $::rpmdragora_options{'urpmi-root'}[0]; +    $::rpmdragora_options{'rpm-root'}[0] ||= $::rpmdragora_options{'urpmi-root'}[0]; +    urpm::args::set_root($urpm, $::rpmdragora_options{'rpm-root'}[0]) if $::rpmdragora_options{'rpm-root'}[0]; +    urpm::args::set_debug($urpm) if $::rpmdragora_options{debug}; +    $urpm->get_global_options; +    $urpm->{options}{wait_lock} = $::rpmdragora_options{'wait-lock'}; +    $urpm->{options}{'verify-rpm'} = !$::rpmdragora_options{'no-verify-rpm'} if defined $::rpmdragora_options{'no-verify-rpm'}; +    $urpm->{options}{auto} = $::rpmdragora_options{auto} if defined $::rpmdragora_options{auto}; +    urpm::args::set_verbosity(); +    if ($::rpmdragora_options{env} && $::rpmdragora_options{env}[0]) { +        $::env = $::rpmdragora_options{env}[0]; +        # prevent crashing in URPM.pm prevent when using --env: +        $::env = "$ENV{PWD}/$::env" if $::env !~ m!^/!; +        urpm::set_env($urpm, $::env); +    } + +    $urpm::args::options{justdb} = $::rpmdragora_options{justdb}; + +    urpm::media::read_config($urpm, 0); +    foreach (@{$urpm->{media}}) { +	    next if $_->{ignore}; +	    urpm::media::_tempignore($_, 1) if $ignore_debug_media->[0] && $_->{name} =~ /debug/i; +    } +    # FIXME: seems uneeded with newer urpmi: +    if ($error_happened) { +        touch('/etc/urpmi/urpmi.cfg'); +        exec('edit-urpm-sources.pl'); +    } +    $urpm; +} + +sub is_it_a_devel_distro() { +    state $res; +    return $res if defined $res; +     +    my $path = $::rpmdragora_options{'urpmi-root'}[0] . '/etc/product.id'; +    $res = common::parse_LDAP_namespace_structure(cat_($path))->{branch} eq 'Devel'; +    return $res; +} + +sub get_backport_media { +    my ($urpm) = @_; +    grep { $_->{name} =~ /backport/i &&  +	       $_->{name} !~ /debug|sources/i } @{$urpm->{media}}; +} + +sub get_inactive_backport_media { +    my ($urpm) = @_; +    map { $_->{name} } grep { $_->{ignore} } get_backport_media($urpm); +} + +sub get_update_medias { +    my ($urpm) = @_; +    if (is_it_a_devel_distro()) { +        grep { !$_->{ignore} } @{$urpm->{media}}; +    } else { +        grep { !$_->{ignore} && $_->{update} } @{$urpm->{media}}; +    } +} + +sub open_urpmi_db { +    my (%urpmi_options) = @_; +    my $urpm = fast_open_urpmi_db(); +    my $media = ref $::rpmdragora_options{media} ? join(',', @{$::rpmdragora_options{media}}) : ''; + +    my $searchmedia = $urpmi_options{update} ? undef : join(',', get_inactive_backport_media($urpm)); +    $urpm->{lock} = urpm::lock::urpmi_db($urpm, undef, wait => $urpm->{options}{wait_lock}) if !$::env; +    my $previous = $::rpmdragora_options{'previous-priority-upgrade'}; +    urpm::select::set_priority_upgrade_option($urpm, (ref $previous ? join(',', @$previous) : ())); +    urpm::media::configure($urpm, media => $media, if_($searchmedia, searchmedia => $searchmedia), %urpmi_options); +    $urpm; +} + +1; diff --git a/lib/AdminPanel/Rpmdragora/pkg.pm b/lib/AdminPanel/Rpmdragora/pkg.pm new file mode 100644 index 0000000..e8c1a08 --- /dev/null +++ b/lib/AdminPanel/Rpmdragora/pkg.pm @@ -0,0 +1,997 @@ +# vim: set et ts=4 sw=4: +package AdminPanel::Rpmdragora::pkg; +#***************************************************************************** +# +#  Copyright (c) 2002 Guillaume Cottenceau +#  Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com> +#  Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +#  Copyright (c) 2005-2007 Mandriva SA +# +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +# +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +# +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +#***************************************************************************** +# +# $Id: pkg.pm 270160 2010-06-22 19:55:40Z jvictor $ + +use strict; +use MDK::Common::Func 'any'; +use lib qw(/usr/lib/libDrakX); +use common; +use POSIX qw(_exit); +use URPM; +use utf8; +use AdminPanel::Rpmdragora::open_db; +use AdminPanel::Rpmdragora::gurpm; +use AdminPanel::Rpmdragora::formatting; +use AdminPanel::Rpmdragora::rpmnew; + +use AdminPanel::rpmdragora; +use urpm; +use urpm::lock; +use urpm::install; +use urpm::signature; +use urpm::get_pkgs; +use urpm::select; +use urpm::main_loop; +use urpm::args qw(); + + +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw( +                    $priority_up_alread_warned +                    download_callback +                    extract_header +                    find_installed_version +                    get_pkgs +                    perform_installation +                    perform_removal +                    run_rpm +                    sort_packages +                    ); + +#use mygtk2 qw(gtknew); +#use ugtk2 qw(:all); + +our $priority_up_alread_warned; + +sub sort_packages_biarch { +    sort { +        my ($na, $aa) = $a =~ /^(.*-[^-]+-[^-]+)\.([^.-]+)$/; +        my ($nb, $ab) = $b =~ /^(.*-[^-]+-[^-]+)\.([^.-]+)$/; +        $na cmp $nb || ($ab =~ /64$/) <=> ($aa =~ /64$/); +    } @_; +} + +sub sort_packages_monoarch { +    sort { uc($a) cmp uc($b) } @_; +} + +*sort_packages = arch() =~ /x86_64/ ? \&sort_packages_biarch : \&sort_packages_monoarch; + +sub run_rpm { +    foreach (qw(LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION LC_ALL)) { +        local $ENV{$_} = $ENV{$_} . '.UTF-8' if $ENV{$_} && $ENV{$_} !~ /UTF-8/; +    } +    my @l = map { ensure_utf8($_); $_ } run_program::get_stdout(@_); +    wantarray() ? @l : join('', @l); +} + + +sub extract_header { +    my ($pkg, $urpm, $xml_info, $o_installed_version) = @_; +    my %fields = ( +        info => 'description', +        files => 'files', +        changelog => 'changelog', +    ); +    # already extracted: +    return if $pkg->{$fields{$xml_info}}; + +    my $p = $pkg->{pkg}; + +    if (!$p) { +        warn ">> ghost package '$pkg' has no URPM object!!!\n"; +        return; +    } + +    my $name = $p->fullname; +    # fix extracting info for SRPMS and RPM GPG keys: +    $name =~ s!\.src!!; + +    if ($p->flag_installed && !$p->flag_upgrade) { +        my @files = map { chomp_($_) } run_rpm("rpm -ql $name"); +	add2hash($pkg, { files => [ @files ? @files : N("(none)") ], +                         description => rpm_description(scalar(run_rpm("rpm -q --qf '%{description}' $name"))), +                         changelog => format_changelog_string($o_installed_version, scalar(run_rpm("rpm -q --changelog $name"))) }); +    } else { +	my $medium = pkg2medium($p, $urpm); +        my ($local_source, %xml_info_pkgs, $bar_id); +        my $_statusbar_clean_guard = before_leaving { $bar_id and statusbar_msg_remove($bar_id) }; +        my $dir = urpm::file_from_local_url($medium->{url}); +        print "p->filename: ". $p->filename."\n"; +	    $local_source = "$dir/" . $p->filename if $dir; +        print "local_source: $local_source\n"; +        if (-e $local_source) { +            $bar_id = statusbar_msg(N("Getting information from %s...", $dir), 0); +            $urpm->{log}("getting information from rpms from $dir"); +        } else { +            my $gurpm; +            $bar_id = statusbar_msg(N("Getting '%s' from XML meta-data...", $xml_info), 0); +            my $_gurpm_clean_guard = before_leaving { undef $gurpm }; +                if (my $xml_info_file = eval { urpm::media::any_xml_info($urpm, $medium, $xml_info, undef, sub { +                                                                      $gurpm ||= Rpmdragora::gurpm->new(N("Please wait"), +                                                                                                      '', # FIXME: add a real string after cooker +                                                                                                      transient => $::main_window); +                                                                      download_callback($gurpm, @_) +                                                                        or goto header_non_available; +                                                                  }) }) { +                    require urpm::xml_info; +                    require urpm::xml_info_pkg; +                    $urpm->{log}("getting information from $xml_info_file"); +                    my %nodes = eval { urpm::xml_info::get_nodes($xml_info, $xml_info_file, [ $name ]) }; +                    goto header_non_available if $@; +                    put_in_hash($xml_info_pkgs{$name} ||= {}, $nodes{$name}); +                } else { +                    if ($xml_info eq 'info') { +                        $urpm->{info}(N("No xml info for medium \"%s\", only partial result for package %s", $medium->{name}, $name)); +                    } else { +                        $urpm->{error}(N("No xml info for medium \"%s\", unable to return any result for package %s", $medium->{name}, $name)); +                    } +                } +	} + +        #- even if non-root, search for a header in the global cachedir +        if (-s $local_source) { +            $p->update_header($local_source) or do { +		warn "Warning, could not extract header for $name from $medium!"; +		goto header_non_available; +	    }; +	    my @files = $p->files; +	    @files = N("(none)") if !@files; +	    add2hash($pkg, { description => rpm_description($p->description), +	        files => \@files, +	        url => $p->url, +		changelog => format_changelog_changelogs($o_installed_version, $p->changelogs) }); +	    $p->pack_header; # needed in order to call methods on objects outside ->traverse +        } elsif ($xml_info_pkgs{$name}) { +            if ($xml_info eq 'info') { +                add2hash($pkg, { description => rpm_description($xml_info_pkgs{$name}{description}), +                                 url => $xml_info_pkgs{$name}{url} +                             }); +            } elsif ($xml_info eq 'files') { +                my @files = map { chomp_(to_utf8($_)) } split("\n", $xml_info_pkgs{$name}{files}); +                add2hash($pkg, { files => [ @files ? @files : N("(none)") ] }); +            } elsif ($xml_info eq 'changelog') { +                add2hash($pkg, {  +                    changelog => format_changelog_changelogs($o_installed_version, +                                                             @{$xml_info_pkgs{$name}{changelogs}}) +                }); +            } +        } else { +            goto header_non_available; +        } +        return; +           header_non_available: +             add2hash($pkg, { summary => $p->summary || N("(Not available)"), description => undef }); +    } +} + +sub find_installed_version { +    my ($p) = @_; +    my $version; +    open_rpm_db()->traverse_tag_find('name', $p->name, sub { $version = $_[0]->EVR }); +    $version || N("(none)"); +} + +my $canceled; +sub download_callback { +    my ($gurpm, $mode, $file, $percent, $total, $eta, $speed) = @_; +    $canceled = 0; +    if ($mode eq 'start') { +        $gurpm->label(N("Downloading package `%s'...", basename($file))); +        $gurpm->validate_cancel(but(N("Cancel")), sub { $canceled = 1 }); +    } elsif ($mode eq 'progress') { +        $gurpm->label( +            join("\n", +                 N("Downloading package `%s'...", basename($file)), +                 (defined $total && defined $eta ? +                    N("        %s%% of %s completed, ETA = %s, speed = %s", $percent, $total, $eta, $speed) +                      : N("        %s%% completed, speed = %s", $percent, $speed) +                  ) =~ /^\s*(.*)/ +              ), +        ); +	#$gurpm->progress($percenti/100); +        $gurpm->progress($percent); +    } elsif ($mode eq 'end') { +        $gurpm->progress(100); +        $gurpm->invalidate_cancel; +    } +    !$canceled; +} + + +# -=-=-=---=-=-=---=-=-=-- install packages -=-=-=---=-=-=---=-=-=- + +my (@update_medias, $is_update_media_already_asked); + +sub warn_about_media { +    my ($w, %options) = @_; + +    return if $::MODE ne 'update'; +    return if $::rpmdragora_options{'no-media-update'}; + +    # we use our own instance of the urpmi db in order not to mess up with skip-list managment (#31092): +    # and no need to fully configure urpmi since we may have to do it again anyway because of new media: +    my $urpm = fast_open_urpmi_db(); + +    my $_lock = urpm::lock::urpmi_db($urpm, undef, wait => $urpm->{options}{wait_lock}); + +    # build media list: +    @update_medias = get_update_medias($urpm); + +    # do not update again media after installing/removing some packages: +    $::rpmdragora_options{'no-media-update'} ||= 1; + +	    if (@update_medias > 0) { +		if (!$options{skip_updating_mu} && !$is_update_media_already_asked) { +              $is_update_media_already_asked = 1; +		     $::rpmdragora_options{'no-confirmation'} or interactive_msg(N("Confirmation"), +N("I need to contact the mirror to get latest update packages. +Please check that your network is currently running. + +Is it ok to continue?"), yesno => 1, +                   widget =>  gtknew('CheckButton', text => N("Do not ask me next time"), +                                     active_ref => \$::rpmdragora_options{'no-confirmation'} +                                 )) or myexit(-1); +		    writeconf(); +		    urpm::media::select_media($urpm, map { $_->{name} } @update_medias); +		    update_sources($urpm, noclean => 1, medialist => [ map { $_->{name} } @update_medias ]); +		} +	    } else { +		if (any { $_->{update} } @{$urpm->{media}}) { +		    interactive_msg(N("Already existing update media"), +N("You already have at least one update medium configured, but +all of them are currently disabled. You should run the Software +Media Manager to enable at least one (check it in the \"%s\" +column). + +Then, restart \"%s\".", N("Enabled"), $rpmdragora::myname_update)); +		    myexit(-1); +		} +		my ($mirror) = choose_mirror($urpm, transient => $w->{real_window} || $::main_window, +                                       message => join("\n\n", +                                                       N("You have no configured update media. MageiaUpdate cannot operate without any update media."), +                                                       N("I need to contact the Mageia website to get the mirror list. +Please check that your network is currently running. + +Is it ok to continue?"), +                                                         ), +                                   ); +		my $m = ref($mirror) ? $mirror->{url} : ''; +		$m or interactive_msg(N("How to choose manually your mirror"), +N("You may also choose your desired mirror manually: to do so, +launch the Software Media Manager, and then add a `Security +updates' medium. + +Then, restart %s.", $rpmdragora::myname_update)), myexit(-1); +		add_distrib_update_media($urpm, $mirror, only_updates => 1); +	    } +} + + +sub get_parallel_group() { +    $::rpmdragora_options{parallel} ? $::rpmdragora_options{parallel}[0] : undef; +} + +my ($count, $level, $limit, $new_stage, $prev_stage, $total); + +sub init_progress_bar { +    my ($urpm) = @_; +    undef $_ foreach $count, $prev_stage, $new_stage, $limit; +    $level = 0.05; +    $total = @{$urpm->{depslist}}; +} +     +sub reset_pbar_count { +    undef $prev_stage; +    $count = 0; +    $limit = $_[0]; +} + +sub update_pbar { +    my ($gurpm) = @_; +    return if !$total;          # don't die if there's no source +    $count++; +    $new_stage = $level+($limit-$level)*$count/$total; +    $prev_stage = 0 if(!defined($prev_stage)); +    if ($prev_stage + 0.01*100 < $new_stage) { +        $prev_stage = $new_stage; +        $gurpm->progress($new_stage); +    } +} + + +sub get_installed_packages { +    my ($urpm, $db, $all_pkgs, $gurpm) = @_; + +    $urpm->{global_config}{'prohibit-remove'} = '' if(!defined($urpm->{global_config}{'prohibit-remove'})); +    my @base = ("basesystem", split /,\s*/, $urpm->{global_config}{'prohibit-remove'}); +    my (%base, %basepackages, @installed_pkgs, @processed_base); +    reset_pbar_count(0.33); +    while (defined(local $_ = shift @base)) { +	exists $basepackages{$_} and next; +	$db->traverse_tag(m|^/| ? 'path' : 'whatprovides', [ $_ ], sub { +			      update_pbar($gurpm); +			      my $name = $_[0]->fullname; +			      # workaround looping in URPM: +			      return if member($name, @processed_base); +			      push @processed_base, $name; +			      push @{$basepackages{$_}}, $name; +			      push @base, $_[0]->requires_nosense; +			  }); +    } +    foreach (values %basepackages) { +	my $n = @$_;            #- count number of times it's provided +	foreach (@$_) { +	    $base{$_} = \$n; +	} +    } +    # costly: +    $db->traverse(sub { +                      my ($pkg) = @_; +                      update_pbar($gurpm); +                      my $fullname = urpm_name($pkg); +                      return if $fullname =~ /@/; +                      $all_pkgs->{$fullname} = { +                          pkg => $pkg, urpm_name => $fullname, +                      } if !($all_pkgs->{$fullname} && $all_pkgs->{$fullname}{description}); +                      if (my $name = $base{$fullname}) { +                          $all_pkgs->{$fullname}{base} = \$name; +                          $pkg->set_flag_base(1) if $$name == 1; +                      } +                      push @installed_pkgs, $fullname; +                      $pkg->set_flag_installed; +                      $pkg->pack_header; # needed in order to call methods on objects outside ->traverse +                  }); +    @installed_pkgs; +} + +urpm::select::add_packages_to_priority_upgrade_list('rpmdragora', 'perl-Glib', 'perl-Gtk2'); + +my ($priority_state, $priority_requested); +our $need_restart; + +our $probe_only_for_updates; + +sub get_updates_list { +    my ($urpm, $db, $state, $requested, $requested_list, $requested_strict, $all_pkgs, %limit_preselect) = @_; + +    $urpm->request_packages_to_upgrade( +	$db, +	$state, +	$requested, +	%limit_preselect +    ); + +    my %common_opts = ( +        callback_choices => \&Rpmdragora::gui::callback_choices, +        priority_upgrade => $urpm->{options}{'priority-upgrade'}, +    ); + +    if ($urpm->{options}{'priority-upgrade'}) { +        $need_restart = +          urpm::select::resolve_priority_upgrades_after_auto_select($urpm, $db, $state, +                                                                    $requested, %common_opts); +    } + +    # list of updates (including those matching /etc/urpmi/skip.list): +    @$requested_list = sort map { +	my $name = urpm_name($_); +        $all_pkgs->{$name} = { pkg => $_ }; +	$name; +    } @{$urpm->{depslist}}[keys %$requested]; + +    # list of pure updates (w/o those matching /etc/urpmi/skip.list but with their deps): +    if ($probe_only_for_updates && !$need_restart) { +        @$requested_strict = sort map { +            urpm_name($_); +        } $urpm->resolve_requested($db, $state, $requested, callback_choices => \&Rpmdragora::gui::callback_choices); + +        if (my @l = grep { $state->{selected}{$_->id} } +              urpm::select::_priority_upgrade_pkgs($urpm, $urpm->{options}{'priority-upgrade'})) { +            if (!$need_restart) { +                $need_restart = +                  urpm::select::_resolve_priority_upgrades($urpm, $db, $state, $state->{selected}, +                                                           \@l, %common_opts); +            } +        } +    } + +    if ($need_restart) { +        $requested_strict = [ map { scalar $_->fullname } @{$urpm->{depslist}}[keys %{$state->{selected}}] ]; +        # drop non priority updates: +        @$requested_list = (); +    } + +    # list updates including skiped ones + their deps in MageiaUpdate: +    @$requested_list = uniq(@$requested_list, @$requested_strict); + +    # do not pre select updates in rpmdragora: +    @$requested_strict = () if !$probe_only_for_updates; +} + +sub get_pkgs { +    my (%options) = @_; +    my $w = $::main_window; + +    my $gurpm = AdminPanel::Rpmdragora::gurpm->new(1 ? N("Please wait") : N("Package installation..."), N("Initializing..."), transient => $::main_window); +    my $_gurpm_clean_guard = before_leaving { undef $gurpm }; +    #my $_flush_guard = Gtk2::GUI_Update_Guard->new; + +    warn_about_media($w, %options); + +    my $urpm = open_urpmi_db(update => $probe_only_for_updates && !is_it_a_devel_distro()); + +    my $_drop_lock = before_leaving { undef $urpm->{lock} }; + +    $priority_up_alread_warned = 0; + +    # update media list in case warn_about_media() added some: +    @update_medias = get_update_medias($urpm); + +    $gurpm->label(N("Reading updates description")); +    $gurpm->progress(100); + +	#- parse the description file +    my $update_descr = urpm::get_updates_description($urpm, @update_medias); + +    my $_unused = N("Please wait, finding available packages..."); + +    # find out installed packages: + +    init_progress_bar($urpm); + +    $gurpm->label(N("Please wait, listing base packages...")); +    $gurpm->progress($level*100); +     +    my $db = eval { open_rpm_db() }; +    if (my $err = $@) { +	interactive_msg(N("Error"), N("A fatal error occurred: %s.", $err)); +        return; +    } + +    my $sig_handler = sub { undef $db; exit 3 }; +    local $SIG{INT} = $sig_handler; +    local $SIG{QUIT} = $sig_handler; +     +    $gurpm->label(N("Please wait, finding installed packages...")); +    $gurpm->progress($level = 0.33*100); +    reset_pbar_count(0.66*100); +    my (@installed_pkgs, %all_pkgs); +    if (!$probe_only_for_updates) { +        @installed_pkgs = get_installed_packages($urpm, $db, \%all_pkgs, $gurpm); +    } + +    if (my $group = get_parallel_group()) { +        urpm::media::configure($urpm, parallel => $group); +    } + +    # find out availlable packages: + +    $urpm->{state} = {}; + +    $gurpm->label(N("Please wait, finding available packages...")); +    $gurpm->progress($level = 0.66*100); + +    check_update_media_version($urpm, @update_medias); + +    my $requested = {}; +    my $state = {}; +    my (@requested, @requested_strict); + +    if ($compute_updates->[0] || $::MODE eq 'update') { +        my %filter; +        if ($options{pure_updates}) { +            # limit to packages from update-media (dependencies can still come from other media) +            %filter = (idlist => [ map { $_->{start} .. $_->{end} } @update_medias ]); +        } +        get_updates_list($urpm, $db, $state, $requested, \@requested, \@requested_strict, \%all_pkgs, %filter); +    } + +    if ($need_restart) { +        $priority_state = $state; +        $priority_requested = $requested; +    } else { +        ($priority_state, $priority_requested) = (); +    } + +    if (!$probe_only_for_updates) { +        $urpm->compute_installed_flags($db); # TODO/FIXME: not for updates +        $urpm->{depslist}[$_]->set_flag_installed foreach keys %$requested; #- pretend it's installed +    } +    $urpm->{rpmdragora_state} = $state; #- Don't forget it +    $gurpm->progress($level = 0.7*100); + +    my %l; +    reset_pbar_count(1); +    foreach my $pkg (@{$urpm->{depslist}}) { +        update_pbar($gurpm); +	$pkg->flag_upgrade or next; +	my $key = $pkg->name . $pkg->arch; +	$l{$key} = $pkg if !$l{$key} || $l{$key}->compare($pkg); +    } +    my @installable_pkgs = map { my $n = $_->fullname; $all_pkgs{$n} = { pkg => $_ }; $n } values %l; +    undef %l; + +    my @inactive_backports; +    my @active_backports; +    my @backport_medias = get_backport_media($urpm); + +    foreach my $medium (@backport_medias) { +        update_pbar($gurpm); + +        # The 'searchmedia' flag differentiates inactive backport medias +        # (because that option was passed to urpm::media::configure to +        # temporarily enable them) + +        my $backports =   +            $medium->{searchmedia} ? \@inactive_backports : \@active_backports; + +      foreach my $pkg_id ($medium->{start} .. $medium->{end}) { +          next if !$pkg_id; +          my $pkg = $urpm->{depslist}[$pkg_id]; +          $pkg->flag_upgrade or next; +          my $name = $pkg->fullname; +          push @$backports, $name; +          $all_pkgs{$name} = { pkg => $pkg, is_backport => 1 }; +      } +    } +    my @updates = @requested; +    # selecting updates by default but skipped ones (MageiaUpdate only): +    foreach (@requested_strict) { +	$all_pkgs{$_}{selected} = 1; +    } + +    # urpmi only care about the first medium where it found the package, +    # so there's no need to list the same package several time: +    @installable_pkgs = uniq(difference2(\@installable_pkgs, \@updates)); + +    my @meta_pkgs = grep { /^task-|^basesystem/ } keys %all_pkgs; +  +    my @gui_pkgs = map { chomp; $_ } cat_('/usr/share/rpmdrake/gui.lst'); +    # add meta packages to GUI packages list (which expect basic names not fullnames): +    push @gui_pkgs, map { (split_fullname($_))[0] } @meta_pkgs; + +    +{ urpm => $urpm, +       all_pkgs => \%all_pkgs, +       installed => \@installed_pkgs, +       installable => \@installable_pkgs, +       updates => \@updates, +       meta_pkgs => \@meta_pkgs, +       gui_pkgs => [ grep { my $p = $all_pkgs{$_}{pkg}; $p && member(($p->fullname)[0], @gui_pkgs) } keys %all_pkgs ], +       update_descr => $update_descr, +       backports => [ @inactive_backports, @active_backports ], +       inactive_backports => \@inactive_backports +   }; +} + +sub display_READMEs_if_needed { +    my ($urpm, $w) = @_; +    return if !$urpm->{readmes}; +    my %Readmes = %{$urpm->{readmes}}; +    if (keys %Readmes) {        #- display the README*.urpmi files +        interactive_packtable( +            N("Upgrade information"), +            $w, +            N("These packages come with upgrade information"), +            [ map { +                my $fullname = $_; +                [ gtkpack__( +                    gtknew('HBox'), +                    gtkset_selectable(gtknew('Label', text => $Readmes{$fullname}),1), +                ), +                  gtksignal_connect( +                      gtknew('Button', text => N("Upgrade information about this package")), +                      clicked => sub { +                          interactive_msg( +                              N("Upgrade information about package %s", $Readmes{$fullname}), +                              (join '' => map { s/$/\n/smg; $_ } formatAlaTeX(scalar cat_($fullname))), +                              scroll => 1, +                          ); +                      }, +                  ), +		    ] } keys %Readmes ], +            [ gtknew('Button', text => N("Ok"), clicked => sub { Gtk2->main_quit }) ] +        ); +    } +} + +sub perform_parallel_install { +    my ($urpm, $group, $w, $statusbar_msg_id) = @_; +    my @pkgs = map { if_($_->flag_requested, urpm_name($_)) } @{$urpm->{depslist}}; + +    my @error_msgs; +    my $res = !run_program::run('urpmi', '2>', \@error_msgs, '-v', '--X', '--parallel', $group, @pkgs); + +    if ($res) { +        $$statusbar_msg_id = statusbar_msg( +            #N("Everything installed successfully"), +            N("All requested packages were installed successfully."), +        ); +    } else { +        interactive_msg( +            N("Problem during installation"), +            N("There was a problem during the installation:\n\n%s", join("\n", @error_msgs)), +            scroll => 1, +        ); +    } +    open_rpm_db('force_sync'); +    $w->set_sensitive(1); +    return 0; +} + +sub perform_installation {  #- (partially) duplicated from /usr/sbin/urpmi :-( +    my ($urpm, $pkgs) = @_; + +    my @error_msgs; +    my $statusbar_msg_id; +    my $gurpm; +    local $urpm->{fatal} = sub { +        my $fatal_msg = $_[1]; +        printf STDERR "Fatal: %s\n", $fatal_msg; +        undef $gurpm; +        interactive_msg(N("Installation failed"), +                        N("There was a problem during the installation:\n\n%s", $fatal_msg)); +        goto return_with_exit_code; +    }; +    local $urpm->{error} = sub { printf STDERR "Error: %s\n", $_[0]; push @error_msgs, $_[0] }; + +    my $w = $::main_window; +    #$w->set_sensitive(0); +    #my $_restore_sensitive = before_leaving { $w->set_sensitive(1) }; + +    # my $_flush_guard = Gtk2::GUI_Update_Guard->new; + +    if (my $group = get_parallel_group()) { +        return perform_parallel_install($urpm, $group, $w, \$statusbar_msg_id); +    } + +    my ($lock, $rpm_lock); +    if (!$::env) { +        $lock = urpm::lock::urpmi_db($urpm, undef, wait => $urpm->{options}{wait_lock}); +        $rpm_lock = urpm::lock::rpm_db($urpm, 'exclusive'); +    } +    my $state = $priority_state || $probe_only_for_updates ? { } : $urpm->{rpmdragora_state}; + +    my $bar_id = statusbar_msg(N("Checking validity of requested packages..."), 0); + +    # FIXME: THIS SET flag_requested on all packages!!!! +    # select packages to install / enssure selected pkg set is consistant: +    my %saved_flags; +    my $requested = { map { +        $saved_flags{$_->id} = $_->flag_requested; +        $_->id => undef; +    } grep { $_->flag_selected } @{$urpm->{depslist}} }; +    urpm::select::resolve_dependencies( +        $urpm, $state, $requested, +        rpmdb => $::env && "$::env/rpmdb.cz", +        callback_choices => \&Rpmdragora::gui::callback_choices, +    ); +    statusbar_msg_remove($bar_id); + +    my ($local_sources, $blist) = urpm::get_pkgs::selected2local_and_blists($urpm, $state->{selected}); +    if (!$local_sources && (!$blist || !@$blist)) { +        interactive_msg( +	    N("Unable to get source packages."), +	    N("Unable to get source packages, sorry. %s", +		@error_msgs ? N("\n\nError(s) reported:\n%s", join("\n", @error_msgs)) : ''), +	    scroll => 1, +	); +        goto return_with_exit_code; +    } + +    my @to_install = @{$urpm->{depslist}}[keys %{$state->{selected}}]; +    my @pkgs = map { scalar($_->fullname) } sort(grep { $_->flag_selected } @to_install); + +    @{$urpm->{ask_remove}} = sort(urpm::select::removed_packages($urpm->{state})); +    my @to_remove = map { if_($pkgs->{$_}{selected} && !$pkgs->{$_}{pkg}->flag_upgrade, $pkgs->{$_}{urpm_name}) } keys %$pkgs; + +    my $r = format_list(map { scalar(urpm::select::translate_why_removed_one($urpm, $urpm->{state}, $_)) } @to_remove); + +    my ($size, $filesize) = $urpm->selected_size_filesize($state); +    my $install_count = int(@pkgs); +    my $to_install = $install_count == 0 ? '' : +      ($priority_state ? '<b>' . N("Rpmdragora or one of its priority dependencies needs to be updated first. Rpmdragora will then restart.") . '</b>' . "\n\n" : '') . +      (P("The following package is going to be installed:", "The following %d packages are going to be installed:", $install_count, $install_count) +      . "\n\n" . format_list(map { s!.*/!!; $_ } @pkgs)); +    my $remove_count =  scalar(@to_remove); +    interactive_msg(($to_install ? N("Confirmation") : N("Some packages need to be removed")), +                    join("\n\n",  +                     ($r ?  +                        (!$to_install ? (P("Remove one package?", "Remove %d packages?", $remove_count, $remove_count), $r) : + (($remove_count == 1 ? + N("The following package has to be removed for others to be upgraded:") +   : N("The following packages have to be removed for others to be upgraded:")), $r), if_($to_install, $to_install)) +                          : $to_install), +                         format_size($size), +                         format_filesize($filesize), +                         N("Is it ok to continue?")), +                     scroll => 1, +                     yesno => 1) or return 1; + +    my $_umount_guard = before_leaving { urpm::removable::try_umounting_removables($urpm) }; + +    # select packages to uninstall for !update mode: +    perform_removal($urpm, { map { $_ => $pkgs->{$_} } @to_remove }) if !$probe_only_for_updates; + +    # $gurpm = Rpmdragora::gurpm->new(1 ? N("Please wait") : N("Package installation..."), N("Initializing..."), transient => $::main_window); +    # my $_gurpm_clean_guard = before_leaving { undef $gurpm }; +    my $something_installed; +  +    if (@to_install && $::rpmdragora_options{auto_orphans}) { +        urpm::orphans::compute_future_unrequested_orphans($urpm, $state); +        if (my @orphans = map { scalar $_->fullname } @{$state->{orphans_to_remove}}) { +            interactive_msg(N("Orphan packages"), P("The following orphan package will be removed.", +                    "The following orphan packages will be removed.", scalar(@orphans)) +              . "\n" . urpm::orphans::add_leading_spaces(join("\n", @orphans) . "\n"), scroll => 1); +        } +    } + +    urpm::orphans::mark_as_requested($urpm, $state, 0); + +    my ($progress, $total, @rpms_upgrade); +    my $transaction; +    my ($progress_nb, $transaction_progress_nb, $remaining, $done); +    my $callback_inst = sub { +        my ($urpm, $type, $id, $subtype, $amount, $total) = @_; +        my $pkg = defined $id ? $urpm->{depslist}[$id] : undef; +        if ($subtype eq 'start') { +            if ($type eq 'trans') { +                print(1 ? N("Preparing package installation...") : N("Preparing package installation transaction...")); +                # $gurpm->label(1 ? N("Preparing package installation...") : N("Preparing package installation transaction...")); +                } elsif (defined $pkg) { +                    $something_installed = 1; +                    print(N("Installing package `%s' (%s/%s)...", $pkg->name, ++$transaction_progress_nb, scalar(@{$transaction->{upgrade}}))."\n" . N("Total: %s/%s", ++$progress_nb, $install_count)); +                        # $gurpm->label(N("Installing package `%s' (%s/%s)...", $pkg->name, ++$transaction_progress_nb, scalar(@{$transaction->{upgrade}})) +                        #                     . "\n" . N("Total: %s/%s", ++$progress_nb, $install_count)); +                } +        } elsif ($subtype eq 'progress') { +            $gurpm->progress($total ? ($amount/$total)*100 : 100); +            print("Progress: ".($total ? ($amount/$total)*100 : 100)."\n"); +        } +    }; + +    # FIXME: sometimes state is lost: +    my @ask_unselect = urpm::select::unselected_packages($state); + +    # fix flags for orphan computing: +    foreach (keys %{$state->{selected}}) { +        my $pkg = $urpm->{depslist}[$_]; +        $pkg->set_flag_requested($saved_flags{$pkg->id}); +    } +    my $exit_code =  +      urpm::main_loop::run($urpm, $state, 1, \@ask_unselect, +                         { +                             completed => sub { +                                 # explicitly destroy the progress window when it's over; we may +                                 # have sg to display before returning (errors, rpmnew/rpmsave, ...): +                                 undef $gurpm; +                                        +                                 undef $lock; +                                 undef $rpm_lock; +                             }, +                             inst => $callback_inst, +                             trans => $callback_inst, +                             ask_yes_or_no => sub { +                                 # handle 'allow-force' and 'allow-nodeps' options: +                                 my ($title, $msg) = @_; +                                 local $::main_window = $gurpm->{real_window}; +                                 interactive_msg($title, $msg, yesno => 1, scroll => 1, +                                 ); +                             }, +                             message => sub { +                                 my ($title, $message) = @_; +                                 interactive_msg($title, $message, scroll => 1); +                             }, +                             # cancel installation when 'cancel' button is pressed: +                             trans_log => sub { download_callback($gurpm, @_) or goto return_with_exit_code }, +                             post_extract => sub { +                                 my ($set, $transaction_sources, $transaction_sources_install) = @_; +                                 $transaction = $set; +                                 $transaction_progress_nb = 0; +                                 $done += grep { !/\.src\.rpm$/ } values %$transaction_sources;         #updates +                                 $total = keys(%$transaction_sources_install) + keys %$transaction_sources; +                                 push @rpms_upgrade, keys %$transaction_sources; +                                 $done += grep { !/\.src\.rpm$/ } values %$transaction_sources_install; # installs +                             }, +                             pre_removable => sub { +                                 # Gtk2::GUI_Update_Guard->new use of alarm() kill us when +                                 # running system(), thus making DVD being ejected and printing +                                 # wrong error messages (#30463) +                                        +                                 local $SIG{ALRM} = sub { die "ALARM" }; +                                 $remaining = alarm(0); +                             }, + +                             post_removable => sub { alarm $remaining }, +                             copy_removable => sub { +                                 my ($medium) = @_; +                                 interactive_msg( +                                     N("Change medium"), +                                     N("Please insert the medium named \"%s\"", $medium), +                                     yesno => 1, text => { no => N("Cancel"), yes => N("Ok") }, +                                 ); +                             }, +                             pre_check_sig => sub { $gurpm->label(N("Verifying package signatures...")) }, +                             check_sig => sub { $gurpm->progress((++$progress/$total)*100) }, +                             bad_signature => sub { +                                 my ($msg, $msg2) = @_; +                                 local $::main_window = $gurpm->{real_window}; +                                 $msg =~ s/:$/\n\n/m; # FIXME: to be fixed in urpmi after 2008.0 +                                 interactive_msg( +                                     N("Warning"), "$msg\n\n$msg2", yesno => 1, if_(10 < ($msg =~ tr/\n/\n/), scroll => 1), +                                 ); +                             }, +                             post_download => sub { +                                 $canceled and goto return_with_exit_code; +                                 $gurpm->invalidate_cancel_forever; +                             }, +                             need_restart => sub { +                                 my ($need_restart_formatted) = @_; +                                 # FIXME: offer to restart the system +                                 interactive_msg(N("Warning"), join("\n", values %$need_restart_formatted), scroll => 1); +                             }, +                             trans_error_summary => sub { +                                 my ($nok, $errors) = @_; +                                 interactive_msg( +                                     N("Problem during installation"), +                                     if_($nok, N("%d installation transactions failed", $nok) . "\n\n") . +                                       N("There was a problem during the installation:\n\n%s", +                                         join("\n\n", @$errors, @error_msgs)), +                                     scroll => 1, +                                 ); +                             }, +                             need_restart => sub { +                                 my ($need_restart_formatted) = @_; +                                 interactive_msg(N("Warning"), +                                                 join("\n\n", values %$need_restart_formatted)); +                             }, +                             success_summary => sub { +                                 if (!($done || @to_remove)) { +                                     interactive_msg(N("Error"), +                                                     N("Unrecoverable error: no package found for installation, sorry.")); +                                     return; +                                 } +                                 my $id = statusbar_msg(N("Inspecting configuration files..."), 0); +                                 my %pkg2rpmnew; +                                 foreach my $id (@rpms_upgrade) { +				     my $pkg = $urpm->{depslist}[$id]; +				     next if $pkg->arch eq 'src'; +				     $pkg2rpmnew{$pkg->fullname} = [ grep { -r "$_.rpmnew" || -r "$_.rpmsave" } $pkg->conf_files ]; +                                 } +                                 statusbar_msg_remove($id); +                                 dialog_rpmnew(N("The installation is finished; everything was installed correctly. + +Some configuration files were created as `.rpmnew' or `.rpmsave', +you may now inspect some in order to take actions:"), +                                               %pkg2rpmnew) +                                   and statusbar_msg(N("All requested packages were installed successfully."), 1); +                                 statusbar_msg(N("Looking for \"README\" files..."), 1); +                                 display_READMEs_if_needed($urpm, $w); +                             }, +                             already_installed_or_not_installable => sub { +                                 my ($msg1, $msg2) = @_; +                                 my $msg = join("\n", @$msg1, @$msg2); +                                 return if !$msg; # workaround missing state +                                 interactive_msg(N("Error"), $msg); +                             }, +                         }, +                     ); + +    #- restart rpmdragora if needed, keep command line for that. +    if ($need_restart && !$exit_code && $something_installed) { +        log::explanations("restarting rpmdragora"); +        #- it seems to work correctly with exec instead of system, provided we stop timers +        #- added --previous-priority-upgrade to allow checking if yet if +        #-   priority-upgrade list has changed. and make sure we don't uselessly restart +        my @argv = ('--previous-priority-upgrade=' . $urpm->{options}{'priority-upgrade'},  +                grep { !/^--no-priority-upgrade$|--previous-priority-upgrade=/ } @Rpmdragora::init::ARGV_copy); +        # remove "--emmbedded <id>" from argv: +        my $i = 0; +        foreach (@argv) { +            splice @argv, $i, 2 if /^--embedded$/; +            $i++; +        } +        alarm(0); +        # remember not to ask again questions and the like: +        writeconf(); +        exec($0, @argv); +        exit(0); +    } + +    my $_s1 = N("RPM transaction %d/%d", 0, 0); +    my $_s2 = N("Unselect all"); +    my $_s3 = N("Details"); + +    statusbar_msg_remove($statusbar_msg_id); #- XXX maybe remove this + +    if ($exit_code == 0 && !$::rpmdragora_options{auto_orphans}) { +        if (urpm::orphans::check_unrequested_orphans_after_auto_select($urpm)) { +            if (my $msg = urpm::orphans::get_now_orphans_gui_msg($urpm)) { +                interactive_msg(N("Orphan packages"), $msg, scroll => 1); +            } +        } +    } + +  return_with_exit_code: +    return !($something_installed || scalar(@to_remove)); +} + + +# -=-=-=---=-=-=---=-=-=-- remove packages -=-=-=---=-=-=---=-=-=- + +sub perform_removal { +    my ($urpm, $pkgs) = @_; +    my @toremove = map { if_($pkgs->{$_}{selected}, $pkgs->{$_}{urpm_name}) } keys %$pkgs; +    return if !@toremove; +    my $gurpm = Rpmdragora::gurpm->new(1 ? N("Please wait") : N("Please wait, removing packages..."), N("Initializing..."), transient => $::main_window); +    my $_gurpm_clean_guard = before_leaving { undef $gurpm }; + +    my $may_be_orphans = 1; +    urpm::orphans::unrequested_orphans_after_remove($urpm, \@toremove) +	or $may_be_orphans = 0; + +    my $progress = -1; +    local $urpm->{log} = sub { +        my $str = $_[0]; +        print $str; +        $progress++; +        return if $progress <= 0; # skip first "creating transaction..." message +        $gurpm->label($str); # display "removing package %s" +        $gurpm->progress(min(0.99*100, scalar($progress/@toremove)*100)); +	#gtkflush(); +    }; + +    my @results; +    slow_func_statusbar( +	N("Please wait, removing packages..."), +	$::main_window, +	sub { +	    @results = $::rpmdragora_options{parallel} +		? urpm::parallel::remove($urpm, \@toremove) +		: urpm::install::install($urpm, \@toremove, {}, {}, +                                   callback_report_uninst => sub { $gurpm->label($_[0]) }, +                               ); +	    open_rpm_db('force_sync'); +	}, +    ); +    if (@results) { +	interactive_msg( +	    N("Problem during removal"), +	    N("There was a problem during the removal of packages:\n\n%s", join("\n",  @results)), +	    if_(@results > 1, scroll => 1), +	); +	return 1; +    } else { +        if ($may_be_orphans && !$::rpmdragora_options{auto_orphans}) { +            if (my $msg = urpm::orphans::get_now_orphans_gui_msg($urpm)) { +                interactive_msg(N("Information"), $msg, scroll => 1); +            } +        } +	return 0; +    } +} + +1; diff --git a/lib/AdminPanel/Rpmdragora/rpmnew.pm b/lib/AdminPanel/Rpmdragora/rpmnew.pm new file mode 100644 index 0000000..3c4335d --- /dev/null +++ b/lib/AdminPanel/Rpmdragora/rpmnew.pm @@ -0,0 +1,205 @@ +# vim: set et ts=4 sw=4: +package AdminPanel::Rpmdragora::rpmnew; +#***************************************************************************** +# +#  Copyright (c) 2002 Guillaume Cottenceau +#  Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com> +#  Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +#  Copyright (c) 2005-2007 Mandriva SA +# +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +# +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +# +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +#***************************************************************************** +# +# $Id: rpmnew.pm 263914 2009-12-03 17:41:02Z tv $ + +use strict; +use lib qw(/usr/lib/libDrakX); +use common; +use AdminPanel::rpmdragora; +use AdminPanel::Rpmdragora::init; +use AdminPanel::Rpmdragora::pkg; +use AdminPanel::Rpmdragora::open_db; +use AdminPanel::Rpmdragora::formatting; +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw(dialog_rpmnew do_merge_if_needed); + +# /var/lib/nfs/etab /var/lib/nfs/rmtab /var/lib/nfs/xtab /var/cache/man/whatis +my %ignores_rpmnew = map { $_ => 1 } qw( +    /etc/adjtime +    /etc/fstab +    /etc/group +    /etc/ld.so.conf +    /etc/localtime +    /etc/modules +    /etc/passwd +    /etc/security/fileshare.conf +    /etc/shells +    /etc/sudoers +    /etc/sysconfig/alsa +    /etc/sysconfig/autofsck +    /etc/sysconfig/harddisks +    /etc/sysconfig/harddrake2/previous_hw +    /etc/sysconfig/init +    /etc/sysconfig/installkernel +    /etc/sysconfig/msec +    /etc/sysconfig/nfs +    /etc/sysconfig/pcmcia +    /etc/sysconfig/rawdevices +    /etc/sysconfig/saslauthd +    /etc/sysconfig/syslog +    /etc/sysconfig/usb +    /etc/sysconfig/xinetd +); + +sub inspect { +	my ($file) = @_; +	my ($rpmnew, $rpmsave) = ("$file.rpmnew", "$file.rpmsave"); +	my @inspect_wsize = ($typical_width*2.5, 500); +	my $rpmfile = 'rpmnew'; +	-r $rpmnew or $rpmfile = 'rpmsave'; +	-r $rpmnew && -r $rpmsave && (stat $rpmsave)[9] > (stat $rpmnew)[9] and $rpmfile = 'rpmsave'; +	$rpmfile eq 'rpmsave' and $rpmnew = $rpmsave; + +	foreach (qw(LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION LC_ALL)) { +            local $ENV{$_} = $ENV{$_} . '.UTF-8' if $ENV{$_} && $ENV{$_} !~ /UTF-8/; +	} +	my @diff = map { ensure_utf8($_); $_ } `/usr/bin/diff -u '$file' '$rpmnew'`; +	@diff = N("(none)") if !@diff; +	my $d = ugtk2->new(N("Inspecting %s", $file), grab => 1, transient => $::main_window); +	my $save_wsize = sub { @inspect_wsize = $d->{rwindow}->get_size }; +	my %texts; +	require Gtk2::SourceView2; +	my $lang_manager = Gtk2::SourceView2::LanguageManager->get_default; +	gtkadd( +	    $d->{window}, +	    gtkpack_( +		gtknew('VBox', spacing => 5), +		1, create_vpaned( +		    create_vpaned( +			gtkpack_( +			    gtknew('VBox'), +			    0, gtknew('Label', text_markup => qq(<span font_desc="monospace">$file:</span>)), +			    1, gtknew('ScrolledWindow', child => $texts{file} = Gtk2::SourceView2::View->new), +			), +			gtkpack_( +			    gtknew('VBox'), +			    0, gtknew('Label', text_markup => qq(<span font_desc="monospace">$rpmnew:</span>)), +			    1, gtknew('ScrolledWindow', child => $texts{rpmnew} = Gtk2::SourceView2::View->new), +			), +			resize1 => 1, +		    ), +		    gtkpack_( +			gtknew('VBox'), +			0, gtknew('Label', text => N("Changes:")), +			1, gtknew('ScrolledWindow', child => $texts{diff} = Gtk2::SourceView2::View->new), +		    ), +		    resize1 => 1, +		), +		0, Gtk2::HSeparator->new, +		0, gtknew('WrappedLabel', +                    # prevent bad sizing of Gtk2::WrappedLabel: +                    width => $inspect_wsize[0], +                    text => N("You can either remove the .%s file, use it as main file or do nothing. If unsure, keep the current file (\"%s\").", +                              $rpmfile, N("Remove .%s", $rpmfile)), +                    ), +		0, gtkpack__( +		    gtknew('HButtonBox'), +		    gtksignal_connect( +			gtknew('Button', text => N("Remove .%s", $rpmfile)), +			clicked => sub { $save_wsize->(); unlink $rpmnew; Gtk2->main_quit }, +		    ), +		    gtksignal_connect( +			gtknew('Button', text => N("Use .%s as main file", $rpmfile)), +			clicked => sub { $save_wsize->(); renamef($rpmnew, $file); Gtk2->main_quit }, +		    ), +		    gtksignal_connect( +			gtknew('Button', text => N("Do nothing")), +			clicked => sub { $save_wsize->(); Gtk2->main_quit }, +		    ), +		) +	    ) +	); +	my %files = (file => $file, rpmnew => $rpmnew); +     foreach (keys %files) { +         gtktext_insert($texts{$_}, [ [ scalar(cat_($files{$_})), { 'font' => 'monospace' } ] ]); +         my $lang = $lang_manager->guess_language($files{$_}); +         $lang ||= $lang_manager->get_language('sh'); +         my $buffer = $texts{$_}->get_buffer; +         $buffer->set_language($lang) if $lang; +     } +	gtktext_insert($texts{diff}, [ [ join('', @diff), { 'font' => 'monospace' } ] ]); +	my $buffer = $texts{diff}->get_buffer; +	my $lang = $lang_manager->get_language('diff'); +	$buffer->set_language($lang) if $lang; +	$d->{rwindow}->set_default_size(@inspect_wsize); +	$d->main; +} + +sub dialog_rpmnew { +    my ($msg, %p2r) = @_; +    @{$p2r{$_}} = grep { !$ignores_rpmnew{$_} } @{$p2r{$_}} foreach keys %p2r; +    my $sum_rpmnew = sum(map { int @{$p2r{$_}} } keys %p2r); +    $sum_rpmnew == 0 and return 1; +    interactive_packtable( +	N("Installation finished"), +	$::main_window, +	$msg, +	[ map { my $pkg = $_; +	    map { +		my $f = $_; +		my $b; +		[ gtkpack__( +		    gtknew('HBox'), +		    gtkset_markup( +			gtkset_selectable(gtknew('Label'), 1), +			qq($pkg:<span font_desc="monospace">$f</span>), +		    ) +		), +		gtksignal_connect( +		    $b = gtknew('Button', text => N("Inspect...")), +		    clicked => sub { +			inspect($f); +			-r "$f.rpmnew" || -r "$f.rpmsave" or $b->set_sensitive(0); +		    }, +		) ]; +	    } @{$p2r{$pkg}}; +	} keys %p2r ], +	[ gtknew('Button', text => N("Ok"),  +	    clicked => sub { Gtk2->main_quit }) ] +    ); +    return 0; +} + + +sub do_merge_if_needed() { +    if ($rpmdragora_options{'merge-all-rpmnew'}) { +        my %pkg2rpmnew; +        my $wait = wait_msg(N("Please wait, searching...")); +        print "Searching .rpmnew and .rpmsave files...\n"; +        # costly: +        open_rpm_db()->traverse(sub { +                          my $n = my_fullname($_[0]); +                          $pkg2rpmnew{$n} = [ grep { m|^/etc| && (-r "$_.rpmnew" || -r "$_.rpmsave") } map { chomp_($_) } $_[0]->conf_files ]; +                      }); +        print "done.\n"; +        undef $wait; +        $typical_width = 330; +        dialog_rpmnew('', %pkg2rpmnew) and print "Nothing to do.\n"; +        myexit(0); +    } +} + +1; diff --git a/lib/AdminPanel/Rpmdragora/widgets.pm b/lib/AdminPanel/Rpmdragora/widgets.pm new file mode 100644 index 0000000..8484406 --- /dev/null +++ b/lib/AdminPanel/Rpmdragora/widgets.pm @@ -0,0 +1,52 @@ +# vim: set et ts=4 sw=4: +package Gtk2::Mdv::TextView; +#***************************************************************************** +# +#  Copyright (c) 2002 Guillaume Cottenceau +#  Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com> +#  Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +#  Copyright (c) 2005-2007 Mandriva SA +# +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +# +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +# +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +#***************************************************************************** +# +# $Id: widgets.pm 233986 2008-02-06 14:14:06Z tv $ + +use strict; +use MDK::Common::Func 'any'; +use lib qw(/usr/lib/libDrakX); + +use Time::HiRes; +use feature 'state'; + + +sub new { +    my ($_class) = @_; +    my $w = gtknew('TextView', editable => 0); +    state $time; +    $w->signal_connect(size_allocate => sub { +        my ($w, $requisition) = @_; +        return if !ref($w->{anchors}); +        return if Time::HiRes::clock_gettime() - $time < 0.200; +        $time = Time::HiRes::clock_gettime(); +        foreach my $anchor (@{$w->{anchors}}) { +            $_->set_size_request($requisition->width-30, -1) foreach $anchor->get_widgets; +        } +        1; +    }); +    $w; +} + +1; diff --git a/lib/AdminPanel/SettingsReader.pm b/lib/AdminPanel/SettingsReader.pm new file mode 100644 index 0000000..d93ebd5 --- /dev/null +++ b/lib/AdminPanel/SettingsReader.pm @@ -0,0 +1,45 @@ +# vim: set et ts=4 sw=4: +#    Copyright 2012 Angelo Naselli +# +#    This file is part of AdminPanel +# +#    AdminPanel is free software: you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation, either version 2 of the License, or +#    (at your option) any later version. +# +#    AdminPanel is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with AdminPanel.  If not, see <http://www.gnu.org/licenses/>. + + +#Class SettingsReader +package AdminPanel::SettingsReader; + +use strict; +use warnings; +use diagnostics; +use XML::Simple; +use Data::Dumper; + +sub new { +    my ($class, $fileName) = @_; +     +    my $self = { +        my $settings = 0, +        my $justToGetRidOfERROR = 0 +    }; +    bless $self, 'AdminPanel::SettingsReader'; +     +    my $xml = new XML::Simple (KeyAttr=>[]); +    $self->{settings} = $xml->XMLin($fileName); + +    return $self->{settings}; +} + + +1; diff --git a/lib/AdminPanel/Shared.pm b/lib/AdminPanel/Shared.pm new file mode 100644 index 0000000..583b214 --- /dev/null +++ b/lib/AdminPanel/Shared.pm @@ -0,0 +1,712 @@ +#!/usr/bin/perl +# vim: set et ts=4 sw=4: +#    Copyright 2012-2013 Angelo Naselli <anaselli@linux.it> +# +#    This file is part of AdminPanel +# +#    AdminPanel is free software: you can redistribute it and/or modify +#    it under the terms of the GNU General Public License as published by +#    the Free Software Foundation, either version 2 of the License, or +#    (at your option) any later version. +# +#    AdminPanel is distributed in the hope that it will be useful, +#    but WITHOUT ANY WARRANTY; without even the implied warranty of +#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#    GNU General Public License for more details. +# +#    You should have received a copy of the GNU General Public License +#    along with AdminPanel.  If not, see <http://www.gnu.org/licenses/>. + +package AdminPanel::Shared; + +=head1 NAME + +AdminPanel::Shared - AdminPanel::Shared contains all the shared routines  +                     needed by AdminPanel and modules + +=head1 SYNOPSIS + +     + +=head1 DESCRIPTION + +This module collects all the routines shared between AdminPanel and its modules. + +=head1 EXPORT + +    warningMsgBox +    msgBox +    infoMsgBox +    ask_YesOrNo +    ask_OkCancel +    AboutDialog +    trim + + +=head1 SUPPORT + +You can find documentation for this module with the perldoc command: + +    perldoc AdminPanel::Shared + +=head1 AUTHOR + +Angelo Naselli <anaselli@linux.it> + +=head1 COPYRIGHT and LICENSE + +Copyright (C) 2013, Angelo Naselli. + +This file is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This file is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file.  If not, see <http://www.gnu.org/licenses/>. + +=head1 FUNCTIONS + +=cut + +use strict; +use warnings; +use diagnostics; + +use lib qw(/usr/lib/libDrakX); +use common qw(N  +              N_); +use yui; +use base qw(Exporter); + +# TODO move GUI dialogs to Shared::GUI +our @EXPORT = qw( +                warningMsgBox +                msgBox +                infoMsgBox +                ask_YesOrNo +                ask_OkCancel +                ask_fromList +                AboutDialog +                trim  +                member +); + + +=head1 VERSION + +Version 0.01 + +=cut + +our $VERSION = '0.01'; + +our $License = N_("This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +"); + + +#============================================================= + +=head2 warningMsgBox + +=head3 INPUT + +    $st: string to be swhon into the dialog + +=head3 DESCRIPTION + +This function creates an Warning dialog and show the message  +passed as input. + +=cut + +#============================================================= + +sub warningMsgBox { +    my ($st) = @_; +    my $factory = yui::YUI::widgetFactory; +    my $msg_box = $factory->createPopupDialog($yui::YDialogWarnColor); +    my $layout = $factory->createVBox($msg_box); +    my $align = $factory->createAlignment($layout, 3, 0); +    $factory->createLabel( $align, $st, 1, 0); +    $align = $factory->createAlignment($layout, 3, 0); +    $factory->createPushButton($align, N("Ok")); +    $msg_box->waitForEvent(); + +    destroy $msg_box; +} + +#============================================================= + +=head2 infoMsgBox + +=head3 INPUT + +    $st: string to be swhon into the dialog + +=head3 DESCRIPTION + +This function creates an Info dialog and show the message  +passed as input. + +=cut + +#============================================================= + +sub infoMsgBox { +    my ($st) = @_; +    my $factory = yui::YUI::widgetFactory; +    my $msg_box = $factory->createPopupDialog($yui::YDialogInfoColor); +    my $layout = $factory->createVBox($msg_box); +    my $align = $factory->createAlignment($layout, 3, 0); +    $factory->createLabel( $align, $st, 1, 0); +    $align = $factory->createAlignment($layout, 3, 0); +    $factory->createPushButton($align, N("Ok")); +    $msg_box->waitForEvent(); + +    destroy $msg_box; +} + +#============================================================= + +=head2 msgBox + +=head3 INPUT + +    $st: string to be swhon into the dialog + +=head3 DESCRIPTION + +This function creates a dialog and show the message passed as input. + +=cut + +#============================================================= + +sub msgBox { +    my ($st) = @_; +    my $factory = yui::YUI::widgetFactory; +    my $msg_box = $factory->createPopupDialog($yui::YDialogNormalColor); +    my $layout = $factory->createVBox($msg_box); +    my $align = $factory->createAlignment($layout, 3, 0); +    $factory->createLabel( $align, $st, 1, 0); +    $align = $factory->createAlignment($layout, 3, 0); +    $factory->createPushButton($align, N("Ok")); +    $msg_box->waitForEvent(); + +    destroy $msg_box; +} + +#============================================================= + +=head2 ask_OkCancel + +=head3 INPUT + +    $title: Title shown as heading +    $text:  text to be shown into the dialog + +=head3 OUTPUT + +    0: Cancel button has been pressed +    1: Ok button has been pressed + +=head3 DESCRIPTION + +This function create an OK-Cancel dialog with a 'title' and a  +'text' passed as parameters. + +=cut + +#============================================================= + +sub ask_OkCancel { +    my ($title, $text) = @_; +    my $retVal = 0; +    my $factory = yui::YUI::widgetFactory; + +    my $msg_box = $factory->createPopupDialog($yui::YDialogNormalColor); +    my $layout = $factory->createVBox($msg_box); + +    my $align = $factory->createAlignment($layout, 3, 0); +    ## title with headings true +    $factory->createLabel( $align, $title, 1, 0); +    $align = $factory->createLeft($layout); +    $factory->createLabel( $align, $text, 0, 0); + +    $align = $factory->createRight($layout); +    my $hbox = $factory->createHBox($align); +    my $okButton = $factory->createPushButton($hbox, N("Ok")); +    my $cancelButton = $factory->createPushButton($hbox, N("Cancel")); + +    my $event = $msg_box->waitForEvent(); + +    my $eventType = $event->eventType(); + +    if ($eventType == $yui::YEvent::WidgetEvent) { +        # widget selected +        my $widget      = $event->widget(); +        $retVal = ($widget == $okButton) ? 1 : 0; +    } + +    destroy $msg_box; + +    return $retVal; +} + +#============================================================= + +=head2 ask_YesOrNo + +=head3 INPUT + +    $title: Title shown as heading +    $text:  question text to be shown into the dialog + +=head3 OUTPUT + +    0: "No" button has been pressed +    1: "Yes" button has been pressed + +=head3 DESCRIPTION + +This function create a Yes-No dialog with a 'title' and a  +question 'text' passed as parameters. + +=cut + +#============================================================= + +sub ask_YesOrNo { +    my ($title, $text) = @_; +    my $retVal = 0; +    my $factory = yui::YUI::widgetFactory; + +    my $msg_box = $factory->createPopupDialog($yui::YDialogNormalColor); +    my $layout = $factory->createVBox($msg_box); + +    my $align = $factory->createAlignment($layout, 3, 0); +    ## title with headings true +    $factory->createLabel( $align, $title, 1, 0); +    $align = $factory->createLeft($layout); +    $factory->createLabel( $align, $text, 0, 0); + +    $align = $factory->createRight($layout); +    my $hbox = $factory->createHBox($align); +    my $yesButton = $factory->createPushButton($hbox, N("Yes")); +    my $noButton  = $factory->createPushButton($hbox, N("No")); + +    my $event = $msg_box->waitForEvent(); + +    my $eventType = $event->eventType(); + +    if ($eventType == $yui::YEvent::WidgetEvent) { +        # widget selected +        my $widget      = $event->widget(); +        $retVal = ($widget == $yesButton) ? 1 : 0; +    } + +    destroy $msg_box; + +    return $retVal; +} + + +#============================================================= + +=head2 ask_fromList + +=head3 INPUT + +    $title: dialog title +    $text:  combobox heading +    $list:  item list  + +=head3 OUTPUT + +    undef:          if Cancel button has been pressed +    selected item:  if Ok button has been pressed + +=head3 DESCRIPTION + +This function create a dialog with a combobox in which to  +choose an item from a given list. + +=cut + +#============================================================= + +sub ask_fromList { +    my ($title, $text, $list) = @_; +     +    die "Title is mandatory"   if (! $title); +    die "Heading is mandatory" if (! $text); +    die "List is mandatory"   if (! $list ); +    die "At least one element is mandatory into list"   if (scalar(@$list) < 1); + +    my $choice  = undef; +    my $factory = yui::YUI::widgetFactory; + +    ## push application title +    my $appTitle = yui::YUI::app()->applicationTitle(); +    ## set new title to get it in dialog +    yui::YUI::app()->setApplicationTitle($title); + +    my $dlg = $factory->createPopupDialog($yui::YDialogNormalColor); +    my $layout = $factory->createVBox($dlg); + +    my $combo   = $factory->createComboBox($layout, $text, 0); +    my $itemColl = new yui::YItemCollection; +    foreach (@$list) { +            my $item = new yui::YItem ($_, 0); +            $itemColl->push($item); +            $item->DISOWN(); +    } +    $combo->addItems($itemColl); + +    my $align = $factory->createRight($layout); +    my $hbox = $factory->createHBox($align); +    my $okButton = $factory->createPushButton($hbox, N("Ok")); +    my $cancelButton = $factory->createPushButton($hbox, N("Cancel")); + +    while (1) { +        my $event = $dlg->waitForEvent(); + +        my $eventType = $event->eventType(); +        #event type checking +        if ($eventType == $yui::YEvent::CancelEvent) { +            last; +        } +        elsif ($eventType == $yui::YEvent::WidgetEvent) { +            # widget selected +            my $widget = $event->widget(); + +            if ($widget == $cancelButton) { +                last; +            } +            elsif ($widget == $okButton) { +                my $item = $combo->selectedItem(); +                $choice = $item->label() if ($item); +                last; +            } +        } +    } + +    destroy $dlg; + +    #restore old application title +    yui::YUI::app()->setApplicationTitle($appTitle); +     +    return $choice; +} + + +#============================================================= + +=head2 AboutDialog + +=head3 INPUT + +    $opts: optional options needed to get info for dialog. +           name          => Application Name, +           version       => Application Version, +           copyright     => Copyright ususally like "Copyright (C) copyright-holder Year", +           license       => License text,  +           comments      => A comment related to application to be shown, +           website       => Web site URL, +           website_label => Label to hide previous link, +           authors       => Application authors, +           translator_credits => Application translators  +           documenters   => Application documenters  +           artists       => Graphic applicaton designers +           logo          => picture path to be shown as application logo + +=head3 OUTPUT + +    Output_Parameter: out_par_description + +=head3 DESCRIPTION + +About dialog implementation, this dialog can be used by +modules, to show authors, license, credits, etc. + +=cut + +#============================================================= + +sub AboutDialog { +    my ($opts) = @_; +     +    # Credits dialog +    sub Credits { +        my ($opts) = @_; +         +        my $factory  = yui::YUI::widgetFactory; +        my $optional = yui::YUI::optionalWidgetFactory; +         +        my $creditsdlg = $factory->createPopupDialog(); +        my $layout = $factory->createVBox($creditsdlg); +         +        # header +        $factory->createHBox($layout); +        my $hbox  = $factory->createHBox($layout); +        my $align = $factory->createHVCenter($hbox); +        $hbox     = $factory->createHBox($align); +        $factory->createHeading($hbox, N("Credits")); +         +        # Credits tab widget +        if ($optional->hasDumbTab()) { +            $hbox = $factory->createHBox($layout); +            $align = $factory->createAlignment($hbox, 3, 0); +            my $dumptab = $optional->createDumbTab($align); +            my $item = new yui::YItem(N("Written by")); +            $item->setSelected(); +            $dumptab->addItem( $item ); +            $item->DISOWN(); +            if (exists $opts->{documenters}) { +                $item = new yui::YItem(N("Documented by")); +                $dumptab->addItem( $item ); +                $item->DISOWN(); +            } +            if (exists $opts->{translator_credits}) { +                $item = new yui::YItem(N("Translated by")); +                $dumptab->addItem( $item ); +                $item->DISOWN(); +            } +            if (exists $opts->{artists}) { +                $item = new yui::YItem(N("Artwork by")); +                $dumptab->addItem( $item ); +                $item->DISOWN(); +            } +            my $vbox = $factory->createVBox($dumptab); +            $align = $factory->createLeft($vbox); +            $factory->createVSpacing($vbox, 1.0); +            my $label = $factory->createLabel( $align, "***", 0); +            $factory->createVSpacing($vbox, 1.0); +        +            # start value for first Item +            $label->setValue($opts->{authors}) if exists $opts->{authors}; +         +            # Close button +            $align = $factory->createRight($layout); +            my $closeButton = $factory->createPushButton($align, N("Close")); +             +            # manage Credits dialog events +            while(1) { +                my $event     = $creditsdlg->waitForEvent(); +                my $eventType = $event->eventType(); +                 +                #event type checking +                if ($eventType == $yui::YEvent::CancelEvent) { +                    last; +                } +                elsif ($eventType == $yui::YEvent::WidgetEvent) { +                    # widget selected +                    my $widget = $event->widget(); + +                    if ($widget == $closeButton) { +                        last; +                    }                   +                } +                elsif ($event->item() ) { +                    # $eventType MenuEvent!!! +                    my $itemLabel = $event->item()->label(); +                    $itemLabel =~ s/&//; #remove shortcut from label +                    if ($itemLabel eq N("Written by")) { +                        $label->setValue($opts->{authors}) if exists $opts->{authors}; +                    } +                    elsif ($itemLabel eq N("Documented by")) { +                        $label->setValue($opts->{documenters}) if exists $opts->{documenters}; +                    } +                    elsif ($itemLabel eq N("Translated by")) { +                        $label->setValue($opts->{translator_credits}) if exists $opts->{translator_credits}; +                    } +                    elsif ($itemLabel eq N("Artwork by")) { +                        $label->setValue($opts->{artists}) if exists $opts->{artists}; +                    }   +                } +            } +        } +        else { +            print "No tab widgets available!\n"; +        } +        destroy $creditsdlg; +    } +     +    # License dialog +    sub License { +        my ($license) = @_; +         +        my $factory = yui::YUI::widgetFactory; +        my $licensedlg = $factory->createPopupDialog(); +        my $layout = $factory->createVBox($licensedlg); +         +        # header +        $factory->createHBox($layout); +        my $hbox  = $factory->createHBox($layout); +        my $align = $factory->createHVCenter($hbox); +        $hbox     = $factory->createHBox($align); +        $factory->createHeading($hbox, N("License")); +         +        # license +        $hbox = $factory->createHBox($layout); +        $align = $factory->createAlignment($hbox, 3, 0); +        $factory->createLabel( $align, $license); +             +        $align = $factory->createRight($layout); +        my $closeButton = $factory->createPushButton($align, N("Close")); +         +        $licensedlg->waitForEvent(); +         +        destroy $licensedlg; +    } +     +    my $website = "http://www.mageia.org"; +    my $website_label = "Mageia"; +    my $factory = yui::YUI::widgetFactory; +    my $aboutdlg = $factory->createPopupDialog(); +    my $layout = $factory->createVBox($aboutdlg); + +    # header +    $factory->createHBox($layout); +    my $hbox_iconbar  = $factory->createHBox($layout); +    my $align  = $factory->createHVCenter($hbox_iconbar); +    $hbox_iconbar     = $factory->createHBox($align); +    $factory->createImage($hbox_iconbar, $opts->{logo}) if exists $opts->{logo}; +    my $header = $opts->{name} . " " . $opts->{version}; +    $factory->createHeading($hbox_iconbar, $header); + +    # comments +    my $hbox = $factory->createHBox($layout); +    $align = $factory->createAlignment($hbox, 3, 0); +    $factory->createLabel( $align, $opts->{comments}, 0, 0) if exists $opts->{comments}; +     +    # copyright +    $hbox = $factory->createHBox($layout); +    $align = $factory->createHVCenter($hbox); +    $factory->createLabel( $align, $opts->{copyright}, 0, 0) if exists $opts->{copyright}; + +    # website / website_label +    $hbox = $factory->createHBox($layout); +    $align = $factory->createHVCenter($hbox); +    $website = $opts->{website} if exists $opts->{website}; +    $website_label = $opts->{website_label} if exists $opts->{website_label}; +    my $webref = "<a href=\"". $website ."\">". $website_label ."</a>"; +    $factory->createRichText( $align, $webref); +     +    # Credits, License and Close buttons +    $hbox = $factory->createHBox($layout); +    $align = $factory->createLeft($hbox); +    my $hbox1 = $factory->createHBox($align); +    my $creditsButton = $factory->createPushButton($hbox1, N("Credits")); +    my $licenseButton = $factory->createPushButton($hbox1, N("License")); +    $factory->createHSpacing($hbox, 2.0); +    $align = $factory->createRight($hbox); +    my $closeButton = $factory->createPushButton($align, N("Close")); +     +    # AboutDialog Events +    while(1) { +        my $event     = $aboutdlg->waitForEvent(); +        my $eventType = $event->eventType(); +         +        #event type checking +        if ($eventType == $yui::YEvent::CancelEvent) { +            last; +        } +        elsif ($eventType == $yui::YEvent::WidgetEvent) { +            # widget selected +            my $widget = $event->widget(); + +            if($widget == $licenseButton) { +                License($opts->{license}) if exists $opts->{license}; +            } +            elsif ($widget == $creditsButton) { +                Credits($opts); +            } +            elsif ($widget == $closeButton) { +                last; +            } +        } +        elsif ($eventType == $yui::YEvent::MenuEvent) { +            my  $menuEvent = yui::YMGAWidgetFactory::getYMenuEvent($event); +            #TODO check why is not working +            run_program::raw({ detach => 1 }, 'www-browser', $menuEvent->id()); +        } +    } +     +    destroy $aboutdlg; +} + +#============================================================= + +=head2 trim + +=head3 INPUT + +    $st: String to be trimmed + +=head3 OUTPUT + +    $st: trimmed string + +=head3 DESCRIPTION + +This function trim the given string. + +=cut + +#============================================================= + +sub trim { +    my ($st) = shift; +    $st =~s /^\s+//g; +    $st =~s /\s+$//g; +    return $st; +} + +#============================================================= + +=head2 member + +=head3 INPUT + +    $e: Array element to be found into array +    @_: any array + +=head3 OUTPUT + +    1 or 0: if $e is a member of the given array + +=head3 DESCRIPTION + +This function look for an element into an array + +=cut + +#============================================================= +sub member {  +    my $e = shift;  +    foreach (@_) {  +        $e eq $_ and return 1; +    }  +    0;  +} + +1; # End of AdminPanel::Shared diff --git a/lib/AdminPanel/Shared/Hosts.pm b/lib/AdminPanel/Shared/Hosts.pm new file mode 100644 index 0000000..3e1fb90 --- /dev/null +++ b/lib/AdminPanel/Shared/Hosts.pm @@ -0,0 +1,103 @@ +# vim: set et ts=4 sw=4: +#***************************************************************************** +#  +#  Copyright (c) 2013-2014 Matteo Pasotti <matteo.pasotti@gmail.com> +#  +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +#  +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +#  +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#  +#***************************************************************************** +package AdminPanel::Shared::Hosts;  + +use Moose; +use diagnostics; +use local::lib; +use Config::Hosts; +use utf8; + +# costants by Config::Hosts +my $is_ip = 1; +my $is_host = -1; +my $is_none = 0; + +has 'configHosts' => ( +	is => 'rw', +	init_arg => undef, +	builder => '_initialize' +); + +sub _initialize { +	my $self = shift(); +	$self->configHosts(Config::Hosts->new()); +} + +=pod + +=head2 _getHosts + +=head3 OUTPUT + +    @result: array of hashes; each one of them represent a host definition from the hosts configuration file +     +    NOTE: the 'hosts' item into each hash is an array: it contains the hostname and -eventually- the aliases + +=head3 DESCRIPTION + +retrieve data from the hosts file (/etc/hosts) using the Config::Hosts module + +=cut + +sub _getHosts { +	my $self = shift(); +	# $self->configHosts(Config::Hosts->new()); +	my $hosts = $self->configHosts->read_hosts(); +	my @result = (); +	while( my ($key, $value) = each($hosts)){ +		if($self->configHosts->determine_ip_or_host($key) == $is_ip){ +			my $tmp = {}; +			$tmp = $self->configHosts->query_host($key); +			$tmp->{'ip'} = $key; +			push @result,$tmp;  +		} +	} +	return @result; +} + +sub _insertHost { +	my $self = shift(); +	# remember that the order matters! +	my $ip = shift(); +	my @host_definitions = @_; +	# $self->configHosts = Config::Hosts->new(); +	return $self->configHosts->insert_host(ip => $ip, hosts => @host_definitions); +} + +sub _dropHost { +	my $self = shift(); +	my $host_ip = shift(); +	return $self->configHosts->delete_host($host_ip); +} + +sub _modifyHost { +    my $self = shift(); +    my $host_ip = shift(); +    my @host_definitions = @_; +    return $self->configHosts->update_host($host_ip, hosts => @host_definitions); +} + +sub _writeHosts { +	my $self = shift(); +	return $self->configHosts->write_hosts(); +} + +1; diff --git a/lib/AdminPanel/Shared/Services.pm b/lib/AdminPanel/Shared/Services.pm new file mode 100644 index 0000000..4993743 --- /dev/null +++ b/lib/AdminPanel/Shared/Services.pm @@ -0,0 +1,444 @@ +# vim: set et ts=4 sw=4: +#***************************************************************************** +#  +#  Copyright (c) 2013 Angelo Naselli <anaselli@linux.it> +#  from drakx services +#  +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +#  +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +#  +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#  +#***************************************************************************** + +package AdminPanel::Shared::Services; + + +#-###################################################################################### +#- misc imports +#-###################################################################################### + +use strict; +use common; +use run_program; + +use File::Basename qw( basename ); +use base qw(Exporter); + +our @EXPORT = qw( +                services +                xinetd_services +                is_service_running +                restart_or_start +                stop +                start +                set_service +                ); + +sub description { +    my %services = ( +acpid => N_("Listen and dispatch ACPI events from the kernel"),	     +alsa => N_("Launch the ALSA (Advanced Linux Sound Architecture) sound system"), +anacron => N_("Anacron is a periodic command scheduler."), +apmd => N_("apmd is used for monitoring battery status and logging it via syslog. +It can also be used for shutting down the machine when the battery is low."), +atd => N_("Runs commands scheduled by the at command at the time specified when +at was run, and runs batch commands when the load average is low enough."), +'avahi-deamon' => N_("Avahi is a ZeroConf daemon which implements an mDNS stack"), +chronyd => N_("An NTP client/server"), +cpufreq => N_("Set CPU frequency settings"), +crond => N_("cron is a standard UNIX program that runs user-specified programs +at periodic scheduled times. vixie cron adds a number of features to the basic +UNIX cron, including better security and more powerful configuration options."), +cups => N_("Common UNIX Printing System (CUPS) is an advanced printer spooling system"), +dm => N_("Launches the graphical display manager"), +fam => N_("FAM is a file monitoring daemon. It is used to get reports when files change. +It is used by GNOME and KDE"), +g15daemon => N_("G15Daemon allows users access to all extra keys by decoding them and  +pushing them back into the kernel via the linux UINPUT driver. This driver must be loaded  +before g15daemon can be used for keyboard access. The G15 LCD is also supported. By default,  +with no other clients active, g15daemon will display a clock. Client applications and  +scripts can access the LCD via a simple API."), +gpm => N_("GPM adds mouse support to text-based Linux applications such the +Midnight Commander. It also allows mouse-based console cut-and-paste operations, +and includes support for pop-up menus on the console."), +haldaemon => N_("HAL is a daemon that collects and maintains information about hardware"), +harddrake => N_("HardDrake runs a hardware probe, and optionally configures +new/changed hardware."), +httpd => N_("Apache is a World Wide Web server. It is used to serve HTML files and CGI."), +inet => N_("The internet superserver daemon (commonly called inetd) starts a +variety of other internet services as needed. It is responsible for starting +many services, including telnet, ftp, rsh, and rlogin. Disabling inetd disables +all of the services it is responsible for."), +ip6tables => N_("Automates a packet filtering firewall with ip6tables"), +iptables => N_("Automates a packet filtering firewall with iptables"), +irqbalance => N_("Evenly distributes IRQ load across multiple CPUs for enhanced performance"), +keytable => N_("This package loads the selected keyboard map as set in +/etc/sysconfig/keyboard.  This can be selected using the kbdconfig utility. +You should leave this enabled for most machines."), +kheader => N_("Automatic regeneration of kernel header in /boot for +/usr/include/linux/{autoconf,version}.h"), +kudzu => N_("Automatic detection and configuration of hardware at boot."), +'laptop-mode' => N_("Tweaks system behavior to extend battery life"), +linuxconf => N_("Linuxconf will sometimes arrange to perform various tasks +at boot-time to maintain the system configuration."), +lpd => N_("lpd is the print daemon required for lpr to work properly. It is +basically a server that arbitrates print jobs to printer(s)."), +lvs => N_("Linux Virtual Server, used to build a high-performance and highly +available server."), +mandi => N_("Monitors the network (Interactive Firewall and wireless"), +mdadm => N_("Software RAID monitoring and management"), +messagebus => N_("DBUS is a daemon which broadcasts notifications of system events and other messages"), +msec => N_("Enables MSEC security policy on system startup"), +named => N_("named (BIND) is a Domain Name Server (DNS) that is used to resolve host names to IP addresses."), +netconsole => N_("Initializes network console logging"), +netfs => N_("Mounts and unmounts all Network File System (NFS), SMB (Lan +Manager/Windows), and NCP (NetWare) mount points."), +network => N_("Activates/Deactivates all network interfaces configured to start +at boot time."), +'network-auth' => N_("Requires network to be up if enabled"), +'network-up' => N_("Wait for the hotplugged network to be up"), +nfs => N_("NFS is a popular protocol for file sharing across TCP/IP networks. +This service provides NFS server functionality, which is configured via the +/etc/exports file."), +nfslock => N_("NFS is a popular protocol for file sharing across TCP/IP +networks. This service provides NFS file locking functionality."), +ntpd => N_("Synchronizes system time using the Network Time Protocol (NTP)"), +numlock => N_("Automatically switch on numlock key locker under console +and Xorg at boot."), +oki4daemon => N_("Support the OKI 4w and compatible winprinters."), +partmon => N_("Checks if a partition is close to full up"), +pcmcia => N_("PCMCIA support is usually to support things like ethernet and +modems in laptops.  It will not get started unless configured so it is safe to have +it installed on machines that do not need it."), +portmap => N_("The portmapper manages RPC connections, which are used by +protocols such as NFS and NIS. The portmap server must be running on machines +which act as servers for protocols which make use of the RPC mechanism."), +portreserve => N_("Reserves some TCP ports"), +postfix => N_("Postfix is a Mail Transport Agent, which is the program that moves mail from one machine to another."), +random => N_("Saves and restores system entropy pool for higher quality random +number generation."), +rawdevices => N_("Assign raw devices to block devices (such as hard disk drive +partitions), for the use of applications such as Oracle or DVD players"), +resolvconf => N_("Nameserver information manager"), +routed => N_("The routed daemon allows for automatic IP router table updated via +the RIP protocol. While RIP is widely used on small networks, more complex +routing protocols are needed for complex networks."), +rstatd => N_("The rstat protocol allows users on a network to retrieve +performance metrics for any machine on that network."), +rsyslog => N_("Syslog is the facility by which many daemons use to log messages to various system log files.  It is a good idea to always run rsyslog."), +rusersd => N_("The rusers protocol allows users on a network to identify who is +logged in on other responding machines."), +rwhod => N_("The rwho protocol lets remote users get a list of all of the users +logged into a machine running the rwho daemon (similar to finger)."), +saned => N_("SANE (Scanner Access Now Easy) enables to access scanners, video cameras, ..."), +shorewall => N_("Packet filtering firewall"), +smb => N_("The SMB/CIFS protocol enables to share access to files & printers and also integrates with a Windows Server domain"), +sound => N_("Launch the sound system on your machine"), +'speech-dispatcherd' => N_("layer for speech analysis"), +sshd => N_("Secure Shell is a network protocol that allows data to be exchanged over a secure channel between two computers"), +syslog => N_("Syslog is the facility by which many daemons use to log messages +to various system log files.  It is a good idea to always run syslog."), +'udev-post' => N_("Moves the generated persistent udev rules to /etc/udev/rules.d"), +usb => N_("Load the drivers for your usb devices."), +vnStat => N_("A lightweight network traffic monitor"), +xfs => N_("Starts the X Font Server."), +xinetd => N_("Starts other deamons on demand."), +    ); +    my ($name) = @_; +    my $s = $services{$name}; +    if ($s) { +        $s = translate($s); +    } else { +        my $file = "$::prefix/usr/lib/systemd/system/$name.service"; +        if (-e $file) { +                $s = cat_($file); +                $s = $s =~ /^Description=(.*)/mg ? $1 : ''; +        } else { +                $file = find { -e $_ } map { "$::prefix$_/$name" } '/etc/rc.d/init.d', '/etc/init.d', '/etc/xinetd.d'; +                $s = cat_($file); +                $s =~ s/\\\s*\n#\s*//mg; +                $s = +                        $s =~ /^#\s+(?:Short-)?[dD]escription:\s+(.*?)^(?:[^#]|# {0,2}\S)/sm ? $1 : +                        $s =~ /^#\s*(.*?)^[^#]/sm ? $1 : ''; + +                $s =~ s/#\s*//mg; +        } +    } +    $s =~ s/\n/ /gm; $s =~ s/\s+$//; +    $s; +} + + +sub set_service { +    my ($service, $enable) = @_; +     +    my @xinetd_services = map { $_->[0] } xinetd_services(); + +    if (member($service, @xinetd_services)) { +        run_program::rooted($::prefix, "chkconfig", $enable ? "--add" : "--del", $service); +    } elsif (running_systemd() || has_systemd()) { +        # systemctl rejects any symlinked units. You have to enabled the real file +        if (-l "/lib/systemd/system/$service.service") { +            my $name = readlink("/lib/systemd/system/$service.service"); +            $service = File::Basename::basename($name); +        } else { +            $service = $service . ".service"; +        } +        run_program::rooted($::prefix, "/bin/systemctl", $enable ? "enable" : "disable", $service); +    } else { +        my $script = "/etc/rc.d/init.d/$service"; +        run_program::rooted($::prefix, "chkconfig", $enable ? "--add" : "--del", $service); +        #- FIXME: handle services with no chkconfig line and with no Default-Start levels in LSB header +        if ($enable && cat_("$::prefix$script") =~ /^#\s+chkconfig:\s+-/m) { +            run_program::rooted($::prefix, "chkconfig", "--level", "35", $service, "on"); +        } +    } +} + +sub _run_action { +    my ($service, $action) = @_; +    if (running_systemd()) { +        run_program::rooted($::prefix, '/bin/systemctl', '--no-block', $action, "$service.service"); +    } else { +        run_program::rooted($::prefix, "/etc/rc.d/init.d/$service", $action); +    } +} + +sub running_systemd() { +    run_program::rooted($::prefix, '/bin/mountpoint', '-q', '/sys/fs/cgroup/systemd'); +} + +sub has_systemd() { +    run_program::rooted($::prefix, '/bin/rpm', '-q', 'systemd'); +} + +sub xinetd_services() { +    local $ENV{LANGUAGE} = 'C'; +    my @xinetd_services; +    foreach (run_program::rooted_get_stdout($::prefix, '/sbin/chkconfig', '--list', '--type', 'xinetd')) { +        if (my ($xinetd_name, $on_off) = m!^\t(\S+):\s*(on|off)!) { +            push @xinetd_services, [ $xinetd_name, $on_off eq 'on' ]; +        } +    } +    @xinetd_services; +} + +sub _systemd_services() { +    local $ENV{LANGUAGE} = 'C'; +    my @services; +    my %loaded; +    # Running system using systemd +    log::explanations("Detected systemd running. Using systemctl introspection."); +    foreach (run_program::rooted_get_stdout($::prefix, '/bin/systemctl', '--full', '--all', 'list-units')) { +        if (my ($name) = m!^(\S+)\.service\s+loaded!) { +            # We only look at non-template, non-linked service files in /lib +            # We also check for any non-masked sysvinit files as these are +            # also handled by systemd +            if ($name !~ /.*\@$/g && (-e "$::prefix/lib/systemd/system/$name.service" or -e "$::prefix/etc/rc.d/init.d/$name") && ! -l "$::prefix/lib/systemd/system/$name.service") { +                push @services, [ $name, !!run_program::rooted($::prefix, '/bin/systemctl', '--quiet', 'is-enabled', "$name.service") ]; +                $loaded{$name} = 1; +            } +        } +    } +    # list-units will not list disabled units that can be enabled +    foreach (run_program::rooted_get_stdout($::prefix, '/bin/systemctl', '--full', 'list-unit-files')) { +        if (my ($name) = m!^(\S+)\.service\s+disabled!) { +            # We only look at non-template, non-linked service files in /lib +            # We also check for any non-masked sysvinit files as these are +            # also handled by systemd +            if (!exists $loaded{$name} && $name !~ /.*\@$/g && (-e "$::prefix/lib/systemd/system/$name.service" or -e "$::prefix/etc/rc.d/init.d/$name") && ! -l "$::prefix/lib/systemd/system/$name.service") { +                # Limit ourselves to "standard" targets which can be enabled +                my $wantedby = cat_("$::prefix/lib/systemd/system/$name.service") =~ /^WantedBy=(graphical|multi-user).target$/sm ? $1 : ''; +                if ($wantedby) { +                    push @services, [ $name, 0 ]; +                } +            } +        } +    } + +    @services; +} + +sub _legacy_services() { +    local $ENV{LANGUAGE} = 'C'; +    my @services; +    my $has_systemd = has_systemd(); +    if ($has_systemd) { +        # The system not using systemd but will be at next boot. This is +        # is typically the case in the installer. In this mode we must read +        # as much as is practicable from the native systemd unit files and +        # combine that with information from chkconfig regarding legacy sysvinit +        # scripts (which systemd will parse and include when running) +        log::explanations("Detected systemd installed. Using fake service+chkconfig introspection."); +        foreach (glob_("$::prefix/lib/systemd/system/*.service")) { +            my ($name) = m!([^/]*).service$!; + +            # We only look at non-template, non-symlinked service files +            if (!(/.*\@\.service$/g) && ! -l $_) { +                # Limit ourselves to "standard" targets +                my $wantedby = cat_($_) =~ /^WantedBy=(graphical|multi-user).target$/sm ? $1 : ''; +                if ($wantedby) { +                    # Exclude if enabled statically +                    # Note DO NOT use -e when testing for files that could +                    # be symbolic links as this will fail under a chroot +                    # setup where -e will fail if the symlink target does +                    # exist which is typically the case when viewed outside +                    # of the chroot. +                    if (!-l "$::prefix/lib/systemd/system/$wantedby.target.wants/$name.service") { +                        push @services, [ $name, !!-l "$::prefix/etc/systemd/system/$wantedby.target.wants/$name.service" ]; +                    } +                } +            } +        } +    } else { +        log::explanations("Could not detect systemd. Using chkconfig service introspection."); +    } + +    # Regardless of whether we expect to use systemd on next boot, we still +    # need to instrospect information about non-systemd native services. +    my $runlevel; +    my $on_off; +    if (!$::isInstall) { +        $runlevel = (split " ", `/sbin/runlevel`)[1]; +    } +    foreach (run_program::rooted_get_stdout($::prefix, '/sbin/chkconfig', '--list', '--type', 'sysv')) { +        if (my ($name, $l) = m!^(\S+)\s+(0:(on|off).*)!) { +            # If we expect to use systemd (i.e. installer) only show those +            # sysvinit scripts which are not masked by a native systemd unit. +            my $has_systemd_unit = systemd_unit_exists($name); +            if (!$has_systemd || !$has_systemd_unit) { +                if ($::isInstall) { +                    $on_off = $l =~ /\d+:on/g; +                } else { +                    $on_off = $l =~ /$runlevel:on/g; +                } +                push @services, [ $name, $on_off ]; +            } +        } +    } +    @services; +} + +#- returns: +#--- the listref of installed services +#--- the listref of "on" services +sub services() { +    my @services; +    if (running_systemd()) { +        @services = _systemd_services(); +    } else { +        @services = _legacy_services(); +    } + +    my @l = xinetd_services(); +    push @l, @services; +    @l = sort { $a->[0] cmp $b->[0] } @l; +    [ map { $_->[0] } @l ], [ map { $_->[0] } grep { $_->[1] } @l ]; +} + + + +sub systemd_unit_exists { +    my ($name) = @_; +    # we test with -l as symlinks are not valid when the system is chrooted: +    -e "$::prefix/lib/systemd/system/$name.service" or -l "$::prefix/lib/systemd/system/$name.service"; +} + +sub service_exists { +    my ($service) = @_; +    -x "$::prefix/etc/rc.d/init.d/$service" or systemd_unit_exists($service); +} + +sub restart ($) { +    my ($service) = @_; +    # Exit silently if the service is not installed +    service_exists($service) or return 1; +    _run_action($service, "restart"); +} + +sub restart_or_start ($) { +    my ($service) = @_; +    # Exit silently if the service is not installed +    service_exists($service) or return 1; +    _run_action($service, is_service_running($service) ? "restart" : "start"); +} + +sub start ($) { +    my ($service) = @_; +    # Exit silently if the service is not installed +    service_exists($service) or return 1; +    _run_action($service, "start"); +} + +sub start_not_running_service ($) { +    my ($service) = @_; +    # Exit silently if the service is not installed +    service_exists($service) or return 1; +    is_service_running($service) || _run_action($service, "start"); +} + +sub stop ($) { +    my ($service) = @_; +    # Exit silently if the service is not installed +    service_exists($service) or return 1; +    _run_action($service, "stop"); +} + +sub is_service_running ($) { +    my ($service) = @_; +    # Exit silently if the service is not installed +    service_exists($service) or return 1; +    if (running_systemd()) { +        run_program::rooted($::prefix, '/bin/systemctl', '--quiet', 'is-active', "$service.service"); +    } else { +        run_program::rooted($::prefix, '/sbin/service', $service, 'status'); +    } +} + +sub starts_on_boot { +    my ($service) = @_; +    my (undef, $on_services) = services(); +    member($service, @$on_services); +} + +sub start_service_on_boot ($) { +    my ($service) = @_; +    set_service($service, 1); +} + +sub do_not_start_service_on_boot ($) { +    my ($service) = @_; +    set_service($service, 0); +} + +sub enable { +    my ($service, $o_dont_apply) = @_; +    start_service_on_boot($service); +    restart_or_start($service) unless $o_dont_apply; +} + +sub disable { +    my ($service, $o_dont_apply) = @_; +    do_not_start_service_on_boot($service); +    stop($service) unless $o_dont_apply; +} + +sub set_status { +    my ($service, $enable, $o_dont_apply) = @_; +    if ($enable) { +	enable($service, $o_dont_apply); +    } else { +	disable($service, $o_dont_apply); +    } +} + +1; diff --git a/lib/AdminPanel/Shared/Users.pm b/lib/AdminPanel/Shared/Users.pm new file mode 100644 index 0000000..9dcc5bd --- /dev/null +++ b/lib/AdminPanel/Shared/Users.pm @@ -0,0 +1,126 @@ +package AdminPanel::Shared::Users;  + +use diagnostics; +use strict; + +#-###################################################################################### +#- misc imports +#-###################################################################################### +use common; + +use run_program; + +use base qw(Exporter); + +our @EXPORT = qw( +                facesdir +                face2png +                facenames +                addKdmIcon +                valid_username +                valid_groupname +                GetFaceIcon +                Add2UsersGroup +                ); + +sub facesdir() { +    "$::prefix/usr/share/mga/faces/"; +} +sub face2png { +    my ($face) = @_; +    facesdir() . $face . ".png"; +} +sub facenames() { +    my $dir = facesdir(); +    my @l = grep { /^[A-Z]/ } all($dir); +    map { if_(/(.*)\.png/, $1) } (@l ? @l : all($dir)); +} + +sub addKdmIcon { +    my ($user, $icon) = @_; +    my $dest = "$::prefix/usr/share/faces/$user.png"; +    eval { cp_af(facesdir() . $icon . ".png", $dest) } if $icon; +} + + +sub valid { +    return (0, N("Name field is empty please provide a name")) if (!$_[0] ); + +    $_[0] =~ /^[a-z]+?[a-z0-9_\-\.]*?$/ or do { +        return (0, N("The name must contain only lower cased latin letters, numbers, `.', `-' and `_'")); +    }; +    return (0, N("Name is too long")) if (! (length($_[0]) <= $_[1])); +    return (1, N("Ok")); +} + +sub valid_username { +     return valid($_[0], 32); +} + +sub valid_groupname { +    return valid($_[0], 16); +} + +################################################## +## GetFaceIcon +## params +## +## 'name' icon name for the given name +## 'next' get next icon from the given 'name'  +## +## return +## 'user_icon' icon name +## +sub GetFaceIcon { +    my ($name, $next) = @_; +    my @icons = facenames(); +    my $i; +    my $current_icon; +    # remove shortcut "&" from label +    $name =~ s/&// if ($name);  +    my $user_icon = "$::prefix/usr/share/faces/$name.png" if ($name); +    if ($name) { +        $user_icon    = face2png($name) unless(-e $user_icon); +    } +    if ($name && -e $user_icon) { +        my $current_md5 = common::md5file($user_icon); +        eval { $i = find_index { common::md5file(face2png($_)) eq $current_md5 } @icons }; +        if (!$@) { #- current icon found in @icons, select it +            $current_icon = $icons[$i]; +        } else { #- add and select current icon in @icons +            push @icons, $user_icon; +            $current_icon = $user_icon; +            $i = @icons - 1; +        } +    } else { +        #- no icon yet, select a random one +        $current_icon = $icons[$i = rand(@icons)]; +    } + +    if ($next) { +        $current_icon = $icons[$i = defined $icons[$i+1] ? $i+1 : 0]; +    } +    return $current_icon; +} + +################################################## +## Add2UsersGroup +## params +## +## 'name' username +## 'ctx' USER::ADMIN object  +## +## return +##  gid  group id +## +sub Add2UsersGroup { +    my ($name, $ctx) = @_; +    my $GetValue = -65533; ## Used by USER (for getting values? TODO need explanations, where?) + +    my $usersgroup = $ctx->LookupGroupByName('users'); +    $usersgroup->MemberName($name, 1); +    return $usersgroup->Gid($GetValue); +} + + +1; diff --git a/lib/AdminPanel/rpmdragora.pm b/lib/AdminPanel/rpmdragora.pm new file mode 100644 index 0000000..b96ed53 --- /dev/null +++ b/lib/AdminPanel/rpmdragora.pm @@ -0,0 +1,984 @@ +# vim: set et ts=4 sw=4: +#***************************************************************************** +#  +#  Copyright (c) 2002 Guillaume Cottenceau +#  Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com> +#  Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +#  Copyright (c) 2005, 2007 Mandriva SA +#  Copyright (c) 2013 Matteo Pasotti <matteo.pasotti@gmail.com> +#  +#  This program is free software; you can redistribute it and/or modify +#  it under the terms of the GNU General Public License version 2, as +#  published by the Free Software Foundation. +#  +#  This program is distributed in the hope that it will be useful, +#  but WITHOUT ANY WARRANTY; without even the implied warranty of +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +#  GNU General Public License for more details. +#  +#  You should have received a copy of the GNU General Public License +#  along with this program; if not, write to the Free Software +#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#  +#***************************************************************************** +# +# $Id: rpmdragora.pm 267936 2010-04-26 16:40:21Z jvictor $ + +package AdminPanel::rpmdragora; + +use lib qw(/usr/lib/libDrakX); +use urpm::download (); +use urpm::prompt; +use urpm::media; + +use MDK::Common; +use MDK::Common::System; +use urpm; +use urpm::cfg; +use URPM; +use URPM::Resolve; +use strict; +use c; +use POSIX qw(_exit); +use common; +use Locale::gettext; +use feature 'state'; + +use AdminPanel::Shared; + +our @ISA = qw(Exporter); +our $VERSION = '2.27'; +our @EXPORT = qw( +    $changelog_first_config +    $compute_updates +    $filter +    $dont_show_selections +    $ignore_debug_media +    $mandrakeupdate_wanted_categories +    $mandrivaupdate_height +    $mandrivaupdate_width +    $max_info_in_descr +    $mode +    $NVR_searches +    $offered_to_add_sources +    $rpmdragora_height +    $rpmdragora_width +    $tree_flat +    $tree_mode +    $use_regexp +    $typical_width +    $clean_cache +    $auto_select +    add_distrib_update_media +    add_medium_and_check +    but +    but_ +    check_update_media_version +    choose_mirror +    distro_type +    fatal_msg +    getbanner +    get_icon +    interactive_list +    interactive_list_ +    interactive_msg +    interactive_packtable +    myexit +    readconf +    remove_wait_msg +    run_drakbug +    show_urpm_progress +    slow_func +    slow_func_statusbar +    statusbar_msg +    statusbar_msg_remove +    strip_first_underscore +    update_sources +    update_sources_check +    update_sources_interactive +    update_sources_noninteractive +    wait_msg +    warn_for_network_need +    writeconf +); +our $typical_width = 280; + +our $dont_show_selections; + +# i18n: IMPORTANT: to get correct namespace (rpmdragora instead of libDrakX) +BEGIN { unshift @::textdomains, qw(rpmdragora urpmi rpm-summary-main rpm-summary-contrib rpm-summary-devel rpm-summary-non-free) } + +use yui; +use Glib; +#ugtk2::add_icon_path('/usr/share/rpmdragora/icons'); + +Locale::gettext::bind_textdomain_codeset('rpmdragora', 'UTF8'); + +our $mageia_release = cat_( +    -e '/etc/mageia-release' ? '/etc/mageia-release' : '/etc/release' +) || ''; +chomp $mageia_release; +our ($distro_version) = $mageia_release =~ /(\d+\.\d+)/; +our ($branded, %distrib); +$branded = -f '/etc/sysconfig/oem' +    and %distrib = MDK::Common::System::distrib(); +our $myname_update = $branded ? N_("Software Update") : N_("Mageia Update"); + +@rpmdragora::prompt::ISA = 'urpm::prompt'; + +sub rpmdragora::prompt::prompt { +    my ($self) = @_; +    my @answers; +    my $d = ugtk2->new("", grab => 1, if_($::main_window, transient => $::main_window)); +    $d->{rwindow}->set_position('center_on_parent'); +    gtkadd( +	$d->{window}, +	gtkpack( +	    Gtk2::VBox->new(0, 5), +	    Gtk2::WrappedLabel->new($self->{title}), +	    (map { gtkpack( +		Gtk2::HBox->new(0, 5), +		Gtk2::Label->new($self->{prompts}[$_]), +		$answers[$_] = gtkset_visibility(gtkentry(), !$self->{hidden}[$_]), +	    ) } 0 .. $#{$self->{prompts}}), +	    gtksignal_connect(Gtk2::Button->new(N("Ok")), clicked => sub { Gtk2->main_quit }), +	), +    ); +    $d->main; +    map { $_->get_text } @answers; +} + +$urpm::download::PROMPT_PROXY = new rpmdragora::prompt( +    N_("Please enter your credentials for accessing proxy\n"), +    [ N_("User name:"), N_("Password:") ], +    undef, +    [ 0, 1 ], +); + +sub myexit { +    writeconf(); +    #ugtk2::exit(undef, @_); +} + +my ($root) = grep { $_->[2] == 0 } list_passwd(); +$ENV{HOME} = $> == 0 ? $root->[7] : $ENV{HOME} || '/root'; +$ENV{HOME} = $::env if $::env = $Rpmdragora::init::rpmdragora_options{env}[0]; + +our $configfile = "$ENV{HOME}/.rpmdragora"; + +# +# Configuration File Options +# + +# clear download cache after successfull installation of packages +our $clean_cache; + +# automatic select dependencies without user intervention +our $auto_select; + +our ($changelog_first_config, $compute_updates, $filter, $max_info_in_descr, $mode, $NVR_searches, $tree_flat, $tree_mode, $use_regexp); +our ($mandrakeupdate_wanted_categories, $ignore_debug_media, $offered_to_add_sources, $no_confirmation); +our ($rpmdragora_height, $rpmdragora_width, $mandrivaupdate_height, $mandrivaupdate_width); + +our %config = ( +    clean_cache => {  +	var => \$clean_cache,  +	default => [ 0 ]  +    }, +    auto_select => {  +	var => \$auto_select,  +	default => [ 0 ]  +    }, +    changelog_first_config => { var => \$changelog_first_config, default => [ 0 ] }, +    compute_updates => { var => \$compute_updates, default => [ 1 ] }, +    dont_show_selections => { var => \$dont_show_selections, default => [ $> ? 1 : 0 ] }, +    filter => { var => \$filter, default => [ 'all' ] }, +    ignore_debug_media => { var => \$ignore_debug_media, default => [ 0 ] }, +    mandrakeupdate_wanted_categories => { var => \$mandrakeupdate_wanted_categories, default => [ qw(security) ] }, +    mandrivaupdate_height => { var => \$mandrivaupdate_height, default => [ 0 ] }, +    mandrivaupdate_width => { var => \$mandrivaupdate_width, default => [ 0 ] }, +    max_info_in_descr => { var => \$max_info_in_descr, default => [] }, +    mode => { var => \$mode, default => [ 'by_group' ] }, +    NVR_searches => { var => \$NVR_searches, default => [ 0 ] }, +    'no-confirmation' => { var => \$no_confirmation, default => [ 0 ] }, +    offered_to_add_sources => { var => \$offered_to_add_sources, default => [ 0 ] }, +    rpmdragora_height => { var => \$rpmdragora_height, default => [ 0 ] }, +    rpmdragora_width => { var => \$rpmdragora_width, default => [ 0 ] }, +    tree_flat => { var => \$tree_flat, default => [ 0 ] }, +    tree_mode => { var => \$tree_mode, default => [ qw(gui_pkgs) ] }, +    use_regexp => { var => \$use_regexp, default => [ 0 ] }, +); + +sub readconf() { +    ${$config{$_}{var}} = $config{$_}{default} foreach keys %config; +    foreach my $l (cat_($configfile)) { +	foreach (keys %config) { +	    ${$config{$_}{var}} = [ split ' ', $1 ] if $l =~ /^\Q$_\E(.*)/; +	} +    } +    # special cases: +    $::rpmdragora_options{'no-confirmation'} = $no_confirmation->[0] if !defined $::rpmdragora_options{'no-confirmation'}; +    $Rpmdragora::init::default_list_mode = $tree_mode->[0] if ref $tree_mode && !$Rpmdragora::init::overriding_config; +} + +sub writeconf() { +    return if $::env; +    unlink $configfile; + +    # special case: +    $no_confirmation->[0] = $::rpmdragora_options{'no-confirmation'}; + +    output($configfile, map { "$_ " . (ref ${$config{$_}{var}} ? join(' ', @${$config{$_}{var}}) : undef) . "\n" } keys %config); +} + +sub getbanner() { +    $::MODE or return undef; +    if (0) { +	+{ +	remove  => N("Software Packages Removal"), +	update  => N("Software Packages Update"), +	install => N("Software Packages Installation"), +	}; +    } +#    Gtk2::Banner->new($ugtk2::wm_icon, $::MODE eq 'update' ? N("Software Packages Update") : N("Software Management")); +} + +# return value: +# - undef if if closed (aka really canceled) +# - 0 if if No/Cancel +# - 1 if if Yes/Ok +sub interactive_msg { +    my ($title, $contents, %options) = @_; +    return ask_YesOrNo($title, $contents); +=comment +    $options{transient} ||= $::main_window if $::main_window; +    local $::isEmbedded; +    my $factory = yui::YUI::widgetFactory; +    my $d = $factory->createPopupDialog(); + +    my $d = ugtk2->new($title, grab => 1, if_(exists $options{transient}, transient => $options{transient})); +    $d->{rwindow}->set_position($options{transient} ? 'center_on_parent' : 'center_always'); +    if ($options{scroll}) { +        $contents = ugtk2::markup_to_TextView_format($contents) if !ref $contents; +    } else { #- because we'll use a WrappedLabel +        $contents = formatAlaTeX($contents) if !ref $contents; +    } + +    my $button_yes; +    my $vbox = $factory->createVBox($d); +    my $text_w = $factory->createMultiLineEdit($vbox, ""); +    my $hbox = $factory->createHBox($vbox); +     +    ref($options{yesno}) eq 'ARRAY' ? map {ss +		my $label = $_; +		my $button_yes = $factory->createIconButton($hbox,"",$label); +		} @{$options{yesno}} +		: ( +		$options{yesno} ? ( +			my $button_no = $factory->createIconButton($hbox, "", $options{text}{no} || N("No")); +			$button_yes = $factory->createIconButton($hbox,"", $options{text}{yes} || N("Yes")); +		) +		: $button_yes = $factory->createIconButton($hbox,"",N("Ok")); +		) +     +    #$d->{window}->set_focus($button_yes); +    #$text_w->set_size_request($typical_width*2, $options{scroll} ? 300 : -1); +    #$d->main; +    return $d->{retval}; +=cut +} + +sub interactive_packtable { +    my ($title, $parent_window, $top_label, $lines, $action_buttons) = @_; +     +    my $w = ugtk2->new($title, grab => 1, transient => $parent_window); +    local $::main_window = $w->{real_window}; +    $w->{rwindow}->set_position($parent_window ? 'center_on_parent' : 'center'); +    my $packtable = create_packtable({}, @$lines); + +    gtkadd($w->{window}, +	   gtkpack_(Gtk2::VBox->new(0, 5), +		    if_($top_label, 0, Gtk2::Label->new($top_label)), +		    1, create_scrolled_window($packtable), +		    0, gtkpack__(create_hbox(), @$action_buttons))); +    my $preq = $packtable->size_request; +    my ($xpreq, $ypreq) = ($preq->width, $preq->height); +    my $wreq = $w->{rwindow}->size_request; +    my ($xwreq, $ywreq) = ($wreq->width, $wreq->height); +    $w->{rwindow}->set_default_size(max($typical_width, min($typical_width*2.5, $xpreq+$xwreq)), + 				    max(200, min(450, $ypreq+$ywreq))); +    $w->main; +} + +sub interactive_list { +    my ($title, $contents, $list, $callback, %options) = @_; + +    my $factory = yui::YUI::widgetFactory; +    my $mainw = $factory->createPopupDialog(); +    my $vbox = $factory->createVBox($mainw); +    my $lbltitle = $factory->createLabel($vbox, N("Dependencies")); +    my $radiobuttongroup = $factory->createRadioButtonGroup($vbox); +    my $rbbox = $factory->createVBox($radiobuttongroup); +    foreach my $item(@$list){ +        my $radiobutton = $factory->createRadioButton($rbbox,$item); +        $radiobutton->setNotify(0); +        $radiobuttongroup->addRadioButton($radiobutton); +    } +    my $submitButton = $factory->createIconButton($vbox,"", N("OK")); +    my $choice; + +    while(1) { +        my $event = $mainw->waitForEvent(); +        my $eventType = $event->eventType(); +        #event type checking +        if ($eventType == $yui::YEvent::CancelEvent) { +            $mainw->destroy(); +            last; +        } +        elsif ($eventType == $yui::YEvent::WidgetEvent) { +            # widget selected +            my $widget = $event->widget(); + +            if($widget == $submitButton) { +                $choice = $radiobuttongroup->currentButton->label(); +                $choice =~s/\&//g; +                last; +            } +        } +    } +    $mainw->destroy(); +    return $choice; +} + +sub interactive_list_ { interactive_list(@_, if_($::main_window, transient => $::main_window)) } + +sub fatal_msg { +    interactive_msg @_; +    myexit -1; +} + +sub wait_msg { +    my ($msg, %options) = @_; +    #OLD my $mainw = ugtk2->new(N("Please wait"), grab => 1, if_(exists $options{transient}, transient => $options{transient})); +    #$mainw->{real_window}->set_position($options{transient} ? 'center_on_parent' : 'center_always'); +    #my $label = $factory->createLabel($vbox, $msg); +    #OLD my $label = ref($msg) =~ /^Gtk/ ? $msg : Gtk2::WrappedLabel->new($msg); +    #gtkadd( +	#$mainw->{window}, +	#gtkpack__( +	#    gtkset_border_width(Gtk2::VBox->new(0, 5), 6), +	#    $label, +	#    if_(exists $options{widgets}, @{$options{widgets}}), +	#) +    #); +    my $factory = yui::YUI::widgetFactory; +    my $mainw = $factory->createPopupDialog(); +    my $vbox = $factory->createVBox($mainw); +    my $title = $factory->createLabel($vbox, N("Please wait")); +    #$mainw->recalcLayout(); +    #$mainw->doneMultipleChanges(); +    $mainw->pollEvent(); +    #$mainw->recalcLayout(); +    #$mainw->doneMultipleChanges(); +    $mainw; +} + +sub remove_wait_msg { +    my $w = shift; +    #gtkset_mousecursor_normal($w->{rwindow}->window); +    $w->destroy; +} + +sub but { "    $_[0]    " } +sub but_ { "        $_[0]        " } + +sub slow_func ($&) { +    my ($param, $func) = @_; +    if (ref($param) =~ /^Gtk/) { +	#gtkset_mousecursor_wait($param); +	#ugtk2::flush(); +	#$func->(); +	#gtkset_mousecursor_normal($param); +    } else { +		my $w = wait_msg($param); +		$func->(); +		remove_wait_msg($w); +    } +} + +sub statusbar_msg { +    unless ($::statusbar) { #- fallback if no status bar +	if (defined &::wait_msg_) { goto &::wait_msg_ } else { goto &wait_msg } +    } +    my ($msg, $o_timeout) = @_; +    $::statusbar->setText($msg); +    #- always use the same context description for now +    #my $cx = $::statusbar->get_context_id("foo"); +    #$::w and $::w->{rwindow} and gtkset_mousecursor_wait($::w->{rwindow}->window); +    #- returns a msg_id to be passed optionnally to statusbar_msg_remove +    #my $id = $::statusbar->push($cx, $msg); +    #gtkflush(); +    #Glib::Timeout->add(5000, sub { statusbar_msg_remove($id); 0 }) if $o_timeout; +    Glib::Timeout->add(5000, sub { statusbar_msg_remove(); 0 }) if $o_timeout; +    #$id; +} + +sub statusbar_msg_remove { +    #my ($msg_id) = @_; +    #if (!$::statusbar || ref $msg_id) { #- fallback if no status bar +	#goto &remove_wait_msg; +    #} +    #my $cx = $::statusbar->get_context_id("foo"); +    #if (defined $msg_id) { +	#$::statusbar->remove($cx, $msg_id); +    #} else { +	#$::statusbar->pop($cx); +    #} +    #$::w and $::w->{rwindow} and gtkset_mousecursor_normal($::w->{rwindow}->window); +    $::statusbar->setValue(""); +} + +sub slow_func_statusbar ($$&) { +    my ($msg, $w, $func) = @_; +    gtkset_mousecursor_wait($w->window); +    my $msg_id = statusbar_msg($msg); +    gtkflush(); +    $func->(); +    statusbar_msg_remove($msg_id); +    gtkset_mousecursor_normal($w->window); +} + +my %u2l = ( +	   at => N_("Austria"), +	   au => N_("Australia"), +	   be => N_("Belgium"), +	   br => N_("Brazil"), +	   ca => N_("Canada"), +	   ch => N_("Switzerland"), +	   cr => N_("Costa Rica"), +	   cz => N_("Czech Republic"), +	   de => N_("Germany"), +	   dk => N_("Danmark"), +	   el => N_("Greece"), +	   es => N_("Spain"), +	   fi => N_("Finland"), +	   fr => N_("France"), +	   gr => N_("Greece"), +	   hu => N_("Hungary"), +	   il => N_("Israel"), +	   it => N_("Italy"), +	   jp => N_("Japan"), +	   ko => N_("Korea"), +	   nl => N_("Netherlands"), +	   no => N_("Norway"), +	   pl => N_("Poland"), +	   pt => N_("Portugal"), +	   ru => N_("Russia"), +	   se => N_("Sweden"), +	   sg => N_("Singapore"), +	   sk => N_("Slovakia"), +	   tw => N_("Taiwan"), +	   uk => N_("United Kingdom"), +	   cn => N_("China"), +	   com => N_("United States"), +	   org => N_("United States"), +	   net => N_("United States"), +	   edu => N_("United States"), +	  ); +my $us = [ qw(com org net edu) ]; +my %t2l = ( +	   'America/\w+' =>       $us, +	   'Asia/Tel_Aviv' =>     [ qw(il ru it cz at de fr se) ], +	   'Asia/Tokyo' =>        [ qw(jp ko tw), @$us ], +	   'Asia/Seoul' =>        [ qw(ko jp tw), @$us ], +	   'Asia/Taipei' =>       [ qw(tw jp), @$us ], +	   'Asia/(Shanghai|Beijing)' => [ qw(cn tw sg), @$us ], +	   'Asia/Singapore' =>    [ qw(cn sg), @$us ], +	   'Atlantic/Reykjavik' => [ qw(uk no se fi dk), @$us, qw(nl de fr at cz it) ], +	   'Australia/\w+' =>     [ qw(au jp ko tw), @$us ], +	   'Brazil/\w+' =>        [ 'br', @$us ], +	   'Canada/\w+' =>        [ 'ca', @$us ], +	   'Europe/Amsterdam' =>  [ qw(nl be de at cz fr se dk it) ], +	   'Europe/Athens' =>     [ qw(gr pl cz de it nl at fr) ], +	   'Europe/Berlin' =>     [ qw(de be at nl cz it fr se) ], +	   'Europe/Brussels' =>   [ qw(be de nl fr cz at it se) ], +	   'Europe/Budapest' =>   [ qw(cz it at de fr nl se) ], +	   'Europe/Copenhagen' => [ qw(dk nl de be se at cz it) ], +	   'Europe/Dublin' =>     [ qw(uk fr be nl dk se cz it) ], +	   'Europe/Helsinki' =>   [ qw(fi se no nl be de fr at it) ], +	   'Europe/Istanbul' =>   [ qw(il ru it cz it at de fr nl se) ], +	   'Europe/Lisbon' =>     [ qw(pt es fr it cz at de se) ], +	   'Europe/London' =>     [ qw(uk fr be nl de at cz se it) ], +	   'Europe/Madrid' =>     [ qw(es fr pt it cz at de se) ], +	   'Europe/Moscow' =>     [ qw(ru de pl cz at se be fr it) ], +	   'Europe/Oslo' =>       [ qw(no se fi dk de be at cz it) ], +	   'Europe/Paris' =>      [ qw(fr be de at cz nl it se) ], +	   'Europe/Prague' =>     [ qw(cz it at de fr nl se) ], +	   'Europe/Rome' =>       [ qw(it fr cz de at nl se) ], +	   'Europe/Stockholm' =>  [ qw(se no dk fi nl de at cz fr it) ], +	   'Europe/Vienna' =>     [ qw(at de cz it fr nl se) ], +	  ); + +#- get distrib release number (2006.0, etc) +sub etc_version() { +    (my $v) = split / /, cat_('/etc/version'); +    return $v; +} + +#- returns the keyword describing the type of the distribution. +#- the parameter indicates whether we want base or update sources +sub distro_type { +    my ($want_base_distro) = @_; +    return 'cauldron' if $mageia_release =~ /cauldron/i; +    #- we can't use updates for community while official is not out (release ends in ".0") +    if ($want_base_distro || $mageia_release =~ /community/i && etc_version() =~ /\.0$/) { +	return 'official' if $mageia_release =~ /official|limited/i; +	return 'community' if $mageia_release =~ /community/i; +	#- unknown: fallback to updates +    } +    return 'updates'; +} + +sub compat_arch_for_updates($) { +    # FIXME: We prefer 64-bit packages to update on biarch platforms, +    # since the system is populated with 64-bit packages anyway. +    my ($arch) = @_; +    return $arch =~ /x86_64|amd64/ if arch() eq 'x86_64'; +    MDK::Common::System::compat_arch($arch); +} + +sub mirrors { +    my ($urpm, $want_base_distro) = @_; +    my $cachedir = $urpm->{cachedir} || '/root'; +    require mirror; +    mirror::register_downloader( +        sub { +            my ($url) = @_; +            my $file = $url; +            $file =~ s!.*/!$cachedir/!; +            unlink $file;       # prevent "partial file" errors +            before_leaving(sub { unlink $file }); + +            my ($gurpm, $id, $canceled); +            # display a message in statusbar (if availlable): +            $::statusbar and $id = statusbar_msg( +                $branded +                  ? N("Please wait, downloading mirror addresses.") +                    : N("Please wait, downloading mirror addresses from the Mageia website."), +                0); +            my $_clean_guard = before_leaving { +                undef $gurpm; +                $id and statusbar_msg_remove($id); +            }; + +            require Rpmdragora::gurpm; +            require Rpmdragora::pkg; + +            my $res = urpm::download::sync_url($urpm, $url, +                                           dir => $cachedir, +                                           callback => sub { +                                               $gurpm ||=  +                                                 Rpmdragora::gurpm->new(N("Please wait"), +                                                                      transient => $::main_window); +                                               $canceled ||= +                                                 !Rpmdragora::pkg::download_callback($gurpm, @_); +                                               gtkflush(); +                                           }, +                                       ); +            $res or die N("retrieval of [%s] failed", $file) . "\n"; +            return $canceled ? () : cat_($file); +        }); +    my @mirrors = @{ mirror::list(common::parse_LDAP_namespace_structure(cat_('/etc/product.id')), 'distrib') || [] }; +    require timezone; +    my $tz = ${timezone::read()}{timezone}; +    foreach my $mirror (@mirrors) { +	    my $goodness; +	    each_index { $_ = $u2l{$_} || $_; $_ eq $mirror->{country} and $goodness ||= 100-$::i } (map { if_($tz =~ /^$_$/, @{$t2l{$_}}) } keys %t2l), @$us; +         $mirror->{goodness} = $goodness + rand(); +         $mirror->{country} = translate($mirror->{country}); +    } +    unless (-x '/usr/bin/rsync') { +	@mirrors = grep { $_->{url} !~ /^rsync:/ } @mirrors; +    } +    return sort { $b->{goodness} <=> $a->{goodness} } @mirrors; +} + +sub warn_for_network_need { +    my ($message, %options) = @_; +    $message ||=  +$branded +? N("I need to access internet to get the mirror list. +Please check that your network is currently running. + +Is it ok to continue?") +: N("I need to contact the Mageia website to get the mirror list. +Please check that your network is currently running. + +Is it ok to continue?"); +    interactive_msg(N("Mirror choice"), $message, yesno => 1, %options) or return ''; +} + +sub choose_mirror { +    my ($urpm, %options) = @_; +    delete $options{message}; +    my @transient_options = exists $options{transient} ? (transient => $options{transient}) : (); +    warn_for_network_need($options{message}, %options) or return; +    my @mirrors = eval { mirrors($urpm, $options{want_base_distro}) }; +    my $error = $@; +    if ($error) { +        $error = "\n$error\n"; +	interactive_msg(N("Error during download"), +($branded +? N("There was an error downloading the mirror list: + +%s +The network, or the website, may be unavailable. +Please try again later.", $error) +: N("There was an error downloading the mirror list: + +%s +The network, or the Mageia website, may be unavailable. +Please try again later.", $error)), %options + +	); +	return ''; +    } + +    !@mirrors and interactive_msg(N("No mirror"), +($branded +? N("I can't find any suitable mirror.") +: N("I can't find any suitable mirror. + +There can be many reasons for this problem; the most frequent is +the case when the architecture of your processor is not supported +by Mageia Official Updates.")), %options +    ), return ''; + +    my $w = ugtk2->new(N("Mirror choice"), grab => 1, @transient_options); +    $w->{rwindow}->set_position($options{transient} ? 'center_on_parent' : 'center_always'); +    my $tree_model = Gtk2::TreeStore->new("Glib::String"); +    my $tree = Gtk2::TreeView->new_with_model($tree_model); +    $tree->get_selection->set_mode('browse'); +    $tree->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, text => 0)); +    $tree->set_headers_visible(0); + +    gtkadd( +	$w->{window},  +	gtkpack_( +	    Gtk2::VBox->new(0,5), +	    0, N("Please choose the desired mirror."), +	    1, create_scrolled_window($tree), +	    0, gtkpack( +		create_hbox('edge'), +		map { +		    my $retv = $_->[1]; +		    gtksignal_connect( +			Gtk2::Button->new(but($_->[0])), +			clicked => sub { +			    if ($retv) { +				my ($model, $iter) = $tree->get_selection->get_selected; +				$model and $w->{retval} = { sel => $model->get($iter, 0) }; +			    } +			    Gtk2->main_quit; +			}, +		    ); +		} [ N("Cancel"), 0 ], [ N("Ok"), 1 ] +	    ), +	) +    ); +    my %roots; +    $tree_model->append_set($roots{$_->{country}} ||= $tree_model->append_set(undef, [ 0 => $_->{country} ]), +			    [ 0 => $_->{url} ]) foreach @mirrors; + +    $w->{window}->set_size_request(500, 400); +    $w->{rwindow}->show_all; + +    my $path = Gtk2::TreePath->new_first; +    $tree->expand_row($path, 0); +    $path->down; +    $tree->get_selection->select_path($path); + +    $w->main && return grep { $w->{retval}{sel} eq $_->{url} } @mirrors; +} + +sub show_urpm_progress { +    my ($label, $pb, $mode, $file, $percent, $total, $eta, $speed) = @_; +    $file =~ s|([^:]*://[^/:\@]*:)[^/:\@]*(\@.*)|$1xxxx$2|; #- if needed... +    state $medium; +    if ($mode eq 'copy') { +	$pb->set_fraction(0); +	$label->set_label(N("Copying file for medium `%s'...", $file)); +    } elsif ($mode eq 'parse') { +	$pb->set_fraction(0); +	$label->set_label(N("Examining file of medium `%s'...", $file)); +    } elsif ($mode eq 'retrieve') { +	$pb->set_fraction(0); +	$label->set_label(N("Examining remote file of medium `%s'...", $file)); +        $medium = $file; +    } elsif ($mode eq 'done') { +	$pb->set_fraction(1.0); +	$label->set_label($label->get_label . N(" done.")); +        $medium = undef; +    } elsif ($mode eq 'failed') { +	$pb->set_fraction(1.0); +	$label->set_label($label->get_label . N(" failed!")); +        $medium = undef; +    } else { +        # FIXME: we're displaying misplaced quotes such as "downloading `foobar from 'medium Main Updates'ยด" +        $file = $medium && length($file) < 40 ? #-PO: We're downloading the said file from the said medium +                                                 N("%s from medium %s", basename($file), $medium) +                                               : basename($file); +        if ($mode eq 'start') { +            $pb->set_fraction(0); +            $label->set_label(N("Starting download of `%s'...", $file)); +        } elsif ($mode eq 'progress') { +            if (defined $total && defined $eta) { +                $pb->set_fraction($percent/100); +                $label->set_label(N("Download of `%s'\ntime to go:%s, speed:%s", $file, $eta, $speed)); +            } else { +                $pb->set_fraction($percent/100); +                $label->set_label(N("Download of `%s'\nspeed:%s", $file, $speed)); +            } +        } +    } +    Gtk2->main_iteration while Gtk2->events_pending; +} + +sub update_sources { +    my ($urpm, %options) = @_; +    my $cancel = 0; +    my $w; my $label; $w = wait_msg( +	$label = Gtk2::Label->new(N("Please wait, updating media...")), +	no_wait_cursor => 1, +	widgets => [ +	    my $pb = gtkset_size_request(Gtk2::ProgressBar->new, 300, -1), +	    gtkpack( +		create_hbox(), +		gtksignal_connect( +		    Gtk2::Button->new(N("Cancel")), +		    clicked => sub { +			$cancel = 1; +                        $urpm->{error}->(N("Canceled")); +			$w and $w->destroy; +		    }, +		), +	    ), +	], +    ); +    my @media; @media = @{$options{medialist}} if ref $options{medialist}; +    my $outerfatal = $urpm->{fatal}; +    local $urpm->{fatal} = sub { $w->destroy; $outerfatal->(@_) }; +    urpm::media::update_those_media($urpm, [ urpm::media::select_media_by_name($urpm, \@media) ], +	%options, allow_failures => 1, +	callback => sub { +	    $cancel and goto cancel_update; +	    my ($type, $media) = @_; +	    return if $type !~ /^(?:start|progress|end)$/ && @media && !member($media, @media); +	    if ($type eq 'failed') { +		$urpm->{fatal}->(N("Error retrieving packages"), +N("It's impossible to retrieve the list of new packages from the media +`%s'. Either this update media is misconfigured, and in this case +you should use the Software Media Manager to remove it and re-add it in order +to reconfigure it, either it is currently unreachable and you should retry +later.", +    $media)); +	    } else { +		show_urpm_progress($label, $pb, @_); +	    } +	}, +    ); +    $w->destroy; +  cancel_update: +} + +sub update_sources_check { +    my ($urpm, $options, $error_msg, @media) = @_; +    my @error_msgs; +    local $urpm->{fatal} = sub { push @error_msgs, $_[1]; goto fatal_error }; +    local $urpm->{error} = sub { push @error_msgs, $_[0] }; +    update_sources($urpm, %$options, noclean => 1, medialist => \@media); +  fatal_error: +    if (@error_msgs) { +        interactive_msg(N("Error"), sprintf(translate($error_msg), join("\n", map { formatAlaTeX($_) } @error_msgs)), scroll => 1); +        return 0; +    } +    return 1; +} + +sub update_sources_interactive { +    my ($urpm, %options) = @_; +    my $w = ugtk2->new(N("Update media"), grab => 1, center => 1, %options); +    $w->{rwindow}->set_position($options{transient} ? 'center_on_parent' : 'center_always'); +    my @buttons; +    my @media = grep { ! $_->{ignore} } @{$urpm->{media}}; +    unless (@media) { +        interactive_msg(N("Warning"), N("No active medium found. You must enable some media to be able to update them.")); +	return 0; +    } +    gtkadd( +	$w->{window}, +	gtkpack_( +	    0, Gtk2::VBox->new(0,5), +	    0, Gtk2::Label->new(N("Select the media you wish to update:")), +            1, gtknew('ScrolledWindow', height => 300, child => +                     # FIXME: using a listview would be just better: +                     gtknew('VBox', spacing => 5, children_tight => [ +                         @buttons = map { +                             Gtk2::CheckButton->new_with_label($_->{name}); +                         } @media +                     ]) +	    ), +	    0, Gtk2::HSeparator->new, +	    0, gtkpack( +		create_hbox(), +		gtksignal_connect( +		    Gtk2::Button->new(N("Cancel")), +		    clicked => sub { $w->{retval} = 0; Gtk2->main_quit }, +		), +		gtksignal_connect( +		    Gtk2::Button->new(N("Select all")), +		    clicked => sub { $_->set_active(1) foreach @buttons }, +		), +		gtksignal_connect( +		    Gtk2::Button->new(N("Update")), +		    clicked => sub { +			$w->{retval} = any { $_->get_active } @buttons; +			# list of media listed in the checkbox panel +			my @buttonmedia = grep { !$_->{ignore} } @{$urpm->{media}}; +			@media = map_index { if_($_->get_active, $buttonmedia[$::i]{name}) } @buttons; +			Gtk2->main_quit; +		    }, +		), +	    ) +	) +    ); +    if ($w->main) { +        return update_sources_noninteractive($urpm, \@media, %options); +    } +    return 0; +} + +sub update_sources_noninteractive { +    my ($urpm, $media, %options) = @_; + +        urpm::media::select_media($urpm, @$media); +        update_sources_check( +	    $urpm, +	    {}, +	    N_("Unable to update medium; it will be automatically disabled.\n\nErrors:\n%s"), +	    @$media, +	); +	return 1; +} + +sub add_medium_and_check { +    my ($urpm, $options) = splice @_, 0, 2; +    my @newnames = ($_[0]); #- names of added media +    my $fatal_msg; +    my @error_msgs; +    local $urpm->{fatal} = sub { printf STDERR "Fatal: %s\n", $_[1]; $fatal_msg = $_[1]; goto fatal_error }; +    local $urpm->{error} = sub { printf STDERR "Error: %s\n", $_[0]; push @error_msgs, $_[0] }; +    if ($options->{distrib}) { +	@newnames = urpm::media::add_distrib_media($urpm, @_); +    } else { +	urpm::media::add_medium($urpm, @_); +    } +    if (@error_msgs) { +        interactive_msg( +	    N("Error"), +	    N("Unable to add medium, errors reported:\n\n%s", +	    join("\n", map { formatAlaTeX($_) } @error_msgs)) . "\n\n" . N("Medium: ") . "$_[0] ($_[1])", +	    scroll => 1, +	); +        return 0; +    } + +    foreach my $name (@newnames) { +	urpm::download::set_proxy_config($_, $options->{proxy}{$_}, $name) foreach keys %{$options->{proxy} || {}}; +    } + +    if (update_sources_check($urpm, $options, N_("Unable to add medium, errors reported:\n\n%s"), @newnames)) { +        urpm::media::write_config($urpm); +	$options->{proxy} and urpm::download::dump_proxy_config(); +    } else { +	urpm::media::read_config($urpm, 0); +        return 0; +    } + +    my %newnames; @newnames{@newnames} = (); +    if (any { exists $newnames{$_->{name}} } @{$urpm->{media}}) { +        return 1; +    } else { +        interactive_msg(N("Error"), N("Unable to create medium.")); +        return 0; +    } + +  fatal_error: +    interactive_msg(N("Failure when adding medium"), +                    N("There was a problem adding medium:\n\n%s", $fatal_msg)); +    return 0; +} + +#- Check whether the default update media (added by installation) +#- matches the current mdk version +sub check_update_media_version { +    my $urpm = shift; +    foreach (@_) { +	if ($_->{name} =~ /(\d+\.\d+).*\bftp\du\b/ && $1 ne $distro_version) { +	    interactive_msg( +		N("Warning"), +		$branded +		? N("Your medium `%s', used for updates, does not match the version of %s you're running (%s). +It will be disabled.", +		    $_->{name}, $distrib{system}, $distrib{product}) +		: N("Your medium `%s', used for updates, does not match the version of Mageia you're running (%s). +It will be disabled.", +		    $_->{name}, $distro_version) +	    ); +	    $_->{ignore} = 1; +	    urpm::media::write_config($urpm) if -w $urpm->{config}; +	    return 0; +	} +    } +    1; +} + +sub add_distrib_update_media { +    my ($urpm, $mirror, %options) = @_; +    #- ensure a unique medium name +    my $medium_name = $rpmdragora::mageia_release =~ /(\d+\.\d+) \((\w+)\)/ ? $2 . $1 . '-' : 'distrib'; +    my $initial_number = 1 + max map { $_->{name} =~ /\(\Q$medium_name\E(\d+)\b/ ? $1 : 0 } @{$urpm->{media}}; +    add_medium_and_check( +        $urpm, +        { nolock => 1, distrib => 1 }, +        $medium_name, +        ($mirror ? $mirror->{url} : (undef, mirrorlist => '$MIRRORLIST')), +        probe_with => 'synthesis', initial_number => $initial_number, %options,  +        usedistrib => 1, +    ); +} + +sub open_help { +    my ($mode) = @_; +    use run_program; +    run_program::raw({ detach => 1, as_user => 1 },  'drakhelp', '--id', $mode ?  "software-management-$mode" : 'software-management'); +    my $_s = N("Help launched in background"); +    statusbar_msg(N("The help window has been started, it should appear shortly on your desktop."), 1); +} + +sub run_drakbug { +    my ($id) = @_; +    run_program::raw({ detach => 1, as_user => 1 }, 'drakbug', '--report', $id); +} + +#mygtk2::add_icon_path('/usr/share/mcc/themes/default/'); +sub get_icon { +    my ($mcc_icon, $fallback_icon) = @_; +    my $icon = eval { mygtk2::_find_imgfile($mcc_icon) }; +    $icon ||= eval { mygtk2::_find_imgfile($fallback_icon) }; +    $icon; +} + +sub strip_first_underscore { join '', map { s/_//; $_ } @_ } + +1;  | 
