Attachment 'Gallery2-1.3.5-14.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/Description 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 optional:
 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 on MoinMoin versions less 1.5.0
 278       at FeatureRequests/UploadMultipleAttachmentFiles/RulesForUnzip
 279 
 280     RESTRICTIONS:
 281       The movie 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/modern/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       2005-12-03: Version 1.3.5-14  bug fixed of to_slide tools function a wrong mark was used
 354                                     all output of convert or ppm2fli redirected to /dev/null
 355                                     it is not necessary to keep output in the webservers log file
 356                                     
 357 """
 358 Dependencies = []
 359 from MoinMoin.action import AttachFile
 360 from MoinMoin import wikiutil, config
 361 from MoinMoin.Page import Page
 362 
 363 import os, string, re, Image, StringIO, codecs
 364 
 365 from MoinMoin.parser import EXIF
 366 
 367 from MoinMoin.parser import wiki
 368 
 369 def server(request):
 370 
 371   if request.is_ssl:
 372      url_pattern = 'https'
 373   else:
 374      url_pattern = 'http'
 375         
 376   return "%(url_pattern)s://%(server_name)s:%(server_port)s" %   {
 377                              "url_pattern":url_pattern,
 378                              "server_name":request.server_name, 
 379                              "server_port":str(request.server_port)}
 380 
 381 def show_tools_restricted(pagename,this_target,thumbnail_width,full,alias,target,exif_date,request):
 382     if request.user.may.delete(pagename):
 383         return tools_restricted_html(pagename,this_target,thumbnail_width,full,alias,target,exif_date,request)
 384     else:
 385         return ''    
 386 
 387 def tools_restricted_html(pagename,this_target,thumbnail_width,full,alias,target,exif_date,request):
 388     text='''
 389                    <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
 390                         <td>
 391                             <input type="hidden" name="action" value="gallery2image">
 392                             <input type="hidden" name="do" value="RL">
 393                             <input type="hidden" name="target" value="%(this_target)s">
 394                             <input type="image" value="submit" src="%(server)s/wiki/modern/img/to_left.png" title="rotate to left">
 395                         </td>
 396                     </form>
 397                     <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
 398                         <td>
 399                             <input type="hidden" name="action" value="gallery2image">
 400                             <input type="hidden" name="do" value="RR">
 401                             <input type="hidden" name="target" value="%(this_target)s">
 402                             <input type="image"  value="submit" src="%(server)s/wiki/modern/img/to_right.png" title="rotate to right" >
 403                         </td>
 404                     </form>
 405                     <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
 406                         <td>
 407                             <input type="hidden" name="action" value="gallery2image">
 408                             <input type="hidden" name="do" value="RM">
 409                             <input type="hidden" name="target" value="%(this_target)s">
 410                             <input type="image" value="submit" src="%(server)s/wiki/modern/img/to_bak.png" title="move to bak" >
 411                         </td>
 412                     </form>''' %  {
 413             "server" : server(request), 
 414             'baseurl': request.getScriptname(),
 415             "pagename":pagename,
 416             "this_target":this_target}                   
 417     return text                
 418     
 419 def tools_html(pagename,this_image,thumbnail_width,full,alias,target,exif_date,request):
 420     text='''
 421             <TABLE align="center" width="%(thumbnail_width)s">
 422                 <TR>
 423                     <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
 424                         <td>
 425                             <input type="hidden" name="action" value="AttachFile">
 426                             <input type="hidden" name="do" value="get">
 427                             <input type="hidden" name="target" value='%(this_target)s'>
 428                             <input type="image" value="submit" src="%(server)s/wiki/modern/img/to_full.png" title="load image">
 429                         </td>
 430                     </form>
 431                     <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
 432                         <td>
 433                             <input type="hidden" name="action" value="gallery2image">
 434                             <input type="hidden" name="do" value="VS">
 435                             <input type="hidden" name="full" value='%(full)s'>
 436                             <input type="hidden" name="alias" value='%(alias)s'>
 437                             <input type="hidden" name="target" value='%(target)s'>
 438                             <input type="hidden" name="exif_date" value='%(exif_date)s'>
 439                             <input type="image" value="submit" src="%(server)s/wiki/modern/img/to_slide.png" title="slide_show" >
 440                        </td>
 441                     </form>
 442                     %(show_tools_restricted)s
 443                 </TR>
 444             </TABLE>'''   %  {
 445             "server" : server(request),
 446             'baseurl': request.getScriptname(),
 447             "pagename":pagename,
 448             "thumbnail_width":thumbnail_width,
 449             "full":full,
 450             "alias":alias,
 451             "exif_date":exif_date,
 452             "target":target,
 453             "this_target":this_image,
 454             "show_tools_restricted":show_tools_restricted(pagename,this_image,thumbnail_width,full,alias,target,exif_date,request)
 455             } 
 456    
 457     return text
 458 
 459 def show_alias_mode2(show_alias,thumbnail_width,this_alias,text_width):
 460    if show_alias == '1': 
 461        return '''
 462         <td valign="top" width="%(text_width)s">
 463            %(this_alias)s
 464         </td>''' % {
 465         "this_alias":this_alias,
 466         "text_width":text_width}
 467    else:
 468       return ''     
 469         
 470 def show_date_mode2(show_date,this_exif_date):    
 471    if show_date == '1': 
 472        return '''
 473    <td>
 474      <p>%(this_exif_date)s</p>
 475    </td>''' % {
 476    "this_exif_date":this_exif_date }
 477    else:
 478         return '';
 479                
 480 def show_tools_mode2(show_tools,pagename,this_target,thumbnail_width,full,alias,target,exif_date,request):  
 481     if show_tools == '1' :
 482         return "<td align=""center""> %s </td>" % tools_html(pagename,this_target,thumbnail_width,full,alias,target,exif_date,request)
 483     else:
 484         return ''
 485       
 486 
 487 
 488 def mode2_html(pagename,border_thick,width,thumbnail_width,text_width,full,this_image,alias,this_alias,exif_date,this_exif_date,target,this_target,submit,show_tools,show_date,show_alias,request):
 489     text='''
 490     <tr valign="center">
 491         <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
 492             <td align="center" valign="center" width="%(thumbnail_width)s">
 493                 <input type="hidden" name="action" value="gallery2image">
 494                 <input type="hidden" name="do" value="VS">
 495                 <input type="hidden" name="full" value='%(full)s'>
 496                 <input type="hidden" name="alias" value='%(alias)s'>
 497                 <input type="hidden" name="exif_date" value='%(exif_date)s'>
 498                 <input type="hidden" name="target" value='%(target)s'>
 499                 <input type="image" value="submit" src="%(server)s%(submit)s">
 500             </td>
 501         </form>
 502             %(alias_html)s 
 503     </tr>
 504     <tr>%(tools_html)s%(date_html)s</tr>'''%       {   
 505      "server" : server(request),
 506      "baseurl": request.getScriptname(),
 507      "pagename":pagename,
 508      "thumbnail_width":thumbnail_width,
 509      "full":full,
 510      "alias":alias,
 511      "exif_date":exif_date,
 512      "target":target,
 513      "submit":submit,
 514      "tools_html":show_tools_mode2(show_tools,pagename,this_image,thumbnail_width,full,alias,target,exif_date,request),
 515      "date_html": show_date_mode2(show_date,this_exif_date),
 516      "alias_html": show_alias_mode2(show_alias,thumbnail_width,this_alias,text_width)
 517      }
 518     
 519     return text
 520 
 521 def show_tools_mode1(show_tools,pagename,this_image,thumbnail_width,full,alias,target,exif_date,request):  
 522     if show_tools == '1' :
 523         text="<tr><td align=""center"">%s </td></tr>" % tools_html(pagename,this_image,thumbnail_width,full,alias,target,exif_date,request)
 524     else:
 525         text=''
 526     return text        
 527 
 528 def show_date_mode1(show_date,this_exif_date):    
 529    if show_date == '1': 
 530        return '''
 531     <TR>
 532         <td>%(this_exif_date)s</td>
 533     </TR>''' % {
 534     "this_exif_date":this_exif_date}
 535    else:
 536        return ''
 537 
 538 def show_alias_mode1(show_alias,thumbnail_width,this_alias,text_width):
 539    if show_alias == '1': 
 540        return '''
 541     <TR>
 542         <td align="left" width="%(thumbnail_width)s"> %(this_alias)s</td>
 543     </TR>''' % {
 544     "thumbnail_width": thumbnail_width,
 545     "this_alias":this_alias}
 546    else:
 547        return ''     
 548        
 549 def mode1_html(pagename,border_thick,width,thumbnail_width,text_width,full,this_image,alias,this_alias,exif_date,this_exif_date,target,this_target,submit,
 550                show_tools,show_date,show_alias,request):
 551    text='''
 552 <table width="%(thumbnail_width)s" align="center" valign="center">
 553     <TR align="center" valign="center">
 554         <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
 555             <td align="center" valign="middle" width="%(thumbnail_width)s">
 556                 <input type="hidden" name="action" value="gallery2image">
 557                 <input type="hidden" name="do" value="VS">
 558                 <input type="hidden" name="full" value='%(full)s'>
 559                 <input type="hidden" name="alias" value='%(alias)s'>
 560                 <input type="hidden" name="exif_date" value='%(exif_date)s'>
 561                 <input type="hidden" name="target" value='%(target)s'>
 562                 <input type="image" value="submit" src="%(server)s%(submit)s" >
 563             </td>
 564         </form>
 565     </TR>
 566       %(alias_html)s
 567       %(date_html)s
 568       %(tools_html)s
 569 </table>'''%       {  
 570      "server" : server(request), 
 571      "baseurl": request.getScriptname() ,
 572      "pagename":pagename,
 573      "full":full,
 574      "alias":alias,
 575      "exif_date":exif_date,
 576      "target":target,
 577      "submit":submit,
 578      "thumbnail_width":thumbnail_width,
 579      "tools_html": show_tools_mode1(show_tools,pagename,this_image,thumbnail_width,full,alias,target,exif_date,request),  
 580      "date_html":show_date_mode1(show_date,this_exif_date),
 581      "alias_html": show_alias_mode1(show_alias,thumbnail_width,this_alias,text_width)
 582      }
 583 
 584    return text
 585 
 586 def get_files(kw,path,files,quotes,request):
 587     web=[]
 588     full=[]
 589     thumb=[]
 590     exif_date=[]
 591     img_type=[]
 592     description=[]
 593     
 594     
 595     ddict={}
 596     n=len(quotes['image'])
 597     if n > 0 :
 598         i = 0
 599         for txt in quotes['image']:
 600             ddict[txt]=quotes['alias'][i]
 601             i += 1    
 602             
 603     video_type = ''
 604     source_type = ''
 605     for attfile in files:
 606         # only files not thumb or webnails
 607         if attfile.find('thumbnail_') == -1 and attfile.find('webnail_') == -1:
 608             # only images
 609             if wikiutil.isPicture(attfile):
 610                 description.append(ddict.get(attfile, attfile))
 611                 full.append(attfile)
 612                 
 613                 fname, ext = os.path.splitext(attfile)
 614                 if ext in ('.gif', '.png'):
 615                     img_type.append('PNG')
 616                     webnail = 'webnail_%s.png' % fname
 617                     thumbfile = 'thumbnail_%s.png' % fname
 618                     video_type = 'fli'
 619                     source_type = ext[1:]
 620                 else:
 621                     img_type.append("JPEG")
 622                     webnail = 'webnail_%s.jpg' % fname
 623                     thumbfile = 'thumbnail_%s.jpg' % fname
 624                     video_type = 'mpg'
 625                     source_type = 'jpg'    
 626                 
 627                     
 628                 infile = os.path.join(path, attfile)
 629                 if os.path.exists(infile):
 630                      web.append(webnail)
 631                      thumb.append(thumbfile)
 632                      
 633                 f = open(infile, 'rb')
 634                 tags = EXIF.process_file(f)
 635                 if tags.has_key('EXIF DateTimeOriginal'):
 636                     date = str(tags['EXIF DateTimeOriginal'])
 637                     date = date.replace(':', '-', 2)
 638                 else:
 639                     date = '--'
 640                 exif_date.append(date)
 641                 f.close() 
 642     if kw['sequence_type'] != '': 
 643           video_type = kw['sequence_type']
 644           
 645     return thumb,web,full,video_type, exif_date,img_type,source_type,description  
 646     
 647 def to_htmltext(text):        
 648   
 649     if text.find ("'"):   
 650         text = text.split("'")
 651         text = '&#39;'.join(text)
 652        
 653     return text
 654 
 655 def to_wikiname(request,formatter,text):
 656   ##taken from MiniPage
 657     out=StringIO.StringIO()
 658     request.redirect(out)
 659     wikiizer = wiki.Parser(text.strip(),request)
 660     wikiizer.format(formatter)
 661     result=out.getvalue()
 662     request.redirect()
 663     del out
 664     
 665    
 666     
 667     result = result.replace('<a id="line-1"></a>','')
 668     result = result.replace('<p>','')
 669     result = result.replace('</p>','')
 670     result = result.strip() 
 671     return result
 672     
 673         
 674 def get_quotes(self,formatter):
 675     quotes = self.raw.split('\n')
 676     quotes = [quote.strip() for quote in quotes]
 677     quotes = [quote[2:] for quote in quotes if quote.startswith('* ')]
 678 
 679     
 680     image=[]
 681     text=[]
 682     
 683     for line in quotes:
 684         im, na=line[1:-1].split(' ',1)
 685         na = na.strip()
 686         na = to_htmltext(na)
 687         na = to_wikiname(self.request,formatter,na)
 688         text.append(na)
 689         image.append(im.strip())
 690 
 691     return {
 692         'alias': text,
 693         'image': image,
 694     }
 695 
 696 
 697 
 698 class Parser:
 699         
 700     def __init__(self, raw, request, **kw):
 701         self.raw = raw
 702         self.request = request
 703         self.form = request.form
 704         self._ = request.getText
 705         self.kw = {
 706             'sort_by_date': '0',
 707             'sort_by_name': '1',
 708             'sort_by_alias': '0',
 709             'album': '0',
 710             'album_name': 'album',
 711             'front_image':'',
 712             'template_itemlist': '0', 
 713             'reverse_sort': '0',
 714             'border_thick': '1',
 715             'columns': '4',
 716             'filter': '.',
 717             'mode': '1',
 718             'help': '0',
 719             'show_text': '1',
 720             'show_date': '1',
 721             'show_tools': '1',
 722             'only_items': '0',
 723             'image_for_webnail': '0',
 724             'renew': '0',
 725             'thumbnail_width': '128',
 726             'webnail_width': '640',
 727             'text_width': '140',
 728             'sequence_name' : '',
 729             'sequence_type' : '',
 730         }
 731        
 732 
 733         for arg in kw.get('format_args','').split(','):
 734 
 735             if arg.find('=') > -1:
 736                 key, value=arg.split('=')
 737                 self.kw[key]=wikiutil.escape(value, quote=1)
 738 
 739 
 740         self.kw['width']=str((int(self.kw['thumbnail_width'])+int(self.kw['text_width'])))
 741 
 742 
 743     def format(self, formatter):
 744         kw=self.kw
 745         Dict = {}
 746         quotes=get_quotes(self,formatter)
 747         current_pagename=formatter.page.page_name
 748         attachment_path = AttachFile.getAttachDir(self.request, current_pagename, create=1)
 749  
 750         if kw['help'] == '1':
 751             self.request.write('''
 752 <br>
 753 {{{<br>
 754 #!Gallery2 [columns=columns],[filter=filter],[mode=mode],<br>
 755            [show_text=show_text],[show_date=show_date], [show_tools=show_tools],<br>
 756            [sort_by_name=sort_by_name],[sort_by_date=sort_by_date],[sort_by_alias=sort_by_alias]<br>
 757            [reverse_sort=reverse_sort],<br>
 758            [only_items=only_items],[template_itemlist=template_itemlist],<br>
 759            [album=album],[album_name=album_name],[front_image=front_image],<br>
 760            [thumbnail_width=thumbnail_width],[webnail_width=webnail_width],[text_width=text_width],<br>
 761            [image_for_webnail=image_for_webnail],<br>
 762            [sequence_name=sequence_name],[sequence_type=sequence_type]<br>
 763            [border_thick=border_thick],[renew=renew],[help=help]<br>
 764  * [image1.jpg alias]<br>
 765  * [image2.jpg alias]<br>
 766 }}}<br>''')
 767             return    
 768             
 769         
 770         if kw['only_items'] == '1':
 771             all_files=quotes['image']
 772             result=[]
 773             for attfile in all_files:
 774                 infile=os.path.join(attachment_path,attfile)
 775                 if os.path.exists(infile):
 776                    result.append(attfile) 
 777             all_files = result        
 778             
 779             if kw['sort_by_alias'] == '1':
 780                 new_ordered_files=[]
 781                 alias_text=quotes['alias']
 782                 
 783                 i=0
 784                 for attfile in all_files:
 785                     infile=os.path.join(attachment_path,attfile)
 786                     ft_file=str(os.path.getmtime(infile))+os.tmpnam()
 787                     Dict[alias_text[i]]=attfile
 788                     i += 1
 789        
 790                 keys = Dict.keys()   
 791                 keys.sort()
 792                 for txt in keys:
 793                     new_ordered_files.append(Dict[txt])
 794                    
 795 
 796                 all_files=new_ordered_files
 797                 Dict.clear()
 798            
 799         else:
 800             all_files=os.listdir(attachment_path)
 801 
 802         result = []
 803 
 804         for test in all_files:
 805            if re.match(kw['filter'], test):
 806               result.append(test)
 807         all_files=result
 808         
 809         if not all_files:
 810             self.request.write("<br><br><h1>No matching image file found!</h1>")
 811             return
 812             
 813         
 814 
 815         if kw['sort_by_name'] == '1' and kw['only_items'] == '0': 
 816             all_files.sort()
 817           
 818         if kw['sort_by_date']=='1': 
 819            for attfile in all_files:
 820                infile=os.path.join(attachment_path,attfile)
 821                ft_file=str(os.path.getmtime(infile))+os.tmpnam()
 822                Dict[ft_file]=attfile
 823                
 824            keys = Dict.keys()
 825            keys.sort()
 826            file_mdate=[]
 827            for txt in keys:
 828                file_mdate.append(Dict[txt])
 829            all_files=file_mdate
 830            Dict.clear()
 831          
 832         if kw['reverse_sort']=='1': 
 833              all_files.reverse()   
 834 
 835  
 836         cells=[]
 837         cell_name=[]
 838         img=[]
 839         
 840         thumb, web, full, video_type, exif_date, imgtype, source_type, description = get_files(kw, attachment_path, all_files, quotes, self.request)
 841         
 842         if kw['template_itemlist'] == '1':
 843             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>')
 844             for attfile in full : 
 845                 self.request.write(' * [%(attfile)s %(date)s]<br>' % {
 846                                    'attfile' : attfile,
 847                                    'date'    : 'alias'
 848                                     })
 849                                     
 850         
 851         i = 0  
 852         z = 1
 853         cols = int(kw['columns'])
 854         
 855            
 856         n = len(full)
 857         if  kw['album'] == '0' :
 858             self.request.write("<table align='center' border='%s' >" % self.kw['border_thick'])
 859             if kw['mode'] == '1' or cols > 1   :
 860                 self.request.write('<TR valign="bottom">')      
 861                 self.request.write('<TD>') 
 862  
 863                            
 864         if kw['album'] == '1' :
 865             if kw['front_image'] == '' :
 866                 front_image = full[0]  
 867             else:
 868                 front_image =  kw['front_image']   
 869             ii = 0
 870             for tst in full : 
 871                 if tst == front_image :
 872                     break 
 873                 ii += 1   
 874             
 875         
 876         for attfile in full :
 877             if  kw['album'] == '1' :
 878                 if tst == front_image :
 879                    i = ii
 880              
 881                                  
 882             this_description=description[i]
 883             this_exif_date=exif_date[i]
 884             this_webnail=web[i]
 885             this_imgtype=imgtype[i]
 886             this_thumbfile=thumb[i]
 887             
 888            
 889             thumbf=os.path.join(attachment_path,this_thumbfile)
 890             webf=os.path.join(attachment_path,this_webnail)            
 891             
 892             
 893             if kw['renew'] == '1':
 894                 if os.path.exists(thumbf):
 895                    os.unlink(thumbf)
 896                 if os.path.exists(webf):
 897                    os.unlink(webf)
 898                    
 899             if not os.path.exists(webf) or not os.path.exists(thumbf):
 900                 infile = os.path.join(attachment_path,attfile)
 901                 im = Image.open(infile)
 902                 
 903                 if not os.path.exists(webf):
 904                     im.thumbnail(((int(kw['webnail_width'])),((int(kw['webnail_width'])))), Image.ANTIALIAS)
 905                     if kw['image_for_webnail'] == '1' :
 906                        os.link(os.path.join(attachment_path,attfile),webf)
 907                     else:
 908                        im.save(webf, this_imgtype)
 909                 if not os.path.exists(thumbf):
 910                     im.thumbnail(((int(kw['thumbnail_width'])),((int(kw['thumbnail_width'])))),
 911                                    Image.ANTIALIAS)
 912                     im.save(thumbf, this_imgtype)       
 913                     
 914                     
 915             if kw['image_for_webnail'] == '1' :     
 916                  this_webnailimg = attfile
 917                  webimg = full
 918             else: 
 919                  this_webnailimg = this_webnail
 920                  webimg = web
 921                  
 922            
 923             if kw['mode'] == '1':
 924                 text = mode1_html(current_pagename,
 925                         kw['border_thick'],
 926                         kw['width'],
 927                         kw['thumbnail_width'],
 928                         kw['text_width'],
 929                         attfile + "," + ','.join(full),
 930                         attfile,
 931                         this_description + '!,!' + '!,!'.join(description),
 932                         this_description,
 933                         to_htmltext(this_exif_date + ',' + ','.join(exif_date)),
 934                         to_htmltext(this_exif_date),
 935                         this_webnailimg + ',' + ','.join(webimg),
 936                         this_webnailimg,
 937                         AttachFile.getAttachUrl(current_pagename, this_thumbfile, self.request),
 938                         kw['show_tools'],
 939                         kw['show_date'],
 940                         kw['show_text'],
 941                         self.request
 942                         )
 943                 self.request.write(''.join(text))    
 944                         
 945             if kw['mode'] == '2':
 946                 text = mode2_html(current_pagename,
 947                         kw['border_thick'],
 948                         kw['width'],
 949                         kw['thumbnail_width'],
 950                         kw['text_width'],
 951                         attfile + "," + ','.join(full),
 952                         attfile,
 953                         this_description + '!,!' + '!,!'.join(description),
 954                         this_description,
 955                         to_htmltext(this_exif_date + ',' + ','.join(exif_date)),
 956                         to_htmltext(this_exif_date),
 957                         this_webnailimg + ',' + ','.join(webimg),
 958                         this_webnailimg,
 959                         AttachFile.getAttachUrl(current_pagename, this_thumbfile, self.request),
 960                         kw['show_tools'],
 961                         kw['show_date'],
 962                         kw['show_text'],
 963                         self.request
 964                         )
 965             
 966                 if cols > 1 : self.request.write('<table valign="bottom">')
 967                 self.request.write(''.join(text))
 968                 if cols > 1 : self.request.write('</table>')
 969             
 970             if kw['mode'] == '1' or cols > 1:    
 971                 if kw['album'] == '0' :
 972                     if  z < cols :
 973                         self.request.write('</TD>')
 974                         if z <  n and  i < n - 1 :
 975                             self.request.write('<TD>')
 976                         if i == n - 1 :    
 977                             self.request.write('</TR>')
 978                     else: 
 979                         self.request.write('</TD>')
 980                         self.request.write('</TR>')
 981                         if i < n - 1 :
 982                             self.request.write('<TR valign="bottom">')
 983                             self.request.write('<TD>')
 984                             
 985             i += 1         
 986             z += 1
 987             if z > cols :
 988                 z = 1
 989             
 990             if kw['album'] == '1' :
 991                 self.request.write("%(n)s images (%(album_name)s)" % {"n": str(n), "album_name":kw['album_name']})
 992                 break
 993         if kw['album'] == '0' :        
 994             if i < n :
 995                 self.request.write('</TD>')
 996                 self.request.write('</TR>')
 997             self.request.write('</table>')       
 998             
 999         ############################################
1000         ##TODO: syntax change to formatter - later #
1001         ############################################
1002         
1003         if os.name !=  'posix':
1004            text = '<p><FONT color="Red" >sequence_name feature not defined for this platform: %(platform)s </FONT><p>' % {"platform":os.name}
1005            self.request.write(text)  
1006            return
1007             
1008         if  kw['sequence_name'] != '' :       
1009             
1010             ppm2fli = '/usr/local/bin/ppm2fli'
1011             video_file = os.path.join(attachment_path,kw['sequence_name']) 
1012             
1013             if video_type == 'fli' : 
1014                
1015                if kw['renew'] == '1':
1016                   if os.path.exists(video_file + '.fli'):
1017                      os.unlink(video_file + '.fli')
1018                liste_file = '%(flcfile)s.txt' % { 'flcfile': video_file}     
1019                if not os.path.exists(video_file+'.fli'):
1020                    
1021                    data = open(liste_file,'w')
1022                    x=[]
1023                    y=[]
1024                    for attfile in full :
1025                        file = os.path.join(attachment_path,attfile)   
1026                        im = Image.open(file)
1027                        size = im.size
1028                        x.append(size[0])
1029                        y.append(size[1])
1030                        data.write("%(file)s\n" % {'file':attfile})
1031                    data.close()
1032          
1033                    
1034                    if max(x) > 1280.0:
1035                       this_x = 1280.0
1036                       f = this_x / max(x)
1037                       this_y = max(y) *  f
1038                    else: 
1039                       this_x = max(x)
1040                       this_y = max(y)   
1041                       
1042                    if this_y > 1024.0:
1043                        new_y = 1024.0
1044                        f =   new_y /this_y
1045                        this_y = new_y
1046                        this_x =  this_x * f
1047                    
1048                    sz = '%(szx)sx%(szy)s' % { 'szx': long(this_x),
1049                                               'szy': long(this_y) }
1050 #wir mueesen in den pfad wechseln    
1051 #wenn Zeit ist ppm12fli überarbeiten                                  
1052                    cmd = ''
1053                    if source_type == 'png':         
1054                       cmd = 'cd "%(attachment_path)s" &&  %(ppm2fli)s  -g %(sz)s -fpngtopnm -s 1000 %(flcfile)s.txt %(flcfile)s.fli > /dev/null ' % {
1055                                  'ppm2fli': ppm2fli,
1056                                  'flcfile': kw['sequence_name'] ,
1057                                  'attachment_path': attachment_path,
1058                                  'sz': sz }
1059                                  
1060                    if source_type == 'gif':             
1061                       cmd = 'cd "%(attachment_path)s" && %(ppm2fli)s  -g %(sz)s -fgiftopnm -s 1000 %(flcfile)s.txt %(flcfile)s.fli > /dev/null ' % {
1062                                  'ppm2fli': ppm2fli,
1063                                  'flcfile': kw['sequence_name'] ,
1064                                  'attachment_path': attachment_path,
1065                                  'sz': sz }
1066                    
1067                    if source_type == 'jpg':    
1068                               
1069                       cmd = 'cd "%(attachment_path)s" && %(ppm2fli)s  -g %(sz)s -fjpegtopnm -s 1000 %(flcfile)s.txt %(flcfile)s.fli 1> /dev/null 2> /dev/null' % {
1070                                  'ppm2fli': ppm2fli,
1071                                  'flcfile': kw['sequence_name'] ,
1072                                  'attachment_path': attachment_path,
1073                                  'sz': sz }
1074                      
1075                            
1076                    if cmd != '':
1077                       os.system(cmd)    
1078                
1079                if os.path.exists(video_file+'.fli'):
1080                   if os.path.exists(liste_file):
1081                      os.unlink(liste_file) # liste entfernen wenn file da ist
1082                   dict = {}
1083                   dict['src'] = AttachFile.getAttachUrl(current_pagename,'%(flcfile)s.fli'  % { 
1084                      'flcfile': kw['sequence_name']},self.request)
1085                   image_link = '%(flcfile)s.fli' % { 'flcfile':kw['sequence_name']}
1086                
1087                   self.request.write('<BR>')
1088                   text = formatter.url(1,dict['src'] ) + image_link + formatter.url(0)
1089                   self.request.write('Download this image sequence %(text)s for your archive' % { 'text': text})
1090             
1091                       
1092             if video_type == 'mpg':      
1093             #  http://www.stillhq.com/jpeg2mpeg
1094                if kw['renew'] == '1':
1095                   if os.path.exists(video_file + '.mpg'):
1096                      os.unlink(video_file + '.mpg')
1097                if not os.path.exists(os.path.join(attachment_path,'%(file)s.mpg' % {'file':kw['sequence_name']})):
1098                    if kw['sequence_type'] == 'mpg':
1099                       liste = string.join(full,' ')
1100                    else:
1101                       liste = string.join(web,' ')
1102                       
1103                    cmd = 'cd "%(attachment_path)s" && convert -delay 100  %(liste)s %(file)s.mpg 1> /dev/null 2> /dev/null &' % {
1104                                     'liste':liste,
1105                                     'file':kw['sequence_name'] ,
1106                                     'attachment_path':attachment_path}
1107                    os.system(cmd)  
1108                    self.request.write('<P><FONT color="Red" > MPEG creation as background process started </FONT></P>')
1109                if os.path.exists(os.path.join(attachment_path,'%(file)s.mpg' % {'file':kw['sequence_name']})):
1110                   dict = {}
1111                   dict['src'] = AttachFile.getAttachUrl(current_pagename,'%(file)s.mpg'  % { 
1112                      'file': kw['sequence_name']},self.request)
1113                      
1114                   image_link = '%(file)s.mpg' % { 'file':kw['sequence_name']}
1115                
1116                   self.request.write('<BR>')
1117                   text = formatter.url(1,dict['src'] ) + image_link + formatter.url(0)
1118                   self.request.write('Download this image sequence %(text)s for your archive' % { 'text': text})
1119               
1120               
1121          ## the sequence block needs to be refactored by better names
1122         
1123        

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.
  • [get | view] (2006-08-06 08:57:54, 41.8 KB) [[attachment:EXIF.py]]
  • [get | view] (2005-03-24 20:14:37, 10.6 KB) [[attachment:Gallery2-1.3.3-1.py]]
  • [get | view] (2005-03-26 08:39:13, 11.6 KB) [[attachment:Gallery2-1.3.3-2.py]]
  • [get | view] (2005-03-26 12:41:49, 13.1 KB) [[attachment:Gallery2-1.3.3-3.py]]
  • [get | view] (2005-03-27 20:23:01, 19.0 KB) [[attachment:Gallery2-1.3.3-4.py]]
  • [get | view] (2005-08-03 19:30:10, 23.2 KB) [[attachment:Gallery2-1.3.3-5.py]]
  • [get | view] (2005-08-18 07:58:38, 31.9 KB) [[attachment:Gallery2-1.3.5-10.py]]
  • [get | view] (2005-09-02 19:55:13, 34.1 KB) [[attachment:Gallery2-1.3.5-11.py]]
  • [get | view] (2005-11-13 18:09:11, 35.4 KB) [[attachment:Gallery2-1.3.5-12.py]]
  • [get | view] (2005-11-18 20:13:04, 46.2 KB) [[attachment:Gallery2-1.3.5-13.py]]
  • [get | view] (2005-12-03 15:33:06, 46.6 KB) [[attachment:Gallery2-1.3.5-14.py]]
  • [get | view] (2006-01-01 09:20:19, 43.3 KB) [[attachment:Gallery2-1.3.5-15.py]]
  • [get | view] (2005-08-07 15:46:28, 26.9 KB) [[attachment:Gallery2-1.3.5-6.py]]
  • [get | view] (2005-08-13 15:13:59, 28.7 KB) [[attachment:Gallery2-1.3.5-7.py]]
  • [get | view] (2005-08-14 13:02:00, 27.5 KB) [[attachment:Gallery2-1.3.5-8.py]]
  • [get | view] (2005-08-14 14:38:32, 28.7 KB) [[attachment:Gallery2-1.3.5-9.py]]
  • [get | view] (2006-08-06 08:45:47, 41.8 KB) [[attachment:Gallery2-1.5.4-16.py]]
  • [get | view] (2006-08-22 20:29:39, 42.0 KB) [[attachment:Gallery2-1.5.4-18.py]]
  • [get | view] (2006-08-06 08:57:36, 514.8 KB) [[attachment:example.swf]]
  • [get | view] (2005-08-17 18:10:27, 11.3 KB) [[attachment:gallery2image_test.py]]
  • [get | view] (2005-08-10 16:49:16, 1.3 KB) [[attachment:patchpullfromdir.diff]]
  • [get | view] (2006-08-17 16:32:50, 41.9 KB) [[attachment:text_x_gallery2-1.6.0-17.py]]
  • [get | view] (2006-08-22 20:23:06, 42.1 KB) [[attachment:text_x_gallery2-1.6.0-18.py]]
  • [get | view] (2008-02-06 10:08:05, 42.2 KB) [[attachment:text_x_gallery2-1.6.0-19.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.