cubicweb/web/views/__init__.py
author Philippe Pepiot <philippe.pepiot@logilab.fr>
Thu, 19 Jan 2017 15:27:39 +0100
changeset 11899 bf6106b91633
parent 11767 432f87a63057
child 12503 b01dd0ef43aa
permissions -rw-r--r--
[schema] load schema from modules names instead of directories Introspect cubicweb, cubes and apphome using pkgutil to generate the full list of modules names for loading the schema. Keep historical behavior and check if source .py file exists if a module is found using python bytecode file (.pyc and .pyo) Loading schema from apphome require apphome to be present in sys.path and that "schema" module resolve to a file located in apphome. Update migraction tests to explicitely update sys.path when loading schema from different apps, use a contextmanager for this so it's more readable. Require updated logilab-common and yams

# copyright 2003-2011 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/>.
"""Views, forms, actions... for the CubicWeb web client"""



import os
import sys
import tempfile

from six import add_metaclass

from rql import nodes
from logilab.mtconverter import xml_escape
from logilab.common.deprecation import class_deprecated


def need_table_view(rset, schema):
    """return True if we think that a table view is more appropriate than a
    list or primary view to display the given result set
    """
    rqlst = rset.syntax_tree()
    if len(rqlst.children) > 1:
        # UNION query, use a table
        return True
    selected = rqlst.children[0].selection
    try:
        mainvar = selected[0]
    except AttributeError:
        # not a variable ref, using table view is probably a good option
        return True
    if not (isinstance(mainvar, nodes.VariableRef) or
            (isinstance(mainvar, nodes.Constant) and mainvar.uid)):
        return True
    for i, etype in enumerate(rset.description[0][1:]):
        # etype may be None on outer join
        if etype is None:
            return True
        # check the selected index node is a VariableRef (else we
        # won't detect aggregate function
        if not isinstance(selected[i+1], nodes.VariableRef):
            return True
        # if this is not a final entity
        if not schema.eschema(etype).final:
            return True
        # if this is a final entity not linked to the main variable
        var = selected[i+1].variable
        for vref in var.references():
            rel = vref.relation()
            if rel is None:
                continue
            if mainvar.is_equivalent(rel.children[0]):
                break
        else:
            return True
    return False

# FIXME: VID_BY_MIMETYPE is unfortunately a bit too naive since
#        some browsers (e.g. FF2) send a bunch of mimetypes in
#        the Accept header, for instance:
#          text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,
#          text/plain;q=0.8,image/png,*/*;q=0.5
VID_BY_MIMETYPE = {
    #'text/xml': 'xml',
    # XXX rss, owl...
}
def vid_from_rset(req, rset, schema, check_table=True):
    """given a result set, return a view id"""
    if rset is None:
        return 'index'
    for mimetype in req.parse_accept_header('Accept'):
        if mimetype in VID_BY_MIMETYPE:
            return VID_BY_MIMETYPE[mimetype]
    nb_rows = len(rset)
    # empty resultset
    if nb_rows == 0:
        return 'noresult'
    # entity result set
    if not schema.eschema(rset.description[0][0]).final:
        if check_table and need_table_view(rset, schema):
            return 'table'
        if nb_rows == 1:
            if req.search_state[0] == 'normal':
                return 'primary'
            return 'outofcontext-search'
        if len(rset.column_types(0)) == 1:
            return 'sameetypelist'
        return 'list'
    return 'table'


def linksearch_select_url(req, rset):
    """when searching an entity to create a relation, return a URL to select
    entities in the given rset
    """
    req.add_js( ('cubicweb.ajax.js', 'cubicweb.edition.js') )
    target, eid, r_type, searchedtype = req.search_state[1]
    if target == 'subject':
        id_fmt = '%s:%s:%%s' % (eid, r_type)
    else:
        id_fmt = '%%s:%s:%s' % (r_type, eid)
    triplets = '-'.join(id_fmt % row[0] for row in rset.rows)
    return "javascript: selectForAssociation('%s', '%s');" % (triplets, eid)


def add_etype_button(req, etype, csscls='addButton right', **urlkwargs):
    vreg = req.vreg
    eschema = vreg.schema.eschema(etype)
    if eschema.has_perm(req, 'add'):
        url = vreg['etypes'].etype_class(etype).cw_create_url(req, **urlkwargs)
        return u'<a href="%s" class="%s">%s</a>' % (
            xml_escape(url), csscls, req.__('New %s' % etype))
    return u''



@add_metaclass(class_deprecated)
class TmpFileViewMixin(object):
    __deprecation_warning__ = '[3.18] %(cls)s is deprecated'
    binary = True
    content_type = 'application/octet-stream'
    cache_max_age = 60*60*2 # stay in http cache for 2 hours by default

    def call(self):
        self.cell_call()

    def cell_call(self, row=0, col=0):
        self.cw_row, self.cw_col = row, col # in case one needs it
        fd, tmpfile = tempfile.mkstemp('.png')
        os.close(fd)
        self._generate(tmpfile)
        self.w(open(tmpfile, 'rb').read())
        os.unlink(tmpfile)