view data/plugin/parser/text_x_arnica.py @ 572:d53b63f94da8

arnica: fix for slide show link of toolbar. parameter webnail_width was missing.
author sudo_dirk<d.alders@arcor.de>
date Sun, 05 Feb 2012 22:02:59 +0100
parents 01fc8ba9809b
children 0db77eb786fb
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.
    Optionally, a description of an image can be added.

    By default the image name and its creation date is shown.
    If you click on a thumbnail you get navigation tools shown to slide through your images.

    Based on Gallery2 by ReimarBauer 2005-2008, ThomasWaldmann 2005, FlorianFesti 2006

    @copyright: 2008-2012 by MoinMoin:ReimarBauer,
    @copyright: 2012 by MoinMoin:Dirk Alders
    @license: GNU GPL, see COPYING for details.
"""

import os, re
from random import randint
from MoinMoin import search
from MoinMoin import wikiutil
from MoinMoin.action import AttachFile
from MoinMoin.packages import packLine
from MoinMoin.Page import Page

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


def arnica_settings(target_page=u'', columns=4, file_regex=u'.',
                    image_for_webnail=False,
                    show_text=True, show_date=True, show_tools=False, show_album_title=True,
                    sort_by=("name", "date", "alias"),
                    reverse_sort=False,
                    only_items=False, template_itemlist=False,
                    album=False, album_title=unicode, album_image=u'', album_link_page=False,
                    album_overview=False, album_short_title=False,
                    renew=False,
                    thumbnail_width=128,
                    webnail_width=640):
    """ dummy function to initialize all default parameters for arnica. The parameters are checked for wrong input.
    @param target_page: page to read attachments from. empty pagename is current page.
                        for album_overview, target_page is now a string for title search. alternatively it is possible to use regex:... or category:... (e.g. regex:title:.* for all pages)
    @param columns: number of columns for thumbnails, default is 4. 0 means no linebreak
    @param file_regex: regex for selecting images
    @param image_for_webnail if set then the image is shown instead of the webnail
    @param show_text: default shows description
    @param show_album_title: show album title
    @param show_date: default shows date from exif header, if available
    @param show_tools: default does not show the icon toolbar
    @param sort_by: default, sorts images by name, optional by date or by alias
    @param reverse_sort: if set, the file list is sorted in reverse order
    @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, selects album mode, only thumbnail from first image is shown, related is album title and album_image
    @param album_title: default is pagename of the images for the album.
    @param album_image: image to show on album default is the first image
    @param album_link_page: link to page instead of slideshow
    @param album_overview: if set, a thumbnail overview will be created (see also target_page)
    @param album_short_title: reduces the album_title that way that it fits in one line
    @param renew: if set then all selected thumbnails_* and webnails_* are removed and will be recreated
    @param thumbnail_width: default width of thumbnail is 128px
    @param webnail_width: default width of webnail is 640px
    """
    return locals()

def _get_files(request, pagename):
    """ get files dependent on isPicture and ignores tmp. files
    @param pagename: name of the page where to get attachments
    """
    # ToDo remove tmp. files check later
    files = AttachFile._get_files(request, pagename)
    files = [fn for fn in files if wikiutil.isPicture(fn) and not fn.startswith('tmp.')]
    return files

class Parser:
    """ arnica parser """

    extensions = ['.jpg', '.jpeg', '.gif', '.png']

    def __init__(self, raw, request, **kw):
        self.pagename = request.page.page_name
        self.raw = raw
        self.request = request
        self.formatter = request.formatter
        self.form = None
        self._ = request.getText

        args = kw.get('format_args', '')
        self.init_settings = False
        self.top_image_sorted = None
        # 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, arnica_settings, args)
            for key, value in settings.items():
                setattr(self, key, value)
            # saves the state of valid input
            self.init_settings = True
        except ValueError, err:
            # ToDo use formatter
            request.write(self.formatter.text(err))
        else:
            self.arnica_image = {} # self.arnica_image[image] = (webnail, thumbnail, exif_date, description, order)
            self.Image = wikiutil.importWikiPlugin(self.request.cfg, "macro", "Image", function="Image")
            self.breakable_word_length = self.thumbnail_width / 8

    def shortenName(self, name, length, prestring='...'):
        if len(name) > length:
            return prestring + name[-length + len(prestring):]
        else:
            return name

    def html_tools_restricted(self, this_target):
        """ shows restricted tools
            @param this_target: image
        """
        if self.album or not self.request.user.may.delete(self.pagename):
            return ''
        return """
