# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1234981608 -3600 # Node ID a5e6acffde30554a500d3989adaca9a39b159535 # Parent cb8ccbef8fa5b836a718e3b50a3312634ca1f723 merge, split baseviews (new csvexport, xmlrss and editviews modules) diff -r cb8ccbef8fa5 -r a5e6acffde30 web/test/test_views.py --- a/web/test/test_views.py Wed Feb 18 19:19:57 2009 +0100 +++ b/web/test/test_views.py Wed Feb 18 19:26:48 2009 +0100 @@ -37,11 +37,11 @@ def test_manual_tests(self): rset = self.execute('Any P,F,S WHERE P is EUser, P firstname F, P surname S') self.view('table', rset, template=None, displayfilter=True, displaycols=[0,2]) - rset = self.execute('Any P,F,S LIMIT 1 WHERE P is EUser, P firstname F, P surname S') - rset.req.form['rtype'] = 'firstname' - self.view('editrelation', rset, template=None) - rset.req.form['rtype'] = 'use_email' - self.view('editrelation', rset, template=None) +# rset = self.execute('Any P,F,S LIMIT 1 WHERE P is EUser, P firstname F, P surname S') +# rset.req.form['rtype'] = 'firstname' +# self.view('editrelation', rset, template=None) +# rset.req.form['rtype'] = 'use_email' +# self.view('editrelation', rset, template=None) def test_sortable_js_added(self): diff -r cb8ccbef8fa5 -r a5e6acffde30 web/views/actions.py --- a/web/views/actions.py Wed Feb 18 19:19:57 2009 +0100 +++ b/web/views/actions.py Wed Feb 18 19:26:48 2009 +0100 @@ -14,8 +14,7 @@ has_editable_relation, has_permission, has_add_permission, ) from cubicweb.web.action import Action -from cubicweb.web.views import linksearch_select_url -from cubicweb.web.views.baseviews import vid_from_rset +from cubicweb.web.views import linksearch_select_url, vid_from_rset _ = unicode diff -r cb8ccbef8fa5 -r a5e6acffde30 web/views/baseforms.py --- a/web/views/baseforms.py Wed Feb 18 19:19:57 2009 +0100 +++ b/web/views/baseforms.py Wed Feb 18 19:26:48 2009 +0100 @@ -639,7 +639,6 @@ __select__ = (match_kwargs('ptype', 'peid', 'rtype') & specified_etype_implements('Any')) - EDITION_BODY = u'''\
@@ -670,8 +669,6 @@ self.w(self.req._('no such entity type %s') % etype) return self.edit_form(entity, ptype, peid, rtype, role, **kwargs) - - class InlineEntityEditionForm(InlineFormMixIn, EditionForm): @@ -728,7 +725,6 @@ ctx['count'] = entity.row + 1 return ctx - class CopyEditionForm(EditionForm): id = 'copy' @@ -773,7 +769,6 @@ return self.req._('element copied') - class TableEditForm(FormMixIn, EntityView): id = 'muledit' title = _('multiple edit') @@ -874,126 +869,10 @@ 'widget': wobj.edit_render(entity)}) w(u'') return '\n'.join(html) - - -class UnrelatedDivs(EntityView): - id = 'unrelateddivs' - __select__ = match_form_params('relation') - - @property - def limit(self): - if self.req.form.get('__force_display'): - return None - return self.req.property_value('navigation.related-limit') + 1 - - def cell_call(self, row, col): - entity = self.entity(row, col) - relname, target = self.req.form.get('relation').rsplit('_', 1) - rschema = self.schema.rschema(relname) - hidden = 'hidden' in self.req.form - is_cell = 'is_cell' in self.req.form - self.w(self.build_unrelated_select_div(entity, rschema, target, - is_cell=is_cell, hidden=hidden)) - - def build_unrelated_select_div(self, entity, rschema, target, - is_cell=False, hidden=True): - options = [] - divid = 'div%s_%s_%s' % (rschema.type, target, entity.eid) - selectid = 'select%s_%s_%s' % (rschema.type, target, entity.eid) - if rschema.symetric or target == 'subject': - targettypes = rschema.objects(entity.e_schema) - etypes = '/'.join(sorted(etype.display_name(self.req) for etype in targettypes)) - else: - targettypes = rschema.subjects(entity.e_schema) - etypes = '/'.join(sorted(etype.display_name(self.req) for etype in targettypes)) - etypes = cut(etypes, self.req.property_value('navigation.short-line-size')) - options.append('' % (self.req._('select a'), etypes)) - options += self._get_select_options(entity, rschema, target) - options += self._get_search_options(entity, rschema, target, targettypes) - if 'Basket' in self.schema: # XXX - options += self._get_basket_options(entity, rschema, target, targettypes) - relname, target = self.req.form.get('relation').rsplit('_', 1) - return u"""\ -
- -
-""" % (hidden and 'hidden' or '', divid, selectid, html_escape(dumps(entity.eid)), - is_cell and 'true' or 'null', relname, '\n'.join(options)) - - def _get_select_options(self, entity, rschema, target): - """add options to search among all entities of each possible type""" - options = [] - eid = entity.eid - pending_inserts = self.req.get_pending_inserts(eid) - rtype = rschema.type - for eview, reid in entity.vocabulary(rschema, target, self.limit): - if reid is None: - options.append('' % html_escape(eview)) - else: - optionid = relation_id(eid, rtype, target, reid) - if optionid not in pending_inserts: - # prefix option's id with letters to make valid XHTML wise - options.append('' % - (optionid, reid, html_escape(eview))) - return options - - def _get_search_options(self, entity, rschema, target, targettypes): - """add options to search among all entities of each possible type""" - options = [] - _ = self.req._ - for eschema in targettypes: - mode = '%s:%s:%s:%s' % (target, entity.eid, rschema.type, eschema) - url = self.build_url(entity.rest_path(), vid='search-associate', - __mode=mode) - options.append((eschema.display_name(self.req), - '' % ( - html_escape(url), _('Search for'), eschema.display_name(self.req)))) - return [o for l, o in sorted(options)] - - def _get_basket_options(self, entity, rschema, target, targettypes): - options = [] - rtype = rschema.type - _ = self.req._ - for basketeid, basketname in self._get_basket_links(self.req.user.eid, - target, targettypes): - optionid = relation_id(entity.eid, rtype, target, basketeid) - options.append('' % ( - optionid, basketeid, _('link to each item in'), html_escape(basketname))) - return options - - def _get_basket_links(self, ueid, target, targettypes): - targettypes = set(targettypes) - for basketeid, basketname, elements in self._get_basket_info(ueid): - baskettypes = elements.column_types(0) - # if every elements in the basket can be attached to the - # edited entity - if baskettypes & targettypes: - yield basketeid, basketname - - def _get_basket_info(self, ueid): - basketref = [] - basketrql = 'Any B,N WHERE B is Basket, B owned_by U, U eid %(x)s, B name N' - basketresultset = self.req.execute(basketrql, {'x': ueid}, 'x') - for result in basketresultset: - basketitemsrql = 'Any X WHERE X in_basket B, B eid %(x)s' - rset = self.req.execute(basketitemsrql, {'x': result[0]}, 'x') - basketref.append((result[0], result[1], rset)) - return basketref -class ComboboxView(EntityView): - """the view used in combobox (unrelated entities) +# XXX bw compat - THIS IS A TEXT VIEW. DO NOT HTML_ESCAPE - """ - id = 'combobox' - title = None - - def cell_call(self, row, col): - """the combo-box view for an entity: same as text out of context view - by default - """ - self.wview('textoutofcontext', self.rset, row=row, col=col) - +from logilab.common.deprecation import class_moved +from cubicweb.web.views import editviews +ComboboxView = class_moved(editviews.ComboboxView) diff -r cb8ccbef8fa5 -r a5e6acffde30 web/views/baseviews.py --- a/web/views/baseviews.py Wed Feb 18 19:19:57 2009 +0100 +++ b/web/views/baseviews.py Wed Feb 18 19:26:48 2009 +0100 @@ -14,21 +14,15 @@ __docformat__ = "restructuredtext en" from warnings import warn -from time import timezone from rql import nodes -from logilab.common.decorators import cached -from logilab.mtconverter import TransformError, html_escape, xml_escape +from logilab.mtconverter import TransformError, html_escape -from cubicweb import Unauthorized, NoSelectableObject, typed_eid -from cubicweb.selectors import (yes, empty_rset, nonempty_rset, one_line_rset, - non_final_entity, match_search_state, match_form_params) +from cubicweb import Unauthorized, NoSelectableObject +from cubicweb.selectors import yes, empty_rset from cubicweb.view import EntityView, AnyRsetView, View -from cubicweb.common.uilib import (cut, printable_value, UnicodeCSVWriter, - ajax_replace_url, rql_for_eid, simple_sgml_tag) -from cubicweb.web.httpcache import MaxAgeHTTPCacheManager -from cubicweb.web.views import vid_from_rset, linksearch_select_url +from cubicweb.common.uilib import cut, printable_value _ = unicode @@ -87,19 +81,6 @@ return self.wdata(printable_value(self.req, etype, value, props, displaytime=displaytime)) - -class EditableFinalView(FinalView): - """same as FinalView but enables inplace-edition when possible""" - id = 'editable-final' - - def cell_call(self, row, col, props=None, displaytime=False): - etype = self.rset.description[row][col] - value = self.rset.rows[row][col] - entity, rtype = self.rset.related_entity(row, col) - if entity is not None: - self.w(entity.view('reledit', rtype=rtype)) - else: - super(EditableFinalView, self).cell_call(row, col, props, displaytime) PRIMARY_SKIP_RELS = set(['is', 'is_instance_of', 'identity', 'owned_by', 'created_by', @@ -171,9 +152,8 @@ def iter_attributes(self, entity): for rschema, targetschema in entity.e_schema.attribute_definitions(): - attr = rschema.type - if attr in self.skip_attrs: - continue + if rschema.type in self.skip_attrs: + continue yield rschema, targetschema def iter_relations(self, entity): @@ -298,36 +278,6 @@ label = display_name(self.req, rschema.type, role) self.field(label, value, show_label=show_label, w=self.w, tr=False) - -class SideBoxView(EntityView): - """side box usually displaying some related entities in a primary view""" - id = 'sidebox' - - def call(self, boxclass='sideBox', title=u''): - """display a list of entities by calling their view - """ - if title: - self.w(u'
%s
' % title) - self.w(u'
' % boxclass) - # if not too much entities, show them all in a list - maxrelated = self.req.property_value('navigation.related-limit') - if self.rset.rowcount <= maxrelated: - if len(self.rset) == 1: - self.wview('incontext', self.rset, row=0) - elif 1 < len(self.rset) < 5: - self.wview('csv', self.rset) - else: - self.wview('simplelist', self.rset) - # else show links to display related entities - else: - self.rset.limit(maxrelated) - rql = self.rset.printable_rql(encoded=False) - self.wview('simplelist', self.rset) - self.w(u'[%s]' % (self.build_url(rql=rql), - self.req._('see them all'))) - self.w(u'
\n
\n') - - class SecondaryView(EntityView): id = 'secondary' @@ -341,6 +291,7 @@ self.w(u' ') self.wview('oneline', self.rset, row=row, col=col) + class OneLineView(EntityView): id = 'oneline' title = _('oneline') @@ -353,6 +304,7 @@ self.w(html_escape(self.view('text', self.rset, row=row, col=col))) self.w(u'') + class TextView(EntityView): """the simplest text view for an entity""" id = 'text' @@ -415,7 +367,8 @@ def cell_call(self, row, col): entity = self.entity(row, col) self.w(entity.dc_title()) - + + class OutOfContextTextView(InContextTextView): id = 'textoutofcontext' @@ -443,8 +396,9 @@ self.w(u'' % self.entity(row, col).absolute_url()) self.w(html_escape(self.view('textoutofcontext', self.rset, row=row, col=col))) self.w(u'') + -# list and table related views ################################################ +# list views ################################################################## class ListView(EntityView): id = 'list' @@ -545,387 +499,7 @@ def cell_call(self, row, col): self.wview('incontext', self.rset, row=row, col=col) - -# xml and xml/rss views ####################################################### - -class XmlView(EntityView): - id = 'xml' - title = _('xml') - templatable = False - content_type = 'text/xml' - xml_root = 'rset' - item_vid = 'xmlitem' - - def cell_call(self, row, col): - self.wview(self.item_vid, self.rset, row=row, col=col) - - def call(self): - """display a list of entities by calling their view""" - self.w(u'\n' % self.req.encoding) - self.w(u'<%s size="%s">\n' % (self.xml_root, len(self.rset))) - for i in xrange(self.rset.rowcount): - self.cell_call(i, 0) - self.w(u'\n' % self.xml_root) - - -class XmlItemView(EntityView): - id = 'xmlitem' - - def cell_call(self, row, col): - """ element as an item for an xml feed """ - entity = self.complete_entity(row, col) - self.w(u'<%s>\n' % (entity.e_schema)) - for rschema, attrschema in entity.e_schema.attribute_definitions(): - attr = rschema.type - try: - value = entity[attr] - except KeyError: - # Bytes - continue - if value is not None: - if attrschema == 'Bytes': - from base64 import b64encode - value = '' % b64encode(value.getvalue()) - elif isinstance(value, basestring): - value = xml_escape(value) - self.w(u' <%s>%s\n' % (attr, value, attr)) - self.w(u'\n' % (entity.e_schema)) - - - -class XMLRsetView(AnyRsetView): - """dumps xml in CSV""" - id = 'rsetxml' - title = _('xml export') - templatable = False - content_type = 'text/xml' - xml_root = 'rset' - - def call(self): - w = self.w - rset, descr = self.rset, self.rset.description - eschema = self.schema.eschema - labels = self.columns_labels(False) - w(u'\n' % self.req.encoding) - w(u'<%s query="%s">\n' % (self.xml_root, html_escape(rset.printable_rql()))) - for rowindex, row in enumerate(self.rset): - w(u' \n') - for colindex, val in enumerate(row): - etype = descr[rowindex][colindex] - tag = labels[colindex] - attrs = {} - if '(' in tag: - attrs['expr'] = tag - tag = 'funccall' - if val is not None and not eschema(etype).is_final(): - attrs['eid'] = val - # csvrow.append(val) # val is eid in that case - val = self.view('textincontext', rset, - row=rowindex, col=colindex) - else: - val = self.view('final', rset, displaytime=True, - row=rowindex, col=colindex, format='text/plain') - w(simple_sgml_tag(tag, val, **attrs)) - w(u' \n') - w(u'\n' % self.xml_root) - - -class RssView(XmlView): - id = 'rss' - title = _('rss') - templatable = False - content_type = 'text/xml' - http_cache_manager = MaxAgeHTTPCacheManager - cache_max_age = 60*60*2 # stay in http cache for 2 hours by default - - def cell_call(self, row, col): - self.wview('rssitem', self.rset, row=row, col=col) - - def call(self): - """display a list of entities by calling their view""" - req = self.req - self.w(u'\n' % req.encoding) - self.w(u'''''') - self.w(u' \n' % html_escape(req.url())) - self.w(u' %s RSS Feed\n' % html_escape(self.page_title())) - self.w(u' %s\n' % html_escape(req.form.get('vtitle', ''))) - params = req.form.copy() - params.pop('vid', None) - self.w(u' %s\n' % html_escape(self.build_url(**params))) - self.w(u' \n') - self.w(u' \n') - for entity in self.rset.entities(): - self.w(u' \n' % html_escape(entity.absolute_url())) - self.w(u' \n') - self.w(u' \n') - self.w(u' \n') - for i in xrange(self.rset.rowcount): - self.cell_call(i, 0) - self.w(u'') - - -class RssItemView(EntityView): - id = 'rssitem' - date_format = '%%Y-%%m-%%dT%%H:%%M%+03i:00' % (timezone / 3600) - - def cell_call(self, row, col): - entity = self.complete_entity(row, col) - self.w(u'\n' % html_escape(entity.absolute_url())) - self._marker('title', entity.dc_long_title()) - self._marker('link', entity.absolute_url()) - self._marker('description', entity.dc_description()) - self._marker('dc:date', entity.dc_date(self.date_format)) - if entity.creator: - self.w(u'') - self._marker('name', entity.creator.name()) - email = entity.creator.get_email() - if email: - self._marker('email', email) - self.w(u'') - self.w(u'\n') - - def _marker(self, marker, value): - if value: - self.w(u' <%s>%s\n' % (marker, html_escape(value), marker)) - - -class CSVMixIn(object): - """mixin class for CSV views""" - templatable = False - content_type = "text/comma-separated-values" - binary = True # avoid unicode assertion - csv_params = {'dialect': 'excel', - 'quotechar': '"', - 'delimiter': ';', - 'lineterminator': '\n'} - - def set_request_content_type(self): - """overriden to set a .csv filename""" - self.req.set_content_type(self.content_type, filename='cubicwebexport.csv') - - def csvwriter(self, **kwargs): - params = self.csv_params.copy() - params.update(kwargs) - return UnicodeCSVWriter(self.w, self.req.encoding, **params) - - -class CSVRsetView(CSVMixIn, AnyRsetView): - """dumps rset in CSV""" - id = 'csvexport' - title = _('csv export') - - def call(self): - writer = self.csvwriter() - writer.writerow(self.columns_labels()) - rset, descr = self.rset, self.rset.description - eschema = self.schema.eschema - for rowindex, row in enumerate(rset): - csvrow = [] - for colindex, val in enumerate(row): - etype = descr[rowindex][colindex] - if val is not None and not eschema(etype).is_final(): - # csvrow.append(val) # val is eid in that case - content = self.view('textincontext', rset, - row=rowindex, col=colindex) - else: - content = self.view('final', rset, - displaytime=True, format='text/plain', - row=rowindex, col=colindex) - csvrow.append(content) - writer.writerow(csvrow) - - -class CSVEntityView(CSVMixIn, EntityView): - """dumps rset's entities (with full set of attributes) in CSV""" - id = 'ecsvexport' - title = _('csv entities export') - - def call(self): - """ - the generated CSV file will have a table per entity type - found in the resultset. ('table' here only means empty - lines separation between table contents) - """ - req = self.req - rows_by_type = {} - writer = self.csvwriter() - rowdef_by_type = {} - for index in xrange(len(self.rset)): - entity = self.complete_entity(index) - if entity.e_schema not in rows_by_type: - rowdef_by_type[entity.e_schema] = [rs for rs, at in entity.e_schema.attribute_definitions() - if at != 'Bytes'] - rows_by_type[entity.e_schema] = [[display_name(req, rschema.type) - for rschema in rowdef_by_type[entity.e_schema]]] - rows = rows_by_type[entity.e_schema] - rows.append([entity.printable_value(rs.type, format='text/plain') - for rs in rowdef_by_type[entity.e_schema]]) - for etype, rows in rows_by_type.items(): - writer.writerows(rows) - # use two empty lines as separator - writer.writerows([[], []]) - - -## Work in progress ########################################################### - -class SearchForAssociationView(EntityView): - """view called by the edition view when the user asks - to search for something to link to the edited eid - """ - id = 'search-associate' - __select__ = (one_line_rset() & match_search_state('linksearch') - & non_final_entity()) - - title = _('search for association') - - def cell_call(self, row, col): - rset, vid, divid, paginate = self.filter_box_context_info() - self.w(u'
' % divid) - self.pagination(self.req, rset, w=self.w) - self.wview(vid, rset, 'noresult') - self.w(u'
') - - @cached - def filter_box_context_info(self): - entity = self.entity(0, 0) - role, eid, rtype, etype = self.req.search_state[1] - assert entity.eid == typed_eid(eid) - # the default behaviour is to fetch all unrelated entities and display - # them. Use fetch_order and not fetch_unrelated_order as sort method - # since the latter is mainly there to select relevant items in the combo - # box, it doesn't give interesting result in this context - rql = entity.unrelated_rql(rtype, etype, role, - ordermethod='fetch_order', - vocabconstraints=False) - rset = self.req.execute(rql, {'x' : entity.eid}, 'x') - #vid = vid_from_rset(self.req, rset, self.schema) - return rset, 'list', "search-associate-content", True - - -class OutOfContextSearch(EntityView): - id = 'outofcontext-search' - def cell_call(self, row, col): - entity = self.entity(row, col) - erset = entity.as_rset() - if self.req.match_search_state(erset): - self.w(u'%s [...]' % ( - html_escape(linksearch_select_url(self.req, erset)), - self.req._('select this entity'), - html_escape(entity.view('textoutofcontext')), - html_escape(entity.absolute_url(vid='primary')), - self.req._('view detail for this entity'))) - else: - entity.view('outofcontext', w=self.w) - - -class EditRelationView(EntityView): - """Note: This is work in progress - - This view is part of the edition view refactoring. - It is still too big and cluttered with strange logic, but it's a start - - The main idea is to be able to call an edition view for a specific - relation. For example : - self.wview('editrelation', person_rset, rtype='firstname') - self.wview('editrelation', person_rset, rtype='works_for') - """ - id = 'editrelation' - - __select__ = match_form_params('rtype') - - # TODO: inlineview, multiple edit, (widget view ?) - def cell_call(self, row, col, rtype=None, role='subject', targettype=None, - showlabel=True): - self.req.add_js( ('cubicweb.ajax.js', 'cubicweb.edition.js') ) - entity = self.entity(row, col) - rtype = self.req.form.get('rtype', rtype) - showlabel = self.req.form.get('showlabel', showlabel) - assert rtype is not None, "rtype is mandatory for 'edirelation' view" - targettype = self.req.form.get('targettype', targettype) - role = self.req.form.get('role', role) - category = entity.rtags.get_category(rtype, targettype, role) - if category in ('primary', 'secondary') or self.schema.rschema(rtype).is_final(): - if hasattr(entity, '%s_format' % rtype): - formatwdg = entity.get_widget('%s_format' % rtype, role) - self.w(formatwdg.edit_render(entity)) - self.w(u'
') - wdg = entity.get_widget(rtype, role) - if showlabel: - self.w(u'%s' % wdg.render_label(entity)) - self.w(u'%s %s %s' % - (wdg.render_error(entity), wdg.edit_render(entity), - wdg.render_help(entity),)) - else: - self._render_generic_relation(entity, rtype, role) - - def _render_generic_relation(self, entity, relname, role): - text = self.req.__('add %s %s %s' % (entity.e_schema, relname, role)) - # pending operations - operations = self.req.get_pending_operations(entity, relname, role) - if operations['insert'] or operations['delete'] or 'unfold' in self.req.form: - self.w(u'

%s

' % text) - self._render_generic_relation_form(operations, entity, relname, role) - else: - divid = "%s%sreledit" % (relname, role) - url = ajax_replace_url(divid, rql_for_eid(entity.eid), 'editrelation', - {'unfold' : 1, 'relname' : relname, 'role' : role}) - self.w(u'%s' % (url, text)) - self.w(u'
' % divid) - - - def _build_opvalue(self, entity, relname, target, role): - if role == 'subject': - return '%s:%s:%s' % (entity.eid, relname, target) - else: - return '%s:%s:%s' % (target, relname, entity.eid) - - - def _render_generic_relation_form(self, operations, entity, relname, role): - rqlexec = self.req.execute - for optype, targets in operations.items(): - for target in targets: - self._render_pending(optype, entity, relname, target, role) - opvalue = self._build_opvalue(entity, relname, target, role) - self.w(u'- ' - % (opvalue, entity.eid)) - rset = rqlexec('Any X WHERE X eid %(x)s', {'x': target}, 'x') - self.wview('oneline', rset) - # now, unrelated ones - self._render_unrelated_selection(entity, relname, role) - - def _render_pending(self, optype, entity, relname, target, role): - opvalue = self._build_opvalue(entity, relname, target, role) - self.w(u'' - % (optype, opvalue)) - if optype == 'insert': - checktext = '-' - else: - checktext = '+' - rset = self.req.execute('Any X WHERE X eid %(x)s', {'x': target}, 'x') - self.w(u"""[%s""" - % (optype.capitalize(), relname, target, role, - self.view('oneline', rset))) - - def _render_unrelated_selection(self, entity, relname, role): - rschema = self.schema.rschema(relname) - if role == 'subject': - targettypes = rschema.objects(entity.e_schema) - else: - targettypes = rschema.subjects(entity.e_schema) - self.w(u' + %s + +
+""" % (hidden and 'hidden' or '', divid, selectid, html_escape(dumps(entity.eid)), + is_cell and 'true' or 'null', relname, '\n'.join(options)) + + def _get_select_options(self, entity, rschema, target): + """add options to search among all entities of each possible type""" + options = [] + eid = entity.eid + pending_inserts = self.req.get_pending_inserts(eid) + rtype = rschema.type + for eview, reid in entity.vocabulary(rschema, target, self.limit): + if reid is None: + options.append('' % html_escape(eview)) + else: + optionid = relation_id(eid, rtype, target, reid) + if optionid not in pending_inserts: + # prefix option's id with letters to make valid XHTML wise + options.append('' % + (optionid, reid, html_escape(eview))) + return options + + def _get_search_options(self, entity, rschema, target, targettypes): + """add options to search among all entities of each possible type""" + options = [] + _ = self.req._ + for eschema in targettypes: + mode = '%s:%s:%s:%s' % (target, entity.eid, rschema.type, eschema) + url = self.build_url(entity.rest_path(), vid='search-associate', + __mode=mode) + options.append((eschema.display_name(self.req), + '' % ( + html_escape(url), _('Search for'), eschema.display_name(self.req)))) + return [o for l, o in sorted(options)] + + def _get_basket_options(self, entity, rschema, target, targettypes): + options = [] + rtype = rschema.type + _ = self.req._ + for basketeid, basketname in self._get_basket_links(self.req.user.eid, + target, targettypes): + optionid = relation_id(entity.eid, rtype, target, basketeid) + options.append('' % ( + optionid, basketeid, _('link to each item in'), html_escape(basketname))) + return options + + def _get_basket_links(self, ueid, target, targettypes): + targettypes = set(targettypes) + for basketeid, basketname, elements in self._get_basket_info(ueid): + baskettypes = elements.column_types(0) + # if every elements in the basket can be attached to the + # edited entity + if baskettypes & targettypes: + yield basketeid, basketname + + def _get_basket_info(self, ueid): + basketref = [] + basketrql = 'Any B,N WHERE B is Basket, B owned_by U, U eid %(x)s, B name N' + basketresultset = self.req.execute(basketrql, {'x': ueid}, 'x') + for result in basketresultset: + basketitemsrql = 'Any X WHERE X in_basket B, B eid %(x)s' + rset = self.req.execute(basketitemsrql, {'x': result[0]}, 'x') + basketref.append((result[0], result[1], rset)) + return basketref + + +class ComboboxView(EntityView): + """the view used in combobox (unrelated entities) + + THIS IS A TEXT VIEW. DO NOT HTML_ESCAPE + """ + id = 'combobox' + title = None + + def cell_call(self, row, col): + """the combo-box view for an entity: same as text out of context view + by default + """ + self.wview('textoutofcontext', self.rset, row=row, col=col) + + +# class EditRelationView(EntityView): +# """Note: This is work in progress + +# This view is part of the edition view refactoring. +# It is still too big and cluttered with strange logic, but it's a start + +# The main idea is to be able to call an edition view for a specific +# relation. For example : +# self.wview('editrelation', person_rset, rtype='firstname') +# self.wview('editrelation', person_rset, rtype='works_for') +# """ +# id = 'editrelation' + +# __select__ = match_form_params('rtype') + +# # TODO: inlineview, multiple edit, (widget view ?) +# def cell_call(self, row, col, rtype=None, role='subject', targettype=None, +# showlabel=True): +# self.req.add_js( ('cubicweb.ajax.js', 'cubicweb.edition.js') ) +# entity = self.entity(row, col) +# rtype = self.req.form.get('rtype', rtype) +# showlabel = self.req.form.get('showlabel', showlabel) +# assert rtype is not None, "rtype is mandatory for 'edirelation' view" +# targettype = self.req.form.get('targettype', targettype) +# role = self.req.form.get('role', role) +# category = entity.rtags.get_category(rtype, targettype, role) +# if category in ('primary', 'secondary') or self.schema.rschema(rtype).is_final(): +# if hasattr(entity, '%s_format' % rtype): +# formatwdg = entity.get_widget('%s_format' % rtype, role) +# self.w(formatwdg.edit_render(entity)) +# self.w(u'
') +# wdg = entity.get_widget(rtype, role) +# if showlabel: +# self.w(u'%s' % wdg.render_label(entity)) +# self.w(u'%s %s %s' % +# (wdg.render_error(entity), wdg.edit_render(entity), +# wdg.render_help(entity),)) +# else: +# self._render_generic_relation(entity, rtype, role) + +# def _render_generic_relation(self, entity, relname, role): +# text = self.req.__('add %s %s %s' % (entity.e_schema, relname, role)) +# # pending operations +# operations = self.req.get_pending_operations(entity, relname, role) +# if operations['insert'] or operations['delete'] or 'unfold' in self.req.form: +# self.w(u'

%s

' % text) +# self._render_generic_relation_form(operations, entity, relname, role) +# else: +# divid = "%s%sreledit" % (relname, role) +# url = ajax_replace_url(divid, rql_for_eid(entity.eid), 'editrelation', +# {'unfold' : 1, 'relname' : relname, 'role' : role}) +# self.w(u'%s' % (url, text)) +# self.w(u'
' % divid) + + +# def _build_opvalue(self, entity, relname, target, role): +# if role == 'subject': +# return '%s:%s:%s' % (entity.eid, relname, target) +# else: +# return '%s:%s:%s' % (target, relname, entity.eid) + + +# def _render_generic_relation_form(self, operations, entity, relname, role): +# rqlexec = self.req.execute +# for optype, targets in operations.items(): +# for target in targets: +# self._render_pending(optype, entity, relname, target, role) +# opvalue = self._build_opvalue(entity, relname, target, role) +# self.w(u'- ' +# % (opvalue, entity.eid)) +# rset = rqlexec('Any X WHERE X eid %(x)s', {'x': target}, 'x') +# self.wview('oneline', rset) +# # now, unrelated ones +# self._render_unrelated_selection(entity, relname, role) + +# def _render_pending(self, optype, entity, relname, target, role): +# opvalue = self._build_opvalue(entity, relname, target, role) +# self.w(u'' +# % (optype, opvalue)) +# if optype == 'insert': +# checktext = '-' +# else: +# checktext = '+' +# rset = self.req.execute('Any X WHERE X eid %(x)s', {'x': target}, 'x') +# self.w(u"""[%s""" +# % (optype.capitalize(), relname, target, role, +# self.view('oneline', rset))) + +# def _render_unrelated_selection(self, entity, relname, role): +# rschema = self.schema.rschema(relname) +# if role == 'subject': +# targettypes = rschema.objects(entity.e_schema) +# else: +# targettypes = rschema.subjects(entity.e_schema) +# self.w(u'