implement backward compatibility
authorNicolas Chauvat <nicolas.chauvat@logilab.fr>
Sun, 16 Dec 2012 23:32:04 +0100
changeset 4 be48ef70cff4
parent 2 4b919cd49017
child 6 63b988550d72
implement backward compatibility
views/actions.py
views/boxes.py
views/breadcrumbs.py
views/logintemplate.py
views/maintemplate.py
views/primary.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/views/actions.py	Sun Dec 16 23:32:04 2012 +0100
@@ -0,0 +1,3 @@
+def registration_callback(vreg):
+    from cubicweb.web.views.actions import GotRhythmAction
+    vreg.unregister(GotRhythmAction)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/views/boxes.py	Sun Dec 16 23:32:04 2012 +0100
@@ -0,0 +1,302 @@
+# 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/>.
+"""Generic boxes for CubicWeb web client:
+
+* actions box
+* search box
+
+Additional boxes (disabled by default):
+* schema box
+* possible views box
+* startup views box
+"""
+from __future__ import with_statement
+
+__docformat__ = "restructuredtext en"
+_ = unicode
+
+from warnings import warn
+
+from logilab.mtconverter import xml_escape
+from logilab.common.deprecation import class_deprecated
+
+from cubicweb import Unauthorized
+from cubicweb.predicates import (match_user_groups, match_kwargs,
+                                non_final_entity, nonempty_rset,
+                                match_context, contextual)
+from cubicweb.utils import wrap_on_write
+from cubicweb.view import EntityView
+from cubicweb.schema import display_name
+from cubicweb.web import component, box, htmlwidgets
+
+# XXX bw compat, some cubes import this class from here
+BoxTemplate = box.BoxTemplate
+BoxHtml = htmlwidgets.BoxHtml
+
+class EditBox(component.CtxComponent):
+    """
+    box with all actions impacting the entity displayed: edit, copy, delete
+    change state, add related entities...
+    """
+    __regid__ = 'edit_box'
+
+    title = _('actions')
+    order = 2
+    contextual = True
+    __select__ = component.CtxComponent.__select__ & non_final_entity()
+
+    def init_rendering(self):
+        super(EditBox, self).init_rendering()
+        _ = self._cw._
+        self._menus_in_order = []
+        self._menus_by_id = {}
+        # build list of actions
+        actions = self._cw.vreg['actions'].possible_actions(self._cw, self.cw_rset,
+                                                            **self.cw_extra_kwargs)
+        other_menu = self._get_menu('moreactions', _('more actions'))
+        for category, defaultmenu in (('mainactions', self),
+                                      ('moreactions', other_menu),
+                                      ('addrelated', None)):
+            for action in actions.get(category, ()):
+                if action.submenu:
+                    menu = self._get_menu(action.submenu)
+                else:
+                    menu = defaultmenu
+                action.fill_menu(self, menu)
+        # if we've nothing but actions in the other_menu, add them directly into the box
+        if not self.items and len(self._menus_by_id) == 1 and not other_menu.is_empty():
+            self.items = other_menu.items
+        else: # ensure 'more actions' menu appears last
+            self._menus_in_order.remove(other_menu)
+            self._menus_in_order.append(other_menu)
+            for submenu in self._menus_in_order:
+                self.add_submenu(self, submenu)
+        if not self.items:
+            raise component.EmptyComponent()
+
+    def render_title(self, w):
+        title = self._cw._(self.title)
+        if self.cw_rset:
+            etypes = self.cw_rset.column_types(0)
+            if len(etypes) == 1:
+                plural = self.cw_rset.rowcount > 1 and 'plural' or ''
+                etypelabel = display_name(self._cw, iter(etypes).next(), plural)
+                title = u'%s - %s' % (title, etypelabel.lower())
+        w(title)
+
+    def render_body(self, w):
+        self.render_items(w)
+
+    def _get_menu(self, id, title=None, label_prefix=None):
+        try:
+            return self._menus_by_id[id]
+        except KeyError:
+            if title is None:
+                title = self._cw._(id)
+            self._menus_by_id[id] = menu = htmlwidgets.BoxMenu(title)
+            menu.label_prefix = label_prefix
+            self._menus_in_order.append(menu)
+            return menu
+
+    def add_submenu(self, box, submenu, label_prefix=None):
+        appendanyway = getattr(submenu, 'append_anyway', False)
+        if len(submenu.items) == 1 and not appendanyway:
+            boxlink = submenu.items[0]
+            if submenu.label_prefix:
+                # XXX iirk
+                if hasattr(boxlink, 'label'):
+                    boxlink.label = u'%s %s' % (submenu.label_prefix, boxlink.label)
+                else:
+                    boxlink = u'%s %s' % (submenu.label_prefix, boxlink)
+            box.append(boxlink)
+        elif submenu.items:
+            box.append(submenu)
+        elif appendanyway:
+            box.append(xml_escape(submenu.label))
+
+
+class SearchBox(component.CtxComponent):
+    """display a box with a simple search form"""
+    __regid__ = 'search_box'
+
+    title = _('search')
+    order = 0
+    formdef = u"""<form action="%(action)s" class="form-search">
+    <div class="input-append">
+    <input class="norql span9 search-query" type="text" accesskey="q" tabindex="%(tabindex1)s" title="search text" value="%(value)s" name="rql" placeholder="Rechercher"/>
+    <button class="btn" type="submit"><i class="icon-search"></i></button>
+    </div>
+<input type="hidden" name="__fromsearchbox" value="1" />
+<input type="hidden" name="subvid" value="tsearch" />
+ </form>"""
+
+    def render_title(self, w):
+        # w(u"""<span onclick="javascript: toggleVisibility('rqlinput')">%s</span>"""
+        #% self._cw._(self.title))
+        pass
+
+    def render_body(self, w):
+        if self._cw.form.pop('__fromsearchbox', None):
+            rql = self._cw.form.get('rql', '')
+        else:
+            rql = ''
+        tabidx1 = self._cw.next_tabindex()
+        tabidx2 = self._cw.next_tabindex()
+        w(self.formdef % {'action': self._cw.build_url('view'),
+                          'value': xml_escape(rql),
+                          'id': self.cw_extra_kwargs.get('domid', 'tsearch'),
+                          'tabindex1': tabidx1,
+                          'tabindex2': tabidx2})
+
+
+# boxes disabled by default ###################################################
+
+class PossibleViewsBox(component.CtxComponent):
+    """display a box containing links to all possible views"""
+    __regid__ = 'possible_views_box'
+
+    contextual = True
+    title = _('possible views')
+    order = 10
+    visible = False # disabled by default
+
+    def init_rendering(self):
+        self.views = [v for v in self._cw.vreg['views'].possible_views(self._cw,
+                                                                       rset=self.cw_rset)
+                      if v.category != 'startupview']
+        if not self.views:
+            raise component.EmptyComponent()
+        self.items = []
+
+    def render_body(self, w):
+        for category, views in box.sort_by_category(self.views):
+            menu = htmlwidgets.BoxMenu(self._cw._(category), ident=category)
+            for view in views:
+                menu.append(self.action_link(view))
+            self.append(menu)
+        self.render_items(w)
+
+
+class StartupViewsBox(PossibleViewsBox):
+    """display a box containing links to all startup views"""
+    __regid__ = 'startup_views_box'
+
+    contextual = False
+    title = _('startup views')
+    order = 70
+    visible = False # disabled by default
+
+    def init_rendering(self):
+        self.views = [v for v in self._cw.vreg['views'].possible_views(self._cw)
+                      if v.category == 'startupview']
+        if not self.views:
+            raise component.EmptyComponent()
+        self.items = []
+
+
+class RsetBox(component.CtxComponent):
+    """helper view class to display an rset in a sidebox"""
+    __select__ = nonempty_rset() & match_kwargs('title', 'vid')
+    __regid__ = 'rsetbox'
+    cw_property_defs = {}
+    context = 'incontext'
+
+    @property
+    def domid(self):
+        return super(RsetBox, self).domid + unicode(abs(id(self))) + unicode(abs(id(self.cw_rset)))
+
+    def render_title(self, w):
+        w(self.cw_extra_kwargs['title'])
+
+    def render_body(self, w):
+        if 'dispctrl' in self.cw_extra_kwargs:
+            # XXX do not modify dispctrl!
+            self.cw_extra_kwargs['dispctrl'].setdefault('subvid', 'outofcontext')
+            self.cw_extra_kwargs['dispctrl'].setdefault('use_list_limit', 1)
+        self._cw.view(self.cw_extra_kwargs['vid'], self.cw_rset, w=w,
+                      initargs=self.cw_extra_kwargs)
+
+ # helper classes ##############################################################
+
+class SideBoxView(EntityView):
+    """helper view class to display some entities in a sidebox"""
+    __metaclass__ = class_deprecated
+    __deprecation_warning__ = '[3.10] SideBoxView is deprecated, use RsetBox instead (%(cls)s)'
+
+    __regid__ = 'sidebox'
+
+    def call(self, title=u'', **kwargs):
+        """display a list of entities by calling their <item_vid> view"""
+        if 'dispctrl' in self.cw_extra_kwargs:
+            # XXX do not modify dispctrl!
+            self.cw_extra_kwargs['dispctrl'].setdefault('subvid', 'outofcontext')
+            self.cw_extra_kwargs['dispctrl'].setdefault('use_list_limit', 1)
+        if title:
+            self.cw_extra_kwargs['title'] = title
+        self.cw_extra_kwargs.setdefault('context', 'incontext')
+        box = self._cw.vreg['ctxcomponents'].select(
+            'rsetbox', self._cw, rset=self.cw_rset, vid='autolimited',
+            **self.cw_extra_kwargs)
+        box.render(self.w)
+
+
+class ContextualBoxLayout(component.Layout):
+    __select__ = match_context('incontext', 'left', 'right') & contextual()
+    # predefined class in cubicweb.css: contextualBox | contextFreeBox
+    cssclass = 'contextualBox'
+
+    def render(self, w):
+        if self.init_rendering():
+            view = self.cw_extra_kwargs['view']
+            w(u'<div class="%s %s" id="%s">' % (self.cssclass, view.cssclass,
+                                                view.domid))
+            with wrap_on_write(w, u'<div class="boxTitle nav nav-list nav-header"><span>',
+                               u'</span></div>') as wow:
+                view.render_title(wow)
+            w(u'<div class="boxBody nav nav-list">')
+            view.render_body(w)
+            # boxFooter div is a CSS place holder (for shadow for example)
+            w(u'</div><div class="boxFooter"></div></div>\n')
+
+
+class ContextFreeBoxLayout(ContextualBoxLayout):
+    __select__ = match_context('incontext', 'left', 'right') & ~contextual()
+    cssclass = 'contextFreeBox'
+
+
+# XXX backward compatibility with cubicweb < 3.17 (before extraction of cube legacyui)
+from cubicweb import __version__ as cubicwebversion
+cubicwebversion = [int(item) for item in cubicwebversion.split('.')]
+
+if cubicwebversion < (3, 17, 0):
+    from cubicweb.web import LOGGER
+    LOGGER.warning('activating squareui backward compatibility mode. please consider using cubicweb >= 3.17 instead.')
+    from cubicweb.web.views import boxes as legacy
+    def registration_callback(vreg):
+        replacements = ((EditBox, legacy.EditBox),
+                        (SearchBox, legacy.SearchBox),
+                        (PossibleViewsBox, legacy.PossibleViewsBox),
+                        (StartupViewsBox, legacy.StartupViewsBox),
+                        (RsetBox, legacy.RsetBox),
+                        (SideBoxView, legacy.SideBoxView),
+                        (ContextualBoxLayout, legacy.ContextualBoxLayout),
+                        (ContextFreeBoxLayout, legacy.ContextFreeBoxLayout),
+                        )
+        vreg.register_all(globals().values(), __name__, [new for (new,old) in replacements])
+        for new, old in replacements:
+            vreg.register_and_replace(new, old)
--- a/views/breadcrumbs.py	Fri Dec 14 16:18:27 2012 +0100
+++ b/views/breadcrumbs.py	Sun Dec 16 23:32:04 2012 +0100
@@ -100,7 +100,21 @@
         w(u'</ul>')
 
 
