* refactor ms planner (renaming, reorganization)
* fix a bug originaly demonstrated by test_version_depends_on
* enhance crossed relation support, though there is still some bug renaming.
some tests were actually wrong.
Buggy tests (wether they fail or not, they are byggy) marked by XXXFIXME)
"""Set of HTML base actions
: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.selectors import (searchstate_accept, match_user_group, yes,
one_line_rset, two_lines_rset, one_etype_rset,
authenticated_user, none_rset,
match_search_state, chainfirst, chainall)
from cubicweb.web.action import Action, EntityAction, LinkToEntityAction
from cubicweb.web.views import linksearch_select_url, linksearch_match
from cubicweb.web.views.baseviews import vid_from_rset
_ = unicode
# generic primary actions #####################################################
class SelectAction(EntityAction):
"""base class for link search actions. By default apply on
any size entity result search it the current state is 'linksearch'
if accept match.
"""
category = 'mainactions'
__selectors__ = (searchstate_accept,)
search_states = ('linksearch',)
order = 0
id = 'select'
title = _('select')
@classmethod
def accept_rset(cls, req, rset, row, col):
return linksearch_match(req, rset)
def url(self):
return linksearch_select_url(self.req, self.rset)
class CancelSelectAction(Action):
category = 'mainactions'
search_states = ('linksearch',)
order = 10
id = 'cancel'
title = _('cancel select')
def url(self):
target, link_eid, r_type, searched_type = self.req.search_state[1]
return self.build_url(rql="Any X WHERE X eid %s" % link_eid,
vid='edition', __mode='normal')
class ViewAction(Action):
category = 'mainactions'
__selectors__ = (match_user_group, searchstate_accept)
require_groups = ('users', 'managers')
order = 0
id = 'view'
title = _('view')
@classmethod
def accept_rset(cls, req, rset, row, col):
# interesting if it propose another view than the current one
vid = req.form.get('vid')
if vid and vid != vid_from_rset(req, rset, cls.schema):
return 1
return 0
def url(self):
params = self.req.form.copy()
params.pop('vid', None)
params.pop('__message', None)
return self.build_url(self.req.relative_path(includeparams=False),
**params)
class ModifyAction(EntityAction):
category = 'mainactions'
__selectors__ = (one_line_rset, searchstate_accept)
schema_action = 'update'
order = 10
id = 'edit'
title = _('modify')
@classmethod
def has_permission(cls, entity, action):
if entity.has_perm(action):
return True
# if user has no update right but it can modify some relation,
# display action anyway
for dummy in entity.srelations_by_category(('generic', 'metadata'),
'add'):
return True
for rschema, targetschemas, role in entity.relations_by_category(
('primary', 'secondary'), 'add'):
if not rschema.is_final():
return True
return False
def url(self):
entity = self.rset.get_entity(self.row or 0, self.col or 0)
return entity.absolute_url(vid='edition')
class MultipleEditAction(EntityAction):
category = 'mainactions'
__selectors__ = (two_lines_rset, one_etype_rset,
searchstate_accept)
schema_action = 'update'
order = 10
id = 'muledit' # XXX get strange conflicts if id='edit'
title = _('modify')
def url(self):
return self.build_url('view', rql=self.rset.rql, vid='muledit')
# generic secondary actions ###################################################
class ManagePermissions(LinkToEntityAction):
accepts = ('Any',)
category = 'moreactions'
id = 'addpermission'
title = _('manage permissions')
order = 100
etype = 'EPermission'
rtype = 'require_permission'
target = 'object'
def url(self):
return self.rset.get_entity(0, 0).absolute_url(vid='security')
class DeleteAction(EntityAction):
category = 'moreactions'
__selectors__ = (searchstate_accept,)
schema_action = 'delete'
order = 20
id = 'delete'
title = _('delete')
def url(self):
if len(self.rset) == 1:
entity = self.rset.get_entity(0, 0)
return self.build_url(entity.rest_path(), vid='deleteconf')
return self.build_url(rql=self.rset.printable_rql(), vid='deleteconf')
class CopyAction(EntityAction):
category = 'moreactions'
schema_action = 'add'
order = 30
id = 'copy'
title = _('copy')
def url(self):
entity = self.rset.get_entity(self.row or 0, self.col or 0)
return entity.absolute_url(vid='copy')
class AddNewAction(MultipleEditAction):
"""when we're seeing more than one entity with the same type, propose to
add a new one
"""
category = 'moreactions'
id = 'addentity'
order = 40
def etype_rset_selector(cls, req, rset, **kwargs):
if rset is not None and not rset.rowcount:
rqlst = rset.syntax_tree()
if len(rqlst.children) > 1:
return 0
select = rqlst.children[0]
if len(select.defined_vars) == 1 and len(select.solutions) == 1:
rset._searched_etype = select.solutions[0].itervalues().next()
eschema = cls.schema.eschema(rset._searched_etype)
if not (eschema.is_final() or eschema.is_subobject(strict=True)) \
and eschema.has_perm(req, 'add'):
return 1
return 0
def has_add_perm_selector(cls, req, rset, **kwargs):
eschema = cls.schema.eschema(rset.description[0][0])
if not (eschema.is_final() or eschema.is_subobject(strict=True)) \
and eschema.has_perm(req, 'add'):
return 1
return 0
__selectors__ = (match_search_state,
chainfirst(etype_rset_selector,
chainall(two_lines_rset, one_etype_rset,
has_add_perm_selector)))
@property
def rsettype(self):
if self.rset:
return self.rset.description[0][0]
return self.rset._searched_etype
@property
def title(self):
return self.req.__('add a %s' % self.rsettype) # generated msgid
def url(self):
return self.build_url('add/%s' % self.rsettype)
# logged user actions #########################################################
class UserPreferencesAction(Action):
category = 'useractions'
__selectors__ = authenticated_user,
order = 10
id = 'myprefs'
title = _('user preferences')
def url(self):
return self.build_url(self.id)
class UserInfoAction(Action):
category = 'useractions'
__selectors__ = authenticated_user,
order = 20
id = 'myinfos'
title = _('personnal informations')
def url(self):
return self.build_url('euser/%s'%self.req.user.login, vid='edition')
class LogoutAction(Action):
category = 'useractions'
__selectors__ = authenticated_user,
order = 30
id = 'logout'
title = _('logout')
def url(self):
return self.build_url(self.id)
# site actions ################################################################
class ManagersAction(Action):
category = 'siteactions'
__abstract__ = True
__selectors__ = match_user_group,
require_groups = ('managers',)
def url(self):
return self.build_url(self.id)
class SiteConfigurationAction(ManagersAction):
order = 10
id = 'siteconfig'
title = _('site configuration')
class ManageAction(ManagersAction):
order = 20
id = 'manage'
title = _('manage')
class ViewSchemaAction(Action):
category = 'siteactions'
id = 'schema'
title = _("site schema")
__selectors__ = yes,
order = 30
def url(self):
return self.build_url(self.id)
# content type specific actions ###############################################
class FollowAction(EntityAction):
category = 'mainactions'
accepts = ('Bookmark',)
id = 'follow'
title = _('follow')
def url(self):
return self.rset.get_entity(self.row or 0, self.col or 0).actual_url()
class UserPreferencesEntityAction(EntityAction):
__selectors__ = EntityAction.__selectors__ + (one_line_rset, match_user_group,)
require_groups = ('owners', 'managers')
category = 'mainactions'
accepts = ('EUser',)
id = 'prefs'
title = _('preferences')
def url(self):
login = self.rset.get_entity(self.row or 0, self.col or 0).login
return self.build_url('euser/%s'%login, vid='epropertiesform')
# schema view action
class DownloadOWLSchemaAction(Action):
category = 'mainactions'
id = 'download_as_owl'
title = _('download schema as owl')
__selectors__ = none_rset,
def url(self):
return self.build_url('view', vid='owl')