File Coverage

blib/lib/CGI/WebToolkit.pm
Criterion Covered Total %
statement 24 697 3.4
branch 0 364 0.0
condition 0 48 0.0
subroutine 8 70 11.4
pod 24 24 100.0
total 56 1203 4.6


line stmt bran cond sub pod time code
1             package CGI::WebToolkit;
2              
3 1     1   25986 use 5.008006;
  1         4  
  1         36  
4 1     1   6 use strict;
  1         1  
  1         33  
5 1     1   4 use warnings;
  1         6  
  1         41  
6              
7 1     1   6699 use CGI qw(param header);
  1         19561  
  1         8  
8 1     1   1153 use CGI::Carp qw(fatalsToBrowser);
  1         2900  
  1         7  
9 1     1   1117 use Data::Dump qw(dump);
  1         11862  
  1         82  
10 1     1   2996 use DBI;
  1         31601  
  1         73  
11 1     1   13 use Digest::MD5 qw(md5_hex);
  1         3  
  1         11879  
12              
13             our $VERSION = '0.08';
14              
15             our $WTK = undef;
16              
17             our @XHTML_TAGS
18             = qw(a abbr acronym address applet area b base bdo big blockquote
19             body br button caption cite code col colgroup dd del dfn div
20             dl DOCTYPE dt em fieldset form frame frameset h1 h2 h3 h4 h5
21             h6 head hr html i iframe img input ins kbd label legend li
22             link map meta noframes noscript object ol optgroup option p
23             param pre q samp script select small span strong style sub sup
24             table tbody td textarea tfoot th thead title tr tt ul var marquee
25             section header footer nav article
26             emph);
27              
28             # ------------------------------------------------------------------------------
29             # ------------------------------------------------------------------------------
30             # constructor
31              
32             sub new
33             {
34 0     0 1   my ($class, @args) = @_;
35 0           my $self = bless {}, $class;
36 0           return $self->__init( @args );
37             }
38              
39             # ------------------------------------------------------------------------------
40             # ------------------------------------------------------------------------------
41             # methods
42              
43             sub handle
44             {
45 0     0 1   my ($self) = __parse_args(@_);
46            
47             # clear cache
48 0 0 0       $self->__clear_cache()
49             if $self->{'allowclearcache'} == 1
50             && defined param($self->{'clearcacheparam'});
51            
52             # determine name of workflow function
53 0           my $workflow_function_name = param($self->{'workflowparam'});
54 0 0         $workflow_function_name = $self->{'entryaction'}
55             unless defined $workflow_function_name;
56            
57 0           my $mimetype = '';
58 0           my $message = '';
59 0           my $function_name = $workflow_function_name;
60 0           my $args = [];
61 0           while (1) {
62            
63 0           my $result = $self->call( $function_name, @{$args} );
  0            
64            
65 0 0 0       __die("function '$function_name' returned invalid result.".
66             " Use the methods output() or followup() to generate a valid result.")
67             if ref $result ne 'HASH' || !exists $result->{'type'};
68            
69 0 0         if ($result->{'type'} eq 'output') {
    0          
70 0 0 0       if (exists $result->{'status'} && $result->{'status'} == 1) {
71            
72 0 0         __die("missing mimetype in result from function '$function_name'")
73             unless exists $result->{'mimetype'};
74 0 0         __die("missing content in result from function '$function_name'")
75             unless exists $result->{'content'};
76            
77 0           $mimetype = $result->{'mimetype'};
78 0           $message = $result->{'content'};
79              
80 0 0         if ($mimetype eq 'text/html') {
81             # replace any {...:...} placeholders with their default values
82 0           $message =~ s/\{[a-zA-Z0-9\.\_]+\:([^\}]*)\}/$1/mg;
83              
84             # replace any {...} placeholders with empty string
85 0           $message =~ s/\{[a-zA-Z0-9\.\_]+\}//mg;
86             }
87              
88 0           last;
89             }
90             else {
91 0           $function_name = 'core.error';
92             }
93             }
94             elsif ($result->{'type'} eq 'followup') {
95            
96 0 0         __die("missing followup function name in result from function '$function_name'")
97             unless exists $result->{'function_name'};
98              
99 0           $function_name = $result->{'function_name'};
100 0 0         $args = $result->{'arguments'} if exists $result->{'arguments'};
101             }
102             else {
103 0           __die("function '$function_name' returned unknown type of result.".
104             " Use the methods output() or followup() to generate a valid result.");
105             }
106             }
107            
108 0           $| = 1;
109            
110             # add session to every link that points to the cgi script
111 0 0         if (exists $ENV{'SCRIPT_NAME'}) {
112 0           my $script = quotemeta $ENV{'SCRIPT_NAME'};
113 0           my $url_addon = $self->{'idparam'}.'='.$self->{'session_id'}.'&';
114 0           my $form_addon =
115             ' 116             .'" value="'.$self->{'session_id'}.'"/>'.
117             ' 118             .'" value="1"/>';
119            
120 0 0         if ($mimetype eq 'text/html') {
121             # add session id to internal links
122 0           $message =~ s/(href=[\"\']($script)?\?)/$1$url_addon/mig;
123 0           $message =~ s/(href=[\"\']$script)/$1?$url_addon/mig;
124            
125             # add session id as hidden field to internal forms
126 0           $message =~ s/(]*action=[\"\']$script[^\>]*>)/$1$form_addon/mig;
127             }
128             }
129              
130 0           $self->__cleanup();
131            
132 0           return header( -type => $mimetype ).$message;
133             }
134              
135             sub call
136             {
137 0     0 1   my ($self, $function_name, @args) = __parse_args(@_);
138              
139             # check if user is allowed to execute workflow function
140 0 0 0       $function_name = $self->{'entryaction'}
141             if $self->{'checkrights'} == 1 && !$self->allowed($function_name);
142              
143             # check for cache entry
144 0           my $cachehash;
145 0 0         if ($self->{'cachetable'} ne '') {
146 0           $cachehash = $self->__get_cache_hash($function_name, @args);
147 0           my $result = $self->__load_cache($cachehash);
148 0 0         return $result if defined $result;
149             }
150            
151 0 0         unless (exists $self->{"workflow_function_cache"}->{$function_name}) {
152            
153 0           my $function_filename
154             = $self->__get_external_function_filename('workflows', $function_name);
155            
156 0 0         if (defined $function_filename) {
157             # load function ref. into cache
158 0 0         $self->{"workflow_function_cache"}->{$function_name}
159             = __load_file_as_subref($function_filename)
160             unless exists $self->{'workflow_function_cache'}->{$function_name};
161             }
162             else {
163             # define error function
164             $self->{"workflow_function_cache"}->{$function_name}
165 0     0     = sub { __die("failed to load function '$function_name'") };
  0            
166             }
167             }
168              
169             # load library path for modules
170 0           my $libpath = $self->{'privatepath'}.'/modules';
171 0           eval('use lib "'.$libpath.'"');
172 0 0         __die("loading of library path '$libpath' failed: $@") if $@;
173              
174             # load all modules for workflow function
175 0           foreach my $module (@{$self->{'modules'}}) {
  0            
176 0           eval('use '.$module);
177 0 0         __die("loading of module '$module' failed: $@") if $@;
178             }
179            
180             # call workflow function
181 0           $self->{'current_workflow_function'} = $function_name;
182 0           my $result =
183             $self->{"workflow_function_cache"}->{$function_name}->(
184             $self, @args );
185 0           $self->{'current_workflow_function'} = undef;
186            
187             # save result to cache
188 0 0         if ($self->{'cachetable'} ne '') {
189 0           $self->__save_cache($cachehash, $result);
190             }
191            
192 0           return $result;
193             }
194              
195             sub output
196             {
197 0     0 1   my ($self, $status, $info, $content, $mimetype) = __parse_args(@_);
198 0 0         $status = 1 unless defined $status;
199 0 0         $info = 'ok' unless defined $info;
200 0 0         $content = '' unless defined $content;
201 0 0         $mimetype = 'text/html' unless defined $mimetype;
202             return {
203 0           'type' => 'output',
204             'status' => $status,
205             'info' => $info,
206             'content' => $content,
207             'mimetype' => $mimetype,
208             };
209             }
210              
211             sub followup
212             {
213 0     0 1   my ($self, $function_name, @args) = __parse_args(@_);
214             return {
215 0           'type' => 'followup',
216             'function_name' => $function_name,
217             'arguments' => [ @args ],
218             };
219             }
220              
221             # ------------------------------------------------------------------------------
222              
223             sub get
224             {
225 0     0 1   my ($self, $varname) = __parse_args(@_);
226 0 0         return 1 if $self->{'sessiontable'} eq '';
227            
228 0 0         if (exists $self->{'session'}->{$varname}) {
229 0           return $self->{'session'}->{$varname};
230             } else {
231 0           return undef;
232             }
233             }
234              
235             sub set
236             {
237 0     0 1   my ($self, $varname, $value) = __parse_args(@_);
238 0 0         return 1 if $self->{'sessiontable'} eq '';
239            
240 0 0         $self->{'session'}->{$varname} = (defined $value ? $value : '');
241 0           return 1;
242             }
243              
244             sub unset
245             {
246 0     0 1   my ($self, $name) = __parse_args(@_);
247 0 0         return 1 if $self->{'sessiontable'} eq '';
248            
249 0 0         delete $self->{'session'}->{$name}
250             if exists $self->{'session'}->{$name};
251            
252 0           return 1;
253             }
254              
255             # ------------------------------------------------------------------------------
256              
257             sub fill
258             {
259 0     0 1   my ($self, $template_name, $data) = __parse_args(@_);
260 0 0         my @data = (ref($data) eq 'ARRAY' ? @{$data} : ($data));
  0            
261              
262 0           my $filename1 = $self->__get_external_function_filename( 'generators', $template_name );
263 0           my $filename2 = $self->__get_external_function_filename( 'generators', 'core.'.$template_name );
264              
265 0 0         if (defined $filename1) {
    0          
266             # load function ref. into cache
267 0 0         $self->{"template_function_cache"}->{$template_name}
268             = __load_file_as_subref($filename1)
269             unless exists $self->{'template_function_cache'}->{$template_name};
270             }
271             elsif (defined $filename2) {
272             # load function ref. into cache
273 0 0         $self->{"template_function_cache"}->{$template_name}
274             = __load_file_as_subref($filename2)
275             unless exists $self->{'template_function_cache'}->{$template_name};
276             }
277             else {
278             # load static template file from theme
279            
280 0           my @fallback_themes = @{$self->{'templatefallbacks'}};
  0            
281            
282             # check for specific theme
283 0 0         if ($template_name =~ /^([^\:]+)\:(.*)$/) {
284 0           my ($theme, $name) = $template_name =~ /^([^\:]+)\:(.*)$/;
285 0           @fallback_themes = ($theme);
286 0           $template_name = $name;
287             }
288            
289             # look into themes for file
290 0           my $filename;
291 0           foreach my $theme (@fallback_themes) {
292 0           $filename = __identifier_to_filename(
293             $self->{'privatepath'}.'/templates/'.$theme.'/',
294             $template_name, '.html');
295 0 0         last if -f $filename;
296             }
297              
298             # load file
299 0 0         open TMPLFILE, '<'.$filename or __die("failed to open file '$filename': $!");
300 0           my $content = join '', ;
301 0           close TMPLFILE;
302            
303             # create generic function to parse the content
304             $self->{"template_function_cache"}->{$template_name} =
305             sub {
306 0     0     my ($self, @data) = @_;
307 0           my $result = '';
308 0           foreach my $data (@data) {
309 0           my $tmpl = $content;
310             # expand macros
311 0 0         $self->__expand_macros(\$tmpl) if $self->{'allowmacros'} == 1;
312             # replace variables
313 0           __replace_placeholders(\$tmpl, $data);
314             # replace common variables
315 0           __replace_placeholders(\$tmpl, $self->{'common_placeholders'});
316 0           $result .= $tmpl;
317             }
318 0           return $result;
319             }
320 0           }
321              
322             # call template function
323             return
324 0           $self->{"template_function_cache"}->{$template_name}->(
325             $self, @data );
326             }
327              
328             # ------------------------------------------------------------------------------
329              
330             sub _
331             {
332 0     0     my ($self, $phrase, $language) = __parse_args(@_);
333 0 0         return '' unless defined $phrase;
334              
335 0 0         return $phrase
336             if $self->{'phrasetable'} eq '';
337              
338 0 0         $language = $self->get('language') unless defined $language;
339 0 0         $language = $self->{'defaultlanguage'} unless defined $language;
340            
341             # query db for phrase
342 0           my $query
343             = $self->find(
344             -tables => [$self->{'phrasetable'}],
345             -where => {'name' => $phrase},
346             );
347            
348 0 0         if (my $row = $query->fetchrow_hashref()) {
349 0 0         if ($row->{'language'} eq $language) {
350 0           return $phrase;
351             }
352             else {
353             # look for translation
354 0           my $translation = __find_translation($row->{'translations'}, $language);
355 0 0         return (defined $translation ? $translation : $phrase);
356             }
357             }
358             else {
359 0           return $phrase;
360             }
361             }
362              
363             # ------------------------------------------------------------------------------
364              
365             sub lang
366             {
367 0     0 1   my ($self, $language) = __parse_args(@_);
368 0 0         if (defined $language) {
369             # set
370 0           $self->set($language);
371             }
372             # get
373 0           my $lang = $self->get('language');
374 0 0         return (defined $lang ? $lang : $self->{'defaultlanguage'});
375             }
376              
377             # ------------------------------------------------------------------------------
378              
379             sub translate
380             {
381 0     0 1   my ($self, @pairs) = __parse_args(@_);
382 0 0         return 0
383             if $self->{'phrasetable'} eq '';
384            
385 0 0 0       __die("translate() expects language/phrase pairs as parameters")
386             if scalar(@pairs) % 2 == 1 || scalar(@pairs) < 4;
387              
388             # erease any bad characters
389 0           foreach my $p (0..$#pairs) {
390 0           $pairs[$p] =~ s/[\n\r\:]//g;
391             }
392            
393             # create real tuples
394 0           my ($key_language, $key_phrase) = (shift(@pairs), shift(@pairs));
395 0           my %phrases;
396 0           for (my $i = 0; $i < scalar @pairs; $i += 2) {
397 0           $phrases{$pairs[$i]} = $pairs[$i + 1];
398             }
399              
400             # query db for phrase entry
401 0           my $query
402             = $self->find(
403             -tables => [$self->{'phrasetable'}],
404             -where => {'name' => $key_phrase, 'language' => $key_language},
405             );
406              
407 0 0         if (my $row = $query->fetchrow_hashref()) {
408             # update
409 0           my $translations = __find_translation($row->{'translations'});
410 0           foreach my $lang (keys %phrases) {
411 0           $translations->{$lang} = $phrases{$lang};
412             }
413             $self->update(
414 0           -table => $self->{'phrasetable'},
415             -set => {
416             'name' => $key_phrase,
417             'language' => $key_language,
418 0           'translations' => join("\n", map { $_.':'.$translations->{$_} } keys %{$translations}),
  0            
419             },
420             -where => {'name' => $key_phrase, 'language' => $key_language},
421             );
422             }
423             else {
424             # insert
425 0           $self->create(
426             -table => $self->{'phrasetable'},
427             -row => {
428             'name' => $key_phrase,
429             'language' => $key_language,
430 0           'translations' => join("\n", map { $_.':'.$phrases{$_} } keys %phrases),
431             },
432             );
433             }
434             }
435              
436             # ------------------------------------------------------------------------------
437              
438             sub find
439             {
440 0     0 1   my ($self, %options) = __parse_args(@_);
441 0           my $opts = __parse_params( \%options,
442             {
443             tables => [],
444             where => {},
445             wherelike => {},
446             group => [],
447             order => [],
448             limit => 0,
449             distinct => 0,
450             columns => [],
451             joins => {},
452             sortdir => 'asc', # 'asc' or 'desc'
453             });
454              
455 0           my @tables = map { $self->__quotename($_) } @{$opts->{'tables'}};
  0            
  0            
456              
457 0           my @columns = map { $self->__quotename($_) } @{$opts->{'columns'}};
  0            
  0            
458              
459 0           my @joins =
460             map {
461 0           $self->__quotename($_).' = '.$self->__quotename($opts->{'joins'}->{$_});
462             }
463 0           keys %{$opts->{'joins'}};
464              
465 0           my @group = map { $self->__quotename($_) } @{$opts->{'group'}};
  0            
  0            
466              
467 0           my @order = map { $self->__quotename($_) } @{$opts->{'order'}};
  0            
  0            
468            
469 0           my $sql
470             = 'SELECT'
471             .(defined $opts->{'distinct'} ? ' DISTINCT' : '')
472             .' '.(scalar @columns ? join(', ', @columns) : '*')
473             .' FROM '.join(', ', @tables)
474             .' WHERE '
475 0           .(scalar keys %{$opts->{'where'}} ?
476             $self->__make_sql_where_clause($opts->{'where'})
477             : '1')
478 0 0         .(scalar keys %{$opts->{'wherelike'}} ?
    0          
    0          
    0          
    0          
    0          
    0          
    0          
479             ' AND '.$self->__make_sql_where_clause($opts->{'wherelike'}, 1)
480             : '')
481             .(scalar @joins ? ' AND '.join(' AND ', @joins) : '')
482             .(scalar @group ? ' GROUP BY '.join(', ', @group) : '')
483             .(scalar @order ? ' ORDER BY '.join(', ', @order).' '.uc($opts->{'sortdir'}) : '')
484             .($opts->{'limit'} > 0 ? ' LIMIT '.$opts->{'limit'} : '');
485            
486 0           return $self->query($sql);
487             }
488              
489             sub create
490             {
491 0     0 1   my ($self, %options) = __parse_args(@_);
492 0           my $opts = __parse_params( \%options,
493             {
494             table => undef,
495             row => {},
496             });
497              
498 0           my @columns;
499             my @values;
500 0           map {
501 0           push @columns, $self->__quotename($_);
502 0           push @values, $self->__quote($opts->{'row'}->{$_});
503             }
504 0           keys %{$opts->{'row'}};
505              
506 0           my $sql
507             = 'INSERT'
508             .' INTO '.$self->__quotename($opts->{'table'})
509             .' ('.join(', ', @columns).')'
510             .' VALUES ('.join(', ', @values).')';
511              
512 0           $self->query($sql);
513 0           return $self->{'dbh'}->last_insert_id(undef, undef, $opts->{'table'}, 'id');
514             }
515              
516             sub update
517             {
518 0     0 1   my ($self, %options) = __parse_args(@_);
519 0           my $opts = __parse_params( \%options,
520             {
521             table => '',
522             set => {},
523             where => {},
524             wherelike => {},
525             });
526              
527 0           my @sets =
528             map {
529 0           $self->__quotename($_).' = '.$self->__quote($opts->{'set'}->{$_});
530             }
531 0           keys %{$opts->{'set'}};
532              
533 0           my $sql
534             = 'UPDATE'
535             .' '.$self->__quotename($opts->{'table'})
536             .' SET '.join(', ', @sets)
537             .' WHERE '
538 0           .(scalar keys %{$opts->{'where'}} ?
539             $self->__make_sql_where_clause($opts->{'where'})
540             : '1')
541 0 0         .(scalar keys %{$opts->{'wherelike'}} ?
    0          
542             ' AND '.$self->__make_sql_where_clause($opts->{'wherelike'}, 1)
543             : '');
544              
545 0           return $self->query($sql);
546             }
547              
548             sub remove
549             {
550 0     0 1   my ($self, %options) = __parse_args(@_);
551 0           my $opts = __parse_params( \%options,
552             {
553             table => '',
554             where => {},
555             wherelike => {},
556             });
557              
558 0           my $sql
559             = 'DELETE'
560             .' FROM '.$self->__quotename($opts->{'table'})
561             .' WHERE '
562 0           .(scalar keys %{$opts->{'where'}} ?
563             $self->__make_sql_where_clause($opts->{'where'})
564             : '1')
565 0 0         .(scalar keys %{$opts->{'wherelike'}} ?
    0          
566             ' AND '.$self->__make_sql_where_clause($opts->{'wherelike'}, 1)
567             : '');
568              
569 0           return $self->query($sql);
570             }
571              
572             sub load
573             {
574 0     0 1   my ($self, $group, $recordset, $tablename) = __parse_args(@_);
575            
576 0           my $records = __load_data_file($self->{'privatepath'}.'/data/'.$group.'/'.$recordset.'.txt');
577            
578 0           my $inserted = 0;
579 0           foreach my $record (@{$records}) {
  0            
580 0 0         __die("record does not have an id field, in data file '$group/$recordset'")
581             unless exists $record->{'id'};
582            
583 0           my $query
584             = $self->find(
585             -tables => [$tablename],
586             -where => {'id' => $record->{'id'}},
587             -limit => 1,
588             );
589            
590 0 0         if (my $row = $query->fetchrow_hashref()) {
591             # do nothing
592             }
593             else {
594             # insert
595 0           $self->create(
596             -table => $tablename,
597             -row => $record,
598             );
599 0           $inserted ++;
600             }
601             }
602 0           return (scalar @{$records}, $inserted);
  0            
603             }
604              
605             sub query
606             {
607 0     0 1   my ($self, $sql) = __parse_args(@_);
608 0           $self->__connect_to_db();
609 0           my $query = $self->{'dbh'}->prepare($sql);
610 0 0         $query->execute()
611             or __die('the query ['.$sql.'] failed: '.DBI->errstr());
612 0           return $query;
613             }
614              
615             # ------------------------------------------------------------------------------
616              
617             sub getparam
618             {
619 0     0 1   my ($self, $name, $default, $regex) = __parse_args(@_);
620 0 0         $regex = '.*' unless defined $regex;
621 0           my $value = param($name);
622 0 0 0       return (defined $value && $value =~ /$regex/ ? $value : $default);
623             }
624              
625             # ------------------------------------------------------------------------------
626              
627             sub login
628             {
629 0     0 1   my ($self, $loginname, $password) = __parse_args(@_);
630 0 0         return 1 if $self->{'usertable'} eq '';
631              
632 0 0         return 0 unless defined $loginname;
633 0 0         return 0 unless defined $password;
634              
635 0 0         return 0 if $self->{'usertable'} eq '';
636              
637 0           my $query
638             = find(
639             -tables => [$self->{'usertable'}],
640             -where => {
641             'loginname' => $loginname,
642             'password' => md5_hex($password),
643             },
644             -limit => 1,
645             );
646              
647 0 0         if (my $user = $query->fetchrow_hashref()) {
648 0 0         return 0 if $user->{'active'} == 0;
649            
650             # associate session with user id
651 0           set('user', $user);
652            
653             # set language
654 0 0         $self->set('language', $user->{'ui_language'})
655             if $user->{'ui_language'} ne '';
656             }
657             else {
658 0           return 0;
659             }
660             }
661              
662             # ------------------------------------------------------------------------------
663              
664             sub logout
665             {
666 0     0 1   my ($self) = __parse_args(@_);
667 0 0         return 1 if $self->{'usertable'} eq '';
668              
669 0           $self->unset('user');
670 0           return 1;
671             }
672              
673             # ------------------------------------------------------------------------------
674              
675             sub allowed
676             {
677 0     0 1   my ($self,
678             $function_name, # workflow function name
679             $loginname, # loginname of user
680             ) = __parse_args(@_);
681              
682 0 0         return 1 if $self->{'accessconfig'} eq '';
683              
684             # load access config from /accessconfigs/.txt
685             # $mappings = { => , ... }
686 0           my $mappings
687             = __load_config_file(
688             $self->{'privatepath'}.'/accessconfigs/'.$self->{'accessconfig'}.'.txt');
689              
690             # determine and call appropriate access check function(s)
691 0           my $has_access = 0;
692 0           foreach my $rgx (keys %{$mappings}) {
  0            
693 0 0         if ($function_name =~ /$rgx/) {
694 0           my $check_function_name = $mappings->{$rgx};
695 0           my $filename =
696             $self->__get_external_function_filename(
697             'accesschecks', $check_function_name);
698              
699             # load function as subroutine
700 0 0         $self->{"access_function_cache"}->{$check_function_name}
701             = __load_file_as_subref($filename)
702             unless exists $self->{"access_function_cache"}->{$check_function_name};
703              
704 0           my $check_function = $self->{"access_function_cache"}->{$check_function_name};
705              
706 0   0       $has_access = $has_access && $check_function->( $self, $function_name, $loginname );
707             }
708             }
709            
710 0           return $has_access;
711             }
712              
713             # ------------------------------------------------------------------------------
714              
715             sub logmsg
716             {
717 0     0 1   my ($self, $msg, $priority) = __parse_args(@_);
718            
719 0 0         $msg .= "\r\n" if $msg !~ /\r?\n$/;
720            
721 0           $priority = 'DEBUG'
722             if !defined $priority
723             || !scalar
724 0 0 0       grep { $_ eq uc($priority) }
725             qw(DEBUG INFO WARNING ERROR FATAL);
726            
727             # log to priority-logfile
728 0           my $logfile = $self->{'privatepath'}.'/logs/'.uc($priority).'.txt';
729 0           __file_append($logfile, $msg);
730            
731 0 0         if (defined $self->{'current_workflow_function'}) {
732             # log to workflow-function-specific logfile as well
733 0           my $logfile2 = $self->{'privatepath'}.'/logs/'.uc($self->{'current_workflow_function'}).'.txt';
734 0           __file_append($logfile2, $msg);
735             }
736             }
737              
738             # ------------------------------------------------------------------------------
739              
740             sub fail
741             {
742 0     0 1   my ($self, $msg) = __parse_args(@_);
743 0           __die($msg);
744             }
745              
746             # ------------------------------------------------------------------------------
747              
748             sub upload
749             {
750 0     0 1   my ($self, $paramname, $groupname) = __parse_args(@_);
751            
752             # retrieve file from parameters
753 0           my $file = $self->getparam($paramname, undef);
754              
755 0 0         __die("cannot retrieve upload via unknown parameter '$paramname'")
756             unless defined $file;
757              
758 0           my $now = time();
759              
760 0           my $upload_info = CGI::uploadInfo($file);
761 0           my $ending = $upload_info->{'Content-Disposition'};
762 0           $ending =~ s/^.*\.//;
763 0           $ending =~ s/\"$//;
764              
765             # generate filename
766 0           my $filepath = $self->{'publicpath'}.'/uploads/'.$groupname.'/';
767 0           my $filename = $self->{'session_id'}.'_'.$now.'.'.$ending;
768              
769             # write data to file
770 0 0         open UPLOAD, '>'.$filepath.$filename
771             or __die("failed to write upload to file '$filepath$filename': $!");
772              
773             # Dateien in den Binaer-Modus schalten
774 0           binmode $file;
775 0           binmode UPLOAD;
776              
777 0           my $info = {
778             'status' => 1,
779             'info' => 'The file has been successfully saved.',
780             };
781              
782 0           my $data;
783 0           my $bytes_written = 0;
784 0           while (read $file, $data, 1024) {
785 0           print UPLOAD $data;
786 0           $bytes_written += 1024;
787 0 0         if ($bytes_written > $self->{'uploadmaxsize'}) {
788 0           $info->{'status'} = 0;
789 0           $info->{'info'} =
790             'The filesize exceeded the maximum upload size. '.
791             'Aborted after '.$bytes_written.' Bytes.';
792 0           last;
793             }
794             }
795 0           close UPLOAD;
796            
797 0           my $mimetype = $upload_info->{'Content-Type'};
798 0           my $original_filename = $upload_info->{'Content-Disposition'};
799 0           $original_filename =~ s/^.*filename\=\"(.*)\"$/$1/;
800              
801             # success
802 0 0         if ($info->{'status'} == 1) {
803 0           $info->{'path'} = $filepath;
804 0           $info->{'filename'} = $filename;
805 0           $info->{'created'} = $now;
806 0           $info->{'mimetype'} = $mimetype;
807 0           $info->{'original_filename'} = $original_filename;
808             }
809            
810             # save info in session
811 0           push @{$self->{'session'}->{'uploads'}}, $info;
  0            
812            
813 0           return $info->{'status'};
814             }
815              
816             # ------------------------------------------------------------------------------
817              
818             sub AUTOLOAD
819             {
820 0     0     my ($self, @args) = __parse_args(@_);
821            
822 0           my $function_name = $CGI::WebToolkit::AUTOLOAD;
823 0           $function_name =~ s/.*\://g;
824            
825 0 0         if ($function_name eq 'DESTROY') {
    0          
    0          
826 0           return SUPER::DESTROY(@args);
827             }
828             elsif ($function_name =~ /^\_[^\_]/) {
829             # module function execution call
830            
831             # try to find subroutine in
832 0           $function_name =~ s/^\_//;
833 0           foreach my $module (@{$self->{'modules'}}) {
  0            
834 0           my $is_sub = 0;
835 0           eval('$is_sub = (defined &CGI::WebToolkit::Modules::'.ucfirst($module).'::'.$function_name.')');
836 0 0         __die("eval failed: $@") if $@;
837 0 0         if ($is_sub) {
838             # call subroutine
839 0           my @result;
840 0           eval('@result = CGI::WebToolkit::Modules::'.ucfirst($module).'::'.$function_name.'($self, @args)');
841 0 0         __die("eval for subroutine call failed: $@") if $@;
842 0 0         return (wantarray ? @result : (scalar @result ? $result[0] : undef));
    0          
843             }
844             }
845 0           __die("could not find subroutine named 'CGI::WebToolkit::Modules::*::".$function_name."'");
846             }
847             elsif ($function_name =~ /^[A-Z]/) {
848             # template loading call
849            
850 0           my ($theme, $template_name) = split /\_/, $function_name;
851 0 0         unless (defined $template_name) {
852 0           $template_name = $theme;
853 0           $theme = '';
854             }
855 0           $theme = lc $theme;
856            
857             # create template name
858 0           $template_name =~ s/([A-Z])/_$1/g;
859 0           $template_name =~ s/^\_//g;
860 0           $template_name =~ s/\_/./g;
861 0           $template_name = lc $template_name;
862            
863             return
864 0 0         $self->fill(
865             (length $theme ? $theme.':' : '').$template_name,
866             { @args },
867             );
868             }
869             else {
870 0           __die("Unknown function/method called '$function_name'.");
871             }
872             }
873              
874             # ------------------------------------------------------------------------------
875             # ------------------------------------------------------------------------------
876             # cleanup
877              
878             sub __cleanup
879             {
880 0     0     my ($self) = @_;
881            
882 0           $self->__save_session();
883 0           $self->__disconnect_from_db();
884             }
885              
886             # ------------------------------------------------------------------------------
887             # ------------------------------------------------------------------------------
888             # internal helper functions/methods
889              
890             sub __expand_macros
891             {
892 0     0     my ($self, $stringref) = @_;
893 0           my $string = ${$stringref};
  0            
894            
895 0           my $tokens = __tokenize_xml($string);
896            
897             # the generated tokens are now beeing expanded
898              
899 0           my $parsed = '';
900 0           my $t = 0; # offset of current token
901 0           my $abort = 0;
902 0           while (1) {
903 0 0         last if $t >= scalar @{$tokens};
  0            
904              
905 0           my $token = $tokens->[$t];
906            
907 0 0         if (!ref $token) {
908 0           $parsed .= $token;
909 0           $t ++;
910             }
911             else {
912 0           my ($type, $name, $attribs) = @{$token};
  0            
913 0 0 0       if ($type eq 'start' || $type eq 'single') {
    0          
914            
915 0           my $data = __attribs_to_hash($attribs);
916 0           $data->{'content'} = '';
917            
918 0           my $t_span = 1; # assume a "single" tag
919 0 0         if ($type eq 'start') {
    0          
920             # find end tag
921 0           $t_span = __get_token_span($tokens, $t);
922 0 0         unless (defined $t_span) {
923             # if no end tag: tag end is end of tokens
924 0           $t_span = scalar(@{$tokens}) - $t;
  0            
925             }
926              
927             # get tokens from start tag till end tag
928 0           my @subtokens = splice @{$tokens}, $t, $t_span;
  0            
929 0           shift @subtokens;
930 0           pop @subtokens;
931              
932             # convert sub-tokenlist back to xml
933 0           $data->{'content'} = __render_tokens(\@subtokens);
934             }
935             elsif ($type eq 'single') {
936 0           splice @{$tokens}, $t, 1;
  0            
937             }
938            
939             # fill template
940 0           my $filled = $self->fill($name, $data);
941            
942             # tokenize template
943 0           my $sub_tokens = __tokenize_xml($filled);
944              
945             # replace tag with sub-tokenlist
946 0           splice @{$tokens}, $t, 0, @{$sub_tokens};
  0            
  0            
947            
948             # restart expanding at previous token
949             }
950             elsif ($type eq 'end') {
951             # this end tag actually belongs to a start tag
952             # which is out of the scope of this token list
953             # -> ignore this end tag
954 0           $t ++;
955             }
956             }
957 0           $abort ++;
958 0 0         __die("macro expansion ran into endless loop.") if $abort == 100;
959             }
960              
961 0           ${$stringref} = $parsed;
  0            
962 0           return 1;
963             }
964              
965             sub __render_tokens
966             {
967 0     0     my ($tokens) = @_;
968 0           my $string = '';
969 0           foreach my $token (@{$tokens}) {
  0            
970 0 0         if (ref $token) {
971 0 0         $string .=
    0          
    0          
972             '<'.($token->[0] eq 'end' ? '/' : '').
973             $token->[1].($token->[0] eq 'end' ? '' : ' '.$token->[2]).
974             ($token->[0] eq 'single' ? '/' : '').'>';
975             }
976             else {
977 0           $string .= $token;
978             }
979             }
980 0           return $string;
981             }
982              
983             sub __attribs_to_hash
984             {
985 0     0     my ($attribs) = @_;
986 0           my %hash;
987 0           my @pairs = split /[\s\t]+/, $attribs;
988 0           foreach my $pair (@pairs) {
989 0           my ($key, $value) = split /\=/, $pair;
990 0           $value =~ s/^[\"\']?//;
991 0           $value =~ s/[\"\']?$//;
992 0           $hash{$key} = $value;
993             }
994 0           return \%hash;
995             }
996              
997             sub __get_token_span
998             {
999 0     0     my ($tokens, $t) = @_;
1000 0           my $found = 0;
1001 0           my $open = 0; # open tags with same name as token at position $t
1002 0           my $s;
1003 0           foreach my $i ($t+1..scalar(@{$tokens})-1) {
  0            
1004 0           $s = $i;
1005 0 0         next unless ref $tokens->[$s];
1006 0 0         if ($tokens->[$s]->[1] eq $tokens->[$t]->[1]) {
1007 0 0         if ($tokens->[$s]->[0] eq 'start') {
    0          
1008 0           $open ++;
1009             }
1010             elsif ($tokens->[$s]->[0] eq 'end') {
1011 0 0         if ($open <= 0) {
1012 0           $found = 1;
1013 0           last;
1014             }
1015             else {
1016 0           $open --;
1017             }
1018             }
1019             }
1020             }
1021 0 0         my $span = ($found ? ($s - $t + 1) : undef);
1022 0           return $span;
1023             }
1024              
1025             sub __dump_tokens
1026             {
1027 0     0     my ($tokens) = @_;
1028 0           my $s = "[\n";
1029 0           my $i = 0;
1030             map {
1031 0 0         if (!ref $_) {
  0 0          
    0          
    0          
1032 0           $s .= " [".sprintf('%0d',$i)."] ...\n";
1033             }
1034             elsif ($_->[0] eq 'start') {
1035 0           $s .= " [".sprintf('%0d',$i)."] <".$_->[1].">\n";
1036             }
1037             elsif ($_->[0] eq 'end') {
1038 0           $s .= " [".sprintf('%0d',$i)."] [1].">\n";
1039             }
1040             elsif ($_->[0] eq 'single') {
1041 0           $s .= " [".sprintf('%0d',$i)."] <".$_->[1]."/>\n";
1042             }
1043 0           $i ++;
1044 0           } @{$tokens};
1045 0           return $s."]\n";
1046             }
1047              
1048             sub __tokenize_xml
1049             {
1050 0     0     my ($string) = @_;
1051            
1052             # remove comments
1053 0           $string =~ s///sg;
1054            
1055             # this regex parses an xml tag (sloppy...)
1056 0           my $tagregex = '^(\/?)([a-zA-Z0-9\:\_\.]+)([\s\t\n\r]*)([^\>]*[^\/])?(\/?)>(.*)$';
1057            
1058             # what follows is actually a very rudimentary tokenizer
1059             # that splits the source into an array of tokens, either
1060             # tag (start-tag, end-tag or single-tag) and strings
1061              
1062 0           my @tokens = ('');
1063 0           foreach my $tag (split /
1064            
1065 0 0         if ($tag =~ /$tagregex/s) {
1066 0           my ($is_end, $tagname, $space, $attribs, $is_single, $rest)
1067             = $tag =~ /$tagregex/s;
1068            
1069 0 0         $space = '' unless defined $space;
1070 0 0         $attribs = '' unless defined $attribs;
1071 0 0         $is_single = '' unless defined $is_single;
1072 0 0         $rest = '' unless defined $rest;
1073            
1074 0 0         if (scalar grep { $tagname eq $_ } @XHTML_TAGS) {
  0            
1075             # normal html tag
1076 0 0         ref $tokens[-1] ? push(@tokens,'<'.$tag) : ($tokens[-1] .= '<'.$tag);
1077             }
1078             else {
1079             # macro
1080 0 0         if (length $is_end) {
    0          
1081             # end tag
1082 0           push @tokens, ['end', $tagname];
1083             }
1084             elsif (length $is_single) {
1085             # single tag
1086 0           push @tokens, ['single', $tagname, $attribs];
1087             }
1088             else {
1089             # start tag
1090 0           push @tokens, ['start', $tagname, $attribs];
1091             }
1092 0 0         ref $tokens[-1] ? push(@tokens,$rest) : ($tokens[-1] .= $rest);
1093             }
1094             }
1095             else {
1096 0 0         ref $tokens[-1] ? push(@tokens,$tag) : ($tokens[-1] .= $tag);
1097             }
1098             }
1099              
1100 0           return \@tokens;
1101             }
1102              
1103             sub __load_data_file
1104             {
1105 0     0     my ($datafilename) = @_;
1106            
1107 0 0 0       if (-f $datafilename && -r $datafilename) {
1108 0 0         open DATAFILE, '<'.$datafilename
1109             or _die("failed to open file '$datafilename': $!");
1110            
1111 0           my @records;
1112 0           my $current_id = undef;
1113 0           my $current_field = undef;
1114 0           my $current_record = {};
1115 0           foreach my $line () {
1116            
1117 0 0 0       if (defined $current_id && defined $current_field && $line =~ /^[\s\t]/) {
      0        
1118             # possibly field value line
1119 0           $line =~ s/^[\s\t]//;
1120 0           $current_record->{$current_field} .= $line;
1121             }
1122             else {
1123 0 0         if ($line =~ /^\[(\d+)\][\s\t\n\r]*$/) {
    0          
1124             # id line
1125 0 0         if (defined $current_id) {
1126             # save previous record
1127 0           push @records, $current_record;
1128             }
1129             # reset
1130 0           $current_id = $line;
1131 0           $current_id =~ s/^\[(\d+)\][\s\t\n\r]*$/$1/;
1132 0           $current_record = { 'id' => $current_id };
1133             }
1134             elsif ($line =~ /^(\w+)[\s\t]*([\:\.])(.*)\n\r?$/) {
1135             # field line
1136 0           my ($fieldname, $type, $value)
1137             = $line =~ /^(\w+)[\s\t]*([\:\.])(.*)\n\r?$/;
1138 0 0         if ($type eq ':') {
1139 0           $current_record->{$fieldname} = $value;
1140 0           $current_field = undef;
1141             }
1142             else {
1143 0           $current_record->{$fieldname} = '';
1144 0           $current_field = $fieldname;
1145             }
1146             }
1147             }
1148             }
1149 0 0         if (defined $current_id) {
1150             # save last record
1151 0           push @records, $current_record;
1152             }
1153 0           return \@records;
1154             }
1155             else {
1156 0           __die("failed to open file '$datafilename': no file or not readable");
1157             }
1158             }
1159              
1160             sub __parse_translations
1161             {
1162 0     0     my ($translations) = @_;
1163 0           my %phrases;
1164 0           foreach my $translation (split /\n/, @{$translations}) {
  0            
1165 0           my ($language, $phrase) =~ /^([^\:]+)\:(.*)$/;
1166 0           $phrases{$language} = $phrase;
1167             }
1168 0           return \%phrases;
1169             }
1170              
1171             sub __find_translation
1172             {
1173 0     0     my ($translations, $find_language) = @_;
1174 0           foreach my $translation (split /\n\r?/, $translations) {
1175 0           my ($language, $phrase) = $translation =~ /^([^\:]+)\:(.*)$/;
1176 0 0         return $phrase
1177             if $language eq $find_language;
1178             }
1179 0           return undef;
1180             }
1181              
1182             sub __get_cache_hash
1183             {
1184 0     0     my ($self, $function_name, @args) = @_;
1185              
1186             # string of which hash is computed
1187 0           my $string = '';
1188            
1189 0           my $cfgfile = $self->{'privatepath'}.'/cacheconfigs/'.$function_name.'.txt';
1190 0 0         if (-f $cfgfile) {
1191             # open cache config
1192 0           my $cfg
1193             = __read_config_file($cfgfile, {
1194             'session' => [],
1195             'params' => [],
1196             'lifetime' => 3600,
1197             });
1198            
1199 0           map { $string .= __serialize($self->get($_)) } @{$cfg->{'session'}};
  0            
  0            
1200 0           map { $string .= __serialize(param($_)) } @{$cfg->{'params'}};
  0            
  0            
1201 0           $string .= __serialize(\@args);
1202             }
1203             else {
1204             # hash is created out of complete session, post/get and arguments
1205            
1206             # commented, because makes cache renew too often...
1207             #map { $string .= __serialize($self->get($_)) } keys %{$self->{'session'}};
1208            
1209 0 0 0       map {
1210 0           $string .=
1211             ($_ eq $self->{'idparam'} || $_ eq $self->{'clearcacheparam'} ?
1212             '' : __serialize(param($_)));
1213             } param();
1214 0           $string .= __serialize(\@args);
1215             }
1216            
1217 0           return md5_hex($string);
1218             }
1219              
1220             sub __load_cache
1221             {
1222 0     0     my ($self, $cachehash) = @_;
1223 0           my $query
1224             = $self->find(
1225             -tables => [$self->{'cachetable'}],
1226             -where => {'hash' => $cachehash},
1227             -limit => 1,
1228             );
1229            
1230 0 0         if (my $entry = $query->fetchrow_hashref()) {
1231 0           return __deserialize($entry->{'content'});
1232             }
1233             else {
1234 0           return undef;
1235             }
1236             }
1237              
1238             sub __save_cache
1239             {
1240 0     0     my ($self, $cachehash, $data) = @_;
1241            
1242 0           $self->create(
1243             -table => $self->{'cachetable'},
1244             -row => {
1245             'content' => __serialize($data),
1246             'hash' => $cachehash,
1247             'last_update' => time(),
1248             },
1249             );
1250            
1251 0           return 1;
1252             }
1253              
1254             sub __clear_cache
1255             {
1256 0     0     my ($self) = @_;
1257 0 0         return 1 if $self->{'cachetable'} eq '';
1258 0           $self->remove( -table => $self->{'cachetable'} );
1259             }
1260              
1261             sub __file_append
1262             {
1263 0     0     my ($filename, $text) = @_;
1264 0 0         open OUTFILE, '>>'.$filename or __die("failed to open file '$filename': $!");
1265 0           print OUTFILE $text;
1266 0           close OUTFILE;
1267 0           return 1;
1268             }
1269              
1270             # returns the args array with the
1271             # current CGI::WebToolkit instance as first argument
1272             sub __parse_args
1273             {
1274 0 0 0 0     if (scalar @_ && ref($_[0]) eq 'CGI::WebToolkit') {
1275 0           return @_;
1276             } else {
1277 0           return ($WTK, @_);
1278             }
1279             }
1280              
1281             sub __replace_placeholders
1282             {
1283 0     0     my ($stringref, $hash) = @_;
1284 0           foreach my $key (keys %{$hash}) {
  0            
1285 0           my $cleankey = $key;
1286 0           $cleankey =~ s/[^a-zA-Z0-9\_]//g;
1287 0           ${$stringref} =~ s/\{$cleankey[^\}]*\}/$hash->{$key}/mig;
  0            
1288             }
1289 0           return undef;
1290             }
1291              
1292             sub __make_sql_where_clause
1293             {
1294 0     0     my ($self, $where, $use_like) = @_;
1295 0 0         $use_like = 0 unless defined $use_like;
1296            
1297 0           my @parts =
1298             map {
1299 0           my $fieldname = $self->__quotename($_);
1300 0           my $fieldvalue = $self->__quote($where->{$_});
1301            
1302 0           my $s = $fieldname;
1303 0 0         $s .= ($use_like == 1 ? ' LIKE ' : ' = ');
1304 0           $s .= ''.$fieldvalue;
1305 0           $s;
1306             }
1307 0           keys %{$where};
1308            
1309 0           return join(' AND ', @parts);
1310             }
1311              
1312             sub __quote
1313             {
1314 0     0     my ($self, @args) = @_;
1315 0           $self->__connect_to_db();
1316            
1317 0 0         return $self->{'dbh'}->quote(@args)
1318             or __die('quote failed: '.DBI->errstr());
1319             }
1320              
1321             # escapes a CGI::WebToolkit field identifier, e.g. "mytable.myfield" or "myfield" etc.
1322             sub __quotename
1323             {
1324 0     0     my ($self, $fieldname) = @_;
1325 0           $self->__connect_to_db();
1326              
1327 0           my @parts = split /\./, $fieldname;
1328            
1329 0           my $quoted;
1330 0 0         if (scalar @parts == 1) {
1331             #$quoted = $self->{'dbh'}->quote_identifier(undef, undef, $parts[0])
1332             # or _die("quote_identifier() failed: ".DBI->errstr());
1333 0           $quoted = '`'.$parts[0].'`';
1334             }
1335             else {
1336             #$quoted = $self->{'dbh'}->quote_identifier(undef, $parts[0], $parts[1])
1337             # or _die("quote_identifier() failed: ".DBI->errstr());
1338 0           $quoted = '`'.$parts[0].'`'.'.'.'`'.$parts[1].'`';
1339             }
1340 0           return $quoted;
1341             }
1342              
1343             sub __get_external_function_filename
1344             {
1345 0     0     my ($self,
1346             $type, # "functions" or "templates" or other subdirectory in
1347             $name, # name of function in dot-syntax, e.g. "page.home.default"
1348             )
1349             = @_;
1350            
1351             # untaint name
1352 0           $name =~ s/^(([a-z\_]+)(\.([a-z\_]+))*).*$/$1/;
1353              
1354 0           my $filename = __identifier_to_filename(
1355             $self->{'privatepath'}.'/'.$type.'/', $name, '.pl');
1356              
1357 0 0         if (-f $filename) {
1358 0           return $filename;
1359             } else {
1360 0           return undef;
1361             }
1362             }
1363              
1364             # bla.bla.bla -> bla/bla/bla
1365             sub __identifier_to_filename
1366             {
1367 0     0     my ($prefix, $identifier, $suffix) = @_;
1368 0 0         $suffix = '' unless defined $suffix;
1369            
1370 0           my $filename = $identifier;
1371 0           $filename =~ s#\.#/#g;
1372            
1373 0           return $prefix.$filename.$suffix;
1374             }
1375              
1376             # loads an external file into an anonymous perl function ref.
1377             # and returns this ref.
1378             sub __load_file_as_subref
1379             {
1380 0     0     my ($filename) = @_;
1381            
1382 0 0         __die("cannot load file '$filename': does not exist.") unless -f $filename;
1383 0 0         __die("cannot load file '$filename': is not readable.") unless -r $filename;
1384            
1385 0           my $subref = undef;
1386 0 0         open PERLFILE, '<'.$filename or __die("failed to open file '$filename': $!");
1387 0           my $code = join '', ;
1388 0           $code =~ /^(.*).*$/sm; # untaint
1389 0           $code = "$1";
1390 0           $code = '$subref = sub { my ($wtk, @args) = @_;'."\n".$code."\n".'}';
1391 0           close PERLFILE;
1392 0           eval($code);
1393 0 0         __die("function (file '$filename') failed to load with error: $@") if $@;
1394            
1395 0           return $subref;
1396             }
1397              
1398             sub __load_session
1399             {
1400 0     0     my ($self) = @_;
1401 0 0         return 1 if $self->{'sessiontable'} eq '';
1402            
1403             # determine session id
1404 0           $self->{'session_id'} =
1405             $self->getparam( $self->{'idparam'}, undef, '^[a-zA-Z0-9]{32}$' );
1406              
1407 0 0         $self->{'session_id'} = md5_hex( time() )
1408             unless defined $self->{'session_id'};
1409              
1410             # try to find it in db
1411 0           my $query =
1412             $self->find(
1413             -tables => [ $self->{'sessiontable'} ],
1414             -where => { 'session_id' => $self->{'session_id'} },
1415             );
1416            
1417 0           my $sessionstart = 0;
1418 0 0         if (my $session = $query->fetchrow_hashref()) {
1419 0 0         if (time() - $session->{'last_update'} < $self->{'sessiontimeout'}) {
1420 0           $self->{'session'} = __deserialize($session->{'content'});
1421             } else {
1422             # session timed out
1423 0           $self->{'session_id'} = md5_hex( time() ); # gets new session id!
1424 0           $self->{'session'} = {};
1425            
1426 0           $self->set('session_timed_out', 1);
1427            
1428 0           $sessionstart = 1;
1429             }
1430             }
1431             else {
1432             # create empty session
1433 0           $self->{'session'} = {};
1434            
1435 0           $sessionstart = 1;
1436             }
1437            
1438             # trigger callback
1439 0 0 0       $self->call($self->{'onsessionstart'})
1440             if $sessionstart && $self->{'onsessionstart'} ne '';
1441             }
1442              
1443             sub __save_session
1444             {
1445 0     0     my ($self) = @_;
1446 0 0         return 1 if $self->{'sessiontable'} eq '';
1447            
1448             # check if session row exists in database
1449 0           my $query =
1450             $self->find(
1451             -tables => [ $self->{'sessiontable'} ],
1452             -where => { 'session_id' => $self->{'session_id'} },
1453             );
1454            
1455 0 0         if (my $session = $query->fetchrow_hashref()) {
1456             # update
1457 0           $self->update(
1458             -table => $self->{'sessiontable'},
1459             -set => {
1460             'content' => __serialize($self->{'session'}),
1461             'last_update' => time(),
1462             },
1463             -where => { 'session_id' => $self->{'session_id'} },
1464             );
1465             }
1466             else {
1467             # insert
1468 0           $self->create(
1469             -table => $self->{'sessiontable'},
1470             -row => {
1471             'session_id' => $self->{'session_id'},
1472             'content' => __serialize($self->{'session'}),
1473             'last_update' => time(),
1474             },
1475             );
1476             }
1477             }
1478              
1479             sub __serialize
1480             {
1481 0     0     my ($structure) = @_;
1482 0           return dump($structure);
1483             }
1484              
1485             sub __deserialize
1486             {
1487 0     0     my ($string) = @_;
1488 0 0         return {} unless length $string;
1489 0           my $structure = undef;
1490 0           eval('$structure = '.$string);
1491 0 0         __die("deserialization of string failed: $@") if $@;
1492 0           return $structure;
1493             }
1494              
1495             sub __connect_to_db
1496             {
1497 0     0     my ($self) = @_;
1498 0 0         return 1 if defined $self->{'dbh'}; # && ref($self->{'dbh'}) eq 'DBI';
1499            
1500 0 0         $self->{'dbh'}
1501             = DBI->connect(
1502             "DBI:".$self->{'engine'}.":".$self->{'name'}.":".$self->{'host'},
1503             $self->{'user'}, $self->{'password'},
1504             {
1505             #PrintError => 1,
1506             #RaiseError => 1,
1507             #AutoCommit => 1,
1508             #PrintWarn => 1,
1509             })
1510             or __die("Could not connect to database");
1511            
1512 0           return 1;
1513             }
1514              
1515             sub __disconnect_from_db
1516             {
1517 0     0     my ($self) = @_;
1518            
1519 0           return $self->{'dbh'}->disconnect();
1520             }
1521              
1522             sub __init
1523             {
1524 0     0     my ($self, %options) = @_;
1525            
1526 0           my $optdefaults =
1527             {
1528             # path parameters
1529             publicpath => '',
1530             publicurl => '',
1531             privatepath => '',
1532             cgipath => '',
1533             cgiurl => '',
1534              
1535             # configuration parameters
1536             config => "",
1537            
1538             # database parameters
1539             engine => "mysql",
1540             user => "guest",
1541             name => "",
1542             password => "",
1543             host => "localhost",
1544             port => "",
1545            
1546             # template parameters
1547             templatefallbacks => ['core'],
1548             allowmacros => 1,
1549            
1550             # form creation parameters
1551             # ...
1552            
1553             # session parameters
1554             idparam => 'sid',
1555             sessiontable => '',
1556             sessiontimeout => 1800,
1557            
1558             # user/rights management
1559             usertable => '',
1560             checkrights => 0,
1561            
1562             # caching
1563             cachetable => '',
1564             allowclearcache => 1,
1565             clearcacheparam => 'clearcache',
1566            
1567             # locale
1568             phrasetable =>'',
1569             defaultlanguage => 'en_GB',
1570            
1571             # workflow parameters
1572             workflowparam => 'to',
1573             entryaction => 'core.default',
1574             modules => [],
1575            
1576             # combinable files
1577             cssfiles => [],
1578             jsfiles => [],
1579            
1580             # triggers
1581             onsessionstart => '',
1582             onsessionoutofdate => '',
1583            
1584             # uploads
1585             uploadmaxsize => (1024 * 1024 * 6), # 6MB
1586             };
1587              
1588             # check if config filename is given -> if so, load it first
1589             # (so that it can be overwritten by settings from %options later!)
1590 0           my $cfgopts = $optdefaults;
1591 0           foreach my $key (keys %options) {
1592 0           my $name = lc $key;
1593 0           $name =~ s/^\-*//;
1594 0 0         if ($name eq 'config') {
1595 0           $cfgopts = __load_config_file( $options{$key}, $optdefaults );
1596             }
1597             }
1598            
1599 0           my $opts = __parse_params( \%options, $cfgopts );
1600 0           map { $self->{$_} = $opts->{$_} } keys %{$opts};
  0            
  0            
1601            
1602             # add 'default' theme as last fallback if not already added
1603 0           push @{$self->{'templatefallbacks'}}, 'core'
  0            
1604 0 0 0       if !scalar @{$self->{'templatefallbacks'}}
1605             || $self->{'templatefallbacks'}->[-1] ne 'core';
1606            
1607 0           $self->{'dbh'} = undef;
1608 0           $self->{'session'} = undef;
1609 0           $self->{'session_id'} = undef;
1610            
1611             # caches for function refs.
1612 0           $self->{'workflow_function_cache'} = {};
1613 0           $self->{"template_function_cache"} = {};
1614 0           $self->{"access_function_cache"} = {};
1615            
1616 0           $self->__load_session();
1617            
1618             # common placeholders
1619 0 0         $self->{'common_placeholders'} = {
    0          
    0          
1620             'script_url' => (exists $ENV{'SCRIPT_NAME'} ? $ENV{'SCRIPT_NAME'} : '?'),
1621             'public_url' => $self->{'publicurl'},
1622             'clear' => '
',
1623             'session_id' => $self->{'session_id'},
1624             'do_nothing_url' => 'javascript:void(1);',
1625             'javascript_url' => (exists $ENV{'SCRIPT_NAME'} ? $ENV{'SCRIPT_NAME'} : '').'?to=core.combine.javascript',
1626             'css_url' => (exists $ENV{'SCRIPT_NAME'} ? $ENV{'SCRIPT_NAME'} : '').'?to=core.combine.css',
1627             };
1628            
1629             # name of workflow function that is currently executed
1630 0           $self->{'current_workflow_function'} = undef;
1631            
1632             # set current language
1633 0           $self->set('language', $self->{'defaultlanguage'});
1634            
1635             # uploads info
1636 0           $self->{'session'}->{'uploads'} = [];
1637            
1638             # save global CGI::WebToolkit instance
1639 0           $WTK = $self;
1640            
1641 0           return $self;
1642             }
1643              
1644             sub __load_config_file
1645             {
1646 0     0     my ($filename, $defaults) = @_;
1647            
1648 0 0         open CFGFILE, '<'.$filename
1649             or _die("failed to load config file '$filename': $!");
1650 0           my $options = {};
1651 0 0         if (defined $defaults) {
1652             # copy defaults
1653 0           map { $options->{$_} = $defaults->{$_} } keys %{$defaults};
  0            
  0            
1654             }
1655 0           while () {
1656 0           chomp;
1657 0           s/^(.*)\#.*$/$1/g;
1658 0 0         next if /^[\s\t\n\r]*$/;
1659 0           s/^[\s\t\n\r]*//g;
1660 0           s/[\s\t\n\r]*$//g;
1661 0 0         my $rgx =
1662             (defined $defaults ?
1663             '^([a-zA-Z0-9\_]+)[\s\t]*\:[\s\t]*(.*)$' :
1664             '^([^\:]+)[\s\t]*\:[\s\t]*(.*)$');
1665 0 0         if (/$rgx/) {
1666 0           my ($key, $value) = $_ =~ /$rgx/;
1667 0           $key = lc $key;
1668 0           $key =~ s/^\-*//g;
1669            
1670 0 0         if (defined $defaults) {
1671 0 0         if (ref $defaults->{$key} eq 'ARRAY') {
1672             # array variable
1673 0 0         $options->{$key} = [ split(/\s*\,\s*/, $value) ]
1674             if exists $defaults->{$key};
1675             }
1676             else {
1677             # string variable
1678 0 0         $options->{$key} = $value
1679             if exists $defaults->{$key};
1680             }
1681             }
1682             else {
1683 0           $options->{$key} = $value;
1684             }
1685             }
1686             }
1687 0           close CFGFILE;
1688 0           return $options;
1689             }
1690              
1691             # dumps data to browser
1692             sub __dd
1693             {
1694 0     0     print header();
1695 0           print '
'.dump($_[0]).'

';
1696             }
1697              
1698             sub __parse_params
1699             {
1700 0     0     my ($params, $defaults) = @_;
1701 0           my $values = {};
1702 0           foreach my $key (keys %{$defaults}) {
  0            
1703 0           $values->{$key} = $defaults->{$key};
1704             }
1705 0           foreach my $key (keys %{$params}) {
  0            
1706 0           my $cleankey = lc $key;
1707 0           $cleankey =~ s/^\-//;
1708 0 0         $values->{$cleankey} = $params->{$key}
1709             if exists $defaults->{$cleankey};
1710             }
1711 0           return $values;
1712             }
1713              
1714             sub __die
1715             {
1716 0     0     my ($msg) = @_;
1717 0           print header();
1718 0           print
1719             'Lowlevel Error'.
1720             'Lowlevel Error: '.$msg.''.
1721             '';
1722 0           exit;
1723             }
1724              
1725             # ------------------------------------------------------------------------------
1726             # ------------------------------------------------------------------------------
1727             1;
1728             __END__