71cdd6f5c2e04145c41b89536ecfabeed43a2093
[lugs.git] / make-html / make-html.pl
1 #!/usr/bin/perl
2 #
3 # Konvertiert die LUGS-Terminliste (im ASCII Format) in ein HTML-File
4 #
5 # (c) 1996-1998 Roland Alder
6 # (c) 2007-2008, 2011-2013 Steven Schubiger
7
8 use strict;
9 use warnings;
10 use lib qw(lib);
11
12 my $VERSION = '0.03';
13
14 #-----------------------
15 # Start of configuration
16 #-----------------------
17
18 #
19 # If you're looking for the template,
20 # it is contained below __DATA__ at the end
21 # of this script.
22 #
23
24 my $Config = {
25 data_source => './termine.txt',
26 html_file => './index.phtml',
27 colors => {
28 fremd => 'a2eeff', # blau/gruen
29 treff => '99ccff', # ex http://www.zuerich.ch/
30 seeland => 'ffffbb', # gelb
31 aargau => 'ffbbff', # violett
32 bern => 'a5f6bb', # gruen
33 spec => 'ff8a80', # rot
34 winti => 'd6d6ce', # ex http://www.stadt-winterthur.ch/
35 innerschweiz => '8abed7', # ex http://www.luzern.ch/
36 kreuzlingen => 'f9f9f9', # ehemals aargau (ex http://www.ag.ch/)
37 uzwil => 'e2b1a5', # wie heisst diese Farbe? :)
38 stgallen => 'e2b1a5', # wie heisst diese Farbe? :)
39 gnupingu => 'ffd133', # von http://www.gnupingu.ch/
40 },
41 ical_dir => 'ical',
42 };
43
44 #---------------------
45 # End of configuration
46 #---------------------
47
48 #-------------------
49 # Start of internals
50 #-------------------
51
52 {
53 my $termine = LUGS::Termine::Liste->new;
54
55 $termine->init;
56
57 my ($html_before, $html_after) = $termine->extract_html;
58 $termine->parse_template;
59
60 my $fh = $termine->{fh}{out};
61
62 print {$fh} $html_before;
63 $termine->process_events;
64 print {$fh} $html_after;
65
66 $termine->cleanup;
67
68 $termine->finalize;
69 }
70
71 package LUGS::Termine::Liste;
72
73 use constant true => 1;
74
75 use File::Copy qw(copy);
76 use File::Temp qw(tempfile);
77 use LUGS::Events::Parser ();
78
79 # Return a new instance of our class.
80 sub new
81 {
82 my $class = shift;
83
84 return bless {};
85 }
86
87 # Open files and retrieve the modification time.
88 sub init
89 {
90 my $self = shift;
91
92 $self->{mtime} = scalar localtime +(stat($Config->{data_source}))[9];
93
94 open($self->{fh}{in}, '<', $Config->{html_file}) or die "Cannot open $Config->{html_file}: $!\n";
95 ($self->{fh}{out}, $self->{tmp_file}) = tempfile(UNLINK => true);
96 }
97
98 # Close file handles.
99 sub cleanup
100 {
101 my $self = shift;
102
103 foreach my $handle (qw(in out)) {
104 close($self->{fh}{$handle});
105 }
106 }
107
108 # Copy the temporary file to the HTML file's location.
109 sub finalize
110 {
111 my $self = shift;
112
113 copy($self->{tmp_file}, $Config->{html_file})
114 or die "Cannot copy $self->{tmp_file} to $Config->{html_file}: $!\n";
115 }
116
117 # Extract chunks before and after where the events get populated in.
118 sub extract_html
119 {
120 my $self = shift;
121
122 my $fh = $self->{fh}{in};
123 my $html = do { local $/; <$fh> };
124
125 my @regexes = (
126 qr/^ (.+? \n<!-- \s*? TERMINE_BEGIN \s*? --> \s*? \n)/sx,
127 qr/ \n(<!-- \s*? TERMINE_ENDE \s*? --> .*) $/sx,
128 );
129
130 my @chunks;
131 foreach my $regex (@regexes) {
132 push @chunks, $1 if $html =~ $regex;
133 }
134
135 return @chunks;
136 }
137
138 # Dump regular events formatted to the output handle.
139 sub process_events
140 {
141 my $self = shift;
142
143 my $parser = LUGS::Events::Parser->new($Config->{data_source});
144
145 my $i;
146 my %month_names = map { sprintf("%02d", ++$i) => $_ }
147 qw(Januar Februar M&auml;rz April Mai Juni Juli
148 August September Oktober November Dezember);
149
150 my $seen ||= '';
151 my $print_month = sub
152 {
153 my ($event) = @_;
154
155 my $year = $event->get_event_year;
156 my $month = $event->get_event_month;
157 my $day = $event->get_event_day;
158
159 if ($month ne $seen) {
160 $seen = $month;
161 $self->print_template('jahreszeit',
162 {
163 MONAT => $month_names{$month},
164 JAHR => $year,
165 });
166 }
167 };
168
169 $self->print_template('tabellenstart');
170 $self->print_template('kopfdaten');
171
172 while (my $event = $parser->next_event) {
173 $print_month->($event);
174
175 my $anchor = $event->get_event_anchor;
176
177 $self->print_template('farbe',
178 {
179 FARBE => $Config->{colors}->{$event->get_event_color}
180 });
181
182 $self->print_template('anker/wann',
183 {
184 ANKER => $anchor,
185 WOCHENTAG => $event->get_event_weekday,
186 TAG => $event->get_event_day,
187 });
188
189 $event->get_event_time
190 ? $self->print_template('zeit',
191 {
192 UHRZEIT => $event->get_event_time,
193 })
194 : $self->print_template('blank');
195
196 $event->get_event_responsible
197 ? $self->print_template('verantwortlich',
198 {
199 WER => $event->get_event_responsible,
200 })
201 : $self->print_template('blank');
202
203 $self->print_template('titel',
204 {
205 BEZEICHNUNG => $event->get_event_title,
206 });
207
208 $event->get_event_location
209 ? $self->print_template('standort',
210 {
211 STANDORT => $event->get_event_location,
212 })
213 : ();
214
215 $event->get_event_more
216 ? $self->print_template('infos',
217 {
218 INFORMATIONEN => $event->get_event_more,
219 })
220 : ();
221
222 my $ics_file = "$anchor.ics";
223 my $ics_link = join '/', ($Config->{ical_dir}, $ics_file);
224
225 $self->print_template('ical',
226 {
227 LINK => $ics_link,
228 });
229
230 $self->print_raw_html('</td></tr>');
231 }
232
233 $self->print_template('tabellenende');
234 $self->print_template('fussnoten',
235 {
236 AENDERUNG => $self->{mtime},
237 });
238 }
239
240 # Parse the template as outlined below __DATA__ and create
241 # a lookup map.
242 sub parse_template
243 {
244 my $self = shift;
245
246 my $template = do { local $/; <DATA> };
247
248 $self->{template} = [ map { s/\n{2,}$/\n/; $_ } # # description
249 grep /\S/, # -
250 split /\# \s+? .+? \s+? -\n/x,
251 $template ];
252 my @descriptions;
253 push @descriptions, $1 while $template =~ /\# \s+? (.+?) \s+? -\n/gx;
254
255 my $i;
256 $self->{lookup} = { map { $_ => $i++ } @descriptions };
257 }
258
259 # Look up the template item, substitute it with the data
260 # given and print it to the output handle.
261 sub print_template
262 {
263 my $self = shift;
264 my ($keyword, $data) = @_;
265
266 return unless exists $self->{lookup}->{$keyword};
267
268 my $item = $self->{template}->[$self->{lookup}->{$keyword}];
269
270 my %markers = (
271 begin => '[%',
272 end => '%]',
273 );
274 foreach my $marker ($markers{begin}, $markers{end}) {
275 $marker = qr/\Q$marker\E/;
276 }
277
278 foreach my $name (keys %$data) {
279 $item =~ s/$markers{begin}
280 \s*?
281 $name
282 \s*?
283 $markers{end}
284 /$data->{$name}/gx;
285 }
286
287 my $fh = $self->{fh}{out};
288 print {$fh} $item;
289 }
290
291 # Print raw HTML to the output handle.
292 sub print_raw_html
293 {
294 my $self = shift;
295 my ($html) = @_;
296
297 my $fh = $self->{fh}{out};
298 print {$fh} $html, "\n";
299 }
300
301 #-----------------
302 # End of internals
303 #-----------------
304
305 #
306 # Do not change the data descriptions within '# <name>' without
307 # adjusting the code accordingly; furthermore, the hyphen '-'
308 # is required and two trailing newlines at the end of the
309 # template item, too.
310 #
311
312 __DATA__
313
314 # tabellenstart
315 -
316 <table border=0 cellpadding=1 cellspacing=2>
317
318 # kopfdaten
319 -
320 <tr><td>&nbsp;</td></tr>
321 <tr><td colspan=4 align=left><h2>Definitive Daten</h2></td></tr>
322 <tr><th align=left>Tag</th><th align=left>Zeit</th><th align=left>Verantwortlich</th><th align=left>Anlass, Thema</th></tr>
323
324 # jahreszeit
325 -
326 <tr><th align=left colspan=3><br><font size="+1">[% MONAT %] [% JAHR %]</font></th></tr>
327
328 # anker
329 -
330 <a name="[% WERT %]"></a>
331
332 # farbe
333 -
334 <tr bgcolor="#[% FARBE %]">
335
336 # anker/wann
337 -
338 <td valign=top><a name="[% ANKER %]"></a>[% WOCHENTAG %], [% TAG %].</td>
339
340 # zeit
341 -
342 <td valign=top>[% UHRZEIT %]</td>
343
344 # verantwortlich
345 -
346 <td valign=top>[% WER %]</td>
347
348 # titel
349 -
350 <td valign=top><b>[% BEZEICHNUNG %]</b>
351
352 # standort
353 -
354 <br><font size=-1>[% STANDORT %]</font>
355
356 # infos
357 -
358 <br>[% INFORMATIONEN %]
359
360 # ical
361 -
362 <td valign=top><a href="[% LINK %]">iCal</a></td>
363
364 # tabellenende
365 -
366 </table>
367
368 # fussnoten
369 -
370 <p>
371 <font size="-1">Alle Angaben ohne Gew&auml;hr, letzte &Auml;nderung der Terminliste: [% AENDERUNG %]</font>
372
373 # blank
374 -
375 <td>&nbsp;</td>