web/views/idownloadable.py
author Sylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 20 May 2010 20:47:55 +0200
changeset 5556 9ab2b4c74baf
parent 5467 57372dbfd114
child 5714 04a8e48f10bc
permissions -rw-r--r--
[entity] introduce a new 'adapters' registry This changeset introduces the notion in adapters (as in Zope Component Architecture) in a cubicweb way, eg using a specific registry of appobjects. This allows nicer code structure, by avoid clutering entity classes and moving code usually specific to a place of the ui (or something else) together with the code that use the interface. We don't use actual interface anymore, they are implied by adapters (which may be abstract), whose reg id is an interface name. Appobjects that used to 'implements(IFace)' should now be rewritten by: * coding an IFaceAdapter(EntityAdapter) defining (implementing if desired) the interface, usually with __regid__ = 'IFace' * use "adaptable('IFace')" as selector instead Also, the implements_adapter_compat decorator eases backward compatibility with adapter's methods that may still be found on entities implementing the interface. Notice that unlike ZCA, we don't support automatic adapters chain (yagni?). All interfaces defined in cubicweb have been turned into adapters, also some new ones have been introduced to cleanup Entity / AnyEntity classes namespace. At the end, the pluggable mixins mecanism should disappear in favor of adapters as well.

# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
#
# CubicWeb is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
"""Specific views for entities adapting to IDownloadable"""

__docformat__ = "restructuredtext en"
_ = unicode

from logilab.mtconverter import BINARY_ENCODINGS, TransformError, xml_escape

from cubicweb.view import EntityView
from cubicweb.selectors import (one_line_rset, implements, match_context_prop,
                                adaptable, has_mimetype)
from cubicweb.mttransforms import ENGINE
from cubicweb.web.box import EntityBoxTemplate
from cubicweb.web.views import primary, baseviews


def download_box(w, entity, title=None, label=None, footer=u''):
    req = entity._cw
    w(u'<div class="sideBox">')
    if title is None:
        title = req._('download')
    w(u'<div class="sideBoxTitle downloadBoxTitle"><span>%s</span></div>'
      % xml_escape(title))
    w(u'<div class="sideBox downloadBox"><div class="sideBoxBody">')
    w(u'<a href="%s"><img src="%s" alt="%s"/> %s</a>'
      % (xml_escape(entity.cw_adapt_to('IDownloadable').download_url()),
         req.uiprops['DOWNLOAD_ICON'],
         _('download icon'), xml_escape(label or entity.dc_title())))
    w(u'%s</div>' % footer)
    w(u'</div></div>\n')


class DownloadBox(EntityBoxTemplate):
    __regid__ = 'download_box'
    # no download box for images
    # XXX primary_view selector ?
    __select__ = (one_line_rset() & match_context_prop()
                  & adaptable('IDownloadable') & ~has_mimetype('image/'))
    order = 10

    def cell_call(self, row, col, title=None, label=None, **kwargs):
        entity = self.cw_rset.get_entity(row, col)
        download_box(self.w, entity, title, label)


class DownloadView(EntityView):
    """this view is replacing the deprecated 'download' controller and allow
    downloading of entities providing the necessary interface
    """
    __regid__ = 'download'
    __select__ = one_line_rset() & adaptable('IDownloadable')

    templatable = False
    content_type = 'application/octet-stream'
    binary = True
    add_to_breadcrumbs = False

    def set_request_content_type(self):
        """overriden to set the correct filetype and filename"""
        entity = self.cw_rset.complete_entity(self.cw_row or 0, self.cw_col or 0)
        adapter = entity.cw_adapt_to('IDownloadable')
        encoding = adapter.download_encoding()
        if encoding in BINARY_ENCODINGS:
            contenttype = 'application/%s' % encoding
            encoding = None
        else:
            contenttype = adapter.download_content_type()
        self._cw.set_content_type(contenttype or self.content_type,
                                  filename=adapter.download_file_name(),
                                  encoding=encoding)

    def call(self):
        entity = self.cw_rset.complete_entity(self.cw_row or 0, self.cw_col or 0)
        adapter = entity.cw_adapt_to('IDownloadable')
        self.w(adapter.download_data())


class DownloadLinkView(EntityView):
    """view displaying a link to download the file"""
    __regid__ = 'downloadlink'
    __select__ = adaptable('IDownloadable')
    title = None # should not be listed in possible views


    def cell_call(self, row, col, title=None, **kwargs):
        entity = self.cw_rset.get_entity(row, col)
        url = xml_escape(entity.cw_adapt_to('IDownloadable').download_url())
        self.w(u'<a href="%s">%s</a>' % (url, xml_escape(title or entity.dc_title())))


class IDownloadablePrimaryView(primary.PrimaryView):
    __select__ = adaptable('IDownloadable')

    def render_entity_attributes(self, entity):
        super(IDownloadablePrimaryView, self).render_entity_attributes(entity)
        self.w(u'<div class="content">')
        adapter = entity.cw_adapt_to('IDownloadable')
        contenttype = adapter.download_content_type()
        if contenttype.startswith('image/'):
            self.wview('image', entity.cw_rset, row=entity.cw_row)
        else:
            self.wview('downloadlink', entity.cw_rset, title=self._cw._('download'), row=entity.cw_row)
            try:
                if ENGINE.has_input(contenttype):
                    # XXX expect File like schema (access to 'data' attribute)
                    self.w(entity.printable_value('data'))
            except TransformError:
                pass
            except Exception, ex:
                msg = self._cw._("can't display data, unexpected error: %s") \
                      % xml_escape(str(ex))
                self.w('<div class="error">%s</div>' % msg)
        self.w(u'</div>')


class IDownloadableLineView(baseviews.OneLineView):
    __select__ = adaptable('IDownloadable')

    def cell_call(self, row, col, title=None, **kwargs):
        """the oneline view is a link to download the file"""
        entity = self.cw_rset.get_entity(row, col)
        url = xml_escape(entity.absolute_url())
        adapter = entity.cw_adapt_to('IDownloadable')
        name = xml_escape(title or adapter.download_file_name())
        durl = xml_escape(adapter.download_url())
        self.w(u'<a href="%s">%s</a> [<a href="%s">%s</a>]' %
               (url, name, durl, self._cw._('download')))


class ImageView(EntityView):
    __regid__ = 'image'
    __select__ = has_mimetype('image/')

    title = _('image')

    def call(self):
        rset = self.cw_rset
        for i in xrange(len(rset)):
            self.w(u'<div class="efile">')
            self.wview(self.__regid__, rset, row=i, col=0)
            self.w(u'</div>')

    def cell_call(self, row, col, width=None, height=None, link=False):
        entity = self.cw_rset.get_entity(row, col)
        adapter = entity.cw_adapt_to('IDownloadable')
        #if entity.data_format.startswith('image/'):
        imgtag = u'<img src="%s" alt="%s" ' % (
            xml_escape(adapter.download_url()),
            (self._cw._('download %s')  % xml_escape(adapter.download_file_name())))
        if width:
            imgtag += u'width="%i" ' % width
        if height:
            imgtag += u'height="%i" ' % height
        imgtag += u'/>'
        if link:
            self.w(u'<a href="%s">%s</a>' % (entity.absolute_url(vid='download'),
                                             imgtag))
        else:
            self.w(imgtag)