Revert "Menu Instance als Singleton cachen."
[kivitendo-erp.git] / SL / Menu.pm
1 #=====================================================================
2 # LX-Office ERP
3 # Copyright (C) 2004
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
6 #
7 #=====================================================================
8 # SQL-Ledger Accounting
9 # Copyright (C) 2001
10 #
11 #  Author: Dieter Simader
12 #   Email: dsimader@sql-ledger.org
13 #     Web: http://www.sql-ledger.org
14 #
15 #  Contributors:
16 #
17 # This program is free software; you can redistribute it and/or modify
18 # it under the terms of the GNU General Public License as published by
19 # the Free Software Foundation; either version 2 of the License, or
20 # (at your option) any later version.
21 #
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 # GNU General Public License for more details.
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #=====================================================================
30 #
31 # routines for menu items
32 #
33 #=====================================================================
34
35 package Menu;
36
37 use SL::Auth;
38 use SL::Inifile;
39
40 use strict;
41
42 sub new {
43   $main::lxdebug->enter_sub();
44
45   my ($type, $menufile) = @_;
46
47   my $self    = {};
48   my $inifile = Inifile->new($menufile);
49
50   map { $self->{$_} = $inifile->{$_} } keys %{ $inifile };
51
52   bless $self, $type;
53
54   $self->set_access();
55
56   $main::lxdebug->leave_sub();
57
58   return $self;
59 }
60
61 sub menuitem_js {
62   my ($self, $myconfig, $form, $item) = @_;
63
64   my $module = $form->{script};
65   my $action = "section_menu";
66
67   #if ($self->{$item}{module}) {
68   $module = $self->{$item}{module};
69
70   #}
71   if ($self->{$item}{action}) {
72     $action = $self->{$item}{action};
73   }
74
75   my $level = $form->escape($item);
76   my $str   = qq|$module?action=$action&level=$level|;
77   my @vars  = qw(module action target href);
78
79   if ($self->{$item}{href}) {
80     $str  = qq|$self->{$item}{href}|;
81     @vars = qw(module target href);
82   }
83
84   map { delete $self->{$item}{$_} } @vars;
85
86   # add other params
87   foreach my $key (keys %{ $self->{$item} }) {
88     $str .= "&" . $form->escape($key, 1) . "=";
89     my ($value, $conf) = split(/=/, $self->{$item}{$key}, 2);
90     $value = $myconfig->{$value} . "/$conf" if ($conf);
91     $str .= $form->escape($value, 1);
92   }
93
94   $str .= " ";
95
96 }
97
98 sub menuitem_new {
99   $main::lxdebug->enter_sub();
100
101   my ($self, $name, $item) = @_;
102
103   my $form        =  $main::form;
104   my $myconfig    = \%main::myconfig;
105
106   my $module      = $self->{$name}->{module} || $form->{script};
107   my $action      = $self->{$name}->{action};
108
109   $item->{target} = $self->{$name}->{target} || "main_window";
110   $item->{href}   = $self->{$name}->{href}   || "${module}?action=" . $form->escape($action);
111
112   my @vars = qw(module target href);
113   push @vars, 'action' unless ($self->{$name}->{href});
114
115   map { delete $self->{$name}{$_} } @vars;
116
117   # add other params
118   foreach my $key (keys %{ $self->{$name} }) {
119     my ($value, $conf)  = split(m/=/, $self->{$name}->{$key}, 2);
120     $value              = $myconfig->{$value} . "/$conf" if ($conf);
121     $item->{href}      .= "&" . $form->escape($key) . "=" . $form->escape($value);
122   }
123
124   $main::lxdebug->leave_sub();
125 }
126
127 sub menuitem_v3 {
128   $main::lxdebug->enter_sub();
129
130   my ($self, $myconfig, $form, $item, $other) = @_;
131
132   my $module = $form->{script};
133   my $action = "section_menu";
134   my $target = "";
135
136   if ($self->{$item}{module}) {
137     $module = $self->{$item}{module};
138   }
139   if ($self->{$item}{action}) {
140     $action = $self->{$item}{action};
141   }
142   if ($self->{$item}{target}) {
143     $target = $self->{$item}{target};
144   }
145
146   my $level = $form->escape($item);
147
148   my $str = qq|<a href="$module?action=| . $form->escape($action) . qq|&level=| . $form->escape($level);
149
150   my @vars = qw(module action target href);
151
152   if ($self->{$item}{href}) {
153     $str  = qq|<a href=$self->{$item}{href}|;
154     @vars = qw(module target href);
155   }
156
157   map { delete $self->{$item}{$_} } @vars;
158
159   # add other params
160   foreach my $key (keys %{ $self->{$item} }) {
161     $str .= "&" . $form->escape($key, 1) . "=";
162     my ($value, $conf) = split(/=/, $self->{$item}{$key}, 2);
163     $value = $myconfig->{$value} . "/$conf" if ($conf);
164     $str .= $form->escape($value, 1);
165   }
166
167   $str .= '"';
168
169   if ($target) {
170     $str .= qq| target="| . $form->quote($target) . qq|"|;
171   }
172
173   if ($other) {
174     foreach my $key (keys(%{$other})) {
175       $str .= qq| ${key}="| . $form->quote($other->{$key}) . qq|"|;
176     }
177   }
178
179   $str .= ">";
180
181   $main::lxdebug->leave_sub();
182
183   return $str;
184 }
185
186 sub menuitem_XML {
187   $main::lxdebug->enter_sub();
188
189   my ($self, $myconfig, $form, $item, $other) = @_;
190
191   my $module = $form->{script};
192   my $action = "section_menu";
193   my $target = "";
194
195   if ($self->{$item}{module}) {
196     $module = $self->{$item}{module};
197   }
198   if ($self->{$item}{action}) {
199     $action = $self->{$item}{action};
200   }
201   if ($self->{$item}{target}) {
202     $target = $self->{$item}{target};
203   }
204
205   my $level = $form->escape($item);
206
207   my $str = qq| link="$module?action=| . $form->escape($action) .
208     qq|&amp;level=| . $form->escape($level);
209
210   my @vars = qw(module action target href);
211
212   if ($self->{$item}{href}) {
213     $str  = qq| link=$self->{$item}{href}|;
214     @vars = qw(module target href);
215   }
216
217   map { delete $self->{$item}{$_} } @vars;
218
219   # add other params
220   foreach my $key (keys %{ $self->{$item} }) {
221     $str .= "&amp;" . $form->escape($key, 1) . "=";
222     my ($value, $conf) = split(/=/, $self->{$item}{$key}, 2);
223     $value = $myconfig->{$value} . "/$conf" if ($conf);
224     $str .= $form->escape($value, 1);
225   }
226
227   $str .= '"';
228
229
230
231   if ($other) {
232     foreach my $key (keys(%{$other})) {
233       $str .= qq| ${key}="| . $form->quote($other->{$key}) . qq|"|;
234     }
235   }
236
237
238   $main::lxdebug->leave_sub();
239
240   return $str;
241 }
242
243 sub access_control {
244   $main::lxdebug->enter_sub(2);
245
246   my ($self, $myconfig, $menulevel) = @_;
247
248   my @menu = ();
249
250   if ($menulevel eq "") {
251     @menu = grep { !/--/ } @{ $self->{ORDER} };
252   } else {
253     @menu = grep { /^${menulevel}--/ } @{ $self->{ORDER} };
254   }
255
256   $main::lxdebug->leave_sub(2);
257
258   return @menu;
259 }
260
261 sub parse_access_string {
262   my $self   = shift;
263   my $key    = shift;
264   my $access = shift;
265
266   my $form        =  $main::form;
267   my $auth        =  $main::auth;
268   my $myconfig    = \%main::myconfig;
269
270   my @stack;
271   my $cur_ary = [];
272
273   push @stack, $cur_ary;
274
275   while ($access =~ m/^([a-z_]+|\||\&|\(|\)|\s+)/) {
276     my $token = $1;
277     substr($access, 0, length($1)) = "";
278
279     next if ($token =~ /\s/);
280
281     if ($token eq "(") {
282       my $new_cur_ary = [];
283       push @stack, $new_cur_ary;
284       push @{$cur_ary}, $new_cur_ary;
285       $cur_ary = $new_cur_ary;
286
287     } elsif ($token eq ")") {
288       pop @stack;
289       if (!@stack) {
290         $form->error("Error in menu.ini for entry ${key}: missing '('");
291       }
292       $cur_ary = $stack[-1];
293
294     } elsif (($token eq "|") || ($token eq "&")) {
295       push @{$cur_ary}, $token;
296
297     } else {
298       push @{$cur_ary}, $auth->check_right($form->{login}, $token, 1);
299     }
300   }
301
302   if ($access) {
303     $form->error("Error in menu.ini for entry ${key}: unrecognized token at the start of '$access'\n");
304   }
305
306   if (1 < scalar @stack) {
307     $main::form->error("Error in menu.ini for entry ${key}: Missing ')'\n");
308   }
309
310   return SL::Auth::evaluate_rights_ary($stack[0]);
311 }
312
313 sub set_access {
314   my $self = shift;
315
316   my $key;
317
318   foreach $key (@{ $self->{ORDER} }) {
319     my $entry = $self->{$key};
320
321     $entry->{GRANTED}              = $entry->{ACCESS} ? $self->parse_access_string($key, $entry->{ACCESS}) : 1;
322     $entry->{IS_MENU}              = $entry->{submenu} || ($key !~ m/--/);
323     $entry->{NUM_VISIBLE_CHILDREN} = 0;
324
325     if ($key =~ m/--/) {
326       my $parent = $key;
327       substr($parent, rindex($parent, '--')) = '';
328       $entry->{GRANTED} &&= $self->{$parent}->{GRANTED};
329     }
330
331     $entry->{VISIBLE} = $entry->{GRANTED};
332   }
333
334   foreach $key (reverse @{ $self->{ORDER} }) {
335     my $entry = $self->{$key};
336
337     if ($entry->{IS_MENU}) {
338       $entry->{VISIBLE} &&= $entry->{NUM_VISIBLE_CHILDREN} > 0;
339     }
340
341     next if (($key !~ m/--/) || !$entry->{VISIBLE});
342
343     my $parent = $key;
344     substr($parent, rindex($parent, '--')) = '';
345     $self->{$parent}->{NUM_VISIBLE_CHILDREN}++;
346   }
347
348 #   $self->dump_visible();
349
350   $self->{ORDER} = [ grep { $self->{$_}->{VISIBLE} } @{ $self->{ORDER} } ];
351
352   { no strict 'refs';
353   # ToDO: fix this. nuke and pave algorithm without type checking screams for problems.
354   map { delete @{$self->{$_}}{qw(GRANTED IS_MENU NUM_VISIBLE_CHILDREN VISIBLE ACCESS)} if ($_ ne 'ORDER') } keys %{ $self };
355   }
356 }
357
358 sub dump_visible {
359   my $self = shift;
360   foreach my $key (@{ $self->{ORDER} }) {
361     my $entry = $self->{$key};
362     $main::lxdebug->message(0, "$entry->{GRANTED} $entry->{VISIBLE} $entry->{NUM_VISIBLE_CHILDREN} $key");
363   }
364 }
365
366 1;
367