# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1241044912 -7200 # Node ID 3a3263df6cdd8d1affbf7c715df6046e1d7ce48f # Parent 3f91ef2397d0a02bdcd7291848b2bde07a19d6ea new primary view using uicfg.rdisplay (major api cleanup) diff -r 3f91ef2397d0 -r 3a3263df6cdd web/uicfg.py --- a/web/uicfg.py Thu Apr 30 00:41:10 2009 +0200 +++ b/web/uicfg.py Thu Apr 30 00:41:52 2009 +0200 @@ -12,6 +12,48 @@ from cubicweb.rtags import RelationTags, RelationTagsSet +# primary view configuration ################################################## + +# how to display a relation in primary view. +# values a dict with the following keys: +# +# 'where', whose value may be one of: +# * 'attributes', display in the attributes section +# * 'relations', display in the relations section (below attributes) +# * 'sideboxes', display in the side boxes (beside attributes) +# if this key is missing, the relation won't be displayed at all. +# +# 'vid' is an optional view identifier +# +# 'label' is an optional label +# +# 'limit' is a boolean telling if the results should be limited according to +# the configuration +class RDisplayRelationTags(RelationTags): + def __init__(self): + super(RDisplayRelationTags, self).__init__() + self._counter = 0 + + def tag_relation(self, values, *args, **kwargs): + super(RDisplayRelationTags, self).tag_relation(values, *args, **kwargs) + if values: + values['order'] = self.get_timestamp() + + def get_timestamp(self): + self._counter += 1 + return self._counter + +rdisplay = RDisplayRelationTags() +for rtype in ('eid', 'creation_date', 'modification_date', + 'is', 'is_instance_of', 'identity', + 'owned_by', 'created_by', + 'in_state', 'wf_info_for', 'require_permission', + 'from_entity', 'to_entity', + 'see_also'): + rdisplay.tag_relation({}, ('*', rtype, '*'), 'subject') + rdisplay.tag_relation({}, ('*', rtype, '*'), 'object') + + # autoform.AutomaticEntityForm configuration ################################## # relations'category (eg primary/secondary/generic/metadata/generated) diff -r 3f91ef2397d0 -r 3a3263df6cdd web/views/basecomponents.py --- a/web/views/basecomponents.py Thu Apr 30 00:41:10 2009 +0200 +++ b/web/views/basecomponents.py Thu Apr 30 00:41:52 2009 +0200 @@ -16,7 +16,7 @@ from cubicweb.schema import display_name from cubicweb.web.htmlwidgets import MenuWidget, PopupBoxMenu, BoxSeparator, BoxLink -from cubicweb.web.component import Component, RelatedObjectsVComponent +from cubicweb.web import uicfg, component _ = unicode @@ -25,7 +25,7 @@ help=_('display the component or not')), } -class RQLInputForm(Component): +class RQLInputForm(component.Component): """build the rql input form, usually displayed in the header""" id = 'rqlinput' property_defs = VISIBLE_PROP_DEF @@ -54,7 +54,7 @@ self.w(u'') -class ApplLogo(Component): +class ApplLogo(component.Component): """build the application logo, usually displayed in the header""" id = 'logo' property_defs = VISIBLE_PROP_DEF @@ -66,7 +66,7 @@ % (self.req.base_url(), self.req.external_resource('LOGO'))) -class ApplHelp(Component): +class ApplHelp(component.Component): """build the help button, usually displayed in the header""" id = 'help' property_defs = VISIBLE_PROP_DEF @@ -76,7 +76,7 @@ self.req._(u'help'),)) -class UserLink(Component): +class UserLink(component.Component): """if the user is the anonymous user, build a link to login else a link to the connected user object with a loggout link """ @@ -120,7 +120,7 @@ % (self.build_url('login'), self.req._('login'))) -class ApplicationMessage(Component): +class ApplicationMessage(component.Component): """display application's messages given using the __message parameter into a special div section """ @@ -141,7 +141,7 @@ self.w(u'') -class ApplicationName(Component): +class ApplicationName(component.Component): """display the application name""" id = 'appliname' property_defs = VISIBLE_PROP_DEF @@ -151,19 +151,22 @@ self.req.property_value('ui.site-title'))) -class SeeAlsoVComponent(RelatedObjectsVComponent): +uicfg.rdisplay.tag_relation({}, ('*', 'see_also', '*'), 'subject') +uicfg.rdisplay.tag_relation({}, ('*', 'see_also', '*'), 'object') + +class SeeAlsoVComponent(component.RelatedObjectsVComponent): """display any entity's see also""" id = 'seealso' context = 'navcontentbottom' rtype = 'see_also' - target = 'object' + role = 'subject' order = 40 # register msg not generated since no entity use see_also in cubicweb itself title = _('contentnavigation_seealso') help = _('contentnavigation_seealso_description') -class EtypeRestrictionComponent(Component): +class EtypeRestrictionComponent(component.Component): """displays the list of entity types contained in the resultset to be able to filter accordingly. """ diff -r 3f91ef2397d0 -r 3a3263df6cdd web/views/baseviews.py --- a/web/views/baseviews.py Thu Apr 30 00:41:10 2009 +0200 +++ b/web/views/baseviews.py Thu Apr 30 00:41:52 2009 +0200 @@ -373,7 +373,6 @@ from cubicweb.web.views import boxes, xmlrss, primary PrimaryView = class_moved(primary.PrimaryView) -PRIMARY_SKIP_RELS = primary.PRIMARY_SKIP_RELS SideBoxView = class_moved(boxes.SideBoxView) XmlView = class_moved(xmlrss.XmlView) XmlItemView = class_moved(xmlrss.XmlItemView) diff -r 3f91ef2397d0 -r 3a3263df6cdd web/views/cwuser.py --- a/web/views/cwuser.py Thu Apr 30 00:41:10 2009 +0200 +++ b/web/views/cwuser.py Thu Apr 30 00:41:52 2009 +0200 @@ -26,6 +26,8 @@ uicfg.rmode.tag_relation('link', ('*', 'owned_by', 'CWUser'), 'object') uicfg.rmode.tag_relation('link', ('*', 'created_by', 'CWUser'), 'object') uicfg.rmode.tag_relation('create', ('*', 'bookmarked_by', 'CWUser'), 'object') +uicfg.rdisplay.tag_attribute({}, 'CWUser', 'firstname') +uicfg.rdisplay.tag_attribute({}, 'CWUser', 'surname') class UserPreferencesEntityAction(action.Action): @@ -44,23 +46,9 @@ class CWUserPrimaryView(PrimaryView): __select__ = implements('CWUser') - skip_attrs = ('firstname', 'surname') - - def iter_relations(self, entity): - # don't want to display user's entities - for rschema, targetschemas, x in super(CWUserPrimaryView, self).iter_relations(entity): - if x == 'object' and rschema.type in ('owned_by', 'for_user'): - continue - yield rschema, targetschemas, x - def content_title(self, entity): return entity.name() - def is_side_related(self, rschema, eschema): - # XXX only bookmarked_by defined in cw... - return rschema.type in ['interested_in', 'tags', - 'todo_by', 'bookmarked_by'] - class FoafView(EntityView): id = 'foaf' diff -r 3f91ef2397d0 -r 3a3263df6cdd web/views/emailaddress.py --- a/web/views/emailaddress.py Thu Apr 30 00:41:10 2009 +0200 +++ b/web/views/emailaddress.py Thu Apr 30 00:41:52 2009 +0200 @@ -20,7 +20,7 @@ self.skipeids = skipeids super(EmailAddressPrimaryView, self).cell_call(row, col) - def render_entity_attributes(self, entity, siderelations): + def render_entity_attributes(self, entity): self.w(u'

