view data/plugin/parser/text_x_arnica.py @ 60:d88a6a086d04

text_x_arnica.format: simplified if / else
author Reimar Bauer <rb.proj AT googlemail DOT com>
date Wed, 04 Jun 2008 15:16:31 +0200
parents 3406d23a01f2
children 6ab80d79e924
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, wikiutil
from MoinMoin.action import AttachFile
from MoinMoin.packages import packLine
from MoinMoin.Page import Page
from MoinMoin.filter import EXIF
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(macro, target_page=u'', columns=int(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,
                 mode=int(1), only_items=False, template_itemlist=False,
                 album=False, album_name=u'album', front_image=u'',
                 renew=False,
                 thumbnail_width=int(128), webnail_width=int(640),
                 text_width=int(128)):
    """ macro to initialize all default parameters for arnica. The definition is checked for wrong input.
    @param macro: is used from wikiutil.invoke_extension_function

    @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 mode: default is 1 which writes the description below the image,
                 any other number means description right of image
    @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 front_image of series is shown but slideshow over all images
    @param album_name: default is 'album' short name for the album.
    @param front_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
    """
    from inspect import getargspec
    args = getargspec(macro_arnica)[0]
    # ToDo find the proper method do get the real values without entering the list
    values = (target_page, columns, file_regex,
              image_for_webnail,
              show_text, show_date, show_tools,
              sort_by_date, sort_by_name, sort_by_alias,
              reverse_sort,
              mode, only_items, template_itemlist,
              album, album_name, front_image,
              renew,
              thumbnail_width, webnail_width,
              text_width)

    return (args[1:], values)

# 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

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

        macro = self._make_macro()
        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(macro.request, macro_arnica, args, [macro])
            keys, values = settings
            for i in range(len(keys)):
                setattr(self, keys[i], values[i])
            # 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 _make_macro(self):
        """macro helper used to create the macro object"""
        from MoinMoin import macro
        from MoinMoin.parser.text import Parser as text_Parser
        from MoinMoin.formatter.text_html import Formatter
        p = text_Parser("##\n", self.request)
        p.formatter = Formatter(self.request)
        p.formatter.page = Page(self.request, self.pagename)
        self.request.formatter = p.formatter
        p.form = self.request.form
        m = macro.Macro(p)
        return m

    def show_tools_restricted(self, this_target):
        """ show only tools to users with enough rights
        @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="RL">
            <input type="hidden" name="target" value="%(this_target)s">
            <input type="image" value="submit" src="%(htdocs)s/common/arnica/arnica_to_right.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="RR">
            <input type="hidden" name="target" value="%(this_target)s">
            <input type="image"  value="submit" src="%(htdocs)s/common/arnica/arnica_to_left.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="RM">
            <input type="hidden" name="target" value="%(this_target)s">
            <input type="image" value="submit" src="%(htdocs)s/common/arnica/arnica_to_bak.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 tools_html(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_to_full.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="VS">
                            <input type="hidden" name="alias" value='%(description)s'>
                            <input type="hidden" name="target" value='%(target)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_to_slide.png" title="slide_show" >
                       </td>
                    </form>
                    %(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]),
            "show_tools_restricted": self.show_tools_restricted(this_image)
            }

        return text

    def show_alias_mode2(self, idx):
        """ view mode 2 alias text is on righthand of image """
        if self.show_text:
            return '''
<td valign="top" width="%(text_width)s" %(style)s>
 %(this_alias)s
</td>''' % {
        "this_alias": self.description[idx],
        "text_width": self.text_width,
        "style": ' align="left" style="padding:0px; margin:2px 2px; border-style:none"'}
        else:
            return ''

    def show_date_mode2(self, idx):
        """ view mode 2 selection for date """
        if self.show_date:
            return '''<td%(style)s><p>%(this_exif_date)s</p></td>''' % {
                      "style": self.td_style,
                      "this_exif_date": self.exif_date[idx]}
        else:
            return ''

    def show_tools_mode2(self, idx):
        """ view mode 2 selection for tools """
        if self.show_tools:
            return "<td align=""center""%(style)s> %(tools)s </td>" % {
                "style": self.td_style,
                "tools": self.tools_html(idx)}
        else:
            return ''

    def mode2_html(self, idx):
        """ html code of mode 2 """
        text = '''
        <form action="%(baseurl)s/%(pagename)s" method="POST" enctype="multipart/form-data">
                <input type="hidden" name="action" value="arnica_slides">
                <input type="hidden" name="do" value="VS">
                <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="images" value='%(images)s'>
                <input type="hidden" name="image_for_webnail" value='%(image_for_webnail)s'>
                <input type="image" value="submit" src="?action=arnica_slides&do=view&target=%(thumbnail)s">
        </form>
            %(alias_html)s
    <tr>%(tools_html)s%(date_html)s</tr>''' % {
     "tdstyle": self.td_style,
     "baseurl": self.request.getScriptname(),
     "pagename": wikiutil.quoteWikinameURL(self.pagename),
     "thumbnail_width": self.thumbnail_width,
     "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],
     "tools_html": self.show_tools_mode2(idx),
     "date_html": self.show_date_mode2(idx),
     "alias_html": self.show_alias_mode2(idx)
     }

        return text

    def show_tools_mode1(self, idx):
        """ view mode 1 selection of tools """
        if self.show_tools:
            text = "<tr><td align=""center""%(style)s>%(tools)s </td></tr>" % {
                "style": self.td_style,
                "tools": self.tools_html(idx)}
        else:
            text = ''
        return text

    def show_date_mode1(self, idx):
        """ view mode 1 selection of date """
        if self.show_date:
            return '''
<tr>
<td%(style)s>%(this_exif_date)s</td>
</tr>''' % {
        "style": self.td_style,
        "this_exif_date": self.exif_date[idx]}
        else:
            return ''

    def show_alias_mode1(self, idx):
        """ view mode 1 alias text below image """
        if self.show_text:
            return '''
<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])}
        else:
            return ''

    def mode1_html(self, idx):
        """ html code of mode 1 (default)"""
        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>
                <input type="hidden" name="action" value="arnica_slides">
                <input type="hidden" name="do" value="VS">
                <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="images" value='%(images)s'>
                <input type="hidden" name="image_for_webnail" value='%(image_for_webnail)s'>
                <input type="image" value="submit" type="button" src="?action=arnica_slides&do=view&target=%(thumbnail)s" >
            </td>
        </form>
    </tr>
      %(alias_html)s
      %(date_html)s
      %(tools_html)s
</table>'''% {
     "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,
     "tools_html": self.show_tools_mode1(idx),
     "date_html": self.show_date_mode1(idx),
     "alias_html": self.show_alias_mode1(idx)
     }
        #self.request.write(data)

        return text

    def get_files(self, path, files, quotes):
        """ get files 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
            if not attfile.startswith('tmp.'):
                # only images
                if wikiutil.isPicture(attfile):
                    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:
            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 = [attfile for attfile in files if os.path.exists(os.path.join(path, attfile))]

            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 = os.listdir(path)

        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.get_files(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)


        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():
            im_obj = Image.open(imagef)
            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 format(self, formatter):
        """ does the format """

        _ = self._

        # checks if nitializing 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.mode1_html(i)
                if self.mode != 1:
                    text = self.mode2_html(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:
            front_image = self.front_image or self.full[0]
            try:
                i = self.full.index(front_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.mode1_html(i)
            except ValueError:
                text = "You can't use as front image: '%(front_image)s' because it is not listed in only_items!" % {"front_image": front_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)
        self.request.write(browser.format())