Attachment 'Gallery2.py'
Download 1 # -*- coding: iso-8859-1 -*-
2 """
3 MoinMoin - Gallery2 parser
4
5 PURPOSE:
6 This parser is used to visualize a couple of images as a thumbnail gallery.
7 Optional a description of an image could be added including WikiName.
8 On default the image name and it's creation date is shown.
9 If you click on a thumbnail you get the webnails shown. By a menue you are able to toggle between the slides.
10
11 CALLING SEQUENCE:
12 {{{
13 #!Gallery2 [columns=columns],[filter=filter],[mode=mode],
14 [show_text=show_text],[show_date=show_date], [show_tools=show_tools],
15 [sort_by_name=sort_by_name],[sort_by_date=sort_by_date], [sort_by_alias=sort_by_alias],
16 [reverse_sort=reverse_sort],
17 [only_items=only_items],[template_itemlist=template_itemlist],
18 [album=album],[album_name=album_name],[front_image=front_image],
19 [thumbnail_width=thumbnail_width],[webnail_width=webnail_width],[text_width=text_width],
20 [image_for_webnail=image_for_webnail],
21 [sequence_name=sequence_name], [sequence_type=sequence_type],
22 [border_thick=border_thick],[renew=renew],[help=help]
23 * [image1.jpg alias]
24 * [image2.jpg alias]
25 }}}
26
27 KEYWORD PARAMETERS:
28 columns: number of columns for thumbnails
29 filter: regex to select images
30 show_text: default is 1 description is shown
31 any other means no description
32 show_date: default is 1 date info from exif header if available is shown
33 show_tools: default is 1 icon toolbar is show any other disables this
34 sort_by_name: default is 1, the images are sorted by name, but not if only_items is 1
35 sort_by_date: default is 0, if set to 1 the images are sorted to the modification time
36 sort_by_alias default is 0, if set to 1 and only_items set to 1 it is used to order the images by the alias name
37 reverse_sort: default is 0, if set to 1 the file list is reversed
38 any other means no description
39 mode: default is 1 this means description below the image
40 any other number means description right of image
41 only_items: default is 0 if it is set to 1 only images which are described in listitem are shown
42 dependend on the order of the items
43 template_itemlist: default is 0, if set to 1 an item list is shown which could be copied into the script.
44 album: default is 0 if set to 1 only the first image of a series is shown but slideshow over all images
45 album_name: useful for album. default is 'album' use it as short name for the album.
46 front_image: Useful for album. default is ''. The first image is shown in front of the album and slideshow.
47 If set to an existing image name this is shown in front of album and slideshow.
48 The slide show could start by this somewhere.
49 border_thick: default is 1 this is the thickness in pixeln of the outer frame
50 renew: default is 0 if set to 1 then all selected thumbnails_* and webnails_* removed.
51 Afterwards they are new created.
52 thumbnail_width: default is 128
53 webnail_width: default is 640
54 text_width: default is 140
55 image_for_webnail default is 0 if set to 1 then the image is shown as preview and not the webnail
56 help: default is 0 if set a copy of the CALLING SEQUENCE is shown,
57 (there are some new ideas around to show help to an user so this will be later replaced)
58 sequence_name: (ONLY POSIX) default is '' if set this name is used for an image
59 sequence with an duration of one image per second.
60 While for gif and png files the fli format is used a mpg file is created for jpegs.
61 For more info see the description on PROCEDURE below
62 Because mpeg creation could be take much cpu time this is done as background process.
63 sequence_type: (ONLY POSIX) default is '' by this for png/gif fli format and the original image is used
64 and for jpg the mpeg format and the webnails are used. It could be set to fli or mpg.
65 If it is set to mpg then always the original images are used.
66 mpg creation takes much more time and the file could be much bigger as a standard fli file
67
68
69 OPTIONAL INPUTS:
70 itemlist : if it is used and only_items is 1 then only the images in this list are ahown.
71 The alias text is used as description of the image instead of the file name
72
73
74 EXAMPLE:
75 = GalleryTest =
76
77 == all images shown, one is decribed ==
78 {{{
79 { { {
80 #!Gallery2
81 * [100_1185.JPG Bremen, SpaceCenter]
82 } } }
83 }}}
84
85 Result: [[BR]]
86 {{{
87 #!Gallery2
88 * [100_1185.JPG Bremen, SpaceCenter]
89 }}}
90
91 == only thumbnails and only_items ==
92 {{{
93 { { {
94 #!Gallery2 show_text=0,show_tools=0,show_date=0,columns=2,only_items=1
95 * [100_1185.JPG Bremen, SpaceCenter]
96 * [100_1194.JPG Bremen]
97 } } }
98 }}}
99
100 Result: [[BR]]
101 {{{
102 #!Gallery2 show_text=0,show_tools=0,show_date=0,columns=2,only_items=1
103 * [100_1185.JPG Bremen, SpaceCenter]
104 * [100_1194.JPG Bremen]
105 }}}
106
107 == only_items by two columns and text right ==
108
109 {{{
110 { { {
111 #!Gallery2 mode=2,columns=2,only_items=1
112 * [100_1185.JPG Bremen, SpaceCenter]
113 * [100_1194.JPG Bremen]
114 } } }
115 }}}
116
117 Result: [[BR]]
118 {{{
119 #!Gallery2 mode=2,columns=2,only_items=1
120 * [100_1185.JPG Bremen, SpaceCenter]
121 * [100_1194.JPG Bremen, behind SpaceCenter]
122 }}}
123
124 ----
125
126 == only_items by two columns, date supressed ==
127
128 {{{
129 { { {
130 #!Gallery2 columns=2,only_items=1,show_date=0
131 * [100_1185.JPG Bremen, SpaceCenter]
132 * [100_1194.JPG Bremen, behind SpaceCenter]
133 } } }
134 }}}
135
136 Result: [[BR]]
137 {{{
138 #!Gallery2 columns=2,only_items=1,show_date=0
139 * [100_1185.JPG Bremen, SpaceCenter]
140 * [100_1194.JPG Bremen, behind SpaceCenter]
141 }}}
142
143
144 == filter regex used, mode 2, icons and date supressed, one column and border_thick=5 ==
145 {{{
146 { { {
147 #!Gallery2 columns=1,filter=100_118[0-5],mode=2,show_date=0,show_tools=0,border_thick=5
148 } } }
149 }}}
150
151 Result: [[BR]]
152 {{{
153 #!Gallery2 columns=1,filter=100_118[0-7],mode=2,show_date=0,show_tools=0,border_thick=5
154 }}}
155
156 == other macro calls ==
157 {{{
158 { { {
159 #!Gallery2 only_items=1,show_date=0
160 * [100_1189.JPG [[MiniPage(||Bremen||SpaceCenter||\n|| ||SpaceJump||)]]]
161 } } }
162 }}}
163
164 Result: [[BR]]
165 {{{
166 #!Gallery2 only_items=1,show_date=0
167 * [100_1189.JPG [[MiniPage(||Bremen||SpaceCenter||\n|| ||SpaceJump||)]]]
168 }}}
169
170 == renew means always new thumbnails and webnails of selection ==
171 {{{
172 { { {
173 #!Gallery2 only_items=1,show_date=0,show_tools=0,renew=1
174 * [100_1189.JPG [[MiniPage(||["Bremen"]||SpaceCenter||\n|| ||SpaceJump||)]]]
175 } } }
176 }}}
177
178 Result: [[BR]]
179 {{{
180 #!Gallery2 only_items=1,show_date=0,renew=1
181 * [100_1189.JPG [[MiniPage(||["Bremen"]||SpaceCenter||\n|| ||SpaceJump||)]]]
182 }}}
183
184 == template_itemlist ==
185 {{{
186 { { {
187 #!Gallery2 template_itemlist=1
188 * [100_1185.JPG Bremen, SpaceCenter]
189 } } }
190 }}}
191
192 Result: [[BR]]
193 {{{
194 #!Gallery2 template_itemlist=1
195 * [100_1185.JPG Bremen, SpaceCenter]
196 }}}
197
198 == help to show Calling Sequence ==
199 {{{
200 { { {
201 #!Gallery2 help=1
202 } } }
203 }}}
204
205 Result: [[BR]]
206 {{{
207 #!Gallery2 help=1
208 }}}
209
210
211 PROCEDURE:
212
213 HOWTO:
214 Download some images to a page and start with the examples.
215 Aliasing of the filenames are done by adding an itemlist, see example.
216
217 NEEDS:
218 This routine requires the Action macro gallery2Image which is used to rotate or delete a
219 selected image. The actual version is gallery2image-1.3.5-10.py.
220 Only users which have the rights to delete are able to execute this action macro.
221 The icons of these are only shown if you have enough rights.
222 Furthermore it requires:
223 * the PIL (Python Imaging Library).
224 * the EXIF routine from http://home.cfl.rr.com/genecash/digital_camera.html
225
226 and on POSIX systems:
227 * ppm2fli: see http://vento.pi.tu-berlin.de/fli.html
228 * jpeg2mpeg: see http://www.stillhq.com/jpeg2mpeg
229
230 At the moment I have added the EXIF routine to the parsers dir.
231 It's not the best place but during developing it is nice to have it there
232 If you put it to another place you have to change the line
233 from MoinMoin.parser import EXIF too.
234
235 The gallery2image macro does not take care on the EXIF header. This is lost by rotating.
236 If a file is deleted by this macro it is moved to a bak file.
237
238 ABOUT FLI/MPG:
239 The sequence feature is at the moment only for servers using posix platforms available.
240 If one has an idea how to do it on other platforms I like to implement it.
241
242 Sometimes people want to have the image sequence in one file playable without internet.
243 Scientific data could be best animated by the common flc/fli format which was
244 introduced by autodesk.
245 If png/gif files are used fli output is generated.
246 These files could be played by quicktime, mplayer, xanim and several others
247 For additional informations on this format look at:
248 http://woodshole.er.usgs.gov/operations/modeling/flc.html
249 http://vento.pi.tu-berlin.de/fli.html
250 The largest X and largest Y dimension is used and the images are centered to these.
251 The maximum size is 1280x1024 for this format so it is scaled to this if the images are larger
252 It is not the best for true color images if they have very different color tables.
253 Creating a sequence in this format does not take much cpu time while it could be very
254 long time for a mpg file.
255
256 If the parameter sequnece_name is used with jpg then the webnails of the images are used,
257 because they are normally in a size defined viewable on screen.
258 The files are written into a mpg file using these libraries:
259 Follow this inforamtion for installation at http://www.stillhq.com/jpeg2mpeg
260 Because mpeg creation with a duration of 1 second does take a lot of cpu time this is
261 done as background process
262 You have to take care yourself on equal dimensions of all images in a sequence of jpg files
263
264 If you don't like the defaults you could ommit them by using the sequence_type parameter.
265 By this it is possible to write fli files from jpg or mpg files from png/gif files.
266 Always if choosen the original images are used for mpg creation.
267 But if they are larger as 1280x1024 they are scaled down
268 Probably you could got poor quality if you try mpg for png/gif files.
269 The resize mechanism is not used for jpg files by this parameter the original files ae used.
270
271
272 It does not go well if you mix up in a call png, gif and jpg files.
273
274 GENERAL:
275 Please remove the Version number from the code!
276
277 If you want to upload many files at once please look
278 at FeatureRequests/UploadMultipleAttachmentFiles/RulesForUnzip
279
280 RESTRICTIONS:
281 The mnovie mode is at the moment implemented for POSIX only.
282 As soon as I know how to do it on other platforms I like to add it
283 If you rotate an image at the moment the exif is destroyed. PIL ignores the exif header.
284 This is not a quite big problem normally files with an EXIF header are right rotated.
285
286 Required Images:
287 I have put them to wiki/img/ dir. The icons were created by me. License: GPL
288
289 attachment:to_bak.png
290 attachment:to_left.png
291 attachment:to_right.png
292 attachment:to_slide.png
293 attachment:to_full.png
294
295 HISTORY:
296 While recognizing how to write MiniPage I got the idea to write a Gallery Parser.
297 We have used in our wikis in the past the Gallery macro of SimonRyan.
298 I have tried to modify it a bit to change it for 1.3 but my python skills weren't enough
299 or it was easier to write it completly new.
300 So this one shows now a way how a Gallery could be used by the parser and an action Macro.
301 Probably it is a good example for others who like to know how to do this
302
303 MODIFICATION HISTORY:
304 Version 1.3.3.-1
305 @copyright: 2005 by Reimar Bauer (R.Bauer@fz-juelich.de)
306 @license: GNU GPL, see COPYING for details.
307 2005-03-26: Version 1.3.3-2 keyword renew added
308 creation of thumbnails and webnails in two calls splitted
309 Version 1.3.3-3 bug fixed if itemlist is given to describe only some of the images
310 but only_items is not set to 1
311 Example code changed
312 2005-03-27: Version 1.3.3-4 Action macro added and the form to call it. User which have rights to delete
313 could use the functions of gallery2Image.
314 2005-08-03: Version 1.3.3-5 theme path for icons corrected and a platform independent path joining
315 os.unlink removed as suggested by CraigJohnson
316 sort_by_name is default if not only_items is 1
317 optional sort_by_date could be used
318 keyword template_itemlist added
319 keyword help added
320 extra frame by mode=2 removed
321 2005-08-06: Version 1.3.5-6 slideshow mode added
322 keyword image_for_webnail added
323 2005-08-13: Version 1.3.5-7 syntax changed from GET to POST
324 forms instead of links
325 filenames from images submitted to gallery2image too
326 new keyword sort_by_alias
327 internal code clean up
328 this version needs: gallery2image-1.3.5-5.py
329 2005-08-14: Version 1.3.5-8 (TW) cleanup
330 2005-08-14: Version 1.3.5-9 html code for tables changed
331 because of the ugly extra space of form elements
332 div tag removed so now we use the page style
333 slide show action goes to right webnail now
334 this version needs: gallery2image-1.3.5-5.py
335 2005-08-17: Version 1.3.5-10 html code separated in functions
336 structure of code changed, now you see the thumbnails after creation
337 bug removed if quote is given but file does not exist
338 2005-09-02: Version 1.3.5-11 keyword album, album_name and front_image added
339 image urls changed to complete server url
340 2005-11-12: Version 1.3.5-12 bug fixed for image_for_webnail=1
341 bug fixed at last cell table end tr instead of td
342 bug fixed don't render a filename as WikiName
343 bug fixed " is allowed in alias name
344 bug fixed ' is allowed in alias name
345 bug fixed linebreak by a space in alias
346 not quite a bug but makes it very difficult to code in
347 gallery2image so additional id removed in alias name
348 2005-11-17: Version 1.3.5-13 implementation of sequence video clips at first step for posix only
349 sequence_type could be used to ommit the autoselection
350 fli/flc files are used for gif and png files, mpeg files for
351 jpeg files. Duration on both is 1 image/second
352 feature added of recognising the right url pattern (http opr https)
353 """
354 Dependencies = ['time'] # do not cache
355 from MoinMoin.action import AttachFile
356 from MoinMoin import wikiutil, config
357 from MoinMoin.Page import Page
358
359 import os, string, re, Image, StringIO, codecs
360
361 try:
362 from MoinMoin.parser import EXIF
363 except ImportError:
364 EXIF = None
365
366 from MoinMoin.parser import wiki
367
368 class Parser:
369
370 def __init__(self, raw, request, **kw):
371 self.sort_by_date = '0'
372 self.sort_by_name = '1'
373 self.sort_by_alias = '0'
374 self.album = '0'
375 self.album_name = 'album'
376 self.front_image = ''
377 self.template_itemlist = '0'
378 self.reverse_sort = '0'
379 self.border_thick = '1'
380 self.columns = '4'
381 self.filter = '.'
382 self.mode = '1'
383 self.help = '0'
384 self.show_text = '1'
385 self.show_date = '1'
386 self.show_tools = '1'
387 self.only_items = '0'
388 self.image_for_webnail = '0'
389 self.renew = '0'
390 self.thumbnail_width = '128'
391 self.webnail_width = '640'
392 self.text_width = '140'
393 self.sequence_name = ''
394 self.sequence_type = ''
395
396 for arg in kw.get('format_args','').split(','):
397
398 if arg.find('=') > -1:
399 key, value=arg.split('=')
400 setattr(self, key, wikiutil.escape(value, quote=1))
401
402
403 self.width = str(int(self.thumbnail_width) +
404 int(self.text_width))
405
406 self.raw = raw
407 self.request = request
408 self.form = request.form
409 self._ = request.getText
410
411 self.outer_table_style = ' border="%s"' % self.border_thick
412 self.inner_table_style = ' style="border-style:none; margin=10px"'
413 self.td_style = ' align="center" style="padding:0; margin:2px2px; border-style:none"'
414
415 def show_tools_restricted(self, this_target):
416 if not self.request.user.may.delete(self.pagename):
417 return ''
418
419 return '''
420 <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
421 <td%(style)s>
422 <input type="hidden" name="action" value="gallery2image">
423 <input type="hidden" name="do" value="RL">
424 <input type="hidden" name="target" value="%(this_target)s">
425 <input type="image" value="submit" src="%(htdocs)s/img/to_left.png" title="rotate to left">
426 </td>
427 </form>
428 <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
429 <td%(style)s>
430 <input type="hidden" name="action" value="gallery2image">
431 <input type="hidden" name="do" value="RR">
432 <input type="hidden" name="target" value="%(this_target)s">
433 <input type="image" value="submit" src="%(htdocs)s/img/to_right.png" title="rotate to right" >
434 </td>
435 </form>
436 <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
437 <td%(style)s>
438 <input type="hidden" name="action" value="gallery2image">
439 <input type="hidden" name="do" value="RM">
440 <input type="hidden" name="target" value="%(this_target)s">
441 <input type="image" value="submit" src="%(htdocs)s/img/to_bak.png" title="move to bak" >
442 </td>
443 </form>''' % {
444 'baseurl': self.request.getBaseURL(),
445 'style': self.td_style,
446 'htdocs': self.request.cfg.url_prefix,
447 "pagename":self.quoted_pagename,
448 "this_target":this_target}
449
450 def tools_html(self, idx):
451
452 this_image = self.webimg[idx]
453
454 text='''
455 <TABLE align="center" width="%(thumbnail_width)s"%(tablestyle)s>
456 <TR>
457 <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
458 <td%(style)s>
459 <input type="hidden" name="action" value="AttachFile">
460 <input type="hidden" name="do" value="get">
461 <input type="hidden" name="target" value='%(this_target)s'>
462 <input type="image" value="submit" src="%(htdocs)s/img/to_full.png" title="load image">
463 </td>
464 </form>
465 <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
466 <td%(style)s>
467 <input type="hidden" name="action" value="gallery2image">
468 <input type="hidden" name="do" value="VS">
469 <input type="hidden" name="full" value='%(full)s'>
470 <input type="hidden" name="alias" value='%(description)s'>
471 <input type="hidden" name="target" value='%(target)s`>
472 <input type="hidden" name="exif_date" value='%(exif_date)s'>
473 <input type="image" value="submit" src="%(htdocs)s/img/to_slide.png" title="slide_show" >
474 </td>
475 </form>
476 %(show_tools_restricted)s
477 </TR>
478 </TABLE>''' % {
479 'baseurl': self.request.getScriptname(),
480 "pagename":self.quoted_pagename,
481 "htdocs":self.request.cfg.url_prefix,
482 "tablestyle":self.inner_table_style,
483 "style":self.td_style,
484 "thumbnail_width":self.thumbnail_width,
485 "full":self.full[idx] + ',' + ','.join(self.full),
486 "description":self.description[idx] + '!,!' + '!,!'.join(self.description),
487 "exif_date":self.to_htmltext(self.exif_date[idx] + ',' +
488 ','.join(self.exif_date)),
489 "target":self.webimg[idx] + ',' + ','.join(self.webimg),
490 "this_target":self.webimg[idx],
491 "thumbnail":AttachFile.getAttachUrl(self.quoted_pagename,
492 self.thumb[idx],
493 self.request),
494
495 "show_tools_restricted":self.show_tools_restricted(this_image)
496 }
497
498 return text
499
500 def show_alias_mode2(self, idx):
501 if self.show_text == '1':
502 return '''
503 <td valign="top" width="%(text_width)s">
504 %(this_alias)s
505 </td>''' % {
506 "this_alias":self.description[idx],
507 "text_width":self.text_width}
508 else:
509 return ''
510
511 def show_date_mode2(show_date, idx):
512 if show_date == '1':
513 return '''
514 <td>
515 <p>%(this_exif_date)s</p>
516 </td>''' % {
517 "this_exif_date": self.to_htmltext(self.exif_date[idx]) }
518 else:
519 return '';
520
521 def show_tools_mode2(self, idx):
522 if show_tools == '1' :
523 return "<td align=""center""> %s </td>" % self.tools_html(idx)
524 else:
525 return ''
526
527
528
529 def mode2_html(self, idx):
530 text='''
531 <tr valign="center">
532 <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
533 <td align="center" valign="center" width="%(thumbnail_width)s">
534 <input type="hidden" name="action" value="gallery2image">
535 <input type="hidden" name="do" value="VS">
536 <input type="hidden" name="full" value='%(full)s'>
537 <input type="hidden" name="alias" value='%(description)s'>
538 <input type="hidden" name="exif_date" value='%(exif_date)s'>
539 <input type="hidden" name="target" value='%(target)s'>
540 <input type="image" value="submit" src="%(thumbnail)s">
541 </td>
542 </form>
543 %(alias_html)s
544 </tr>
545 <tr>%(tools_html)s%(date_html)s</tr>'''% {
546 "baseurl": request.getScriptname(),
547 "pagename":self.quoted_pagename,
548 "thumbnail_width":self.thumbnail_width,
549 "full":self.full[idx] + ',' + ','.join(self.full),
550 "description":self.description[idx] + '!,!' + '!,!'.join(self.description),
551 "exif_date":self.to_htmltext(self.exif_date[idx] + ',' +
552 ','.join(self.exif_date)),
553 "target":self.webimg[idx] + ',' + ','.join(self.webimg),
554 "thumbnail":AttachFile.getAttachUrl(self.quoted_pagename, self.thumb[idx],
555 self.request),
556 "tools_html":self.show_tools_mode2(idx),
557 "date_html": self.show_date_mode2(idx),
558 "alias_html": self.show_alias_mode2(idx)
559 }
560
561 return text
562
563 def show_tools_mode1(self, idx):
564 if self.show_tools == '1' :
565 text="<tr><td align=""center""%(style)s>%(tools)s </td></tr>" % {
566 "style":self.td_style,
567 "tools":self.tools_html(idx)}
568 else:
569 text=''
570 return text
571
572 def show_date_mode1(self, idx):
573 if self.show_date == '1':
574 return '''
575 <TR>
576 <td%(style)s>%(this_exif_date)s</td>
577 </TR>''' % {
578 "style":self.td_style,
579 "this_exif_date": self.to_htmltext(self.exif_date[idx])}
580 else:
581 return ''
582
583 def show_alias_mode1(self, idx):
584 if self.show_text == '1':
585 return '''
586 <TR>
587 <td width="%(thumbnail_width)s" %(style)s> %(this_alias)s</td>
588 </TR>''' % {
589 "thumbnail_width": self.thumbnail_width,
590 "style":self.td_style,
591 "this_alias": self.description[idx]}
592 else:
593 return ''
594
595 def mode1_html(self, idx):
596
597 text='''
598 <table width="%(thumbnail_width)s" align="center" valign="center"%(style)s>
599 <TR align="center" valign="center">
600 <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
601 <td align="center" valign="middle" width="%(thumbnail_width)s"
602 %(tdstyle)s>
603 <input type="hidden" name="action" value="gallery2image">
604 <input type="hidden" name="do" value="VS">
605 <input type="hidden" name="full" value='%(full)s'>
606 <input type="hidden" name="alias" value='%(description)s'>
607 <input type="hidden" name="exif_date" value='%(exif_date)s'>
608 <input type="hidden" name="target" value='%(target)s'>
609 <input type="image" value="submit" src="%(thumbnail)s" >
610 </td>
611 </form>
612 </TR>
613 %(alias_html)s
614 %(date_html)s
615 %(tools_html)s
616 </table>'''% {
617 "tdstyle" : self.td_style,
618 "style" : self.inner_table_style,
619 "baseurl": self.request.getScriptname() ,
620 "pagename":self.quoted_pagename,
621 "full":self.full[idx] + ',' + ','.join(self.full),
622 "description":self.description[idx] + '!,!' + '!,!'.join(self.description),
623 "exif_date":self.to_htmltext(self.exif_date[idx] + ',' +
624 ','.join(self.exif_date)),
625 "target":self.webimg[idx] + ',' + ','.join(self.webimg),
626 "thumbnail":AttachFile.getAttachUrl(self.quoted_pagename, self.thumb[idx],
627 self.request),
628 "thumbnail_width":self.thumbnail_width,
629 "tools_html":
630 self.show_tools_mode1(idx),
631 "date_html":self.show_date_mode1(idx),
632 "alias_html":self.show_alias_mode1(idx)
633 }
634
635 return text
636
637 def get_files(self, path, files, quotes):
638 self.web = []
639 self.full = []
640 self.thumb = []
641 self.exif_date = []
642 self.imgtype = []
643 self.description = []
644
645
646 ddict={}
647 n=len(quotes['image'])
648 if n > 0 :
649 i = 0
650 for txt in quotes['image']:
651 ddict[txt]=quotes['alias'][i]
652 i += 1
653
654 self.video_type = ''
655 self.source_type = ''
656 for attfile in files:
657 # only files not thumb or webnails
658 if attfile.find('thumbnail_') == -1 and attfile.find('webnail_') == -1:
659 # only images
660 if wikiutil.isPicture(attfile):
661 self.description.append(ddict.get(attfile, attfile))
662 self.full.append(attfile)
663
664 fname, ext = os.path.splitext(attfile)
665 if ext in ('.gif', '.png'):
666 self.imgtype.append('PNG')
667 webnail = 'webnail_%s.png' % fname
668 thumbfile = 'thumbnail_%s.png' % fname
669 video_type = 'fli'
670 source_type = ext[1:]
671 else:
672 self.imgtype.append("JPEG")
673 webnail = 'webnail_%s.jpg' % fname
674 thumbfile = 'thumbnail_%s.jpg' % fname
675 video_type = 'mpg'
676 source_type = 'jpg'
677
678
679 infile = os.path.join(path, attfile)
680 if os.path.exists(infile):
681 self.web.append(webnail)
682 self.thumb.append(thumbfile)
683
684 #f = open(infile, 'rb')
685 #tags = EXIF.process_file(f)
686 #if tags.has_key('EXIF DateTimeOriginal'):
687 # date = str(tags['EXIF DateTimeOriginal'])
688 # date = date.replace(':', '-', 2)
689 #else:
690 # date = '--'
691 #self.exif_date.append(date)
692 #f.close()
693 self.exif_date.append('--')
694 if self.sequence_type:
695 self.video_type = self.sequence_type
696
697 def to_htmltext(self, text):
698
699 if text.find ("'"):
700 text = text.split("'")
701 text = '''.join(text)
702
703 return text
704
705 def to_wikiname(self, text):
706 ##taken from MiniPage
707 out=StringIO.StringIO()
708 self.request.redirect(out)
709 wikiizer = wiki.Parser(text.strip(),self.request)
710 wikiizer.format(self.formatter)
711 result=out.getvalue()
712 request.redirect()
713 del out
714
715 result = result.replace('<a id="line-1"></a>','')
716 result = result.replace('<p>','')
717 result = result.replace('</p>','')
718 result = result.strip()
719 return result
720
721
722 def get_quotes(self, formatter):
723 quotes = self.raw.split('\n')
724 quotes = [quote.strip() for quote in quotes]
725 quotes = [quote[2:] for quote in quotes if quote.startswith('* ')]
726
727 image=[]
728 text=[]
729
730 for line in quotes:
731 im, na=line[1:-1].split(' ',1)
732 na = na.strip()
733 na = to_htmltext(na)
734 na = to_wikiname(self.request,formatter,na)
735 text.append(na)
736 image.append(im.strip())
737
738 return {
739 'alias': text,
740 'image': image,
741 }
742
743 def print_help(self):
744 self.request.write('''
745 <br>
746 {{{<br>
747 #!Gallery2 [columns=columns],[filter=filter],[mode=mode],<br>
748 [show_text=show_text],[show_date=show_date], [show_tools=show_tools],<br>
749 [sort_by_name=sort_by_name],[sort_by_date=sort_by_date],[sort_by_alias=sort_by_alias]<br>
750 [reverse_sort=reverse_sort],<br>
751 [only_items=only_items],[template_itemlist=template_itemlist],<br>
752 [album=album],[album_name=album_name],[front_image=front_image],<br>
753 [thumbnail_width=thumbnail_width],[webnail_width=webnail_width],[text_width=text_width],<br>
754 [image_for_webnail=image_for_webnail],<br>
755 [sequence_name=sequence_name],[sequence_type=sequence_type]<br>
756 [border_thick=border_thick],[renew=renew],[help=help]<br>
757 * [image1.jpg alias]<br>
758 * [image2.jpg alias]<br>
759 }}}<br>''')
760
761 def format(self, formatter):
762
763 if self.help == '1':
764 self.print_help()
765 return
766
767 Dict = {}
768 quotes = self.get_quotes(formatter)
769 current_pagename=formatter.page.page_name
770 self.pagename = current_pagename
771 self.quoted_pagename = wikiutil.quoteWikinameURL(self.pagename)
772 attachment_path = AttachFile.getAttachDir(self.request, current_pagename, create=1)
773
774
775 if self.only_items == '1':
776 all_files=quotes['image']
777 result=[]
778 for attfile in all_files:
779 infile=os.path.join(attachment_path,attfile)
780 if os.path.exists(infile):
781 result.append(attfile)
782 all_files = result
783
784 if self.sort_by_alias == '1':
785 new_ordered_files=[]
786 alias_text=quotes['alias']
787
788 i=0
789 for attfile in all_files:
790 infile=os.path.join(attachment_path,attfile)
791 ft_file=str(os.path.getmtime(infile))+os.tmpnam()
792 Dict[alias_text[i]]=attfile
793 i += 1
794
795 keys = Dict.keys()
796 keys.sort()
797 for txt in keys:
798 new_ordered_files.append(Dict[txt])
799
800
801 all_files=new_ordered_files
802 Dict.clear()
803
804 else:
805 all_files=os.listdir(attachment_path)
806
807 result = []
808
809 for test in all_files:
810 if re.match(self.filter, test):
811 result.append(test)
812 all_files=result
813
814 if not all_files:
815 self.request.write("<br><br><h1>No matching image file found!</h1>")
816 return
817
818
819
820 if self.sort_by_name == '1' and self.only_items == '0':
821 all_files.sort()
822
823 if self.sort_by_date=='1':
824 for attfile in all_files:
825 infile=os.path.join(attachment_path,attfile)
826 ft_file=str(os.path.getmtime(infile))+os.tmpnam()
827 Dict[ft_file]=attfile
828
829 keys = Dict.keys()
830 keys.sort()
831 file_mdate=[]
832 for txt in keys:
833 file_mdate.append(Dict[txt])
834 all_files=file_mdate
835 Dict.clear()
836
837 if self.reverse_sort=='1':
838 all_files.reverse()
839
840
841 cells=[]
842 cell_name=[]
843 img=[]
844
845 self.get_files(attachment_path, all_files, quotes)
846
847 if self.template_itemlist == '1':
848 self.request.write('Copy the following listitems into the script. Replace alias with the label you want. Afterwards disable template_itemlist by setting it to 0:<BR>')
849 for attfile in self.full :
850 self.request.write(' * [%(attfile)s %(date)s]<br>\n' % {
851 'attfile' : attfile,
852 'date' : 'alias'
853 })
854
855
856 i = 0
857 z = 1
858 cols = int(self.columns)
859
860
861 n = len(self.full)
862 if self.album == '0' :
863 self.request.write("<table%s>" % self.outer_table_style)
864 if self.mode == '1' or cols > 1 :
865 self.request.write('<TR valign="top">')
866 self.request.write('<TD%s>' % self.td_style)
867
868
869 if self.album == '1' :
870 if self.front_image == '' :
871 front_image = self.full[0]
872 else:
873 front_image = self.front_image
874 ii = 0
875 for tst in self.full :
876 if tst == front_image :
877 break
878 ii += 1
879
880
881 for attfile in self.full:
882 if self.album == '1' :
883 if tst == front_image :
884 i = ii
885
886
887 this_description=self.description[i]
888 this_exif_date=self.exif_date[i]
889 this_webnail=self.web[i]
890 this_imgtype=self.imgtype[i]
891 this_thumbfile=self.thumb[i]
892
893
894 thumbf=os.path.join(attachment_path,this_thumbfile)
895 webf=os.path.join(attachment_path,this_webnail)
896
897
898 if self.renew == '1':
899 if os.path.exists(thumbf):
900 os.unlink(thumbf)
901 if os.path.exists(webf):
902 os.unlink(webf)
903
904 if not os.path.exists(webf) or not os.path.exists(thumbf):
905 infile = os.path.join(attachment_path,attfile)
906 im = Image.open(infile)
907
908 if not os.path.exists(webf):
909 im.thumbnail((int(self.webnail_width),int(self.webnail_width)), Image.ANTIALIAS)
910 if self.image_for_webnail == '1' :
911 os.link(os.path.join(attachment_path,attfile),webf)
912 else:
913 im.save(webf, this_imgtype)
914 if not os.path.exists(thumbf):
915 im.thumbnail(((int(self.thumbnail_width)),((int(self.thumbnail_width)))),
916 Image.ANTIALIAS)
917 im.save(thumbf, this_imgtype)
918
919
920 if self.image_for_webnail == '1' :
921 this_webnailimg = attfile
922 self.webimg = self.full
923 else:
924 this_webnailimg = this_webnail
925 self.webimg = self.web
926
927
928 if self.mode == '1':
929 text = self.mode1_html(i)
930 self.request.write(''.join(text))
931
932 if self.mode == '2':
933 text = self.mode2_html(i)
934 if cols > 1 : self.request.write('<table valign="bottom">')
935 self.request.write(''.join(text))
936 if cols > 1 : self.request.write('</table>')
937
938 if self.mode == '1' or cols > 1:
939 if self.album == '0' :
940 if z < cols :
941 self.request.write('</TD>')
942 if z < n and i < n - 1 :
943 self.request.write('<TD%s>' % self.td_style)
944 if i == n - 1 :
945 self.request.write('</TR>')
946 else:
947 self.request.write('</TD>')
948 self.request.write('</TR>')
949 if i < n - 1 :
950 self.request.write('<TR valign="top">')
951 self.request.write('<TD%s>' % self.td_style)
952
953 i += 1
954 z += 1
955 if z > cols :
956 z = 1
957
958 if self.album == '1' :
959 self.request.write("%(n)s images (%(album_name)s)" % {"n": str(n), "album_name":self.album_name})
960 break
961 if self.album == '0' :
962 if i < n :
963 self.request.write('</TD>')
964 self.request.write('</TR>')
965 self.request.write('</table>')
966
967 ############################################
968 ##TODO: syntax change to formatter - later #
969 ############################################
970
971 if os.name != 'posix':
972 text = '<p><FONT color="Red" >sequence_name feature not defined for this platform: %(platform)s </FONT><p>' % {"platform":os.name}
973 self.request.write(text)
974 return
975
976 if self.sequence_name != '' :
977
978 ppm2fli = '/usr/local/bin/ppm2fli'
979 video_file = os.path.join(attachment_path,self.sequence_name)
980
981 if video_type == 'fli' :
982
983 if self.renew == '1':
984 if os.path.exists(video_file + '.fli'):
985 os.unlink(video_file + '.fli')
986 liste_file = '%(flcfile)s.txt' % { 'flcfile': video_file}
987 if not os.path.exists(video_file+'.fli'):
988
989 data = open(liste_file,'w')
990 x=[]
991 y=[]
992 for attfile in full :
993 file = os.path.join(attachment_path,attfile)
994 im = Image.open(file)
995 size = im.size
996 x.append(size[0])
997 y.append(size[1])
998 data.write("%(file)s\n" % {'file':attfile})
999 data.close()
1000
1001
1002 if max(x) > 1280.0:
1003 this_x = 1280.0
1004 f = this_x / max(x)
1005 this_y = max(y) * f
1006 else:
1007 this_x = max(x)
1008 this_y = max(y)
1009
1010 if this_y > 1024.0:
1011 new_y = 1024.0
1012 f = new_y /this_y
1013 this_y = new_y
1014 this_x = this_x * f
1015
1016 sz = '%(szx)sx%(szy)s' % { 'szx': long(this_x),
1017 'szy': long(this_y) }
1018 #wir mueesen in den pfad wechseln
1019 #wenn Zeit ist ppm12fli überarbeiten
1020 cmd = ''
1021 if source_type == 'png':
1022 cmd = 'cd "%(attachment_path)s" && %(ppm2fli)s -g %(sz)s -fpngtopnm -s 1000 %(flcfile)s.txt %(flcfile)s.fli > /dev/null ' % {
1023 'ppm2fli': ppm2fli,
1024 'flcfile': self.sequence_name ,
1025 'attachment_path': attachment_path,
1026 'sz': sz }
1027
1028 if source_type == 'gif':
1029 cmd = 'cd "%(attachment_path)s" && %(ppm2fli)s -g %(sz)s -fgiftopnm -s 1000 %(flcfile)s.txt %(flcfile)s.fli > /dev/null ' % {
1030 'ppm2fli': ppm2fli,
1031 'flcfile': self.sequence_name ,
1032 'attachment_path': attachment_path,
1033 'sz': sz }
1034
1035 if source_type == 'jpg':
1036
1037 cmd = 'cd "%(attachment_path)s" && %(ppm2fli)s -g %(sz)s -fjpegtopnm -s 1000 %(flcfile)s.txt %(flcfile)s.fli > /dev/null' % {
1038 'ppm2fli': ppm2fli,
1039 'flcfile': self.sequence_name ,
1040 'attachment_path': attachment_path,
1041 'sz': sz }
1042
1043
1044 if cmd != '':
1045 os.system(cmd)
1046
1047 if os.path.exists(video_file+'.fli'):
1048 if os.path.exists(liste_file):
1049 os.unlink(liste_file) # liste entfernen wenn file da ist
1050 dict = {}
1051 dict['src'] = AttachFile.getAttachUrl(current_pagename,'%(flcfile)s.fli' % {
1052 'flcfile': self.sequence_name},self.request)
1053 image_link = '%(flcfile)s.fli' % { 'flcfile':self.sequence_name}
1054
1055 self.request.write('<BR>')
1056 text = formatter.url(1,dict['src'] ) + image_link + formatter.url(0)
1057 self.request.write('Download this image sequence %(text)s for your archive' % { 'text': text})
1058
1059
1060 if video_type == 'mpg':
1061 # http://www.stillhq.com/jpeg2mpeg
1062 if self.renew == '1':
1063 if os.path.exists(video_file + '.mpg'):
1064 os.unlink(video_file + '.mpg')
1065 if not os.path.exists(os.path.join(attachment_path,'%(file)s.mpg' % {'file':self.sequence_name})):
1066 if self.sequence_type == 'mpg':
1067 liste = string.join(full,' ')
1068 else:
1069 liste = string.join(web,' ')
1070
1071 cmd = 'cd "%(attachment_path)s" && convert -delay 100 %(liste)s %(file)s.mpg > /dev/null &' % {
1072 'liste':liste,
1073 'file':self.sequence_name ,
1074 'attachment_path':attachment_path}
1075 os.system(cmd)
1076 self.request.write('<P><FONT color="Red" > MPEG creation as background process started </FONT></P>')
1077 if os.path.exists(os.path.join(attachment_path,'%(file)s.mpg' % {'file':self.sequence_name})):
1078 dict = {}
1079 dict['src'] = AttachFile.getAttachUrl(current_pagename,'%(file)s.mpg' % {
1080 'file': self.sequence_name},self.request)
1081
1082 image_link = '%(file)s.mpg' % { 'file':self.sequence_name}
1083
1084 self.request.write('<BR>')
1085 text = formatter.url(1,dict['src'] ) + image_link + formatter.url(0)
1086 self.request.write('Download this image sequence %(text)s for your archive' % { 'text': text})
1087
1088
1089 ## the sequence block needs to be refactored by better names
1090
1091
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.