-class BreadCrumbAnyRSetVComponent(basecomponents.HeaderComponent):
+class BreadCrumbETypeVComponent(BreadCrumbEntityVComponent):
+    __select__ = (basecomponents.HeaderComponent.__select__
+                  & multi_lines_rset() & one_etype_rset()
+                  & adaptable('IBreadCrumbs'))
+
+    # def render_breadcrumbs(self, w, contextentity, path):
+    #     # XXX hack: only display etype name or first non entity path part
+    #     root = path.pop(0)
+    #     if isinstance(root, Entity):
+    #         w(u'<a href="%s">%s</a>' % (self._cw.build_url(root.__regid__),
+    #                                     root.dc_type('plural')))
+    #     else:
+    #         self.wpath_part(w, root, contextentity, not path)
+
+class BreadCrumbAnyRSetVComponent(BreadCrumbEntityVComponent):
     __regid__ = 'breadcrumbs'
     __select__ = basecomponents.HeaderComponent.__select__ & any_rset()
 
@@ -122,3 +136,39 @@
             self.__regid__, self._cw, entity=entity, **kwargs)
         ecmp.render(w, **kwargs)
 
+
+# XXX backward compatibility with cubicweb < 3.17 (before extraction of cube legacyui)
+from cubicweb import __version__ as cubicwebversion
+cubicwebversion = [int(item) for item in cubicwebversion.split('.')]
+
+if cubicwebversion < (3, 17, 0):
+    from cubicweb.web import LOGGER
+    LOGGER.warning('activating squareui backward compatibility mode. please consider using cubicweb >= 3.17 instead.')
+    from cubicweb.web.views import ibreadcrumbs as legacy
+    from cubicweb.web.views import workflow, schema, cwsources, emailaddress
+    def registration_callback(vreg):
+        removed = (legacy.BreadCrumbView,
+                   legacy.BreadCrumbTextView,
+                   workflow.TrInfoIBreadCrumbsAdapter,
+                   workflow.WorkflowIBreadCrumbsAdapter,
+                   workflow.WorkflowItemIBreadCrumbsAdapter,
+                   workflow.TransitionItemIBreadCrumbsAdapter,
+                   schema.CWAttributeIBreadCrumbsAdapter,
+                   schema.RQLExpressionIBreadCrumbsAdapter,
+                   schema.CWRelationIBreadCrumbsAdapter,
+                   schema.CWConstraintIBreadCrumbsAdapter,
+                   cwsources.CWDataImportIBreadCrumbsAdapter,
+                   cwsources.CWsourceConfigIBreadCrumbsAdapter,
+                   emailaddress.EmailAddressIBreadCrumbsAdapter,
+                   )
+        replacements = ((IBreadCrumbsAdapter, legacy.IBreadCrumbsAdapter),
+                        (BreadCrumbEntityVComponent, legacy.BreadCrumbEntityVComponent),
+                        (BreadCrumbETypeVComponent, legacy.BreadCrumbETypeVComponent),
+                        (BreadCrumbAnyRSetVComponent, legacy.BreadCrumbAnyRSetVComponent),
+                        (BreadCrumbLinkToVComponent, legacy.BreadCrumbLinkToVComponent),
+                        )
+        for rem in removed:
+            vreg.unregister(rem)
+        vreg.register_all(globals().values(), __name__, [new for (new,old) in replacements])
+        for new, old in replacements:
+            vreg.register_and_replace(new, old)
--- a/views/logintemplate.py	Fri Dec 14 16:18:27 2012 +0100
+++ b/views/logintemplate.py	Sun Dec 16 23:32:04 2012 +0100
@@ -179,3 +179,23 @@
         cw.html_headers.add_onload('jQuery("#__login:visible").focus()')
 
 LogFormTemplate = class_renamed('LogFormTemplate', LogFormView)
