# HG changeset patch # User Sylvain Thénault # Date 1317723832 -7200 # Node ID 08320697ca1adb2ab27cf66d2d13e34385f617ed # Parent 203d574c8a1d93f89ce72afc254516e77ec02fb0# Parent 32ad1c29e4774d0d793c0f6fe41bf4f45c2ce870 backport stable diff -r 203d574c8a1d -r 08320697ca1a doc/book/en/devweb/views/startup.rst --- a/doc/book/en/devweb/views/startup.rst Fri Sep 30 18:10:28 2011 +0200 +++ b/doc/book/en/devweb/views/startup.rst Tue Oct 04 12:23:52 2011 +0200 @@ -1,15 +1,18 @@ Startup views ------------- -(:mod:`cubicweb.web.views.startup`) +Startup views are views requiring no context, from which you usually start +browsing (for instance the index page). The usual selectors are +:class:`~cubicweb.selectors.none_rset` or :class:`~cubicweb.selectors.yes`. -The usual selectors are no_rset or yes. These views don't apply to a -result set. +You'll find here a description of startup views provided by the framework. -*index* - This view defines the home page of your application. It does not require - a result set to apply to. +.. automodule:: cubicweb.web.views.startup + + +Other startup views: *schema* A view dedicated to the display of the schema of the instance +.. XXX to be continued \ No newline at end of file diff -r 203d574c8a1d -r 08320697ca1a entity.py diff -r 203d574c8a1d -r 08320697ca1a server/repository.py --- a/server/repository.py Fri Sep 30 18:10:28 2011 +0200 +++ b/server/repository.py Tue Oct 04 12:23:52 2011 +0200 @@ -1177,8 +1177,7 @@ # delete remaining relations: if user can delete the entity, he can # delete all its relations without security checking with security_enabled(session, read=False, write=False): - eids = [_e.eid for _e in entities] - in_eids = ','.join((str(eid) for eid in eids)) + in_eids = ','.join([str(_e.eid) for _e in entities]) for rschema, _, role in entities[0].e_schema.relation_definitions(): rtype = rschema.type if rtype in schema.VIRTUAL_RTYPES or rtype in pendingrtypes: @@ -1423,7 +1422,11 @@ source = self.sources_by_uri[sourceuri] if source.should_call_hooks: self.hm.call_hooks('before_delete_entity', session, entities=entities) - self._delete_info_multi(session, entities, sourceuri) + if session.deleted_in_transaction(source.eid): + # source is being deleted, think to give scleanup argument + self._delete_info_multi(session, entities, sourceuri, scleanup=source.eid) + else: + self._delete_info_multi(session, entities, sourceuri) source.delete_entities(session, entities) if source.should_call_hooks: self.hm.call_hooks('after_delete_entity', session, entities=entities) diff -r 203d574c8a1d -r 08320697ca1a server/sources/pyrorql.py --- a/server/sources/pyrorql.py Fri Sep 30 18:10:28 2011 +0200 +++ b/server/sources/pyrorql.py Tue Oct 04 12:23:52 2011 +0200 @@ -445,7 +445,7 @@ def delete_entity(self, session, entity): """delete an entity from the source""" - if session.deleted_in_transaction (self.eid): + if session.deleted_in_transaction(self.eid): # source is being deleted, don't propagate self._query_cache.clear() return @@ -466,7 +466,7 @@ def delete_relation(self, session, subject, rtype, object): """delete a relation from the source""" - if session.deleted_in_transaction (self.eid): + if session.deleted_in_transaction(self.eid): # source is being deleted, don't propagate self._query_cache.clear() return diff -r 203d574c8a1d -r 08320697ca1a server/sources/rql2sql.py --- a/server/sources/rql2sql.py Fri Sep 30 18:10:28 2011 +0200 +++ b/server/sources/rql2sql.py Tue Oct 04 12:23:52 2011 +0200 @@ -1426,10 +1426,18 @@ return sql leftvars = cmp.children[0].get_nodes(VariableRef) assert len(leftvars) == 1 - leftalias = self._var_table(leftvars[0].variable.stinfo['attrvar']) + if leftvars[0].variable.stinfo['attrvar'] is None: + assert isinstance(leftvars[0].variable, ColumnAlias) + leftalias = leftvars[0].variable._q_sqltable + else: + leftalias = self._var_table(leftvars[0].variable.stinfo['attrvar']) rightvars = cmp.children[1].get_nodes(VariableRef) assert len(rightvars) == 1 - rightalias = self._var_table(rightvars[0].variable.stinfo['attrvar']) + if rightvars[0].variable.stinfo['attrvar'] is None: + assert isinstance(rightvars[0].variable, ColumnAlias) + rightalias = rightvars[0].variable._q_sqltable + else: + rightalias = self._var_table(rightvars[0].variable.stinfo['attrvar']) if optional == 'right': self._state.replace_tables_by_outer_join( leftalias, rightalias, 'LEFT', sql) diff -r 203d574c8a1d -r 08320697ca1a server/sqlutils.py --- a/server/sqlutils.py Fri Sep 30 18:10:28 2011 +0200 +++ b/server/sqlutils.py Tue Oct 04 12:23:52 2011 +0200 @@ -338,6 +338,17 @@ return _limit_size(text, maxsize) cnx.create_function("TEXT_LIMIT_SIZE", 2, limit_size2) + from logilab.common.date import strptime + def weekday(ustr): + try: + dt = strptime(ustr, '%Y-%m-%d %H:%M:%S') + except: + dt = strptime(ustr, '%Y-%m-%d') + # expect sunday to be 1, saturday 7 while weekday method return 0 for + # monday + return (dt.weekday() + 1) % 7 + cnx.create_function("WEEKDAY", 1, weekday) + import yams.constraints yams.constraints.patch_sqlite_decimal() diff -r 203d574c8a1d -r 08320697ca1a server/test/unittest_querier.py --- a/server/test/unittest_querier.py Fri Sep 30 18:10:28 2011 +0200 +++ b/server/test/unittest_querier.py Tue Oct 04 12:23:52 2011 +0200 @@ -443,7 +443,7 @@ self.execute("INSERT Personne X: X nom 'foo', X datenaiss %(d)s", {'d': datetime(2001, 2,3, 12,13)}) test_data = [('YEAR', 2001), ('MONTH', 2), ('DAY', 3), - ('HOUR', 12), ('MINUTE', 13)] + ('HOUR', 12), ('MINUTE', 13), ('WEEKDAY', 6)] for funcname, result in test_data: rset = self.execute('Any %s(D) WHERE X is Personne, X datenaiss D' % funcname) diff -r 203d574c8a1d -r 08320697ca1a server/test/unittest_rql2sql.py --- a/server/test/unittest_rql2sql.py Fri Sep 30 18:10:28 2011 +0200 +++ b/server/test/unittest_rql2sql.py Tue Oct 04 12:23:52 2011 +0200 @@ -1396,6 +1396,11 @@ '''SELECT CAST(EXTRACT(MONTH from _P.cw_creation_date) AS INTEGER) FROM cw_Personne AS _P''') + def test_weekday_extraction(self): + self._check("Any WEEKDAY(D) WHERE P is Personne, P creation_date D", + '''SELECT (CAST(EXTRACT(DOW from _P.cw_creation_date) AS INTEGER) + 1) +FROM cw_Personne AS _P''') + def test_substring(self): self._check("Any SUBSTRING(N, 1, 1) WHERE P nom N, P is Personne", '''SELECT SUBSTR(_P.cw_nom, 1, 1) @@ -1524,6 +1529,12 @@ FROM (SELECT MAX(_A.cw_ordernum) AS C0 FROM cw_CWAttribute AS _A) AS _T0, cw_CWAttribute AS _A WHERE _A.cw_ordernum=_T0.C0'''), + + ('Any O1 HAVING O1=O2? WITH O1 BEING (Any MAX(O) WHERE A ordernum O, A is CWAttribute), O2 BEING (Any MAX(O) WHERE A ordernum O, A is CWRelation)', + '''SELECT _T0.C0 +FROM (SELECT MAX(_A.cw_ordernum) AS C0 +FROM cw_CWAttribute AS _A) AS _T0 LEFT OUTER JOIN (SELECT MAX(_A.cw_ordernum) AS C0 +FROM cw_CWRelation AS _A) AS _T1 ON (_T0.C0=_T1.C0)'''), )): yield t @@ -1749,11 +1760,16 @@ self._check('Any X WHERE X is CWUser, X creation_date D HAVING YEAR(D) = "2010" OR D = NULL', '''SELECT _X.cw_eid FROM cw_CWUser AS _X -WHERE ((YEAR(_X.cw_creation_date)=2010) OR (_X.cw_creation_date IS NULL))''') +WHERE ((DATEPART(YEAR, _X.cw_creation_date)=2010) OR (_X.cw_creation_date IS NULL))''') def test_date_extraction(self): self._check("Any MONTH(D) WHERE P is Personne, P creation_date D", - '''SELECT MONTH(_P.cw_creation_date) + '''SELECT DATEPART(MONTH, _P.cw_creation_date) +FROM cw_Personne AS _P''') + + def test_weekday_extraction(self): + self._check("Any WEEKDAY(D) WHERE P is Personne, P creation_date D", + '''SELECT DATEPART(WEEKDAY, _P.cw_creation_date) FROM cw_Personne AS _P''') def test_symmetric(self): @@ -1893,9 +1909,9 @@ 'GROUPBY YEAR(XECT),MONTH(XECT) ORDERBY 1 ' 'WHERE X creation_date XSCT, X modification_date XECT, ' 'X ordernum XCE, X is CWAttribute', - '''SELECT ((YEAR(_X.cw_modification_date) * 100) + MONTH(_X.cw_modification_date)), COUNT(_X.cw_eid), SUM(_X.cw_ordernum), AVG((_X.cw_creation_date - _X.cw_modification_date)) + '''SELECT ((DATEPART(YEAR, _X.cw_modification_date) * 100) + DATEPART(MONTH, _X.cw_modification_date)), COUNT(_X.cw_eid), SUM(_X.cw_ordernum), AVG((_X.cw_creation_date - _X.cw_modification_date)) FROM cw_CWAttribute AS _X -GROUP BY YEAR(_X.cw_modification_date),MONTH(_X.cw_modification_date) +GROUP BY DATEPART(YEAR, _X.cw_modification_date),DATEPART(MONTH, _X.cw_modification_date) ORDER BY 1'''), @@ -1910,6 +1926,12 @@ '''SELECT MONTH(_P.cw_creation_date) FROM cw_Personne AS _P''') + def test_weekday_extraction(self): + # custom impl. in cw.server.sqlutils + self._check("Any WEEKDAY(D) WHERE P is Personne, P creation_date D", + '''SELECT WEEKDAY(_P.cw_creation_date) +FROM cw_Personne AS _P''') + def test_regexp(self): self._check("Any X WHERE X login REGEXP '[0-9].*'", '''SELECT _X.cw_eid @@ -2068,6 +2090,11 @@ '''SELECT EXTRACT(MONTH from _P.cw_creation_date) FROM cw_Personne AS _P''') + def test_weekday_extraction(self): + self._check("Any WEEKDAY(D) WHERE P is Personne, P creation_date D", + '''SELECT DAYOFWEEK(_P.cw_creation_date) +FROM cw_Personne AS _P''') + def test_cast(self): self._check("Any CAST(String, P) WHERE P is Personne", '''SELECT CAST(_P.cw_eid AS mediumtext) diff -r 203d574c8a1d -r 08320697ca1a web/uicfg.py --- a/web/uicfg.py Fri Sep 30 18:10:28 2011 +0200 +++ b/web/uicfg.py Tue Oct 04 12:23:52 2011 +0200 @@ -30,6 +30,16 @@ * ``schema`` * ``subobject`` (not displayed by default) + By default only entities on the ``application`` category are shown. + +.. sourcecode:: python + + from cubicweb.web import uicfg + # force hiding + uicfg.indexview_etype_section['HideMe'] = 'subobject' + # force display + uicfg.indexview_etype_section['ShowMe'] = 'application' + Actions box configuration ````````````````````````` diff -r 203d574c8a1d -r 08320697ca1a web/views/pyviews.py --- a/web/views/pyviews.py Fri Sep 30 18:10:28 2011 +0200 +++ b/web/views/pyviews.py Tue Oct 04 12:23:52 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -24,18 +24,29 @@ class PyValTableView(View): - """display a list of list of values into an html table. + """display a list of list of values into an HTML table. Take care, content is NOT xml-escaped. + + If `headers` is specfied, it is expected to be a list of headers to be + inserted as first row (in ). + + If `colheaders` is True, the first column will be considered as an headers + column an its values will be inserted inside instead of . + + `cssclass` is the CSS class used on the tag, and default to + 'listing' (so that the table will look similar to those generated by the + table view). """ __regid__ = 'pyvaltable' __select__ = match_kwargs('pyvalue') - def call(self, pyvalue, headers=None): + def call(self, pyvalue, headers=None, colheaders=False, + cssclass='listing'): if headers is None: headers = self._cw.form.get('headers') w = self.w - w(u'
\n') + w(u'
\n' % cssclass) if headers: w(u'') w(u'') @@ -46,6 +57,9 @@ w(u'') for row in pyvalue: w(u'') + if colheaders: + w(u'' % row[0]) + row = row[1:] for cell in row: w(u'' % cell) w(u'\n') diff -r 203d574c8a1d -r 08320697ca1a web/views/startup.py --- a/web/views/startup.py Fri Sep 30 18:10:28 2011 +0200 +++ b/web/views/startup.py Tue Oct 04 12:23:52 2011 +0200 @@ -15,8 +15,10 @@ # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . -"""Set of HTML startup views. A startup view is global, e.g. doesn't apply to a -result set. +"""This module contains the default index page and management view. + +.. autoclass:: IndexView +.. autoclass:: ManageView """ __docformat__ = "restructuredtext en" @@ -32,6 +34,19 @@ from cubicweb.web import ajax_replace_url, uicfg, httpcache class ManageView(StartupView): + """:__regid__: *manage* + + The manage view, display some information about what's contained by your + site and provides access to administration stuff such as user and groups + management. + + Regarding the section displaying link to entity type, notice by default it + won't display entity types which are related to another one using a + mandatory (cardinality == 1) composite relation. + + You can still configure that behaviour manually using the + `indexview_etype_section` as explained in :mod:`cubicweb.web.uicfg`. + """ __regid__ = 'manage' title = _('manage') http_cache_manager = httpcache.EtagHTTPCacheManager @@ -149,6 +164,13 @@ class IndexView(ManageView): + """:__regid__: *index* + + The default index view, that you'll get when accessing your site's root url. + It's by default indentical to the + :class:`~cubicweb.web.views.startup.ManageView`, but you'll usually want to + customize this one. + """ __regid__ = 'index' title = _('view_index') diff -r 203d574c8a1d -r 08320697ca1a web/views/tableview.py --- a/web/views/tableview.py Fri Sep 30 18:10:28 2011 +0200 +++ b/web/views/tableview.py Tue Oct 04 12:23:52 2011 +0200 @@ -372,6 +372,7 @@ def cell_call(self, row, col): _ = self._cw._ entity = self.cw_rset.get_entity(row, col) + entity.complete() infos = {} for col in self.columns: meth = getattr(self, 'build_%s_cell' % col, None)
%s%s