<li class="tool">
    <form action="%(url)s" method="POST" enctype="multipart/form-data">
        <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/arnica/img/arnica_rotate_to_left.png" title="rotate to left">
    </form>
</li>
<li class="tool">
    <form action="%(url)s" method="POST" enctype="multipart/form-data">
        <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/arnica/img/arnica_rotate_to_right.png" title="rotate to right">
    </form>
</li>
<li class="tool">
    <form action="%(url)s" method="POST" enctype="multipart/form-data">
         <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/arnica/img/arnica_remove_image.png" title="move to bak">
    </form>
</li>""" % {
            'url': Page(self.request, self.pagename).url(self.request),
            'htdocs': self.request.cfg.url_prefix_static,
            "pagename": wikiutil.quoteWikinameURL(self.pagename),
            "this_target": wikiutil.escape(this_target),
        }

    def html_tools(self, image):
        """ html code of thumbnails view with contol
        @param image: image as key for corresponding data
        """
        image_names = self.arnica_image.keys()
        index = [self.arnica_image[img][4] for img in image_names]
        selected_images = [image_names[int(idx)] for idx in index]

        html = """
<ul>
<li class="tool">
    <form action="%(url)s" method="POST" enctype="multipart/form-data">
        <input type="hidden" name="action" value="AttachFile">
        <input type="hidden" name="do" value="view">
        <input type="hidden" name="target" value="%(this_target)s">
        <input type="image" value="submit" src="%(htdocs)s/arnica/img/arnica_full_image.png" title="load image">
    </form>
</li>
<li class="tool">
    <form action="%(url)s" method="POST" enctype="multipart/form-data">
        <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="original_images" value="%(original_images)s">
        <input type="hidden" name="exif_date" value="%(exif_date)s">
        <input type="hidden" name="webnail_width" value="%(webnail_width)d">
        <input type="image" value="submit" title="slide show" src="%(htdocs)s/arnica/img/arnica_load_slide_show.png">
    </form>
</li>
%(html_tools_restricted)s
</ul>
""" % {
        "url": Page(self.request, self.pagename).url(self.request),
        "pagename": self.pagename,
        "htdocs": self.request.cfg.url_prefix_static,
        "description": wikiutil.escape(packLine([(self.arnica_image[image][3])] + [(self.arnica_image[img][3]) for img in selected_images]), quote=1),
        "exif_date": wikiutil.escape(packLine([self.arnica_image[image][2]] + [self.arnica_image[img][2] for img in selected_images]), quote=1),
        "webnail_width": self.webnail_width,
        "target": wikiutil.escape(self.arnica_image[image][0]),
        "original_images": wikiutil.escape(packLine([image] + selected_images)),
        "images": wikiutil.escape(packLine([self.arnica_image[image][0]] + [self.arnica_image[img][0] for img in selected_images])),
        "original_images": wikiutil.escape(packLine([image] + selected_images)),
        "this_target": wikiutil.escape(image),
        "html_tools_restricted": self.html_tools_restricted(image),
        }
        return html

    def html_show_tools(self, image):
        """ shows toolbox
        @param image: image as key for corresponding data
        """
        html = ''
        if self.show_tools:
            html = '<div class="html-show-tools">%(tools)s</div>' % {
                "tools": self.html_tools(image)}
        return html

    def html_show_date(self, image):
        """ shows date
        @param image: image as key for corresponding data
        """

        html = ''
        if self.show_date:
            html = '<div class="show-datetime">%(this_exif_date)s</div>' % {
                "this_exif_date": self.formatter.text(self.arnica_image[image][2])}
        return html

    def html_show_alias(self, image):
        """ alias text below image
        @param image: image as key for corresponding data
        """

        html = ''
        if self.show_text:
            html = '<div class="description"> %(this_alias)s</div>' % {
                    "this_alias": self.to_wikitext(wikiutil.make_breakable(self.arnica_image[image][3], self.breakable_word_length))}
        return html

    def html_arrange_thumbnails(self, image, selected_images):
        """ defines arrangement of thumbnail, text, date and tools
        @param image: image as key for corresponding data
        @param selected_images: ordered list of selected images
        """
        title = ""
        if self.album and self.show_album_title:
            if self.album_short_title:
                title = self.shortenName(self.album_title or self.pagename, self.breakable_word_length)
            else:
                title = wikiutil.make_breakable(self.album_title or self.pagename, self.breakable_word_length)
            title = '<div class="title">%(n)d images<br>(%(album_title)s)</div>' % {"n": len(self.arnica_image),
                                                        "album_title": title}
        html = """
