view data/plugin/parser/text_x_arnica.py @ 100:ee184bd0f428

text_x_arnica: whitespace fix
author Reimar Bauer <rb.proj AT googlemail DOT com>
date Wed, 25 Jun 2008 09:07:17 +0200
parents edb3aa8208b2
children b446b9826cf8
line wrap: on
line source
# -*- coding: iso-8859-1 -*-
"""
    MoinMoin - arnica parser

    This parser is used to visualize a couple of images as a thumbnail gallery.
    Optional a description of an image could be added including WikiName.
    On default the image name and it's creation date is shown.
    If you click on a thumbnail you get the webnails shown.
    By a menue you are able to toggle between the slides.


    MODIFICATION HISTORY:
    based on Gallery2 by ReimarBauer 2005-2008, ThomasWaldmann 2005, FlorianFesti 2006

    @copyright: 2008 by MoinMoin:ReimarBauer
    @license: GNU GPL, see COPYING for details.
"""
#Dependencies = ['time'] # do not cache

import os, re
from random import randint
from MoinMoin import caching, config, wikiutil
from MoinMoin.action import AttachFile
from MoinMoin.filter import EXIF
from MoinMoin.packages import packLine
from MoinMoin.Page import Page
from MoinMoin.util.dataset import TupleDataset, Column
from MoinMoin.widget.browser import DataBrowserWidget

try:
    import Image
except ImportError:
    Image = None

parser_name = __name__.split('.')[-1]

def macro_arnica(target_page=u'', columns=4, file_regex=u'.',
                 image_for_webnail=False,
                 show_text=True, show_date=True, show_tools=True,
                 sort_by_date=False, sort_by_name=True, sort_by_alias=False,
                 reverse_sort=False,
                 only_items=False, template_itemlist=False,
                 album=False, album_title=unicode, album_image=u'',
                 renew=False,
                 thumbnail_width=128, webnail_width=640,
                 text_width=128):
    """ dummy macro to initialize all default parameters for arnica. The definition is checked for wrong input.
    @param target_page: page to read attachments from
    @param columns: number of columns for thumbnails
    @param file_regex: regex to select images
    @param image_for_webnail if set then the image is shown instead of the webnail
    @param show_text: default shows description
    @param show_date: default shows date from exif header if available
    @param show_tools: default shows icon toolbar
    @param sort_by_name: default sorts images by name,
    @param sort_by_date: if set to images are sorted to the modification time
    @param sort_by_alias: if set and also only_items is True it is used to order the images
                          by the alias name
    @param reverse_sort: if set the file list is reverse sorted
    @param only_items: if set only images which are described in listitem are shown, e.g.
                       * [[image1.jpg|alias]]
                       * [[image2.jpg|alias]]
    @param template_itemlist: if set an item list is shown which could be copied into the wiki page
    @param album: if set only the first thumbnail image or if used thumbnail
                  from album_image of series is shown but slideshow over all images
    @param album_title: default is pagename of the images for the album.
    @param album_image:  Useful for album.  default is ''. The first image is shown in front
                         of the album and slideshow.
    @param renew: if set then all selected thumbnails_* and webnails_* are removed
                  and will be recreated
    @param thumbnail_width: default width of thumbnails is 128
    @param webnail_width: default width of webnail is 640
    @param text_width: default width of text is 140
    """
    return locals()

# ToDo may be move to wikiutil
def get_exif_info(file_name):
    """ gets exif info from image file
    @param: image file name
    """
    date = "--"
    if wikiutil.isPicture(file_name):
        id_file = open(file_name, 'rb')
        tags = EXIF.process_file(id_file, 'DateTimeOriginal')
        id_file.close()
        if tags.has_key('EXIF DateTimeOriginal'):
            date = str(tags['EXIF DateTimeOriginal'])
            date = date.replace(':', '-', 2)
    return date

def _get_files(request, pagename):
    attach_dir = AttachFile.getAttachDir(request, pagename)
    files = []
    if os.path.isdir(attach_dir):
        files = [fn.decode(config.charset) for fn in os.listdir(attach_dir)
                 if wikiutil.isPicture(fn) and not fn.startswith('tmp.')]
    return files

