--- a/.hgtags Thu Aug 27 13:02:45 2009 +0200
+++ b/.hgtags Mon Aug 31 19:09:54 2009 +0200
@@ -60,3 +60,5 @@
e244a0fd7d719c25f4267470342ff8334b2dc8b3 cubicweb-debian-version-3.4.4-1
3a65f9b3367c7297dc540a53f84e6507cb309892 cubicweb-version-3.4.5
7fd294cbf6ff3cf34475cc50e972f650a34ae6e8 cubicweb-debian-version-3.4.5-1
+921fdbf8b3038dc27a2ec5398a0fbcbc5b9ba4be cubicweb-version-3.4.6
+52dba800ca4d4b82c47f3befb824bd91ef015368 cubicweb-debian-version-3.4.6-1
--- a/_exceptions.py Thu Aug 27 13:02:45 2009 +0200
+++ b/_exceptions.py Mon Aug 31 19:09:54 2009 +0200
@@ -65,9 +65,8 @@
"""no source support an entity type"""
msg = 'No source supports %r entity\'s type'
-class RTypeNotSupportedBySources(RepositoryError, InternalError):
- """no source support a relation type"""
- msg = 'No source supports %r relation\'s type'
+class MultiSourcesError(RepositoryError, InternalError):
+ """usually due to bad multisources configuration or rql query"""
# security exceptions #########################################################
--- a/common/mail.py Thu Aug 27 13:02:45 2009 +0200
+++ b/common/mail.py Mon Aug 31 19:09:54 2009 +0200
@@ -141,60 +141,28 @@
msgid_timestamp = True
- def user_login(self):
- try:
- # if req is actually a session (we are on the server side), and we
- # have to prevent nested internal session
- return self.req.actual_session().user.login
- except AttributeError:
- return self.req.user.login
-
- def recipients(self):
- finder = self.vreg['components'].select('recipients_finder', self.req,
- rset=self.rset,
- row=self.row or 0,
- col=self.col or 0)
- return finder.recipients()
-
- def subject(self):
- entity = self.entity(self.row or 0, self.col or 0)
- subject = self.req._(self.message)
- etype = entity.dc_type()
- eid = entity.eid
- login = self.user_login()
- return self.req._('%(subject)s %(etype)s #%(eid)s (%(login)s)') % locals()
-
- def context(self, **kwargs):
- entity = self.entity(self.row or 0, self.col or 0)
- for key, val in kwargs.iteritems():
- if val and isinstance(val, unicode) and val.strip():
- kwargs[key] = self.req._(val)
- kwargs.update({'user': self.user_login(),
- 'eid': entity.eid,
- 'etype': entity.dc_type(),
- 'url': entity.absolute_url(),
- 'title': entity.dc_long_title(),})
- return kwargs
+ # this is usually the method to call
+ def render_and_send(self, **kwargs):
+ """generate and send an email message for this view"""
+ delayed = kwargs.pop('delay_to_commit', None)
+ for recipients, msg in self.render_emails(**kwargs):
+ if delayed is None:
+ self.send(recipients, msg)
+ elif delayed:
+ self.send_on_commit(recipients, msg)
+ else:
+ self.send_now(recipients, msg)
def cell_call(self, row, col=0, **kwargs):
self.w(self.req._(self.content) % self.context(**kwargs))
- def construct_message_id(self, eid):
- return construct_message_id(self.config.appid, eid, self.msgid_timestamp)
-
def render_emails(self, **kwargs):
- """generate and send an email message for this view"""
+ """generate and send emails for this view (one per recipient)"""
self._kwargs = kwargs
recipients = self.recipients()
if not recipients:
self.info('skipping %s notification, no recipients', self.id)
return
- if not isinstance(recipients[0], tuple):
- from warnings import warn
- warn('recipients should now return a list of 2-uple (email, language)',
- DeprecationWarning, stacklevel=1)
- lang = self.vreg.property_value('ui.language')
- recipients = zip(recipients, repeat(lang))
if self.rset is not None:
entity = self.entity(self.row or 0, self.col or 0)
# if the view is using timestamp in message ids, no way to reference
@@ -225,16 +193,17 @@
# restore language
self.req.set_language(origlang)
- def render_and_send(self, **kwargs):
- """generate and send an email message for this view"""
- delayed = kwargs.pop('delay_to_commit', None)
- for recipients, msg in self.render_emails(**kwargs):
- if delayed is None:
- self.send(recipients, msg)
- elif delayed:
- self.send_on_commit(recipients, msg)
- else:
- self.send_now(recipients, msg)
+ # recipients / email sending ###############################################
+
+ def recipients(self):
+ """return a list of 2-uple (email, language) to who this email should be
+ sent
+ """
+ finder = self.vreg['components'].select('recipients_finder', self.req,
+ rset=self.rset,
+ row=self.row or 0,
+ col=self.col or 0)
+ return finder.recipients()
def send_now(self, recipients, msg):
self.config.sendmails([(msg, recipients)])
@@ -243,3 +212,43 @@
raise NotImplementedError
send = send_now
+
+ # email generation helpers #################################################
+
+ def construct_message_id(self, eid):
+ return construct_message_id(self.config.appid, eid, self.msgid_timestamp)
+
+ def format_field(self, attr, value):
+ return ':%(attr)s: %(value)s' % {'attr': attr, 'value': value}
+
+ def format_section(self, attr, value):
+ return '%(attr)s\n%(ul)s\n%(value)s\n' % {
+ 'attr': attr, 'ul': '-'*len(attr), 'value': value}
+
+ def subject(self):
+ entity = self.entity(self.row or 0, self.col or 0)
+ subject = self.req._(self.message)
+ etype = entity.dc_type()
+ eid = entity.eid
+ login = self.user_login()
+ return self.req._('%(subject)s %(etype)s #%(eid)s (%(login)s)') % locals()
+
+ def context(self, **kwargs):
+ entity = self.entity(self.row or 0, self.col or 0)
+ for key, val in kwargs.iteritems():
+ if val and isinstance(val, unicode) and val.strip():
+ kwargs[key] = self.req._(val)
+ kwargs.update({'user': self.user_login(),
+ 'eid': entity.eid,
+ 'etype': entity.dc_type(),
+ 'url': entity.absolute_url(),
+ 'title': entity.dc_long_title(),})
+ return kwargs
+
+ def user_login(self):
+ try:
+ # if req is actually a session (we are on the server side), and we
+ # have to prevent nested internal session
+ return self.req.actual_session().user.login
+ except AttributeError:
+ return self.req.user.login
--- a/cwconfig.py Thu Aug 27 13:02:45 2009 +0200
+++ b/cwconfig.py Mon Aug 31 19:09:54 2009 +0200
@@ -134,6 +134,9 @@
'float' : 'Float',
}
+_forced_mode = os.environ.get('CW_MODE')
+assert _forced_mode in (None, 'system', 'user')
+
class CubicWebNoAppConfiguration(ConfigurationMixIn):
"""base class for cubicweb configuration without a specific instance directory
"""
@@ -150,7 +153,7 @@
CUBES_DIR = '%(APYCOT_ROOT)s/local/share/cubicweb/cubes/' % os.environ
# create __init__ file
file(join(CUBES_DIR, '__init__.py'), 'w').close()
- elif exists(join(CW_SOFTWARE_ROOT, '.hg')) or os.environ.get('CW_MODE') == 'user':
+ elif (exists(join(CW_SOFTWARE_ROOT, '.hg')) and _forced_mode != 'system') or _forced_mode == 'user':
mode = 'dev'
CUBES_DIR = abspath(normpath(join(CW_SOFTWARE_ROOT, '../cubes')))
else:
--- a/cwvreg.py Thu Aug 27 13:02:45 2009 +0200
+++ b/cwvreg.py Mon Aug 31 19:09:54 2009 +0200
@@ -16,12 +16,18 @@
from cubicweb import (ETYPE_NAME_MAP, Binary, UnknownProperty, UnknownEid,
ObjectNotFound, NoSelectableObject, RegistryNotFound,
- RegistryOutOfDate, CW_EVENT_MANAGER)
+ RegistryOutOfDate, CW_EVENT_MANAGER, onevent)
from cubicweb.utils import dump_class
from cubicweb.vregistry import VRegistry, Registry, class_regid
from cubicweb.rtags import RTAGS
+@onevent('before-registry-reload')
+def clear_rtag_objects():
+ for rtag in RTAGS:
+ rtag.clear()
+
+
def use_interfaces(obj):
"""return interfaces used by the given object by searchinf for implements
selectors, with a bw compat fallback to accepts_interfaces attribute
@@ -318,8 +324,8 @@
else:
self._needs_iface[obj] = ifaces
- def register(self, obj, **kwargs):
- super(CubicWebVRegistry, self).register(obj, **kwargs)
+ def register(self, obj, *args, **kwargs):
+ super(CubicWebVRegistry, self).register(obj, *args, **kwargs)
# XXX bw compat
ifaces = use_interfaces(obj)
if ifaces:
--- a/debian/changelog Thu Aug 27 13:02:45 2009 +0200
+++ b/debian/changelog Mon Aug 31 19:09:54 2009 +0200
@@ -1,3 +1,9 @@
+cubicweb (3.4.6-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Mon, 31 Aug 2009 14:12:30 +0200
+
cubicweb (3.4.5-1) unstable; urgency=low
* new upstream release
--- a/doc/book/en/development/devcore/vreg.rst Thu Aug 27 13:02:45 2009 +0200
+++ b/doc/book/en/development/devcore/vreg.rst Mon Aug 31 19:09:54 2009 +0200
@@ -1,4 +1,4 @@
-.. -*- coding: utf-8 -*-
+. -*- coding: utf-8 -*-
The VRegistry
--------------
@@ -94,69 +94,94 @@
XXX this part needs to be translated
-Le but final : quand on est sur un Blog, on veut que le lien rss de celui-ci pointe
-vers les entrées de ce blog, non vers l'entité blog elle-même.
+The goal : when on a Blog, one wants the RSS link to refer to blog
+entries, not to the blog entity itself.
-L'idée générale pour résoudre ça : on définit une méthode sur les classes d'entité
-qui renvoie l'url du flux rss pour l'entité en question. Avec une implémentation
-par défaut sur AnyEntity et une implémentation particulière sur Blog qui fera ce
-qu'on veut.
+To do that, one defines a method on entity classes that returns the
+RSS stream url for a given entity. With a default implementation on
+AnyEntity and a specific implementation on Blog, which will do what we
+want.
-La limitation : on est embêté dans le cas ou par ex. on a un result set qui contient
-plusieurs entités Blog (ou autre chose), car on ne sait pas sur quelle entité appeler
-la méthode sus-citée. Dans ce cas, on va conserver le comportement actuel (eg appel
-Ã limited_rql)
+There's a limitation to this schema : when we have a result set
+containing several Blog entities (or different entities), we don't
+know on which entity to call the aforementioned method. In this case,
+we keep the current behaviour (e.g : call to limited_rql).
-Donc : on veut deux cas ici, l'un pour un rset qui contient une et une seule entité,
-l'autre pour un rset qui contient plusieurs entité.
+Hence we want two cases here, one for a single-entity rsets, the other
+for multi-entities rsets.
-Donc... On a déja dans web/views/boxes.py la classe RSSIconBox qui fonctionne. Son
-sélecteur ::
+In web/views/boxes.py lies the RSSIconBox class. Look at its selector ::
class RSSIconBox(ExtResourcesBoxTemplate):
"""just display the RSS icon on uniform result set"""
__select__ = ExtResourcesBoxTemplate.__select__ & non_final_entity()
+It takes into account :
-indique qu'il prend en compte :
+* the inherited selection criteria (one has to look them up in the
+ class hierarchy to know the details)
-* les conditions d'apparition de la boite (faut remonter dans les classes parentes
- pour voir le détail)
-* non_final_entity, qui filtre sur des rset contenant une liste d'entité non finale
+* non_final_entity, which filters on rsets containing non final
+ entities (a 'final entity' being synonym for entity attribute)
-ça correspond donc à notre 2eme cas. Reste à fournir un composant plus spécifique
-pour le 1er cas ::
+This matches our second case. Hence we have to provide a specific
+component for the first case ::
class EntityRSSIconBox(RSSIconBox):
"""just display the RSS icon on uniform result set for a single entity"""
__select__ = RSSIconBox.__select__ & one_line_rset()
+Here, one adds the one_line_rset selector, which filters result sets
+of size 1. When one chains selectors, the final score is the sum of
+the score of each individual selector (unless one of them returns 0,
+in which case the object is non selectable). Thus, on a multiple
+entities selector, one_line_rset makes the EntityRSSIconBox class non
+selectable. For an rset with one entity, the EntityRSSIconBox class
+will have a higher score then RSSIconBox, which is what we wanted.
-Ici, on ajoute le selector one_line_rset, qui filtre sur des result set de taille 1. Il faut
-savoir que quand on chaine des selecteurs, le score final est la somme des scores
-renvoyés par chaque sélecteur (sauf si l'un renvoie zéro, auquel cas l'objet est
-non sélectionnable). Donc ici, sur un rset avec plusieurs entités, onelinerset_selector
-rendra la classe EntityRSSIconBox non sélectionnable, et on obtiendra bien la
-classe RSSIconBox. Pour un rset avec une entité, la classe EntityRSSIconBox aura un
-score supérieur à RSSIconBox et c'est donc bien elle qui sera sélectionnée.
+Of course, once this is done, you have to ::
+
+* fill in the call method of EntityRSSIconBox
-Voili voilou, il reste donc pour finir tout ça :
+* provide the default implementation of the method returning the RSS
+ stream url on AnyEntity
-* Ã définir le contenu de la méthode call de EntityRSSIconBox
-* fournir l'implémentation par défaut de la méthode renvoyant l'url du flux rss sur
- AnyEntity
-* surcharger cette methode dans blog.Blog
-
+* redefine this method on Blog.
When to use selectors?
```````````````````````
-Il faut utiliser les sélecteurs pour faire des choses différentes en
-fonction de ce qu'on a en entrée. Dès qu'on a un "if" qui teste la
-nature de `self.rset` dans un objet, il faut très sérieusement se
-poser la question s'il ne vaut pas mieux avoir deux objets différent
-avec des sélecteurs approprié.
+Selectors are to be used whenever arises the need of dispatching on
+the shape or content of a result set.
Debugging
`````````
-XXX explain traced_selection context manager
+
+Once in a while, one needs to understand why a view (or any AppObject)
+is, or is not selected appropriately. Looking at which selectors fired
+(or did not) is the way. There exists a traced_selection context
+manager to help with that.
+
+Here is an example ::
+
+.. sourcecode:: python
+
+ def possible_objects(self, registry, *args, **kwargs):
+ """return an iterator on possible objects in a registry for this result set
+
+ actions returned are classes, not instances
+ """
+ from cubicweb.selectors import traced_selection
+ with traced_selection():
+ for vobjects in self.registry(registry).values():
+ try:
+ yield self.select(vobjects, *args, **kwargs)
+ except NoSelectableObject:
+ continue
+
+Don't forget the 'from __future__ import with_statement' at the module
+top-level.
+
+This will yield additional WARNINGs in the logs, like this::
+
+ 2009-01-09 16:43:52 - (cubicweb.selectors) WARNING: selector one_line_rset returned 0 for <class 'cubicweb.web.views.basecomponents.WFHistoryVComponent'>
--- a/entities/wfobjs.py Thu Aug 27 13:02:45 2009 +0200
+++ b/entities/wfobjs.py Mon Aug 31 19:09:54 2009 +0200
@@ -303,7 +303,8 @@
@property
def workflow(self):
- return self.state_of[0]
+ # take care, may be missing in multi-sources configuration
+ return self.state_of and self.state_of[0]
def after_deletion_path(self):
"""return (path, parameters) which should be used as redirect
--- a/entity.py Thu Aug 27 13:02:45 2009 +0200
+++ b/entity.py Mon Aug 31 19:09:54 2009 +0200
@@ -209,7 +209,7 @@
def __hash__(self):
return id(self)
- def __cmp__(self):
+ def __cmp__(self, other):
raise NotImplementedError('comparison not implemented for %s' % self.__class__)
def pre_add_hook(self):
--- a/hooks/notification.py Thu Aug 27 13:02:45 2009 +0200
+++ b/hooks/notification.py Mon Aug 31 19:09:54 2009 +0200
@@ -18,6 +18,8 @@
"""delay rendering of notification view until precommit"""
def precommit_event(self):
view = self.view
+ if view.rset is not None and not view.rset:
+ return # entity added and deleted in the same transaction (cache effect)
if view.cw_rset and self.session.deleted_in_transaction(view.cw_rset[cw_rset.cw_row or 0][cw_rset.cw_col or 0]):
return # entity added and deleted in the same transaction
self.view.render_and_send(**getattr(self, 'viewargs', {}))
--- a/hooks/workflow.py Thu Aug 27 13:02:45 2009 +0200
+++ b/hooks/workflow.py Mon Aug 31 19:09:54 2009 +0200
@@ -20,8 +20,12 @@
nocheck = session.transaction_data.setdefault('skip-security', set())
nocheck.add((x, 'in_state', oldstate))
nocheck.add((x, 'in_state', newstate))
- # delete previous state first in case we're using a super session
- session.delete_relation(x, 'in_state', oldstate)
+ # delete previous state first in case we're using a super session,
+ # unless in_state isn't stored in the system source
+ fromsource = session.describe(x)[1]
+ if fromsource == 'system' or \
+ not session.repo.sources_by_uri[fromsource].support_relation('in_state'):
+ session.delete_relation(x, 'in_state', oldstate)
session.add_relation(x, 'in_state', newstate)
--- a/server/msplanner.py Thu Aug 27 13:02:45 2009 +0200
+++ b/server/msplanner.py Mon Aug 31 19:09:54 2009 +0200
@@ -97,9 +97,6 @@
# str() Constant.value to ensure generated table name won't be unicode
Constant._ms_table_key = lambda x: str(x.value)
-AbstractSource.dont_cross_relations = ()
-AbstractSource.cross_relations = ()
-
def need_source_access_relation(vargraph):
if not vargraph:
return False
--- a/server/repository.py Thu Aug 27 13:02:45 2009 +0200
+++ b/server/repository.py Mon Aug 31 19:09:54 2009 +0200
@@ -30,7 +30,7 @@
from cubicweb import (CW_SOFTWARE_ROOT, CW_MIGRATION_MAP, CW_EVENT_MANAGER,
UnknownEid, AuthenticationError, ExecutionError,
- ETypeNotSupportedBySources, RTypeNotSupportedBySources,
+ ETypeNotSupportedBySources, MultiSourcesError,
BadConnectionId, Unauthorized, ValidationError,
typed_eid)
from cubicweb import cwvreg, schema, server
@@ -927,12 +927,21 @@
def locate_relation_source(self, session, subject, rtype, object):
subjsource = self.source_from_eid(subject, session)
objsource = self.source_from_eid(object, session)
- if not (subjsource is objsource and subjsource.support_relation(rtype, 1)):
+ if not subjsource is objsource:
source = self.system_source
- if not source.support_relation(rtype, 1):
- raise RTypeNotSupportedBySources(rtype)
+ if not (subjsource.may_cross_relation(rtype)
+ and objsource.may_cross_relation(rtype)):
+ raise MultiSourcesError(
+ "relation %s can't be crossed among sources"
+ % rtype)
+ elif not subjsource.support_relation(rtype):
+ source = self.system_source
else:
source = subjsource
+ if not source.support_relation(rtype, True):
+ raise MultiSourcesError(
+ "source %s doesn't support write of %s relation"
+ % (source.uri, rtype))
return source
def locate_etype_source(self, etype):
--- a/server/sources/__init__.py Thu Aug 27 13:02:45 2009 +0200
+++ b/server/sources/__init__.py Mon Aug 31 19:09:54 2009 +0200
@@ -80,6 +80,11 @@
# a reference to the instance'schema (may differs from the source'schema)
schema = None
+ # multi-sources planning control
+ dont_cross_relations = ()
+ cross_relations = ()
+
+
def __init__(self, repo, appschema, source_config, *args, **kwargs):
self.repo = repo
self.uri = source_config['uri']
@@ -177,6 +182,19 @@
return wsupport
return True
+ def may_cross_relation(self, rtype):
+ """return True if the relation may be crossed among sources. Rules are:
+
+ * if this source support the relation, can't be crossed unless explicitly
+ specified in .cross_relations
+
+ * if this source doesn't support the relation, can be crossed unless
+ explicitly specified in .dont_cross_relations
+ """
+ if self.support_relation(rtype):
+ return rtype in self.cross_relations
+ return rtype not in self.dont_cross_relations
+
def eid2extid(self, eid, session=None):
return self.repo.eid2extid(self, eid, session)
--- a/server/sources/native.py Thu Aug 27 13:02:45 2009 +0200
+++ b/server/sources/native.py Mon Aug 31 19:09:54 2009 +0200
@@ -268,6 +268,9 @@
# can't claim not supporting a relation
return True #not rtype == 'content_for'
+ def may_cross_relation(self, rtype):
+ return True
+
def authenticate(self, session, login, password):
"""return CWUser eid for the given login/password if this account is
defined in this source, else raise `AuthenticationError`
--- a/server/sources/pyrorql.py Thu Aug 27 13:02:45 2009 +0200
+++ b/server/sources/pyrorql.py Mon Aug 31 19:09:54 2009 +0200
@@ -345,6 +345,7 @@
cu.execute('SET %s WHERE X eid %%(x)s' % ','.join(relations),
kwargs, 'x')
self._query_cache.clear()
+ entity.clear_all_caches()
def delete_entity(self, session, etype, eid):
"""delete an entity from the source"""
@@ -360,6 +361,8 @@
{'x': self.eid2extid(subject, session),
'y': self.eid2extid(object, session)}, ('x', 'y'))
self._query_cache.clear()
+ session.entity_from_eid(subject).clear_all_caches()
+ session.entity_from_eid(object).clear_all_caches()
def delete_relation(self, session, subject, rtype, object):
"""delete a relation from the source"""
@@ -368,6 +371,8 @@
{'x': self.eid2extid(subject, session),
'y': self.eid2extid(object, session)}, ('x', 'y'))
self._query_cache.clear()
+ session.entity_from_eid(subject).clear_all_caches()
+ session.entity_from_eid(object).clear_all_caches()
class RQL2RQL(object):
--- a/sobjects/notification.py Thu Aug 27 13:02:45 2009 +0200
+++ b/sobjects/notification.py Mon Aug 31 19:09:54 2009 +0200
@@ -19,6 +19,8 @@
from cubicweb.common.mail import NotificationView
from cubicweb.server.hookhelper import SendMailOp
+parse_message_id = deprecated('parse_message_id is now defined in cubicweb.common.mail')(parse_message_id)
+
class RecipientsFinder(Component):
"""this component is responsible to find recipients of a notification
--- a/vregistry.py Thu Aug 27 13:02:45 2009 +0200
+++ b/vregistry.py Mon Aug 31 19:09:54 2009 +0200
@@ -187,7 +187,7 @@
raise `NoSelectableObject` if not object apply
"""
if len(args) > 1:
- warn('only the request param can not be named when calling select',
+ warn('[3.5] only the request param can not be named when calling select*',
DeprecationWarning, stacklevel=3)
score, winners = 0, []
for appobject in appobjects:
@@ -210,8 +210,10 @@
[repr(v) for v in winners]))
# return the result of calling the appobject
return winners[0](*args, **kwargs)
+
select_best = deprecated('[3.6] select_best is now private')(_select_best)
+
class VRegistry(dict):
"""class responsible to register, propose and select the various
elements used to build the web interface. Currently, we have templates,
@@ -300,10 +302,11 @@
if obj.__module__ != modname or obj in butclasses:
continue
oid = class_regid(obj)
+ registryname = obj.__registry__
except AttributeError:
continue
if oid and not '__abstract__' in obj.__dict__:
- self.register(obj)
+ self.register(obj, registryname)
def register(self, obj, registryname=None, oid=None, clear=False):
"""base method to add an object in the registry"""
--- a/web/data/cubicweb.ajax.js Thu Aug 27 13:02:45 2009 +0200
+++ b/web/data/cubicweb.ajax.js Mon Aug 31 19:09:54 2009 +0200
@@ -51,7 +51,7 @@
}
// find textareas and wrap them if there are some
if (typeof(FCKeditor) != 'undefined') {
- buildWysiwygEditors(node);
+ buildWysiwygEditors();
}
if (typeof initFacetBoxEvents != 'undefined') {
initFacetBoxEvents(node);
@@ -351,7 +351,10 @@
*/
function buildWysiwygEditors(parent) {
jQuery('textarea').each(function () {
- if (this.getAttribute('cubicweb:type', 'wysiwyg')) {
+ if (this.getAttribute('cubicweb:type') == 'wysiwyg') {
+ // mark editor as instanciated, we may be called a number of times
+ // (see postAjaxLoad)
+ this.setAttribute('cubicweb:type', 'fckeditor');
if (typeof FCKeditor != "undefined") {
var fck = new FCKeditor(this.id);
fck.Config['CustomConfigurationsPath'] = fckconfigpath;
--- a/web/data/cubicweb.css Thu Aug 27 13:02:45 2009 +0200
+++ b/web/data/cubicweb.css Mon Aug 31 19:09:54 2009 +0200
@@ -256,13 +256,17 @@
}
/* Popup on login box and userActionBox */
+div.popupWrapper{
+ position:relative;
+ z-index:100;
+}
+
div.popup {
position: absolute;
- z-index: 400;
background: #fff;
border: 1px solid black;
text-align: left;
- float:left;
+ z-index:400;
}
div.popup ul li a {
@@ -840,7 +844,6 @@
background: #fffff8 url("button.png") bottom left repeat-x;
}
-
/********************************/
/* placement of alt. view icons */
/********************************/
--- a/web/uicfg.py Thu Aug 27 13:02:45 2009 +0200
+++ b/web/uicfg.py Mon Aug 31 19:09:54 2009 +0200
@@ -67,7 +67,7 @@
"""
__docformat__ = "restructuredtext en"
-from cubicweb import neg_role, onevent
+from cubicweb import neg_role
from cubicweb.rtags import (RelationTags, RelationTagsBool,
RelationTagsSet, RelationTagsDict, register_rtag)
from cubicweb.web import formwidgets
@@ -231,14 +231,3 @@
actionbox_appearsin_addmenu = RelationTagsBool('actionbox_appearsin_addmenu',
init_actionbox_appearsin_addmenu)
-
-@onevent('before-registry-reload')
-def clear_rtag_objects():
- primaryview_section.clear()
- primaryview_display_ctrl.clear()
- autoform_section.clear()
- autoform_field.clear()
- autoform_field_kwargs.clear()
- autoform_is_inlined.clear()
- autoform_permissions_overrides.clear()
- actionbox_appearsin_addmenu.clear()
--- a/web/views/basecomponents.py Thu Aug 27 13:02:45 2009 +0200
+++ b/web/views/basecomponents.py Mon Aug 31 19:09:54 2009 +0200
@@ -226,10 +226,10 @@
}
def call(self, vid):
- self.req.add_css('cubes.confman.css')
entity = self.entity(0,0)
- self.w(u'<a href="%s" class="otherView"><img src="data/pdf_icon.gif"/></a>' %
- (xml_escape(entity.absolute_url() + '?vid=%s&__template=pdf-main-template' % vid)))
+ url = entity.absolute_url(vid=vid, __template='pdf-main-template')
+ self.w(u'<a href="%s" class="otherView"><img src="data/pdf_icon.gif" alt="%s"/></a>' %
+ (xml_escape(url), self.req._('download page as pdf')))
--- a/web/views/basetemplates.py Thu Aug 27 13:02:45 2009 +0200
+++ b/web/views/basetemplates.py Mon Aug 31 19:09:54 2009 +0200
@@ -352,6 +352,7 @@
class HTMLPageHeader(View):
"""default html page header"""
id = 'header'
+ main_cell_components = ('appliname', 'breadcrumbs')
def call(self, view, **kwargs):
self.main_header(view)
@@ -373,7 +374,7 @@
self.w(u'</td>\n')
# appliname and breadcrumbs
self.w(u'<td id="headtext">')
- for cid in ('appliname', 'breadcrumbs'):
+ for cid in self.main_cell_components:
comp = self.vreg['components'].select_or_none(
cid, self.req, rset=self.rset)
if comp and comp.cw_propval('visible'):
--- a/web/views/editforms.py Thu Aug 27 13:02:45 2009 +0200
+++ b/web/views/editforms.py Mon Aug 31 19:09:54 2009 +0200
@@ -371,6 +371,8 @@
entity
"""
id = 'copy'
+ warning_message = _('Please note that this is only a shallow copy')
+
def render_form(self, entity):
"""fetch and render the form"""
# make a copy of entity to avoid altering the entity in the
@@ -381,7 +383,7 @@
self.initialize_varmaker()
self.newentity.eid = self.varmaker.next()
self.w(u'<script type="text/javascript">updateMessage("%s");</script>\n'
- % self.req._('Please note that this is only a shallow copy'))
+ % self.req._(self.warning_message))
super(CopyFormView, self).render_form(self.newentity)
del self.newentity
--- a/web/views/idownloadable.py Thu Aug 27 13:02:45 2009 +0200
+++ b/web/views/idownloadable.py Mon Aug 31 19:09:54 2009 +0200
@@ -147,17 +147,17 @@
def cell_call(self, row, col, width=None, height=None, link=False):
entity = self.rset.get_entity(row, col)
#if entity.data_format.startswith('image/'):
- imgtag = u'<img src="%s" alt="%s" ' % (xml_escape(entity.download_url()),
- xml_escape(entity.download_file_name()))
+ imgtag = u'<img src="%s" alt="%s" ' % (
+ xml_escape(entity.download_url()),
+ (self.req._('download %s') % xml_escape(entity.download_file_name())))
if width:
imgtag += u'width="%i" ' % width
if height:
imgtag += u'height="%i" ' % height
imgtag += u'/>'
if link:
- self.w(u'<a href="%s" alt="%s">%s</a>' % (entity.absolute_url(vid='download'),
- self.req._('download image'),
- imgtag))
+ self.w(u'<a href="%s">%s</a>' % (entity.absolute_url(vid='download'),
+ imgtag))
else:
self.w(imgtag)
--- a/web/views/primary.py Thu Aug 27 13:02:45 2009 +0200
+++ b/web/views/primary.py Mon Aug 31 19:09:54 2009 +0200
@@ -52,6 +52,7 @@
boxes = self._prepare_side_boxes(entity)
if boxes or hasattr(self, 'render_side_related'):
self.w(u'<table width="100%"><tr><td style="width: 75%">')
+ self.render_entity_summary(entity)
self.w(u'<div class="mainInfo">')
self.content_navigation_components('navcontenttop')
self.render_entity_attributes(entity)
@@ -91,6 +92,8 @@
def render_entity_metadata(self, entity):
entity.view('metadata', w=self.w)
+
+ def render_entity_summary(self, entity):
summary = self.summary(entity) # deprecate summary?
if summary:
self.w(u'<div class="summary">%s</div>' % summary)