# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1241074314 -7200 # Node ID de0548cb0ab854a85adb9dc7204843e6ce561ae1 # Parent f63d090eb34a5e4f81acedbb48a8c736339f920b# Parent b130c6cec8c2cf978f13e5597f023d524809ab4c merge diff -r b130c6cec8c2 -r de0548cb0ab8 entities/authobjs.py --- a/entities/authobjs.py Wed Apr 29 19:54:50 2009 +0200 +++ b/entities/authobjs.py Thu Apr 30 08:51:54 2009 +0200 @@ -24,7 +24,7 @@ id = 'CWUser' fetch_attrs, fetch_order = fetch_config(['login', 'firstname', 'surname']) fetch_unrelated_order = fetch_order - + # used by repository to check if the user can log in or not AUTHENTICABLE_STATES = ('activated',) @@ -37,7 +37,7 @@ self._groups = groups if properties is not None: self._properties = properties - + @property def groups(self): try: @@ -45,7 +45,7 @@ except AttributeError: self._groups = set(g.name for g in self.in_group) return self._groups - + @property def properties(self): try: @@ -64,7 +64,7 @@ except ValueError: self.warning('incorrect value for eproperty %s of user %s', key, self.login) return self.vreg.property_value(key) - + def matching_groups(self, groups): """return the number of the given group(s) in which the user is @@ -86,7 +86,7 @@ """ checks if user is an anonymous user""" #FIXME on the web-side anonymous user is detected according # to config['anonymous-user'], we don't have this info on - # the server side. + # the server side. return self.groups == frozenset(('guests', )) def owns(self, eid): @@ -116,12 +116,12 @@ return self.req.execute(rql, kwargs, cachekey) except Unauthorized: return False - + # presentation utilities ################################################## - + def name(self): """construct a name using firstname / surname or login if not defined""" - + if self.firstname and self.surname: return self.req._('%(firstname)s %(surname)s') % { 'firstname': self.firstname, 'surname' : self.surname} diff -r b130c6cec8c2 -r de0548cb0ab8 etwist/server.py --- a/etwist/server.py Wed Apr 29 19:54:50 2009 +0200 +++ b/etwist/server.py Thu Apr 30 08:51:54 2009 +0200 @@ -44,6 +44,14 @@ # ensure no tasks will be further added repo._looping_tasks = () +def host_prefixed_baseurl(baseurl, host): + scheme, netloc, url, query, fragment = urlsplit(baseurl) + netloc_domain = '.' + '.'.join(netloc.split('.')[-2:]) + if host.endswith(netloc_domain): + netloc = host + baseurl = urlunsplit((scheme, netloc, url, query, fragment)) + return baseurl + class LongTimeExpiringFile(static.File): """overrides static.File and sets a far futre ``Expires`` date @@ -169,11 +177,8 @@ https = False baseurl = self.base_url if self.config['use-request-subdomain']: - scheme, netloc, url, query, fragment = urlsplit(baseurl) - if '.' in netloc: - netloc = '.'.join(host.split('.')[:1] + netloc.split('.')[1:]) - baseurl = urlunsplit((scheme, netloc, url, query, fragment)) - self.warning('base_url is %s for this request', baseurl) + baseurl = host_prefixed_baseurl(baseurl, host) + self.warning('used baseurl is %s for this request', baseurl) req = CubicWebTwistedRequestAdapter(request, self.appli.vreg, https, baseurl) if req.authmode == 'http': # activate realm-based auth diff -r b130c6cec8c2 -r de0548cb0ab8 etwist/test/unittest_server.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/etwist/test/unittest_server.py Thu Apr 30 08:51:54 2009 +0200 @@ -0,0 +1,35 @@ +from cubicweb.devtools.apptest import EnvBasedTC +from cubicweb.etwist.server import host_prefixed_baseurl + + +class HostPrefixedBaseURLTC(EnvBasedTC): + + def _check(self, baseurl, host, waited): + self.assertEquals(host_prefixed_baseurl(baseurl, host), waited, + 'baseurl %s called through host %s should be considered as %s' + % (baseurl, host, waited)) + + def test1(self): + self._check('http://www.cubicweb.org/hg/', 'code.cubicweb.org', + 'http://code.cubicweb.org/hg/') + + def test2(self): + self._check('http://www.cubicweb.org/hg/', 'cubicweb.org', + 'http://www.cubicweb.org/hg/') + + def test3(self): + self._check('http://cubicweb.org/hg/', 'code.cubicweb.org', + 'http://code.cubicweb.org/hg/') + + def test4(self): + self._check('http://www.cubicweb.org/hg/', 'localhost', + 'http://www.cubicweb.org/hg/') + + def test5(self): + self._check('http://www.cubicweb.org/cubes/', 'hg.code.cubicweb.org', + 'http://hg.code.cubicweb.org/cubes/') + + def test6(self): + self._check('http://localhost:8080/hg/', 'code.cubicweb.org', + 'http://localhost:8080/hg/') + diff -r b130c6cec8c2 -r de0548cb0ab8 goa/skel/views.py --- a/goa/skel/views.py Wed Apr 29 19:54:50 2009 +0200 +++ b/goa/skel/views.py Thu Apr 30 08:51:54 2009 +0200 @@ -1,10 +1,8 @@ # custom application views - -from mx.DateTime import DateTime +from calendar import monthrange +from datetime import date -from cubicweb.web.views import baseviews -from cubicweb.web.views.boxes import BoxTemplate -from cubicweb.web.views.calendar import MONTHNAMES +from cubicweb.web.views import baseviews, boxes, calendar from cubicweb.web.htmlwidgets import BoxLink, BoxWidget _ = unicode @@ -12,15 +10,15 @@ class BlogEntryPrimaryView(baseviews.PrimaryView): accepts = ('BlogEntry',) - + def cell_call(self, row, col): entity = self.entity(row, col) self.w(u'
')
+ boxes = self._prepare_side_boxes(entity)
+ if boxes or hasattr(self, 'render_side_related'):
+ 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,38 @@ 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() or vid == 'reledit': + value = entity.view(vid, rtype=rschema.type, role=role) else: - value = wdg.render(entity) + rset = self._relation_rset(entity, rschema, role, displayinfo) + if rset: + value = self.view(vid, rset) + 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' ') - if self._contextboxes: - for box in self._contextboxes: + else: try: box.dispatch(w=self.w, row=self.row) except NotImplementedError: @@ -200,28 +190,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(' ')
+
+ 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' % label) + self.wview(displayinfo.get('vid', defaultvid), rset) + self.w('%s ' % title)
if self.rset.rowcount <= maxrelated:
if self.rset.rowcount == 1:
self.wview('incontext', self.rset, row=0)
@@ -234,7 +269,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 b130c6cec8c2 -r de0548cb0ab8 web/views/schema.py
--- a/web/views/schema.py Wed Apr 29 19:54:50 2009 +0200
+++ b/web/views/schema.py Thu Apr 30 08:51:54 2009 +0200
@@ -14,9 +14,9 @@
from cubicweb.selectors import implements, yes
from cubicweb.schemaviewer import SchemaViewer
from cubicweb.view import EntityView, StartupView
-from cubicweb.common.uilib import ureport_as_html
+from cubicweb.common import tags, uilib
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,41 +75,44 @@
# 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)
+ def render_entity_attributes(self, entity):
+ super(CWETypeSchemaView, self).render_entity_attributes(entity)
eschema = self.vreg.schema.eschema(entity.name)
viewer = SchemaViewer(self.req)
layout = viewer.visit_entityschema(eschema, skiprels=self.skip_rels)
- self.w(ureport_as_html(layout))
+ self.w(uilib.ureport_as_html(layout))
if not eschema.is_final():
- self.w(u' ' % ( 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): |