]> git.p6c8.net - devedit.git/blob - modules/Template.pm
Changed version number
[devedit.git] / modules / Template.pm
1 package Template;
2
3 #
4 # Template (Version 1.4a)
5 #
6 # Klasse zum Parsen von Templates
7 #
8 # Autor: Patrick Canterino <patrick@patshaping.de>
9 # Letzte Aenderung: 21.3.2005
10 #
11
12 use strict;
13
14 use Carp qw(croak);
15
16 # new()
17 #
18 # Konstruktor
19 #
20 # Parameter: -keine-
21 #
22 # Rueckgabe: Template-Objekt
23
24 sub new
25 {
26 my $class = shift;
27 my $self = {file => '', template => ''};
28 return bless($self,$class);
29 }
30
31 # get_template()
32 #
33 # Kompletten Vorlagentext zurueckgeben
34 #
35 # Parameter: -keine-
36 #
37 # Rueckgabe: Kompletter Vorlagentext (String)
38
39 sub get_template
40 {
41 return shift->{'template'};
42 }
43
44 # set_template()
45 #
46 # Kompletten Vorlagentext aendern
47 #
48 # Parameter: Vorlagentext
49 #
50 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
51
52 sub set_template($)
53 {
54 # Geht nur so...
55
56 my ($self,$template) = @_;
57 $self->{'template'} = $template;
58 }
59
60 # add_text()
61 #
62 # Vorlagentext ans Template-Objekt anhaengen
63 #
64 # Parameter: Vorlagentext
65 #
66 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
67
68 sub add_text($)
69 {
70 my ($self,$text) = @_;
71 $self->set_template($self->get_template.$text);
72 }
73
74 # read_file()
75 #
76 # Einlesen einer Vorlagendatei und {INCLUDE}-Anweisungen ggf. verarbeiten
77 # (Text wird an bereits vorhandenen Text angehaengt)
78 #
79 # Parameter: 1. Datei zum Einlesen
80 # 2. Status-Code (Boolean):
81 # true => {INCLUDE}-Anweisungen nicht verarbeiten
82 # false => {INCLUDE}-Anweisungen verarbeiten (Standard)
83 #
84 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
85
86 sub read_file($;$)
87 {
88 my ($self,$file,$not_include) = @_;
89 local *FILE;
90
91 $self->{'file'} = $file;
92
93 open(FILE,'<'.$file) or croak "Open $file: $!";
94 read(FILE, my $content, -s $file);
95 close(FILE) or croak "Closing $file: $!";
96
97 $self->add_text($content);
98 $self->parse_includes unless($not_include);
99 }
100
101 # fillin()
102 #
103 # Variablen durch Text ersetzen
104 #
105 # Parameter: 1. Variable zum Ersetzen
106 # 2. Text, durch den die Variable ersetzt werden soll
107 #
108 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
109
110 sub fillin($$)
111 {
112 my ($self,$var,$text) = @_;
113
114 $var = quotemeta($var);
115 $text = '' unless defined $text; # Um Fehler zu vermeiden
116
117 my $template = $self->get_template;
118 $template =~ s/\{$var\}/$text/g;
119
120 $self->set_template($template);
121 }
122
123 # fillin_array()
124 #
125 # Variable durch Array ersetzen
126 #
127 # Parameter: 1. Variable zum Ersetzen
128 # 2. Array-Referenz, durch die die Variable ersetzt werden soll
129 # 3. Zeichenkette, mit der das Array verbunden werden soll
130 # (Standard: '')
131 #
132 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
133
134 sub fillin_array($$;$)
135 {
136 my ($self,$var,$array,$glue) = @_;
137 $glue = '' unless defined $glue;
138
139 $self->fillin($var,join($glue,@$array));
140 }
141
142 # to_file()
143 #
144 # Template in Datei schreiben
145 #
146 # Parameter: Datei-Handle
147 #
148 # Rueckgabe: Status-Code (Boolean)
149
150 sub to_file($)
151 {
152 my ($self,$handle) = @_;
153 return print $handle $self->get_template;
154 }
155
156 # parse_if_block()
157 #
158 # IF-Bloecke verarbeiten
159 #
160 # Parameter: 1. Name des IF-Blocks (das, was nach dem IF steht)
161 # 2. Status-Code (true => Inhalt anzeigen
162 # false => Inhalt nicht anzeigen
163 #
164 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
165
166 sub parse_if_block($$)
167 {
168 my ($self,$name,$state) = @_;
169 my $template = $self->get_template;
170
171 my $count = 0;
172
173 while(index($template,'{IF '.$name.'}') >= 0)
174 {
175 # Das alles hier ist nicht wirklich elegant geloest...
176 # ... aber solange es funktioniert... ;-)
177
178 $count++;
179
180 my $start = index($template,'{IF '.$name.'}');
181 my $tpl_tmp = substr($template,$start);
182 my @splitted = split(/\{ENDIF\}/,$tpl_tmp);
183
184 # Wenn sich am Ende der Zeichenkette {ENDIF} befinden, werden diese
185 # von split() ignoriert, was zu einem Verschachtelungsfehler fuehrt
186 # Die fehlenden leeren Zeichenketten muessen von Hand eingefuegt werden
187
188 my $x = 1;
189
190 while(substr($tpl_tmp,-7*$x,7) eq '{ENDIF}')
191 {
192 push(@splitted,'');
193 $x++;
194 }
195
196 my $block = ''; # Kompletter bedingter Block
197 my $ifs = 0; # IF-Zaehler (wird fuer jedes IF erhoeht und fuer jedes ENDIF erniedrigt)
198
199 # {IF}
200
201 for(my $x=0;$x<@splitted;$x++)
202 {
203 croak 'Nesting error found while parsing IF block "'.$name.'" nr. '.$count.' in template file "'.$self->{'file'}.'"' if($x == $#splitted);
204
205 $ifs += substr_count($splitted[$x],'{IF '); # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
206 $ifs--; # Zaehler um 1 erniedrigen
207 $block .= $splitted[$x].'{ENDIF}'; # Daten zum Block hinzufuegen
208
209 if($ifs == 0)
210 {
211 # Zaehler wieder 0, also haben wir das Ende des IF-Blocks gefunden :-))
212
213 last;
214 }
215 }
216
217 my $if_block = substr($block,length($name)+5,-7); # Alles zwischen {IF} und {ENDIF}
218
219 # {ELSE}
220
221 my $else_block = ''; # Alles ab {ELSE}
222 $ifs = 0; # IF-Zaehler
223
224 @splitted = split(/\{ELSE\}/,$if_block);
225
226 for(my $x=0;$x<@splitted;$x++)
227 {
228 $ifs += substr_count($splitted[$x],'{IF '); # Zum Zaehler jedes Vorkommen von IF hinzuzaehlen
229 $ifs -= substr_count($splitted[$x],'{ENDIF}'); # Vom Zaehler jedes Vorkommen von ENDIF abziehen
230
231 if($ifs == 0)
232 {
233 # Zaehler 0, also haben wir das Ende des IF-Abschnitts gefunden
234
235 # Aus dem Rest den ELSE-Block zusammenbauen
236
237 for(my $y=$x+1;$y<@splitted;$y++)
238 {
239 $else_block .= '{ELSE}'.$splitted[$y];
240 }
241
242 if($else_block)
243 {
244 $if_block = substr($if_block,0,length($if_block)-length($else_block));
245 $else_block = (length($else_block) > 6) ? substr($else_block,6) : ''; # Ansonsten gibt es Fehler
246 }
247
248 last;
249 }
250 }
251
252 my $replacement = ($state) ? $if_block : $else_block;
253
254 my $qmblock = quotemeta($block);
255
256 $template =~ s/$qmblock/$replacement/;
257 }
258
259 $self->set_template($template);
260 }
261
262 # parse_condtag()
263 #
264 # Bedingungstags in einem Vorlagentext verarbeiten
265 #
266 # Parameter: 1. Tagname
267 # 2. Status-Code (true => Tag-Inhalt anzeigen
268 # false => Tag-Inhalt nicht anzeigen
269 #
270 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
271
272 sub parse_condtag($$)
273 {
274 my ($self,$condtag,$state) = @_;
275
276 my $template = $self->get_template;
277
278 while(index($template,'<'.$condtag.'>') >= 0)
279 {
280 my $start = index($template,'<'.$condtag.'>'); # Beginn des Blocks
281 my $end = index($template,'</'.$condtag.'>')+length($condtag)+3; # Ende des Blocks
282
283 my $extract = substr($template,$start,$end-$start); # Kompletten Bedingungsblock extrahieren...
284
285 my $replacement = ($state) ? substr($extract,length($condtag)+2,0-length($condtag)-3) : '';
286
287 $extract = quotemeta($extract);
288
289 $template =~ s/$extract/$replacement/g; # Block durch neue Daten ersetzen
290 }
291 $self->set_template($template);
292 }
293
294 # parse_includes()
295 #
296 # {INCLUDE}-Anweisungen verarbeiten
297 #
298 # Parameter: -nichts-
299 #
300 # Rueckgabe: -nichts- (Template-Objekt wird modifiziert)
301
302 sub parse_includes
303 {
304 my $self = shift;
305 my $template = $self->get_template;
306
307 while($template =~ /(\{INCLUDE (\S+?)\})/)
308 {
309 my ($directive,$file) = ($1,$2);
310 my $qm_directive = quotemeta($directive);
311 my $replacement = '';
312
313 if(-f $file)
314 {
315 local *FILE;
316
317 open(FILE,'<'.$file) or croak "Open $file: $!";
318 read(FILE, $replacement, -s $file);
319 close(FILE) or croak "Closing $file: $!";
320 }
321
322 $template =~ s/$qm_directive/$replacement/g;
323 }
324
325 $self->set_template($template);
326 }
327
328 # ==================
329 # Private Funktion
330 # ==================
331
332 # substr_count()
333 #
334 # Zaehlt, wie oft ein String in einem String vorkommt
335 # (Emulation der PHP-Funktion substr_count())
336 #
337 # Parameter: 1. Zu durchsuchender String
338 # 2. Zu suchender String
339 #
340 # Rueckgabe: Anzahl der Vorkommnisse (Integer)
341
342 sub substr_count($$)
343 {
344 my ($haystack,$needle) = @_;
345 my $qmneedle = quotemeta($needle);
346
347 my $count = 0;
348
349 $count++ while($haystack =~ /$qmneedle/g);
350
351 return $count;
352 }
353
354 # ==================
355 # Alias-Funktionen
356 # ==================
357
358 sub addtext($)
359 {
360 shift->add_text(shift);
361 }
362
363 sub as_string
364 {
365 return shift->get_template;
366 }
367
368 sub condtag($$)
369 {
370 shift->parse_condtag(@_);
371 }
372
373 sub readin($)
374 {
375 shift->read_file(shift);
376 }
377
378 # it's true, baby ;-)
379
380 1;
381
382 #
383 ### Ende ###

patrick-canterino.de