class Parser:
    """ arnica parser """
    extensions = '*.jpg'
    def __init__(self, raw, request, **kw):
        self.pagename = request.page.page_name
        self.raw = raw
        self.request = request
        self.form = None
        self._ = request.getText

        args = kw.get('format_args', '')
        self.init_settings = False
        # we use a macro definition to initialize the default init parameters
        # if a user enters a wrong parameter the failure is shown by the exception
        try:
            settings = wikiutil.invoke_extension_function(request, macro_arnica, args)
            for key, value in settings.items():
                setattr(self, key, value)
            # saves the state of valid input
            self.init_settings = True
        except ValueError, err:
            msg = u"arnica: %s" % err.args[0]
            request.write(wikiutil.escape(msg))

        self.inner_table_style = ' style="border-style:none; margin:10px;"'
        self.td_style = ' align="center" style="padding:0; margin:2px 2px; border-style:none"'

        self.web = [] # array for webnails
        self.full = [] # array for  images
        self.thumb = [] # array for thumbnails
        self.exif_date = [] # array of exif date description
        self.imgtype = [] # array of imagetype of full image
        self.description = [] # array of description
        self.webimg = [] # array of images for forms

    def html_show_tools_restricted(self, this_target):
        """ shows restricted tools
            @param this_target: image
        """
        if not self.request.user.may.delete(self.pagename):
            return ''

        return '''\
        <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
          <td%(style)s>
            <input type="hidden" name="action" value="arnica_slides">
            <input type="hidden" name="do" value="rotate_left">
            <input type="hidden" name="target" value="%(this_target)s">
            <input type="image" value="submit" src="%(htdocs)s/common/arnica/arnica_rotate_to_left.png" title="rotate to left">
          </td>
        </form>
        <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
          <td%(style)s>
            <input type="hidden" name="action" value="arnica_slides">
            <input type="hidden" name="do" value="rotate_right">
            <input type="hidden" name="target" value="%(this_target)s">
            <input type="image"  value="submit" src="%(htdocs)s/common/arnica/arnica_rotate_to_right.png" title="rotate to right">
          </td>
        </form>
        <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
          <td%(style)s>
            <input type="hidden" name="action" value="arnica_slides">
            <input type="hidden" name="do" value="delete">
            <input type="hidden" name="target" value="%(this_target)s">
            <input type="image" value="submit" src="%(htdocs)s/common/arnica/arnica_remove_image.png" title="move to bak">
           </td>
        </form>''' % {
            'baseurl': self.request.getBaseURL(),
            'style': self.td_style,
            'htdocs': self.request.cfg.url_prefix_static,
            "pagename": wikiutil.quoteWikinameURL(self.pagename),
            "this_target": this_target}

    def html_tools(self, idx):
        """ html code of thumbnails view with contol
        @param idx: index postion of corresponding data
        """
        this_image = self.full[idx]

        text = '''\
            <table align="center" width="%(thumbnail_width)s"%(tablestyle)s>
                <tr>
                    <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
                        <td%(style)s>
                            <input type="hidden" name="action" value="AttachFile">
                            <input type="hidden" name="do" value="get">
                            <input type="hidden" name="target" value='%(this_target)s'>
                            <input type="image" value="submit" src="%(htdocs)s/common/arnica/arnica_full_image.png" title="load image">
                        </td>
                    </form>
                    <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
                        <td%(style)s>
                            <input type="hidden" name="action" value="arnica_slides">
                            <input type="hidden" name="do" value="slide_show">
                            <input type="hidden" name="alias" value='%(description)s'>
                            <input type="hidden" name="target" value='%(target)s'>
                            <input type="hidden" name="pagename" value='%(pagename)s'>
                            <input type="hidden" name="images" value='%(images)s'>
                            <input type="hidden" name="exif_date" value='%(exif_date)s'>
                            <input type="image" value="submit" src="%(htdocs)s/common/arnica/arnica_load_slide_show.png" title="slide_show">
                       </td>
                    </form>
                    %(html_show_tools_restricted)s
                </tr>
            </table>'''   % {
            "baseurl": self.request.getScriptname(),
            "pagename": wikiutil.quoteWikinameURL(self.pagename),
            "htdocs": self.request.cfg.url_prefix_static,
            "tablestyle": self.inner_table_style,
            "style": self.td_style,
            "thumbnail_width": self.thumbnail_width,
            "description": packLine([self.description[idx]] + self.description),
            "exif_date": packLine([self.exif_date[idx]] + self.exif_date),
            "target": self.webimg[idx],
            "images": packLine([self.webimg[idx]] + self.webimg),
            "this_target": self.full[idx],
            "thumbnail": "%s%s" % (AttachFile.getAttachUrl(self.pagename, '', self.request), self.thumb[idx]),
            "html_show_tools_restricted": self.html_show_tools_restricted(this_image)
            }
        return text

    def html_show_tools(self, idx):
        """ shows toolbox """
        text = ''
        if self.show_tools:
            text = "<tr><td align=""center""%(style)s>%(tools)s</td></tr>" % {
                "style": self.td_style,
                "tools": self.html_tools(idx)}
        return text

    def html_show_date(self, idx):
        """ shows date """
        text = ''
        if self.show_date:
            text = '<tr><td%(style)s>%(this_exif_date)s</td></tr>' % {
                "style": self.td_style,
                "this_exif_date": self.exif_date[idx]}
        return text

    def html_show_alias(self, idx):
        """ view mode 1 alias text below image """
        text = ''
        if self.show_text:
            text = '<tr><td width="%(thumbnail_width)s" %(style)s> %(this_alias)s</td></tr>' % {
                    "thumbnail_width": self.thumbnail_width,
                    "style": ' align="left" style="padding:0em; margin:2px 2px; border-style:none"',
                    "this_alias": self.to_wikitext(self.description[idx])}
        return text

    def html_preview(self, idx):
        """ defines arrangement of thumbnail, text, date and tools """
        title = ""
        if self.album:
            title = "%(n)d images (%(album_title)s)" % {"n": len(self.full), "album_title": self.album_title or self.pagename}

        text = '''\
    <table width="%(thumbnail_width)s" align="center" valign="center"%(style)s>
    <tr align="center" valign="center">
        <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
            <td align="center" valign="middle" width="%(thumbnail_width)s"
            %(tdstyle)s>
                %(title)s
                <input type="hidden" name="action" value="arnica_slides">
                <input type="hidden" name="do" value="slide_show">
                <input type="hidden" name="alias" value='%(description)s'>
                <input type="hidden" name="exif_date" value='%(exif_date)s'>
                <input type="hidden" name="target" value='%(target)s'>
                <input type="hidden" name="pagename" value='%(pagename)s'>
                <input type="hidden" name="images" value='%(images)s'>
                <input type="hidden" name="image_for_webnail" value='%(image_for_webnail)s'>
                <input type="image" value="submit" type="button" src="%(baseurl)s/%(pagename)s?action=arnica_slides&do=view&target=%(thumbnail)s">
            </td>
        </form>
    </tr>
      %(alias_html)s
      %(date_html)s
      %(html_tools)s
</table>'''% {
     "title": title,
     "tdstyle": self.td_style,
     "style": self.inner_table_style,
     "baseurl": self.request.getScriptname(),
     "pagename": wikiutil.quoteWikinameURL(self.pagename),
     "description": packLine([self.description[idx]] + self.description),
     "exif_date": packLine([self.exif_date[idx]] + self.exif_date),
     "image_for_webnail": self.image_for_webnail,
     "target": self.webimg[idx],
     "images": packLine([self.webimg[idx]] + self.webimg),
     "thumbnail": self.thumb[idx],
     "thumbnail_width": self.thumbnail_width,
     "html_tools": self.html_show_tools(idx),
     "date_html": self.html_show_date(idx),
     "alias_html": self.html_show_alias(idx)
     }
        return text

    def define_thumb_webnails(self, path, files, quotes):
        """ creates lists for thumbnails and webnails
        @param path: path to attachment
        @param files: file names of images
        @param quotes: text alias for image file
        """
        ddict = {}
        if len(quotes['image']) > 0:
            i = 0
            for txt in quotes['image']:
                ddict[txt] = quotes['alias'][i]
                i += 1

        for attfile in files:
            # only files not thumb or webnails
            self.description.append(ddict.get(attfile, attfile))
            self.full.append(attfile)
            fname, ext = os.path.splitext(attfile)
            if ext in ('.gif', '.png'):
                self.imgtype.append('PNG')
                webnail = 'tmp.webnail_%s.png' % fname
                thumbfile = 'tmp.thumbnail_%s.png' % fname
            else:
                self.imgtype.append("JPEG")
                webnail = 'tmp.webnail_%s.jpg' % fname
                thumbfile = 'tmp.thumbnail_%s.jpg' % fname

            att_file = os.path.join(path, attfile)
            if os.path.exists(att_file):
                self.web.append(webnail)
                self.thumb.append(thumbfile)
                date = get_exif_info(att_file)
                self.exif_date.append(wikiutil.escape(date, quote=1))

    def to_wikitext(self, text):
        """
        converts text to wiki name if it is written as WikiName or [[wikiname]]
        @param text: text to parse and render
        """
        text = ''.join(text)
        from MoinMoin.parser.text_moin_wiki import Parser as WikiParser
        return wikiutil.renderText(self.request, WikiParser, text)

    def get_quotes(self):
        """  get's the qoutes from the itemlist  """
        quotes = self.raw.split('\n')
        quotes = [quote.strip() for quote in quotes]
        quotes = [quote[2:] for quote in quotes if quote.startswith('* ')]

        image = []
        text = []

        for line in quotes:
            if line.startswith('[[') and line.endswith(']]'):
                img, alias = line[2:-2].split('|', 1)
                alias = alias.strip()
                alias = wikiutil.escape(alias, quote=1)
                text.append(alias)
                image.append(img.strip())

        return {
            'alias': text,
            'image': image,
        }

    def select_files(self, formatter):
        """ select files """

        # we need to take the page_name from the formatter.page otherwise
        # include does not work
        self.pagename = formatter.page.page_name
        if self.target_page:
            self.pagename = self.target_page
        path = AttachFile.getAttachDir(self.request, self.pagename, create=1)

        image_dict = {}
        quotes = self.get_quotes()

        if self.only_items:
            files = quotes['image']
            all_files = [fn for fn in files if wikiutil.isPicture(fn) and AttachFile.exists(self.request, self.pagename, fn)]

            if self.sort_by_alias:
                alias_text = quotes['alias']
                i = 0
                for attfile in all_files:
                    image_dict[alias_text[i]] = attfile
                    i += 1
                keys = image_dict.keys()
                keys.sort()
                all_files = [image_dict[txt] for txt in keys]
        else:
            all_files = _get_files(self.request, self.pagename)

        if self.file_regex != u'.':
            all_files = [attfile for attfile in all_files if re.match(self.file_regex, attfile)]

        if self.sort_by_name and self.only_items is False:
            all_files.sort()

        if self.sort_by_date:
            for attfile in all_files:
                infile = os.path.join(path, attfile)
                ft_file = "%s%s" % (str(os.path.getmtime(infile)), str(randint(0, 65535)))
                image_dict[ft_file] = attfile
            keys = image_dict.keys()
            keys.sort()
            all_files = [image_dict[txt] for txt in keys]

        image_dict.clear()

        if self.reverse_sort:
            all_files.reverse()

        if all_files:
            self.define_thumb_webnails(path, all_files, quotes)

        return all_files

    def create_thumbnail_and_webnail(self, image, webnail, thumbnail, image_type):
        """creates thumbnails and webnails cache files for given image type
        @param image: filename of image
        @param webnail: name of webnail file
        @param thumbnail: name of thumbnail file
        @param image_type: filetype of image
        """
        _ = self.request.getText
        if not Image:
            msg = _('The parser %(parser)s needs python imaging library (PIL) installed' % {'parser': parser_name})
            self.request.write(msg)
            return

        path = AttachFile.getAttachDir(self.request, self.pagename, create=1)
        imagef = os.path.join(path, image)

        if os.path.getsize(imagef) == 0:
            return

        page = Page(self.request, self.pagename)
        cache_web = caching.CacheEntry(self.request, page, webnail, scope='item', use_pickle=True)
        cache_thumb = caching.CacheEntry(self.request, page, thumbnail, scope='item', use_pickle=True)

        arena_dir = caching.get_arena_dir(self.request, page, 'item')
        webf = os.path.join(arena_dir, webnail)
        thumbf = os.path.join(arena_dir, thumbnail)

        if self.renew:
            cache_web.remove()
            cache_thumb.remove()

        if not cache_web.exists() or not cache_thumb.exists():
            try:
                im_obj = Image.open(imagef)
            except IOError:
                return

            if not cache_web.exists():
                if not self.image_for_webnail:
                    im_obj.thumbnail((self.webnail_width, self.webnail_width), Image.ANTIALIAS)
                    im_obj.save(webf, image_type)

            if not cache_thumb.exists():
                im_obj.thumbnail((self.thumbnail_width, self.thumbnail_width), Image.ANTIALIAS)
                im_obj.save(thumbf, image_type)

    def render(self, formatter):
        """ renders thumbnails """

        _ = self._

        # checks if initializing of all attributes in __init__ was done
        if not self.init_settings:
            return

        if self.target_page and (not self.request.user.may.read(self.target_page) or not Page(self.request, self.target_page).exists()):
            self.request.write(_("Page %(pagename)s does not exists or you don't have enough rights." % {"pagename": self.target_page}))
            return

        if not self.select_files(formatter):
            self.request.write(_("No matching image file found!"))
            return

        if self.template_itemlist:
            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:"""))
            self.request.write('<br>')
            for image in self.full:
                self.request.write(' * [[%(image)s|%(alias)s]]<br>\n' % {
                                   'image': image,
                                   'alias': 'alias'
                                    })

        COLUMNS = min([self.columns, len(self.full)])
        if self.album:
            COLUMNS = 1

        data = TupleDataset()
        data.columns = []

        for dummy in range(COLUMNS):
            data.columns.extend([Column('', label=(''))])

        col_count = 1
        result = []
        for image in self.full:
            i = self.full.index(image)
            self.create_thumbnail_and_webnail(image, self.web[i], self.thumb[i], self.imgtype[i])
            if not self.album:
                self.webimg = self.web
                if self.image_for_webnail:
                    self.webimg = self.full

                text = self.html_preview(i)

                if col_count <= COLUMNS:
                    result.append(''.join(text))

                if col_count == COLUMNS:
                    col_count = 0
                    data.addRow(tuple(result))
                    # resets the result list after the row is added
                    result = []

                col_count += 1

        if result and not self.album:
            for i in range(COLUMNS - col_count + 1):
                result.append('')

        if self.album:
            album_image = self.album_image or self.full[0]
            try:
                i = self.full.index(album_image)
                self.create_thumbnail_and_webnail(image, self.web[i], self.thumb[i], self.imgtype[i])
                self.webimg = self.web
                if self.image_for_webnail:
                    self.webimg = self.full
                text = self.html_preview(i)
            except ValueError:
                text = _("You can't use as album image: '%(album_image)s' because it is not listed in only_items!") % {"album_image": album_image, }
            result.append(''.join(text))

        # adds the last row if it is not filled up
        if result:
            data.addRow(tuple(result))

        browser = DataBrowserWidget(self.request, show_header=False)
        browser.setData(data)
        return browser.format()

    def format(self, formatter):
        """ parser output """
        # checks if initializing of all attributes in __init__ was done
        if self.init_settings:
            self.request.write(self.render(formatter))