') entity.view('oneline', w=self.w) if not entity.canonical: @@ -53,7 +53,7 @@ emailofstr = ', '.join(e.view('oneline') for e in emailof) self.field(display_name(self.req, 'use_email', 'object'), emailofstr) - def render_entity_relations(self, entity, siderelations): + def render_entity_relations(self, entity): for i, email in enumerate(entity.related_emails(self.skipeids)): self.w(u'
' % (i%2 and 'even' or 'odd')) email.view('oneline', w=self.w, contexteid=entity.eid) @@ -64,7 +64,8 @@ __select__ = implements('EmailAddress') id = 'shortprimary' title = None # hidden view - def render_entity_attributes(self, entity, siderelations): + + def render_entity_attributes(self, entity): self.w(u'
') entity.view('oneline', w=self.w) self.w(u'
') diff -r 3f91ef2397d0 -r 3a3263df6cdd web/views/idownloadable.py --- a/web/views/idownloadable.py Thu Apr 30 00:41:10 2009 +0200 +++ b/web/views/idownloadable.py Thu Apr 30 00:41:52 2009 +0200 @@ -94,19 +94,11 @@ self.w(u'%s' % (url, html_escape(title or entity.dc_title()))) - class IDownloadablePrimaryView(baseviews.PrimaryView): __select__ = implements(IDownloadable) - # XXX File/Image attributes but this is not specified in the IDownloadable interface - skip_attrs = baseviews.PrimaryView.skip_attrs + ('data', 'name') - def render_entity_title(self, entity): - self.w(u'

