--- 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 <item_vid> view
- """
- if title:
- self.w(u'<div class="sideBoxTitle"><span>%s</span></div>' % title)
- self.w(u'<div class="%s"><div class="sideBoxBody">' % 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'[<a href="%s">%s</a>]' % (self.build_url(rql=rql),
- self.req._('see them all')))
- self.w(u'</div>\n</div>\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'</a>')
+
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'<a href="%s">' % self.entity(row, col).absolute_url())
self.w(html_escape(self.view('textoutofcontext', self.rset, row=row, col=col)))
self.w(u'</a>')
+
-# 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 <item_vid> view"""
- self.w(u'<?xml version="1.0" encoding="%s"?>\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'</%s>\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 = '<![CDATA[%s]]>' % b64encode(value.getvalue())
- elif isinstance(value, basestring):
- value = xml_escape(value)
- self.w(u' <%s>%s</%s>\n' % (attr, value, attr))
- self.w(u'</%s>\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'<?xml version="1.0" encoding="%s"?>\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' <row>\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' </row>\n')
- w(u'</%s>\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 <item_vid> view"""
- req = self.req
- self.w(u'<?xml version="1.0" encoding="%s"?>\n' % req.encoding)
- self.w(u'''<rdf:RDF
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns="http://purl.org/rss/1.0/"
->''')
- self.w(u' <channel rdf:about="%s">\n' % html_escape(req.url()))
- self.w(u' <title>%s RSS Feed</title>\n' % html_escape(self.page_title()))
- self.w(u' <description>%s</description>\n' % html_escape(req.form.get('vtitle', '')))
- params = req.form.copy()
- params.pop('vid', None)
- self.w(u' <link>%s</link>\n' % html_escape(self.build_url(**params)))
- self.w(u' <items>\n')
- self.w(u' <rdf:Seq>\n')
- for entity in self.rset.entities():
- self.w(u' <rdf:li resource="%s" />\n' % html_escape(entity.absolute_url()))
- self.w(u' </rdf:Seq>\n')
- self.w(u' </items>\n')
- self.w(u' </channel>\n')
- for i in xrange(self.rset.rowcount):
- self.cell_call(i, 0)
- self.w(u'</rdf:RDF>')
-
-
-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'<item rdf:about="%s">\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'<author>')
- self._marker('name', entity.creator.name())
- email = entity.creator.get_email()
- if email:
- self._marker('email', email)
- self.w(u'</author>')
- self.w(u'</item>\n')
-
- def _marker(self, marker, value):
- if value:
- self.w(u' <%s>%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'<div id="%s">' % divid)
- self.pagination(self.req, rset, w=self.w)
- self.wview(vid, rset, 'noresult')
- self.w(u'</div>')
-
- @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'<a href="%s" title="%s">%s</a> <a href="%s" title="%s">[...]</a>' % (
- 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'<br/>')
- 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'<h3>%s</h3>' % 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'<a href="%s">%s</a>' % (url, text))
- self.w(u'<div id="%s"></div>' % 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'<a href="javascript: addPendingDelete(\'%s\', %s);">-</a> '
- % (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'<input type="hidden" name="__%s" value="%s" />'
- % (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"""[<a href="javascript: cancelPending%s('%s:%s:%s')">%s</a>"""
- % (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'<select onselect="addPendingInsert(this.selected.value);">')
- for targettype in targettypes:
- unrelated = entity.unrelated(relname, targettype, role) # XXX limit
- for rowindex, row in enumerate(unrelated):
- teid = row[0]
- opvalue = self._build_opvalue(entity, relname, teid, role)
- self.w(u'<option name="__insert" value="%s>%s</option>'
- % (opvalue, self.view('text', unrelated, row=rowindex)))
- self.w(u'</select>')
-
+# context specific views ######################################################
class TextSearchResultView(EntityView):
"""this view is used to display full-text search
@@ -962,16 +536,28 @@
self.w(value.replace('\n', '<br/>'))
-class TooltipView(OneLineView):
+class TooltipView(EntityView):
"""A entity view used in a tooltip"""
id = 'tooltip'
- title = None # don't display in possible views
def cell_call(self, row, col):
self.wview('oneline', self.rset, row=row, col=col)
+
+# XXX bw compat
+
+from logilab.common.deprecation import class_moved
+
try:
from cubicweb.web.views.tableview import TableView
- from logilab.common.deprecation import class_moved
TableView = class_moved(TableView)
except ImportError:
pass # gae has no tableview module (yet)
+
+from cubicweb.web.views import boxes, xmlrss
+SideBoxView = class_moved(boxes.SideBoxView)
+XmlView = class_moved(xmlrss.XmlView)
+XmlItemView = class_moved(xmlrss.XmlItemView)
+XmlRsetView = class_moved(xmlrss.XmlRsetView)
+RssView = class_moved(xmlrss.RssView)
+RssItemView = class_moved(xmlrss.RssItemView)
+