--- a/web/test/test_views.py Wed Feb 18 20:25:57 2009 +0100
+++ b/web/test/test_views.py Wed Feb 18 20:26:07 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):
--- a/web/views/actions.py Wed Feb 18 20:25:57 2009 +0100
+++ b/web/views/actions.py Wed Feb 18 20:26:07 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
--- a/web/views/baseforms.py Wed Feb 18 20:25:57 2009 +0100
+++ b/web/views/baseforms.py Wed Feb 18 20:26:07 2009 +0100
@@ -639,7 +639,6 @@
__select__ = (match_kwargs('ptype', 'peid', 'rtype')
& specified_etype_implements('Any'))
-
EDITION_BODY = u'''\
<div id="div-%(parenteid)s-%(rtype)s-%(eid)s" class="inlinedform">
<div class="iformBody">
@@ -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'</tr>')
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('<option>%s %s</option>' % (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"""\
-<div class="%s" id="%s">
- <select id="%s" onchange="javascript: addPendingInsert(this.options[this.selectedIndex], %s, %s, '%s');">
- %s
- </select>
-</div>
-""" % (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('<option class="separator">-- %s --</option>' % 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('<option id="id%s" value="%s">%s</option>' %
- (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),
- '<option value="%s">%s %s</option>' % (
- 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('<option id="%s" value="%s">%s %s</option>' % (
- 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)
--- a/web/views/baseviews.py Wed Feb 18 20:25:57 2009 +0100
+++ b/web/views/baseviews.py Wed Feb 18 20:26:07 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)
+
--- a/web/views/boxes.py Wed Feb 18 20:25:57 2009 +0100
+++ b/web/views/boxes.py Wed Feb 18 20:26:07 2009 +0100
@@ -20,6 +20,7 @@
from cubicweb.selectors import (any_rset, appobject_selectable,
match_user_groups, non_final_entity)
from cubicweb.web.htmlwidgets import BoxWidget, BoxMenu, BoxHtml, RawBoxItem
+from cubicweb.view import EntityView
from cubicweb.web.box import BoxTemplate
_ = unicode
@@ -223,3 +224,31 @@
if not box.is_empty():
box.render(self.w)
+# helper classes ##############################################################
+
+class SideBoxView(EntityView):
+ """helper view class to display some entities in a sidebox"""
+ 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')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/csvexport.py Wed Feb 18 20:26:07 2009 +0100
@@ -0,0 +1,87 @@
+"""csv export views
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from cubicweb.common.uilib import UnicodeCSVWriter
+from cubicweb.view import EntityView, AnyRsetView
+
+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 raw result set 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
+
+ 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)
+ """
+ id = 'ecsvexport'
+ title = _('csv entities export')
+
+ def call(self):
+ 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 rows in rows_by_type.itervalues():
+ writer.writerows(rows)
+ # use two empty lines as separator
+ writer.writerows([[], []])
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/editviews.py Wed Feb 18 20:26:07 2009 +0100
@@ -0,0 +1,310 @@
+"""Some views used to help to the edition process
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from simplejson import dumps
+
+from logilab.common.decorators import cached
+from logilab.mtconverter import html_escape
+
+from cubicweb import typed_eid
+from cubicweb.view import EntityView
+from cubicweb.selectors import (one_line_rset, non_final_entity,
+ match_search_state, match_form_params)
+from cubicweb.common.uilib import cut
+from cubicweb.web.views import linksearch_select_url
+from cubicweb.web.form import relation_id
+from cubicweb.web.views.baseviews import FinalView
+
+_ = unicode
+
+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')
+ 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 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('<option>%s %s</option>' % (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"""\
+<div class="%s" id="%s">
+ <select id="%s" onchange="javascript: addPendingInsert(this.options[this.selectedIndex], %s, %s, '%s');">
+ %s
+ </select>
+</div>
+""" % (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('<option class="separator">-- %s --</option>' % 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('<option id="id%s" value="%s">%s</option>' %
+ (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),
+ '<option value="%s">%s %s</option>' % (
+ 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('<option id="%s" value="%s">%s %s</option>' % (
+ 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'<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>')
+
+
+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):
+ 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)
--- a/web/views/xbel.py Wed Feb 18 20:25:57 2009 +0100
+++ b/web/views/xbel.py Wed Feb 18 20:26:07 2009 +0100
@@ -1,7 +1,7 @@
"""xbel views
:organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
__docformat__ = "restructuredtext en"
@@ -10,7 +10,8 @@
from logilab.mtconverter import html_escape
from cubicweb.selectors import implements
-from cubicweb.web.views.baseviews import XmlView, EntityView
+from cubicweb.view import EntityView
+from cubicweb.web.views.xmlrss import XmlView
class XbelView(XmlView):
@@ -46,6 +47,7 @@
def url(self, entity):
return entity.absolute_url()
+
class XbelItemBookmarkView(XbelItemView):
__select__ = implements('Bookmark')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/xmlrss.py Wed Feb 18 20:26:07 2009 +0100
@@ -0,0 +1,162 @@
+"""base xml and rss views
+
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from time import timezone
+
+from logilab.mtconverter import xml_escape
+
+from cubicweb.view import EntityView, AnyRsetView
+from cubicweb.web.httpcache import MaxAgeHTTPCacheManager
+from cubicweb.common.uilib import simple_sgml_tag
+
+
+class XmlView(EntityView):
+ """xml view for entities"""
+ 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 raw rset as xml"""
+ 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, xml_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' % xml_escape(req.url()))
+ self.w(u' <title>%s RSS Feed</title>\n' % xml_escape(self.page_title()))
+ self.w(u' <description>%s</description>\n' % xml_escape(req.form.get('vtitle', '')))
+ params = req.form.copy()
+ params.pop('vid', None)
+ self.w(u' <link>%s</link>\n' % xml_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' % xml_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' % xml_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, xml_escape(value), marker))