1 # UI::Primitive - Interchange configuration manager primitives
3 # $Id: Primitive.pm,v 2.28 2008-04-10 22:26:12 docelic Exp $
5 # Copyright (C) 2002-2007 Interchange Development Group
6 # Copyright (C) 1998-2002 Red Hat, Inc.
9 # Michael J. Heins <mikeh@perusion.net>
10 # Stefan Hornburg <racke@linuxia.de>
12 # This file is free software; you can redistribute it and/or modify it
13 # under the terms of the GNU General Public License as published by the
14 # Free Software Foundation; either version 2, or (at your option) any
17 # This file is distributed in the hope that it will be
18 # useful, but WITHOUT ANY WARRANTY; without even the implied warranty
19 # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 # General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this file; see the file COPYING. If not, write to the Free
24 # Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
26 my($order, $label, %terms) = @_;
28 package UI::Primitive;
30 $VERSION = substr(q$Revision: 2.28 $, 10);
42 no warnings qw(uninitialized numeric);
43 use Vend::Util qw/errmsg/;
55 Primitive.pm -- Interchange Configuration Manager Primitives
59 display_directive %options;
63 The Interchange UI is an interface to configure and administer Interchange catalogs.
67 my $ui_safe = new Safe;
68 $ui_safe->untrap(@{$Global::SafeUntrap});
72 if $Vend::Cfg->{RemoteUser}
73 and $Vend::Cfg->{RemoteUser} eq $CGI::remote_user;
74 return 0 if ! $Vend::Session->{logged_in};
75 return 0 if ! $Vend::username;
76 return 0 if $Vend::Cfg->{AdminUserDB} and ! $Vend::admin;
77 my $db = Vend::Data::database_exists_ref(
78 $Vend::Cfg->{Variable}{UI_ACCESS_TABLE} || 'access'
82 my $result = $db->field($Vend::username, 'super');
88 if $Vend::Cfg->{RemoteUser}
89 and $Vend::Cfg->{RemoteUser} eq $CGI::remote_user;
90 return 0 if ! $Vend::Session->{logged_in};
91 return 0 unless $Vend::admin or ! $Vend::Cfg->{AdminUserDB};
101 $Global::SuperUserFunction = \&is_super;
102 my $default = defined $Global::Variable->{UI_SECURITY_OVERRIDE}
103 ? $Global::Variable->{UI_SECURITY_OVERRIDE}
105 if ($Vend::superuser) {
106 return $Vend::UI_entry = { super => 1 };
108 $table = $::Variable->{UI_ACCESS_TABLE} || 'access';
109 $Vend::WriteDatabase{$table} = 1;
110 my $db = Vend::Data::database_exists_ref($table);
111 return $default unless $db;
112 $db = $db->ref() unless $Vend::Interpolate::Db{$table};
113 my $uid = $try || $Vend::username || $CGI::remote_user;
114 if(! $uid or ! $db->record_exists($uid) ) {
117 my $ref = $db->row_hash($uid)
118 or die "Bad database record for $uid.";
119 if($ref->{table_control}) {
120 $ref->{table_control_ref} = $ui_safe->reval($ref->{table_control});
121 ref $ref->{table_control_ref} or delete $ref->{table_control_ref};
124 $Vend::UI_entry = $ref;
127 sub get_ui_table_acl {
128 my ($table, $user, $keys) = @_;
129 $table = $::Values->{mv_data_table} unless $table;
131 if($user and $user ne $Vend::username) {
132 if ($Vend::UI_acl{$user}) {
133 $acl_top = $Vend::UI_acl{$user};
136 my $ui_table = $::Variable->{UI_ACCESS_TABLE} || 'access';
137 my $acl_txt = Vend::Interpolate::tag_data($ui_table, 'table_control', $user);
138 return undef unless $acl_txt;
139 $acl_top = $ui_safe->reval($acl_txt);
140 return undef unless ref($acl_top);
142 $Vend::UI_acl{$user} = $acl_top;
143 return keys %$acl_top if $keys;
144 return $acl_top->{$table};
147 unless ($acl_top = $Vend::UI_entry) {
148 return undef unless ref($acl_top = ui_acl_enabled());
151 return undef unless defined $acl_top->{table_control_ref};
152 return $acl_top->{table_control_ref}{$table};
156 my ($acl, $name, @entries) = @_;
159 @ok{@entries} = @entries;
160 if($val = $acl->{owner_field} and $name eq 'keys') {
161 my $u = $Vend::username;
162 my $t = $acl->{table}
164 ::logError("no table name with owner_field.");
169 my $v = ::tag_data($t, $val, $_);
174 if($val = $acl->{"no_$name"}) {
176 $ok{$_} = ! ui_check_acl($_, $val);
179 if($val = $acl->{"yes_$name"}) {
181 $ok{$_} &&= ui_check_acl($_, $val);
185 return (grep $ok{$_}, @entries);
189 my ($acl, $name, $entry) = @_;
192 if($val = $acl->{"no_$name"}) {
193 $status = ! ui_check_acl($entry, $val);
195 if($val = $acl->{"yes_$name"}) {
196 $status &&= ui_check_acl($entry, $val);
201 sub ui_extended_acl {
202 my ($item, $string) = @_;
203 $string = " $string ";
204 my ($name, $sub) = split /=/, $item, 2;
205 return 0 if $string =~ /[\s,]!$name(?:[,\s])/;
206 return 1 if $string =~ /[\s,]$name(?:[,\s])/;
207 my (@subs) = split //, $sub;
209 return 0 if $string =~ /[\s,]!$name=[^,\s]*$sub/;
210 return 0 unless $string =~ /[\s,]$name=[^,\s]*$sub/;
216 my ($item, $string) = @_;
217 return ui_extended_acl(@_) if $item =~ /=/;
218 $string = " $string ";
219 return 0 if $string =~ /[\s,]!$item[=,\s]/;
220 return 1 if $string =~ /[\s,]$item[=,\s]/;
225 my $record = ui_acl_enabled();
226 # First we see if we have ACL enforcement enabled
227 # If you don't, then people can do anything!
228 unless (ref $record) {
229 $::Scratch->{mv_data_enable} = $record;
232 my $enable = delete $::Scratch->{mv_data_enable} || 1;
233 my $CGI = \%CGI::values;
234 my $Tag = new Vend::Tags;
235 $CGI->{mv_todo} = $CGI->{mv_doit}
236 if ! $CGI->{mv_todo};
237 if( $Tag->if_mm('super')) {
238 $::Scratch->{mv_data_enable} = $enable;
242 if( $CGI->{mv_todo} eq 'set' ) {
243 undef $::Scratch->{mv_data_enable};
244 my $mml_enable = $Tag->if_mm('functions', 'mml');
245 my $html_enable = ! $Tag->if_mm('functions', 'no_html');
246 my $target = $CGI->{mv_data_table};
247 $Vend::WriteDatabase{$target} = 1;
248 my $db = Vend::Data::database_exists_ref($target);
250 $::Scratch->{ui_failure} = "Table $target doesn't exist";
254 my $keyname = $CGI->{mv_data_key};
255 if ($CGI->{mv_auto_export}
256 and $Tag->if_mm('!tables', undef, { table => "$target=x" }, 1) ) {
257 $::Scratch->{ui_failure} = "Unauthorized to export table $target";
258 $CGI->{mv_todo} = 'return';
261 if ($Tag->if_mm('!tables', undef, { table => "$target=e" }, 1) ) {
262 $::Scratch->{ui_failure} = "Unauthorized to edit table $target";
263 $CGI->{mv_todo} = 'return';
267 my @codes = grep /\S/, split /\0/, $CGI->{$keyname};
269 unless( $db->record_exists($_) ) {
270 next if $Tag->if_mm('tables', undef, { table => "$target=c" }, 1);
271 $::Scratch->{ui_failure} = "Unauthorized to insert to table $target";
272 $CGI->{mv_todo} = 'return';
275 next if $Tag->if_mm('keys', $_, { table => $target }, 1);
276 $CGI->{mv_todo} = 'return';
277 $::Scratch->{ui_failure} = errmsg("Unauthorized for key %s", $_);
281 my @fields = grep /\S/, split /[,\s\0]+/, $CGI->{mv_data_fields};
282 push @fields, $CGI->{mv_blob_field}
283 if $CGI->{mv_blob_field};
286 $CGI->{$_} =~ s/\[/[/g unless $mml_enable;
287 $CGI->{$_} =~ s/\</</g unless $html_enable;
288 next if $Tag->if_mm('columns', $_, { table => $target }, 1);
289 $CGI->{mv_todo} = 'return';
290 $::Scratch->{ui_failure} = errmsg("Unauthorized for key %s", $_);
294 $::Scratch->{mv_data_enable} = $enable;
296 elsif ($CGI->{mv_todo} eq 'deliver') {
297 if($Tag->if_mm('files', $CGI->{mv_data_file}, {}, 1 ) ) {
298 $::Scratch->{mv_deliver} = $CGI->{mv_data_file};
301 $::Scratch->{ui_failure} = errmsg(
302 "Unauthorized for file %s",
303 $CGI->{mv_data_file},
314 $table = $::Values->{mv_data_table}
318 if(! ($record = $Vend::UI_entry) ) {
319 $record = ui_acl_enabled();
325 $acl = get_ui_table_acl($table);
326 if($acl and $acl->{yes_keys}) {
327 @keys = grep /\S/, split /\s+/, $acl->{yes_keys};
331 my $db = Vend::Data::database_exists_ref($table);
332 return '' unless $db;
333 $db = $db->ref() unless $Vend::Interpolate::Db{$table};
334 my $keyname = $db->config('KEY');
335 if($db->config('LARGE')) {
336 return ::errmsg('--not listed, too large--');
338 my $query = "select $keyname from $table order by $keyname";
342 ml => $::Variable->{UI_ACCESS_KEY_LIMIT} || 500,
347 @keys = map {$_->[0]} @$keys;
351 while (($k) = $db->each_record()) {
354 if( $db->numeric($db->config('KEY')) ) {
355 @keys = sort { $a <=> $b } @keys;
363 @keys = UI::Primitive::ui_acl_grep( $acl, 'keys', @keys);
365 my $joiner = $opt->{joiner} || "\n";
366 return join($joiner, @keys);
372 my $d = $Vend::Cfg->{Database};
373 @dbs = sort keys %$d;
375 my $record = ui_acl_enabled();
378 and $record->{yes_tables} || $record->{no_tables};
381 next if $::Values->{ui_tables_to_hide} =~ /\b$_\b/;
383 next if $record->{no_tables}
384 and ui_check_acl($_, $record->{no_tables});
385 next if $record->{yes_tables}
386 and ! ui_check_acl($_, $record->{yes_tables});
391 @dbs = $opt->{nohide} ? (@dbs) : (@outdb);
392 $opt->{joiner} = " " if ! $opt->{joiner};
394 my $string = join $opt->{joiner}, grep /\S/, @dbs;
395 if(defined $::Values->{mv_data_table}) {
396 return $string unless $d->{$::Values->{mv_data_table}};
397 my $size = -s $Vend::Cfg->{ProductDir} .
398 "/" . $d->{$::Values->{mv_data_table}}{'file'};
399 $size = 3_000_000 if $size < 1;
400 $::Values->{ui_too_large} = $size > 100_000 ? 1 : '';
401 $::Values->{ui_way_too_large} = $size > 2_000_000 ? 1 : '';
402 local($_) = $::Values->{mv_data_table};
403 $::Values->{ui_rotate_spread} = $::Values->{ui_tables_to_rotate} =~ /\b$_\b/;
409 my ($base, $suf) = @_;
410 return undef unless -d $base;
411 #::logDebug("passed suf=$suf");
412 $suf = '\.(GIF|gif|JPG|JPEG|jpg|jpeg|png|PNG)'
421 return undef unless -f $_;
422 return undef unless $_ =~ $regex;
423 my $n = $File::Find::name;
427 find($wanted, $base . '/');
432 my($spec, $prefix) = @_;
433 my $globspec = $spec;
435 $globspec =~ s:^\s+::;
436 $globspec =~ s:\s+$::;
437 $globspec =~ s:^:$prefix:;
438 $globspec =~ s:\s+: $prefix:g;
440 my @files = glob($globspec);
442 @files = map { s:^$prefix::; $_ } @files;
448 my ($keep, $suf, $base) = @_;
449 $suf = $Vend::Cfg->{HTMLsuffix} if ! $suf;
450 $base = Vend::Util::catfile($Vend::Cfg->{VendRoot}, $base) if $base;
451 $base ||= $Vend::Cfg->{PageDir};
453 $suf = quotemeta($suf);
454 #::logDebug("Finding, ext=$suf base=$base");
456 return undef unless -f $_;
457 return undef unless /$suf$/;
458 my $n = $File::Find::name;
460 $n =~ s/$suf$// unless $keep;
463 find($wanted, $base);
464 #::logDebug("Found files: " . join (",", @names));
477 my($base, $options) = @_;
480 ::logError( errmsg("%s: called rotate without file.", caller() ) );
487 elsif (! ref $options) {
488 $options = {Motion => 'unsave'};
494 if( $options->{Directory} ) {
495 $dir = $options->{Directory};
498 if ($base =~ s:(.*)/:: ) {
502 my $motion = $options->{Motion} || 'save';
504 $options->{max} = 10 if ! defined $options->{max};
508 if("\L$motion" eq 'save' and ! -f "$dir/$base+") {
509 File::Copy::copy("$dir/$base", "$dir/$base+")
510 or die "copy $dir/$base to $dir/$base+: $!\n";
513 opendir(forwardDIR, $dir) || die "opendir $dir: $!\n";
515 @files = grep /^$base/, readdir forwardDIR;
520 if("\L$motion" eq 'save') {
521 @backward = grep s:^($base\++):$dir/$1:, @files;
522 @forward = grep s:^($base-+):$dir/$1:, @files;
524 elsif("\L$motion" eq 'unsave') {
525 return 0 unless -f "$dir/$base-";
526 @forward = grep s:^($base\++):$dir/$1:, @files;
527 @backward = grep s:^($base-+):$dir/$1:, @files;
531 die "Bad motion: $motion";
534 $base = "$dir/$base";
537 my $base_exists = -f $base;
538 push @forward, $base if $base_exists;
540 if (@forward > $options->{max}) {
541 $#forward = $options->{max};
544 for(reverse sort @forward) {
546 rename $_, $_ . $add or die "rename $_ => $_+: $!\n";
549 #return 1 unless $base_exists && @backward;
551 @backward = sort @backward;
553 unshift @backward, $base;
555 if (@backward > $options->{max}) {
556 $#backward = $options->{max};
560 for($i = 0; $i < $#backward; $i++) {
561 rename $backward[$i+1], $backward[$i]
562 or die "rename $backward[$i+1] => $backward[$i]: $!\n";
565 if($options->{Touch}) {
567 utime $now, $now, $base;