+
+
+# XXX backward compatibility with cubicweb < 3.17 (before extraction of cube legacyui)
+from cubicweb import __version__ as cubicwebversion
+cubicwebversion = [int(item) for item in cubicwebversion.split('.')]
+
+if cubicwebversion < (3, 17, 0):
+    from cubicweb.web import LOGGER
+    LOGGER.warning('activating squareui backward compatibility mode. please consider using cubicweb >= 3.17 instead.')
+    from cubicweb.web.views import basetemplates as bt
+    def registration_callback(vreg):
+        replacements = (#(LogInOutTemplate, bt.LogInOutTemplate),
+                        (LogInTemplate, bt.LogInTemplate),
+                        (LoggedOutTemplate, bt.LoggedOutTemplate),
+                        (LogForm, bt.LogForm),
+                        (LogFormView, bt.LogFormView),
+                        )
+        vreg.register_all(globals().values(), __name__, [new for (new,old) in replacements])
+        for new, old in replacements:
+            vreg.register_and_replace(new, old)
--- a/views/maintemplate.py	Fri Dec 14 16:18:27 2012 +0100
+++ b/views/maintemplate.py	Sun Dec 16 23:32:04 2012 +0100
@@ -129,13 +129,16 @@
         self.wview('header', rset=self.cw_rset, view=view)
         w(u'<div class="row-fluid">')
         w(u'<div id="page"><div id="mainLayout">\n')
