source: LATMOS-Accounts/lib/LATMOS/Accounts/Cli.pm @ 849

Last change on this file since 849 was 849, checked in by nanardon, 14 years ago
  • make unxported mode options more explicit
  • fix POD according behavior
  • Property svn:keywords set to Id
File size: 16.1 KB
Line 
1package LATMOS::Accounts::Cli;
2
3# $Id$
4
5use strict;
6use warnings;
7use LATMOS::Accounts::Log;
8use LATMOS::Accounts::Utils;
9use Term::ReadLine;
10use Text::ParseWords;
11
12my $term = Term::ReadLine->new('LA CLI');
13my $OUT = $term->OUT || \*STDOUT;
14
15sub globalenv {
16    my ($labase) = @_;
17    my $env = LATMOS::Accounts::Cli->new({ prompt => sub { $_[0]->base->label . " cli > " }, },
18        $labase);
19    $env->add_func('unexported', {
20        help => 'unexported yes|no|show - switch or show base mode regarding' .
21            ' unexported objects',
22        completion => sub {
23            if (!$_[2]) {
24                return qw(yes no show);
25            }
26        },
27        code => sub {
28            my ($env, $arg) = @_;
29            if ($arg eq 'yes') {
30                $env->base->unexported(1);
31                print $OUT "Unexported are now show";
32            } elsif ($arg eq 'no') {
33                $env->base->unexported(0);
34                print $OUT "Unexported are no longer show";
35            } elsif ($arg eq 'show') {
36                print $OUT "Unexported objects " . $env->base->unexported ?
37                "enable" : "disable";
38            } else {
39                print $OUT "wrong argument";
40            }
41        },
42    });
43    $env->add_func('ls', {
44            help => 'ls object_type - list object of type object_type', 
45            completion => sub {
46                if(!$_[2]) {
47                    return $_[0]->base->list_supported_objects
48                } else { () }
49            },
50            code => sub {
51                if ($_[1]) {
52                    print $OUT map { "$_\n" } $_[0]->base->list_objects($_[1]);
53                } else {
54                    print $OUT "Object type missing";
55                }
56            },
57        });
58    $env->add_func('search', {
59            help => 'search objecttype filter1 [filter2...] - search object according filter',
60            completion => sub {
61                if(!$_[2]) {
62                    return $_[0]->base->list_supported_objects
63                } else { return() }
64            },
65            code => sub {
66                my ($env, @args) = @_;
67                if ($_[1]) {
68                    my @res = $env->base->search_objects(@args);
69                    print $OUT map { "$_\n" } @res;
70                    $env->{_lastsearch} = \@res;
71                    $env->{_lastsearchtype} = $args[0];
72                } else {
73                    print $OUT "Object type missing";
74                }
75            },
76        });
77    $env->add_func('select', {
78            help => 'select object_type - select objects to perform action on it',
79            completion => sub {
80                if ($_[2]) {
81                    return $_[0]->base->list_objects($_[2]);
82                } else {
83                    return '@', $_[0]->base->list_supported_objects;
84                }
85            },
86            code => sub {
87                my ($env, $otype, @ids) = @_;
88                my @objs;
89                if ($otype eq '@') {
90                    if (@{$env->{_lastsearch}}) {
91                        $otype = $env->{_lastsearchtype};
92                        @ids = @{$env->{_lastsearch}};
93                    } else {
94                        print $OUT "No results store from previous search";
95                        return;
96                    }
97                }
98                if (!@ids) {
99                    print $OUT 'not enough arguments';
100                    return;
101                }
102                foreach (@ids) {
103                    my $obj = $env->base->get_object($otype, $_) or do {
104                        print $OUT "Cannot get $otype $_";
105                        return;
106                    };
107                    push(@objs, $obj);
108                }
109                print $OUT "Selecting $otype " . join(', ', @ids);
110                objenv($_[0]->base, $otype, @objs)->cli();
111            },
112        });
113    $env->add_func('user',  { alias => [qw'select user' ] });
114    $env->add_func('group', { alias => [qw'select group'] });
115    return $env
116}
117
118sub objenv {
119    my ($labase, $otype, @objs) = @_;
120    my $objenv = LATMOS::Accounts::Cli->new(
121        {
122            prompt => sub {
123                sprintf("%s %s/%s > ",
124                    $_[0]->base->label,
125                    $_[0]->{_otype},
126                    @{$_[0]->{_objects}} > 1 ? '(' .
127                    scalar(@{$_[0]->{_objects}}) . ' obj.)' : $_[0]->{_objects}[0]->id,
128                );
129            },
130        },
131        $labase
132    );
133    $objenv->{_otype} = $otype;
134    $objenv->{_objects} = [ @objs ];
135    $objenv->add_func('show', {
136        help => 'show attributes - show an attributes of object',
137        code => sub {
138            my ($env, $attr) = @_;
139            if (!$attr) {
140                foreach (@{$env->{_objects}}) {
141                    print $OUT $_->dump;
142                }
143            } else {
144                foreach (@{$env->{_objects}}) {
145                    print $OUT sort map { ($_ || '') . "\n" } $_->get_attributes($attr);
146                }
147            }
148        },
149        completion => sub {
150            if (!$_[2]) {
151                return $_[0]->base->list_canonical_fields($_[0]->{_otype}, 'r')
152            }
153        },
154    });
155    $objenv->add_func('print', {
156        help => 'print fmt - show attributes using template',
157        code => sub {
158            my ($env, $fmt) = @_;
159            foreach (@{$env->{_objects}}) {
160                print $OUT $_->queryformat($fmt) . "\n";
161            }
162        },
163    });
164    $objenv->add_func('unset', {
165        help => 'unset attribute - unset specified attribute',
166        code => sub {
167            my ($env, $attr) = @_;
168            $attr or do {
169                print $OUT "Attributes must be specified";
170                return;
171            };
172            foreach (@{$env->{_objects}}) {
173                defined $_->set_c_fields($attr => undef) or do {
174                    print $OUT "cannot unset attributes $attr for " . $_->id;
175                    return;
176                };
177            }
178            $env->base->commit;
179            print $OUT "Changes applied";
180        },
181        completion => sub {
182            my ($env, $lastw, @args) = @_;
183            if (!$args[0]) {
184                return $env->base->list_canonical_fields($env->{_otype}, 'w')
185            }
186        },
187    });
188    $objenv->add_func('set', {
189        help => 'set attribute value - set an attributes to single value "value"',
190        code => sub {
191            my ($env, $attr, @value) = @_;
192            @value or do {
193                print $OUT "attribute and value must be specified";
194                return;
195            };
196            foreach (@{$env->{_objects}}) {
197                defined $_->set_c_fields($attr => @value <= 1 ? $value[0] :
198                    \@value) or do {
199                    $_->base->rollback;
200                    printf $OUT "Cannot set $attr to %s for %s", join(', ',
201                        @value), $_->id;
202                    return;
203                };
204            }
205            $env->base->commit;
206            print $OUT "done";
207        },
208        completion => sub {
209            my ($env, $lastw, @args) = @_;
210            if (!$args[0]) {
211                return $env->base->list_canonical_fields($env->{_otype}, 'w')
212            } else {
213                if ($env->base->obj_attr_allowed_values($env->{_otype}, $args[0])) {
214                    return $env->base->obj_attr_allowed_values($env->{_otype}, $args[0])
215                }
216                for ($args[0]) {
217                    /^manager|managedBy$/ and return
218                        $env->base->search_objects('user');
219                    /^department$/ and return
220                        $env->base->search_objects('group', 'sutype=dpmt');
221                    /^contratType$/ and return
222                        $env->base->search_objects('group', 'sutype=contrattype');
223                    /^site$/ and return
224                        $env->base->search_objects('site');
225                    if (@{$env->{_objects}} == 1) {
226                        return $env->{_objects}[0]->get_attributes($args[0]);
227                    }
228                }
229            }
230        },
231    });
232    $objenv->add_func('list', {
233        help => 'list current selected objects',
234        code => sub {
235            printf $OUT "%s: %s", $_[0]->{_otype}, join(', ', map { $_->id }
236            @{$_[0]->{_objects}});
237        }
238    });
239    $objenv->add_func('edit', {
240            help => 'edit [object] - edit selected object using vi',
241            completion => sub {
242                return map { $_->id } @{$_[0]->{_objects}}
243            },
244            code => sub {
245                my ($env, $id) = @_;
246                my $obj;
247                if ($id) {
248                    $obj = grep { $_->id = $id } @{$env->{_objects}} or do {
249                        print $OUT "$id is not part of selected objects";
250                        return;
251                    };
252                } elsif (@{$env->{_objects}} == 1) {
253                    $obj = $env->{_objects}[0]
254                } else {
255                    print $OUT "multiple objects selected but can edit only one,"
256                    . "please specify which one";
257                    return;
258                }
259                my $res = LATMOS::Accounts::Utils::dump_read_temp_file(
260                    sub {
261                        my ($fh) = @_;
262                        $obj->text_dump($fh,
263                            {
264                                empty_attr => 1,
265                                only_rw => 1,
266                            }
267                        );
268                    },
269                    sub {
270                        my ($fh) = @_;
271                        my %attr = LATMOS::Accounts::Utils::parse_obj_file($fh);
272                        my $res = $obj->set_c_fields(%attr);
273                        if ($res) {
274                            print $OUT "Changes applied";
275                            $env->base->commit;
276                        }
277                        else { print $OUT "Error applying changes" }
278                        return $res ? 1 : 0;
279                    }
280                );
281            },
282        });
283    $objenv->add_func('delete', {
284        help => 'delete - delete selected object',
285        code => sub {
286            my ($env) = @_;
287            printf $OUT "%s: %s\ndelete selected objects ? (yes/NO) ",
288            $env->{_otype}, join(', ', map { $_->id } @{$env->{_objects}});
289            my $reply = <STDIN> || ''; chomp($reply);
290            if ($reply eq 'yes') {
291                foreach (@{$env->{_objects}}) {
292                    $env->base->delete_object($env->{_otype}, $_->id) or do {
293                        print $OUT "Cannot delete " . $_->id;
294                        return;
295                    };
296                }
297                $env->base->commit;
298                return "EXIT";
299            } else {
300                print $OUT "cancel !"
301            }
302        },
303    });
304
305    if (lc($otype) eq 'user') {
306        $objenv->add_func('group', {
307            help => 'group add|remove goupname',
308            code => sub {
309                my ($env, $action, @groups) = @_;
310                foreach my $obj (@{$env->{_objects}}) {
311                    my %gr;
312                    foreach ($obj->get_attributes('memberOf')) {
313                        $gr{$_} = 1;
314                    }
315                    if ($action eq 'add') {
316                        $gr{$_} = 1 foreach(@groups);
317                    } elsif ($action eq 'remove') {
318                        delete($gr{$_}) foreach(@groups);
319                    } else {
320                        print $OUT 'invalid action';
321                        return;
322                    }
323                    defined $obj->set_c_fields('memberOf' => [ keys %gr ]) or do {
324                        print $OUT "cannot set memberOf attributes for " .
325                        $obj->id;
326                        return;
327                    };
328                }
329                $env->base->commit;
330            },
331            completion => sub {
332                if (!$_[2]) {
333                    return (qw(add remove));
334                } else {
335                    return $_[0]->base->search_objects('group');
336                }
337            },
338        });
339    } elsif ($otype eq 'group') {
340        $objenv->add_func('member', {
341            help => 'member add|remove user',
342            code => sub {
343                my ($env, $action, @groups) = @_;
344                foreach my $obj (@{$env->{_objects}}) {
345                    my %gr;
346                    foreach ($obj->get_attributes('memberUID')) {
347                        $gr{$_} = 1;
348                    }
349                    if ($action eq 'add') {
350                        $gr{$_} = 1 foreach(@groups);
351                    } elsif ($action eq 'remove') {
352                        delete($gr{$_}) foreach(@groups);
353                    } else {
354                        print $OUT 'invalid action';
355                        return;
356                    }
357                    defined $obj->set_c_fields('memberUID' => [ keys %gr ]) or do {
358                        print $OUT "cannot set memberUID attributes for " .
359                        $obj->id;
360                        return;
361                    };
362                }
363                $env->base->commit;
364            },
365            completion => sub {
366                if (!$_[2]) {
367                    return (qw(add remove));
368                } else {
369                    return $_[0]->base->search_objects('user');
370                }
371            },
372        });
373    }
374
375    return $objenv;
376}
377
378sub new {
379    my ($class, $env, $labase) = @_;
380    bless($env, $class);
381    $env->{_labase} = $labase;
382    $env->add_func('quit', { help => 'quit - exit the tool',
383            code => sub { print "\n"; exit(0) }, });
384    $env->add_func('exit', { help => "exit current mode",
385            code => sub { return "EXIT" }, });
386    $env->add_func('help', {
387        help => 'help [command] - print help about command',
388        completion => sub {
389            if (!$_[2]) { return sort keys %{ $_[0]->{funcs} || {}} }
390        },
391        code => sub {
392            my ($self, $name) = @_;
393            if (!$name) {
394                print $OUT join(', ', sort keys %{ $self->{funcs} || {}});
395            } elsif ($self->{funcs}{$name}{alias}) {
396                print $OUT "$name is an alias for " . join(' ',
397                    @{$self->{funcs}{$name}{alias}});
398            } elsif ($self->{funcs}{$name}{help}) {
399                print $OUT $self->{funcs}{$name}{help};
400            } else {
401                print $OUT "No help availlable";
402            }
403        },
404    });
405
406    $env;
407}
408
409sub base { $_[0]->{_labase} }
410
411sub cli {
412    my ($self) = @_;
413    while (1) {
414        $term->Attribs->{completion_function} = sub {
415            $self->complete($_[0], shellwords(substr($_[1], 0, $_[2])));
416        };
417        defined (my $line = $term->readline($self->prompt)) or return;
418        my $res = $self->run(shellwords($line));
419        $self->base->rollback;
420        if ($res && $res eq 'EXIT') { return }
421    }
422}
423
424sub prompt {
425    my ($self) = @_;
426    if (!$self->{prompt}) {
427        return "LA cli > ";
428    } else {
429        $self->{prompt}->($self);
430    }
431}
432
433sub add_func {
434    my ($self, $name, $param) = @_;
435    $self->{funcs}{$name} = $param;
436}
437
438sub parse_arg {
439    my ($self, $name, @args) = @_;
440    if ($self->{funcs}{$name}{opt}) {
441        @ARGV = @args;
442    } else {
443        return @args;
444    }
445    return @ARGV;
446}
447
448sub complete {
449    my ($self, $lastw, $name, @args) = @_;
450    if (!$name) {
451        return grep { /^\Q$lastw\E/ } sort
452            (keys %{ $self->{funcs} || {}});
453    } elsif ($self->{funcs}{$name}{alias}) {
454        $self->complete($lastw, @{$self->{funcs}{$name}{alias}}, @args);
455    } elsif ($self->{funcs}{$name}{completion}) {
456        my @pargs = $self->parse_arg($name, @args);
457        return map { my $t = $_; $t =~ s/\s/\\ /g; $t } grep { $_ && /^\Q$lastw\E/ } $self->{funcs}{$name}{completion}->($self, $lastw, @pargs);
458    } else {
459        return ();
460    }
461}
462
463sub run {
464    my ($self, $name, @args) = @_;
465    return if (!$name);
466    if ($self->{funcs}{$name}{alias}) {
467        $self->run(@{$self->{funcs}{$name}{alias}}, @args);
468    } elsif ($self->{funcs}{$name}{code}) {
469        my @pargs = $self->parse_arg($name, @args);
470        $self->{funcs}{$name}{code}->($self, @args);
471    }
472}
473
4741;
Note: See TracBrowser for help on using the repository browser.