<div class="thumbnail" style="width:%(width)spx">
    %(title)s
    <form action="%(url)s" method="POST" enctype="multipart/form-data">
      <div class="imagecont" style="height:%(width)spx;">
        <div class="image" style="width:%(width)spx;">
             <input type="hidden" name="action" value="%(action)s">
             <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="original_images" value="%(original_images)s">
             <input type="hidden" name="exif_date" value="%(exif_date)s">
             <input type="hidden" name="webnail_width" value="%(webnail_width)d">
             <input type="image" value="submit" title="%(submit_title)s" src="%(thumbnail)s">
        </div>
      </div>
    </form>
    <div style="width:%(width)spx">
    %(html_tools)s
    %(alias_html)s
    %(date_html)s
    </div>
</div>
""" % {
        "title": title,
        "url": Page(self.request, self.pagename).url(self.request),
        "pagename": self.pagename,
        "description": wikiutil.escape(packLine([(self.arnica_image[image][3])] + [(self.arnica_image[img][3]) for img in selected_images]), quote=1),
        "exif_date": wikiutil.escape(packLine([self.arnica_image[image][2]] + [self.arnica_image[img][2] for img in selected_images])),
        "target": wikiutil.escape(self.arnica_image[image][0]),
        "original_images": wikiutil.escape(packLine([image] + selected_images)),
        "images": wikiutil.escape(packLine([self.arnica_image[image][0]] + [self.arnica_image[img][0] for img in selected_images])),
        "thumbnail": wikiutil.escape(self.arnica_image[image][1]),
        "width": self.thumbnail_width,
        "html_tools": self.html_show_tools(image),
        "date_html": self.html_show_date(image),
        "alias_html": self.html_show_alias(image),
        "submit_title": {True: "images", False: "slide show"}[self.album_link_page],
        "action": {True: "show", False: "arnica_slides"}[self.album_link_page],
        "webnail_width": self.webnail_width,
        }
        return html

    def define_thumb_webnails(self, files, image_alias):
        """ creates lists for thumbnails and webnails
        @param files: file names of images
        @param image_alias: text alias for image file
        """
        order = 0
        for attfile in files:
            description = attfile
            if image_alias.get(attfile):
                # use alias
                description = image_alias.get(attfile)[0]
            itemname = self.pagename + '/' + attfile
            img = self.Image(self.request, itemname)
            webnail = img.url(size=(self.webnail_width, self.webnail_width))
            thumbnail = img.url(size=(self.thumbnail_width, self.thumbnail_width))
            try:
                exif_date = self.request.user.getFormattedDateTime(img.ctime)
            except KeyError:
                exif_date = '--'
            self.arnica_image[attfile] = (webnail, thumbnail, exif_date, description, str(order))
            order += 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_image_alias(self):
        """  gets the quotes from the item list and returns a dictionary of image and alias """
        # ToDo simplify
        quotes = self.raw.split('\n')
        quotes = [quote.strip() for quote in quotes]
        quotes = [quote[2:] for quote in quotes if quote.startswith('* ')]
        image_alias = {}
        counter = 0
        for line in quotes:
            if line.startswith('[[') and line.endswith(']]') and '|' in line:
                img, alias = line[2:-2].split('|', 1)
                # don't count an image more than once and verify that it is an image
                if wikiutil.isPicture(img.strip()) and img.strip() not in image_alias.keys():
                    image_alias[img.strip()] = (self.formatter.text(alias.strip()), counter, img)
                    counter += 1
        return image_alias

    def select_files(self, formatter, target_page):
        """ 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 target_page and Page(self.request, target_page).exists() and self.request.user.may.read(target_page):
            self.pagename = target_page

        image_alias = self.get_image_alias()
        if self.only_items:
            # ToDo simplify
            # get the same order of files and aliastext as on the page written
            files = image_alias.keys()
            all_files = [fn for fn in files if wikiutil.isPicture(fn) and
                         AttachFile.exists(self.request, self.pagename, fn)]
        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 all_files:
            self.define_thumb_webnails(all_files, image_alias)
        return all_files

    def render(self, formatter):
        if self.album and self.album_overview:
            # find pages
            if (self.target_page or self.pagename).split(":")[0] in ["regex", "category"]:
                search_term = self.target_page or self.pagename
            else:
                search_term = u'regex:title:^%s/' % (self.target_page or self.pagename)
            search_result = search.searchPages(self.request, search_term)
            target_pages = set(title.page_name for title in search_result.hits)
            # create album overview
            albums = []
            for target_page in target_pages:
                self.arnica_image.clear()  # remove previous images
                html = self.render_thumbnails(formatter, target_page)
                if self.arnica_image:  # skip empty albums
                    if self.sort_by == 'date':
                        albums.append([self.Image(self.request, target_page + '/' + self.top_image_sorted).ctime, html])
                    else:
                        albums.append([self.top_image_sorted, html])
            albums.sort()
            if self.reverse_sort:
                albums.reverse()
            col_count = 1
            cols = min([self.columns, len(albums)])
            result = []
            for entry in albums:
                result.append(''.join(entry[1]))
                if col_count == cols and self.columns != 0:
                    col_count = 0
                    result.append('<br class="clearboth">')
                col_count += 1
            result.append('<br class="clearboth">')
            return ''.join(result)
        else:
            return self.render_thumbnails(formatter, self.target_page)

    def render_thumbnails(self, formatter, target_page):
        """ renders thumbnails """
        _ = self._
        # checks if initializing of all attributes in __init__ was done
        if not self.init_settings:
            return
        if target_page and (not Page(self.request, target_page).exists() or not self.request.user.may.read(target_page)):
            text = _("""Page '%(new_pagename)s' does not exist or you don't have enough rights.""") % {"new_pagename": target_page}
            return self.formatter.text(text)
        if not self.arnica_image and not self.select_files(formatter, target_page):
            text = _("No matching image file found!")
            return self.formatter.text(text)
        if self.template_itemlist:
            self.request.write(self.formatter.div(1, css_class="text"))
            text = _("""\
Copy the following listitems into the script.
Replace alias with the label you want.
Afterwards disable template_itemlist by setting it to False:""")
            self.request.write(self.formatter.text(text))
            self.request.write(self.formatter.div(1))
            self.request.write(self.formatter.preformatted(1))
            keys = self.arnica_image.keys()
            keys.sort()
            for image in keys:
                text = ' * [[%s|alias]]\n' % image
                self.request.write(self.formatter.text(text))
            self.request.write(self.formatter.preformatted(0))
            self.request.write(self.formatter.div(0))
            self.request.write(self.formatter.div(0))

        col_count = 1
        cols = min([self.columns, len(self.arnica_image)])
        result = []
        image_names = self.arnica_image.keys()
        image_dict = {}
        if self.only_items:
            # ToDo simplify
            image_alias = self.get_image_alias()
            image_names = image_alias.keys()
            alias_text = image_alias.values()
            to_order = [int(alias_text[ix][1]) for ix in range(len(alias_text))]
            names = [(alias_text[ix][2]).strip() for ix in range(len(alias_text))]
            alias = [alias_text[ix][0] for ix in range(len(alias_text))]

            i = 0
            for ix in to_order:
                image_names[ix] = names[i]
                alias_text[ix] = alias[i]
                i += 1

            # sort by alias
            if self.sort_by == "alias":
                i = 0
                for img in image_names:
                    image_dict[alias_text[i]] = img
                    i += 1
                keys = image_dict.keys()
                keys.sort()
                image_names = [image_dict[txt] for txt in keys]
        else:
            # sort by date
            if self.sort_by == "date":
                for img in image_names:
                    itemname = self.pagename + '/' + img
                    # to get uniq times (gives order for sorting)
                    ft_file = "%d%x" % (self.Image(self.request, itemname).ctime, randint(0, 256))
                    image_dict[ft_file] =  img
                keys = image_dict.keys()
                keys.sort()
                image_names = [image_dict[txt] for txt in keys]

            # default sort by name
            elif self.sort_by == "name" and not self.only_items:
                image_names.sort()

        image_dict.clear()
        # reverse sort
        if self.reverse_sort:
            image_names.reverse()

        # store first in album
        self.top_image_sorted = image_names[0]

        if self.album:
            cols = 1
            album_image = self.album_image or self.top_image_sorted  # self.high_resolution_image[0]
            if not album_image in self.arnica_image.keys():
                html = self.formatter.text(_("""You can't use as album image:
"%(album_image)s" because it does not exist or it is not listed
in your item list!""") % {"album_image": album_image, })
            else:
                html = self.html_arrange_thumbnails(album_image, image_names)
            result.append(''.join(html))
        else:
            for image in image_names:
                html = self.html_arrange_thumbnails(image, image_names)
                result.append(''.join(html))
                if col_count == cols and self.columns != 0:
                    col_count = 0
                    result.append('<br class="clearboth">')
                col_count += 1

        if not self.album or not self.album_overview:
            # no new line, if album and album_overview
            result.append('<br class="clearboth">')
        result.insert(0, self.formatter.div(1, css_class="arnica"))
        result.append(self.formatter.div(0))
        return ''.join(result)

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