%s %s

' - % (entity.dc_type().capitalize(), - html_escape(entity.dc_title()))) - - def render_entity_attributes(self, entity, siderelations): - super(IDownloadablePrimaryView, self).render_entity_attributes(entity, siderelations) + def render_entity_attributes(self, entity): + super(IDownloadablePrimaryView, self).render_entity_attributes(entity) self.w(u'
') contenttype = entity.download_content_type() if contenttype.startswith('image/'): @@ -123,10 +115,6 @@ self.w('
%s
' % msg) self.w(u'
') - def is_side_related(self, rschema, eschema): - """display all relations as side related""" - return True - class IDownloadableLineView(baseviews.OneLineView): __select__ = implements(IDownloadable) diff -r 3f91ef2397d0 -r 3a3263df6cdd web/views/primary.py --- a/web/views/primary.py Thu Apr 30 00:41:10 2009 +0200 +++ b/web/views/primary.py Thu Apr 30 00:41:52 2009 +0200 @@ -4,19 +4,18 @@ :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr """ +__docformat__ = "restructuredtext en" from warnings import warn +from logilab.mtconverter import html_escape + from cubicweb import Unauthorized from cubicweb.view import EntityView +from cubicweb.web.uicfg import rdisplay _ = unicode -PRIMARY_SKIP_RELS = set(['is', 'is_instance_of', 'identity', - 'owned_by', 'created_by', - 'in_state', 'wf_info_for', 'require_permission', - 'from_entity', 'to_entity', - 'see_also']) class PrimaryView(EntityView): """the full view of an non final entity""" @@ -25,10 +24,44 @@ show_attr_label = True show_rel_label = True skip_none = True - skip_attrs = ('eid', 'creation_date', 'modification_date') - skip_rels = () + rdisplay = rdisplay main_related_section = True + @classmethod + def vreg_initialization_completed(cls): + """set default category tags for relations where it's not yet defined in + the category relation tags + """ + for eschema in cls.schema.entities(): + for rschema, tschemas, role in eschema.relation_definitions(True): + for tschema in tschemas: + if role == 'subject': + X, Y = eschema, tschema + card = rschema.rproperty(X, Y, 'cardinality')[0] + composed = rschema.rproperty(X, Y, 'composite') == 'object' + else: + X, Y = tschema, eschema + card = rschema.rproperty(X, Y, 'cardinality')[1] + composed = rschema.rproperty(X, Y, 'composite') == 'subject' + displayinfo = cls.rdisplay.get(rschema, role, X, Y) + if displayinfo is None: + if rschema.is_final(): + if rschema.meta or tschema.type in ('Password', 'Bytes'): + where = None + else: + where = 'attributes' + elif card in '1+': + where = 'attributes' + elif composed: + where = 'relations' + else: + where = 'sideboxes' + displayinfo = {'where': where, + 'order': cls.rdisplay.get_timestamp()} + cls.rdisplay.tag_relation(displayinfo, (X, rschema, Y), + role) + displayinfo.setdefault('label', '%s_%s' % (rschema, role)) + def html_headers(self): """return a list of html headers (eg something to be inserted between and of the returned page @@ -41,6 +74,7 @@ self.row = row # XXX move render_entity implementation here self.render_entity(self.complete_entity(row, col)) + self.maxrelated = self.req.property_value('navigation.related-limit') def render_entity(self, entity): """return html to display the given entity""" @@ -48,8 +82,8 @@ self.render_entity_metadata(entity) # entity's attributes and relations, excluding meta data # if the entity isn't meta itself - boxes = self._preinit_side_related(entity) - if boxes: + boxes = self._prepare_side_boxes(entity) + if boxes or hasattr(self, 'render_side_related'): self.w(u'
') self.w(u'
') self.w(u'
') @@ -69,16 +103,14 @@ 'deprecated') self.render_entity_relations(entity, []) self.w(u'
') - if boxes: + if boxes or hasattr(self, 'render_side_related'): self.w(u'
') # side boxes self.w(u'
') - try: - self.render_side_related(entity) - except TypeError: # XXX bw compat - warn('siderelations argument of render_entity_relations is ' - 'deprecated') - self.render_entity_relations(entity, []) + if hasattr(self, 'render_side_related'): + warn('render_side_related is deprecated') + self.render_side_related(entity, []) + self.render_side_boxes(boxes) self.w(u'
') self.w(u'
') self.content_navigation_components('navcontentbottom') @@ -97,29 +129,16 @@ comp.dispatch(w=self.w, view=self) self.w(u'
') - def iter_attributes(self, entity): - for rschema, targetschema in entity.e_schema.attribute_definitions(): - if rschema.type in self.skip_attrs: - continue - yield rschema, targetschema - - def iter_relations(self, entity): - skip = set(self.skip_rels) - skip.update(PRIMARY_SKIP_RELS) - for rschema, targetschemas, x in entity.e_schema.relation_definitions(): - if rschema.type in skip: - continue - yield rschema, targetschemas, x - def render_entity_title(self, entity): title = self.content_title(entity) # deprecate content_title? if title: self.w(u'

%s %s

' % (entity.dc_type().capitalize(), title)) + def content_title(self, entity): - """default implementation return an empty string""" - return u'' + """default implementation return dc_title""" + return html_escape(entity.dc_title()) def render_entity_metadata(self, entity): entity.view('metadata', w=self.w) @@ -132,67 +151,39 @@ return u'' def render_entity_attributes(self, entity, siderelations=None): - for rschema, targetschema in self.iter_attributes(entity): - attr = rschema.type - if targetschema.type in ('Password', 'Bytes'): - continue - try: - wdg = entity.get_widget(attr) - except Exception, ex: - value = entity.printable_value(attr, entity[attr], targetschema.type) + for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'attributes'): + vid = displayinfo.get('vid', 'reledit') + if rschema.is_final(): + value = entity.view('reledit', rtype=rschema.type) else: - value = wdg.render(entity) + vid = displayinfo.get('vid', 'reledit') + rset = self._relation_rset(entity, rschema, role, displayinfo) + if rset: + value = self.view(rset, vid) + else: + value = None if self.skip_none and (value is None or value == ''): continue - if rschema.meta: - continue - self._render_related_entities(entity, rschema, value) - - def _preinit_side_related(self, entity): - self._sideboxes = [] - if hasattr(self, 'get_side_boxes_defs'): - self._sideboxes = [(label, rset, 'sidebox') for label, rset in self.get_side_boxes_defs(entity) - if rset] - else: - eschema = entity.e_schema - maxrelated = self.req.property_value('navigation.related-limit') - for rschema, targetschemas, role in self.iter_relations(entity): - if self.is_side_related(rschema, eschema): - try: - related = entity.related(rschema.type, role, limit=maxrelated+1) - except Unauthorized: - continue - if not related: - continue - label = display_name(self.req, rschema.type, role) - self._sideboxes.append((label, related, 'autolimited')) - self._contextboxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset, - row=self.row, view=self, - context='incontext')) - return self._sideboxes or self._contextboxes + self._render_attribute(rschema, value) def render_entity_relations(self, entity, siderelations=None): - eschema = entity.e_schema - for rschema, targetschemas, x in self.iter_relations(entity): - if not self.is_side_related(rschema, eschema): - try: - related = entity.related(rschema.type, x, limit=maxrelated+1) - except Unauthorized: - continue - self._render_related_entities(entity, rschema, related, x) + for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'relations'): + rset = self._relation_rset(entity, rschema, role, displayinfo) + if rset: + self._render_relation(rset, displayinfo, 'autolimited', + self.show_rel_label) - - def render_side_related(self, entity, siderelations=None): + def render_side_boxes(self, boxes): """display side related relations: non-meta in a first step, meta in a second step """ - if self._sideboxes: - for label, rset, vid in self._sideboxes: + for box in boxes: + if isinstance(box, tuple): + label, rset, vid, _ = box self.w(u'
') self.wview(vid, rset, title=label) self.w(u'
') - if self._contextboxes: - for box in self._contextboxes: + else: try: box.dispatch(w=self.w, row=self.row) except NotImplementedError: @@ -200,28 +191,73 @@ # .call() and not cell_call() box.dispatch(w=self.w) - def is_side_related(self, rschema, eschema): - return rschema.meta and \ - not rschema.schema_relation() == eschema.schema_entity() + def _prepare_side_boxes(self, entity): + sideboxes = [] + for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'sideboxes'): + rset = self._relation_rset(entity, rschema, role, displayinfo) + if not rset: + continue + label = display_name(self.req, rschema.type, role) + vid = displayinfo.get('vid', 'autolimited') + sideboxes.append((label, rset, vid, displayinfo.get('order'))) + sideboxes = sorted(sideboxes, key=lambda x: x[-1]) + sideboxes += list(self.vreg.possible_vobjects('boxes', self.req, self.rset, + row=self.row, view=self, + context='incontext')) + return sideboxes - def _render_related_entities(self, entity, rschema, related, - role='subject'): + def _iter_display(self, entity, where): + eschema = entity.e_schema + for rschema, tschemas, role in eschema.relation_definitions(True): + matchtschemas = [] + for tschema in tschemas: + displayinfo = self.rdisplay.etype_get(eschema, rschema, role, + tschema) + assert displayinfo is not None, (str(rschema), role, + str(eschema), str(tschema)) + if displayinfo.get('where') == where: + matchtschemas.append(tschema) + if matchtschemas: + # XXX pick the latest displayinfo + yield rschema, matchtschemas, role, displayinfo + + def _relation_rset(self, entity, rschema, role, displayinfo): + try: + if displayinfo.get('limit'): + rset = entity.related(rschema.type, role, + limit=self.maxrelated+1) + else: + rset = entity.related(rschema.type, role) + except Unauthorized: + return + if 'filter' in displayinfo: + rset = displayinfo['filter'](rset) + return rset + + def _render_relation(self, rset, displayinfo, defaultvid, showlabel): + self.w('
') + if showlabel: + label = self.req._(displayinfo['label']) + self.w('

%s

' % label) + self.wview(displayinfo.get('vid', defaultvid), rset) + self.w('
') + + def _render_attribute(self, rschema, value, role='subject'): if rschema.is_final(): - value = related show_label = self.show_attr_label else: - if not related: - return - value = self.view('autolimited', related) + show_label = self.show_rel_label label = display_name(self.req, rschema.type, role) self.field(label, value, show_label=show_label, tr=False) class RelatedView(EntityView): id = 'autolimited' - def call(self): + def call(self, title=None, **kwargs): # if not too many entities, show them all in a list maxrelated = self.req.property_value('navigation.related-limit') + if title: + self.w(u'
%s
' % title) if self.rset.rowcount <= maxrelated: if self.rset.rowcount == 1: self.wview('incontext', self.rset, row=0) @@ -234,7 +270,7 @@ # else show links to display related entities else: rql = self.rset.printable_rql() - self.rset.limit(maxself.rset) + self.rset.limit(maxrelated) self.w(u'
') self.wview('simplelist', self.rset) self.w(u'[%s]' % (self.build_url(rql=rql), diff -r 3f91ef2397d0 -r 3a3263df6cdd web/views/schema.py --- a/web/views/schema.py Thu Apr 30 00:41:10 2009 +0200 +++ b/web/views/schema.py Thu Apr 30 00:41:52 2009 +0200 @@ -16,7 +16,7 @@ from cubicweb.view import EntityView, StartupView from cubicweb.common.uilib import ureport_as_html from cubicweb.web import uicfg, action -from cubicweb.web.views import TmpFileViewMixin, baseviews +from cubicweb.web.views import TmpFileViewMixin, primary, baseviews uicfg.rcategories.tag_relation('primary', ('CWPermission', 'require_group', '*'), 'subject') @@ -33,6 +33,11 @@ uicfg.rmode.tag_relation('link', ('*', 'from_entity', 'CWEType'), 'object') uicfg.rmode.tag_relation('link', ('*', 'to_entity', 'CWEType'), 'object') +for attr in ('name', 'meta', 'final'): + uicfg.rdisplay.tag_attribute({}, 'CWRType', attr) +for attr in ('name', 'meta', 'final', 'symetric', 'inlined'): + uicfg.rdisplay.tag_attribute({}, 'CWRType', attr) + class ViewSchemaAction(action.Action): id = 'schema' @@ -48,25 +53,13 @@ # schema entity types views ################################################### -class _SchemaEntityPrimaryView(baseviews.PrimaryView): - show_attr_label = False +class CWRDEFPrimaryView(primary.PrimaryView): + __select__ = implements('CWAttribute', 'CWRelation') cache_max_age = 60*60*2 # stay in http cache for 2 hours by default def content_title(self, entity): return html_escape(entity.dc_long_title()) -class CWETypePrimaryView(_SchemaEntityPrimaryView): - __select__ = implements('CWEType') - skip_attrs = _SchemaEntityPrimaryView.skip_attrs + ('name', 'meta', 'final') - -class CWRTypePrimaryView(_SchemaEntityPrimaryView): - __select__ = implements('CWRType') - skip_attrs = _SchemaEntityPrimaryView.skip_attrs + ('name', 'meta', 'final', - 'symetric', 'inlined') - -class ErdefPrimaryView(_SchemaEntityPrimaryView): - __select__ = implements('CWAttribute', 'CWRelation') - show_attr_label = True class CWETypeOneLineView(baseviews.OneLineView): __select__ = implements('CWEType') @@ -82,13 +75,15 @@ # in memory schema views (yams class instances) ############################### +SKIPPED_RELS = ('is', 'is_instance_of', 'identity', 'created_by', 'owned_by', + 'has_text',) -class CWETypeSchemaView(CWETypePrimaryView): +class CWETypeSchemaView(primary.PrimaryView): id = 'eschema' + __select__ = implements('CWEType') title = _('in memory entity schema') main_related_section = False - skip_rels = ('is', 'is_instance_of', 'identity', 'created_by', 'owned_by', - 'has_text',) + skip_rels = SKIPPED_RELS def render_entity_attributes(self, entity, siderelations): super(CWETypeSchemaView, self).render_entity_attributes(entity, siderelations) @@ -102,8 +97,9 @@ html_escape(self.req._('graphical schema for %s') % entity.name))) -class CWRTypeSchemaView(CWRTypePrimaryView): +class CWRTypeSchemaView(primary.PrimaryView): id = 'eschema' + __select__ = implements('CWRType') title = _('in memory relation schema') main_related_section = False @@ -198,8 +194,9 @@ class SchemaImageView(TmpFileViewMixin, StartupView): id = 'schemagraph' + content_type = 'image/png' - skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of') + skip_rels = SKIPPED_RELS def _generate(self, tmpfile): """display global schema information""" skipmeta = not int(self.req.form.get('withmeta', 0)) @@ -209,9 +206,10 @@ class CWETypeSchemaImageView(TmpFileViewMixin, EntityView): id = 'eschemagraph' + __select__ = implements('CWEType') + content_type = 'image/png' - __select__ = implements('CWEType') - skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of') + skip_rels = SKIPPED_RELS def _generate(self, tmpfile): """display schema information for an entity""" diff -r 3f91ef2397d0 -r 3a3263df6cdd web/widgets.py --- a/web/widgets.py Thu Apr 30 00:41:10 2009 +0200 +++ b/web/widgets.py Thu Apr 30 00:41:52 2009 +0200 @@ -63,7 +63,7 @@ autoid = True html_attributes = set(('id', 'class', 'tabindex', 'accesskey', 'onchange', 'onkeypress')) cubicwebns_attributes = set() - + def __init__(self, vreg, subjschema, rschema, objschema, role='subject', description=None, **kwattrs): @@ -83,12 +83,12 @@ because widget instances are cached) """ # brute force copy (subclasses don't have the - # same __init__ prototype) + # same __init__ prototype) widget = self.__new__(self.__class__) widget.__dict__ = dict(self.__dict__) widget.attrs = dict(widget.attrs) return widget - + @staticmethod def size_constraint_attrs(attrs, maxsize): """set html attributes in the attrs dict to consider maxsize""" @@ -105,7 +105,7 @@ elif name in self.html_attributes: attrs.append(u'%s="%s"' % (name, value)) return u' '.join(sorted(attrs)) - + def required(self, entity): """indicates if the widget needs a value to be filled in""" card = self.rschema.cardinality(self.subjtype, self.objtype, self.role) @@ -116,7 +116,7 @@ return self.rname except AttributeError: return eid_param(self.name, entity.eid) - + def render_label(self, entity, label=None): """render widget's label""" label = label or self.rschema.display_name(entity.req, self.role) @@ -130,7 +130,7 @@ else: label = u'%s' % (forattr, label) return label - + def render_error(self, entity): """return validation error for widget's field of the given entity, if any @@ -153,16 +153,16 @@ help.append(u'(%s: %s)' % (req._('sample format'), example)) return u' '.join(help) - + def render_example(self, req): return u'' - + def render(self, entity): """render the widget for a simple view""" if not entity.has_eid(): return u'' return entity.printable_value(self.name) - + def edit_render(self, entity, tabindex=None, includehelp=False, useid=None, **kwargs): """render the widget for edition""" @@ -180,7 +180,7 @@ if includehelp: output += self.render_help(entity) return output - + def _edit_render(self, entity): """do the actual job to render the widget for edition""" raise NotImplementedError @@ -196,7 +196,7 @@ elif entity.has_eid(): return [row[0] for row in entity.related(self.name, self.role)] return () - + def current_value(self, entity): return _value_from_values(self.current_values(entity)) @@ -213,13 +213,13 @@ if not isinstance(cdvalues, (list, tuple)): cdvalues = (cdvalues,) return cdvalues - + def current_display_value(self, entity): """same as .current_value but consider values stored in session in case of validation error """ return _value_from_values(self.current_display_values(entity)) - + def hidden_input(self, entity, qvalue): """return an hidden field which 1. indicates that a field is edited @@ -258,7 +258,7 @@ def __init__(self, vreg, subjschema, rschema, objschema, role='subject', **kwattrs): InputWidget.__init__(self, vreg, subjschema, rschema, objschema, - role='subject', + role='subject', **kwattrs) # disable access key del self.attrs['accesskey'] @@ -270,18 +270,18 @@ def current_display_value(self, entity): value = InputWidget.current_display_value(self, entity) return value or INTERNAL_FIELD_VALUE - + def render_label(self, entity, label=None): """render widget's label""" return u'' - + def render_help(self, entity): return u'' - + def hidden_input(self, entity, value): """no hidden input for hidden input""" return '' - + class EidWidget(HiddenWidget): @@ -297,15 +297,15 @@ """set html attributes in the attrs dict to consider maxsize""" attrs['size'] = min(maxsize, 40) attrs['maxlength'] = maxsize - - + + class AutoCompletionWidget(StringWidget): cubicwebns_attributes = (StringWidget.cubicwebns_attributes | set(('accesskey', 'size', 'maxlength'))) attrs = () - + wdgtype = 'SuggestField' - + def current_value(self, entity): value = StringWidget.current_value(self, entity) return value or INTERNAL_FIELD_VALUE @@ -344,22 +344,22 @@ class StaticFileAutoCompletionWidget(AutoCompletionWidget): wdgtype = 'StaticFileSuggestField' - + def _get_url(self, entity): return entity.req.datadir_url + entity.autocomplete_initfuncs[self.rschema] class RestrictedAutoCompletionWidget(AutoCompletionWidget): - wdgtype = 'RestrictedSuggestField' + wdgtype = 'RestrictedSuggestField' - + class PasswordWidget(InputWidget): input_type = 'password' - + def required(self, entity): if InputWidget.required(self, entity) and not entity.has_eid(): return True return False - + def current_values(self, entity): # on existant entity, show password field has non empty (we don't have # the actual value @@ -374,10 +374,10 @@ html, self.input_type, name, name, entity.req.next_tabindex(), entity.req._('confirm password')) - + class TextWidget(Widget): html_attributes = Widget.html_attributes | set(('rows', 'cols')) - + @staticmethod def size_constraint_attrs(attrs, maxsize): """set html attributes in the attrs dict to consider maxsize""" @@ -385,12 +385,12 @@ attrs['cols'], attrs['rows'] = 60, 5 else: attrs['cols'], attrs['rows'] = 80, 10 - + def render(self, entity): if not entity.has_eid(): return u'' return entity.printable_value(self.name) - + def _edit_render(self, entity, with_format=True): req = entity.req editor = self._edit_render_textarea(entity, with_format) @@ -398,7 +398,7 @@ if isinstance(value, basestring): value = html_escape(value) return u'%s%s' % (self.hidden_input(entity, value), editor) - + def _edit_render_textarea(self, entity, with_format): self.attrs.setdefault('cols', 80) self.attrs.setdefault('rows', 20) @@ -426,8 +426,8 @@ fmtwdgstr = '' return u'%s
' % ( fmtwdgstr, self.rname, self.format_attrs(), dvalue) - - + + class CheckBoxWidget(Widget): html_attributes = Widget.html_attributes | set(('checked', )) def _edit_render(self, entity): @@ -460,7 +460,7 @@ u'%s
' % (self.rname, attrs2, entity.req._('no'))] return '\n'.join(wdgs) - + class FileWidget(Widget): need_multipart = True def _file_wdg(self, entity): @@ -492,7 +492,7 @@ wdgs.append(u'
') wdgs.append(req._('currently attached file: %s' % entity.dc_title())) return '\n'.join(wdgs) - + def _edit_render(self, entity): return self.hidden_input(entity, None) + self._file_wdg(entity) @@ -510,7 +510,7 @@ 'You can either submit a new file using the browse button above' ', or edit file content online with the widget below.') return msg - + def _edit_render(self, entity): wdgs = [self._file_wdg(entity)] if entity.attr_metadata(self.name, 'format') in ('text/plain', 'text/html', 'text/rest'): @@ -534,7 +534,7 @@ class ComboBoxWidget(Widget): html_attributes = Widget.html_attributes | set(('multiple', 'size')) - + def __init__(self, vreg, subjschema, rschema, objschema, multiple=False, **kwattrs): super(ComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema, @@ -545,10 +545,10 @@ self.attrs['size'] = '5' # disable access key (dunno why but this is not allowed by xhtml 1.0) del self.attrs['accesskey'] - + def vocabulary(self, entity): raise NotImplementedError() - + def form_value(self, entity, value, values): if value in values: flag = 'selected="selected"' @@ -574,9 +574,9 @@ res.append(u'') return '\n'.join(res) - + class StaticComboBoxWidget(ComboBoxWidget): - + def __init__(self, vreg, subjschema, rschema, objschema, vocabfunc, multiple=False, sort=False, **kwattrs): super(StaticComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema, @@ -591,11 +591,11 @@ if self.rschema.rproperty(self.subjtype, self.objtype, 'internationalizable'): return zip((entity.req._(v) for v in choices), choices) return zip(choices, choices) - + class EntityLinkComboBoxWidget(ComboBoxWidget): """to be used be specific forms""" - + def current_values(self, entity): if entity.has_eid(): return [r[0] for r in entity.related(self.name, self.role)] @@ -603,13 +603,13 @@ if hasattr(entity, defaultmeth): return getattr(entity, defaultmeth)() return () - + def vocabulary(self, entity): return [('', INTERNAL_FIELD_VALUE)] + entity.vocabulary(self.rschema, self.role) class RawDynamicComboBoxWidget(EntityLinkComboBoxWidget): - + def vocabulary(self, entity, limit=None): req = entity.req # first see if its specified by __linkto form parameters @@ -632,7 +632,7 @@ class DynamicComboBoxWidget(RawDynamicComboBoxWidget): - + def vocabulary(self, entity, limit=None): return sorted(super(DynamicComboBoxWidget, self).vocabulary(entity, limit)) @@ -669,11 +669,11 @@ kwattrs['size'] = 5 kwattrs['maxlength'] = 15 StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) - + def render_example(self, req): return '23' - - + + class FloatWidget(StringWidget): def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): kwattrs['size'] = 5 @@ -683,7 +683,7 @@ def render_example(self, req): formatstr = req.property_value('ui.float-format') return formatstr % 1.23 - + def current_values(self, entity): values = entity.attribute_values(self.name) if values: @@ -702,7 +702,7 @@ kwattrs['size'] = 5 kwattrs['maxlength'] = 15 StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) - + def render_example(self, req): return '345.0300' @@ -724,7 +724,7 @@ daynames = [_(dname) for dname in cls.daynames] req.html_headers.define_var('MONTHNAMES', monthnames) req.html_headers.define_var('DAYNAMES', daynames) - + def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs): kwattrs.setdefault('size', 10) kwattrs.setdefault('maxlength', 10) @@ -784,7 +784,7 @@ kwattrs['size'] = 16 kwattrs['maxlength'] = 16 DateWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) - + def render_example(self, req): formatstr1 = req.property_value('ui.datetime-format') formatstr2 = req.property_value('ui.date-format') @@ -801,26 +801,26 @@ kwattrs['maxlength'] = 5 StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs) - + class EmailWidget(StringWidget): - + def render(self, entity): email = getattr(entity, self.name) if not email: return u'' return u'%s' % (email, email) - + class URLWidget(StringWidget): - + def render(self, entity): url = getattr(entity, self.name) if not url: return u'' url = html_escape(url) return u'%s' % (url, url) - + class EmbededURLWidget(StringWidget): - + def render(self, entity): url = getattr(entity, self.name) if not url: @@ -828,7 +828,7 @@ aurl = html_escape(entity.build_url('embed', url=url)) return u'%s' % (aurl, url) - + def widget_factory(vreg, subjschema, rschema, objschema, role='subject', **kwargs): @@ -857,14 +857,14 @@ # factories to find the most adapated widget according to a type and other constraints - + def _string_widget_factory(vreg, subjschema, rschema, objschema, wcls=None, **kwargs): w = None for c in rschema.rproperty(subjschema, objschema, 'constraints'): if isinstance(c, StaticVocabularyConstraint): # may have been set by a previous SizeConstraint but doesn't make sense # here (even doesn't have the same meaning on a combobox actually) - kwargs.pop('size', None) + kwargs.pop('size', None) return (wcls or StaticComboBoxWidget)(vreg, subjschema, rschema, objschema, vocabfunc=c.vocabulary, **kwargs) if isinstance(c, SizeConstraint) and c.max is not None: @@ -914,7 +914,7 @@ 'String' : StringWidget, 'Time': TimeWidget, } - + # widgets registry WIDGETS = {} def register(widget_list):