web/box.py
changeset 5949 2a273c896a38
parent 5927 654a34e4eb2e
child 5952 adb87a075281
equal deleted inserted replaced
5948:4154bdc85fe4 5949:2a273c896a38
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
    14 # details.
    14 # details.
    15 #
    15 #
    16 # You should have received a copy of the GNU Lesser General Public License along
    16 # You should have received a copy of the GNU Lesser General Public License along
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    18 """abstract box classes for CubicWeb web client
    18 """abstract box classes for CubicWeb web client"""
    19 
    19 
    20 """
       
    21 __docformat__ = "restructuredtext en"
    20 __docformat__ = "restructuredtext en"
    22 _ = unicode
    21 _ = unicode
    23 
    22 
    24 from logilab.mtconverter import xml_escape
    23 from logilab.mtconverter import xml_escape
    25 
    24 
    26 from cubicweb import Unauthorized, role as get_role, target as get_target
    25 from cubicweb import Unauthorized, role as get_role, target as get_target
    27 from cubicweb.schema import display_name
    26 from cubicweb.schema import display_name
    28 from cubicweb.selectors import (no_cnx, one_line_rset,  primary_view,
    27 from cubicweb.selectors import (no_cnx, one_line_rset,  primary_view,
    29                                 match_context_prop, partial_has_related_entities)
    28                                 match_context_prop, partial_relation_possible,
       
    29                                 partial_has_related_entities)
    30 from cubicweb.view import View, ReloadableMixIn
    30 from cubicweb.view import View, ReloadableMixIn
    31 
    31 from cubicweb.uilib import domid, js
    32 from cubicweb.web import INTERNAL_FIELD_VALUE
    32 from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs
    33 from cubicweb.web.htmlwidgets import (BoxLink, BoxWidget, SideBoxWidget,
    33 from cubicweb.web.htmlwidgets import (BoxLink, BoxWidget, SideBoxWidget,
    34                                       RawBoxItem, BoxSeparator)
    34                                       RawBoxItem, BoxSeparator)
    35 from cubicweb.web.action import UnregisteredAction
    35 from cubicweb.web.action import UnregisteredAction
    36 
    36 
    37 
    37 
   239                 entity = self._cw.entity_from_eid(eid)
   239                 entity = self._cw.entity_from_eid(eid)
   240                 if filteretype is None or entity.__regid__ == filteretype:
   240                 if filteretype is None or entity.__regid__ == filteretype:
   241                     entities.append(entity)
   241                     entities.append(entity)
   242         return entities
   242         return entities
   243 
   243 
       
   244 
       
   245 class AjaxEditRelationBoxTemplate(EntityBoxTemplate):
       
   246     __select__ = EntityBoxTemplate.__select__ & partial_relation_possible()
       
   247 
       
   248     # view used to display related entties
       
   249     item_vid = 'incontext'
       
   250     # values separator when multiple values are allowed
       
   251     separator = ','
       
   252     # msgid of the message to display when some new relation has been added/removed
       
   253     added_msg = None
       
   254     removed_msg = None
       
   255 
       
   256     # class attributes below *must* be set in concret classes (additionaly to
       
   257     # rtype / role [/ target_etype]. They should correspond to js_* methods on
       
   258     # the json controller
       
   259 
       
   260     # function(eid)
       
   261     # -> expected to return a list of values to display as input selector
       
   262     #    vocabulary
       
   263     fname_vocabulary = None
       
   264 
       
   265     # function(eid, value)
       
   266     # -> handle the selector's input (eg create necessary entities and/or
       
   267     # relations). If the relation is multiple, you'll get a list of value, else
       
   268     # a single string value.
       
   269     fname_validate = None
       
   270 
       
   271     # function(eid, linked entity eid)
       
   272     # -> remove the relation
       
   273     fname_remove = None
       
   274 
       
   275     def cell_call(self, row, col, **kwargs):
       
   276         req = self._cw
       
   277         entity = self.cw_rset.get_entity(row, col)
       
   278         related = entity.related(self.rtype, self.role)
       
   279         rdef = entity.e_schema.rdef(self.rtype, self.role, self.target_etype)
       
   280         if self.role == 'subject':
       
   281             mayadd = rdef.has_perm(req, 'add', fromeid=entity.eid)
       
   282             maydel = rdef.has_perm(req, 'delete', fromeid=entity.eid)
       
   283         else:
       
   284             mayadd = rdef.has_perm(req, 'add', toeid=entity.eid)
       
   285             maydel = rdef.has_perm(req, 'delete', toeid=entity.eid)
       
   286         if not (related or mayadd):
       
   287             return
       
   288         if mayadd or maydel:
       
   289             req.add_js(('cubicweb.ajax.js', 'cubicweb.ajax.box.js'))
       
   290         _ = req._
       
   291         w = self.w
       
   292         divid = domid(self.__regid__) + unicode(entity.eid)
       
   293         w(u'<div class="sideBox" id="%s%s">' % (domid(self.__regid__), entity.eid))
       
   294         w(u'<div class="sideBoxTitle"><span>%s</span></div>' %
       
   295                rdef.rtype.display_name(req, self.role))
       
   296         w(u'<div class="sideBox"><div class="sideBoxBody">')
       
   297         if related:
       
   298             w(u'<table>')
       
   299             for rentity in related.entities():
       
   300                 # for each related entity, provide a link to remove the relation
       
   301                 subview = rentity.view(self.item_vid)
       
   302                 if maydel:
       
   303                     jscall = js.ajaxBoxRemoveLinkedEntity(
       
   304                         self.__regid__, entity.eid, rentity.eid,
       
   305                         self.fname_remove,
       
   306                         self.removed_msg and _(self.removed_msg))
       
   307                     w(u'<tr><td>[<a href="javascript: %s">-</a>]</td>'
       
   308                       '<td class="tagged">%s</td></tr>' % (xml_escape(jscall),
       
   309                                                            subview))
       
   310                 else:
       
   311                     w(u'<tr><td class="tagged">%s</td></tr>' % (subview))
       
   312             w(u'</table>')
       
   313         else:
       
   314             w(_('no related entity'))
       
   315         if mayadd:
       
   316             req.add_js('jquery.autocomplete.js')
       
   317             req.add_css('jquery.autocomplete.css')
       
   318             multiple = rdef.role_cardinality(self.role) in '*+'
       
   319             w(u'<table><tr><td>')
       
   320             jscall = js.ajaxBoxShowSelector(
       
   321                 self.__regid__, entity.eid, self.fname_vocabulary,
       
   322                 self.fname_validate, self.added_msg and _(self.added_msg),
       
   323                 _(stdmsgs.BUTTON_OK[0]), _(stdmsgs.BUTTON_CANCEL[0]),
       
   324                 multiple and self.separator)
       
   325             w('<a class="button sglink" href="javascript: %s">%s</a>' % (
       
   326                 xml_escape(jscall),
       
   327                 multiple and _('add_relation') or _('update_relation')))
       
   328             w(u'</td><td>')
       
   329             w(u'<div id="%sHolder"></div>' % divid)
       
   330             w(u'</td></tr></table>')
       
   331         w(u'</div>\n')
       
   332         w(u'</div></div>\n')