-        self.nav_column(view, 'left')
+        lboxes = list(self._cw.vreg['ctxcomponents'].poss_visible_objects(
+                self._cw, rset=self.cw_rset, view=view, context='left'))
         rboxes = list(self._cw.vreg['ctxcomponents'].poss_visible_objects(
                 self._cw, rset=self.cw_rset, view=view, context='right'))
+        width = 12
         if rboxes:
-            width = '6'
-        else:
-            width = '9'
+            width -= 3
+        if lboxes:
+            width -= 3
+        self.nav_column(view, 'left')
         w(u'<div id="contentColumn" class="span%s">\n' % width)
         components = self._cw.vreg['components']
         rqlcomp = components.select_or_none('rqlinput', self._cw, rset=self.cw_rset)
@@ -387,3 +390,25 @@
             w(u'</tr></table>\n')
 
 
+# XXX backward compatibility with cubicweb < 3.17 (before extraction of cube legacyui)
+from cubicweb import __version__ as cubicwebversion
+cubicwebversion = [int(item) for item in cubicwebversion.split('.')]
+
+if cubicwebversion < (3, 17, 0):
+    from cubicweb.web import LOGGER
+    LOGGER.warning('activating squareui backward compatibility mode. please consider using cubicweb >= 3.17 instead.')
+    from cubicweb.web.views import basetemplates as bt
+    def registration_callback(vreg):
+        replacements = ((NonTemplatableViewTemplate, bt.NonTemplatableViewTemplate),
+                        (TheMainTemplate, bt.TheMainTemplate),
+                        (HTMLHeader, bt.HTMLHeader),
+                        (HTMLPageHeader, bt.HTMLPageHeader),
+                        (HTMLPageFooter, bt.HTMLPageFooter),
+                        (HTMLContentHeader, bt.HTMLContentHeader),
+                        (HTMLContentFooter, bt.HTMLContentFooter),
+                        (ErrorTemplate, bt.ErrorTemplate),
+                        (SimpleMainTemplate, bt.SimpleMainTemplate),
+                        )
+        vreg.register_all(globals().values(), __name__, [new for (new,old) in replacements])
+        for new, old in replacements:
+            vreg.register_and_replace(new, old)
--- a/views/primary.py	Fri Dec 14 16:18:27 2012 +0100
+++ b/views/primary.py	Sun Dec 16 23:32:04 2012 +0100
@@ -474,3 +474,24 @@
     _pvs.tag_subject_of(('*', rtype, '*'), 'hidden')
     _pvs.tag_object_of(('*', rtype, '*'), 'hidden')
 
+
+
+# XXX backward compatibility with cubicweb < 3.17 (before extraction of cube legacyui)
+from cubicweb import __version__ as cubicwebversion
+cubicwebversion = [int(item) for item in cubicwebversion.split('.')]
+
+if cubicwebversion < (3, 17, 0):
+    from cubicweb.web import LOGGER
+    LOGGER.warning('activating squareui backward compatibility mode. please consider using cubicweb >= 3.17 instead.')
+    from cubicweb.web.views import primary as legacy
+    def registration_callback(vreg):
+        replacements = ((PrimaryView, legacy.PrimaryView),
+                        (RelatedView, legacy.RelatedView),
+                        (AttributeView, legacy.AttributeView),
+                        (URLAttributeView, legacy.URLAttributeView),
+                        (VerbatimAttributeView, legacy.VerbatimAttributeView),
+                        (ToolbarLayout, legacy.ToolbarLayout),
+                        )
+        vreg.register_all(globals().values(), __name__, [new for (new,old) in replacements])
+        for new, old in replacements:
+            vreg.register_and_replace(new, old)