web/box.py
changeset 5949 2a273c896a38
parent 5927 654a34e4eb2e
child 5952 adb87a075281
--- a/web/box.py	Thu Jul 08 18:48:44 2010 +0200
+++ b/web/box.py	Thu Jul 08 18:59:42 2010 +0200
@@ -15,9 +15,8 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""abstract box classes for CubicWeb web client
+"""abstract box classes for CubicWeb web client"""
 
-"""
 __docformat__ = "restructuredtext en"
 _ = unicode
 
@@ -26,10 +25,11 @@
 from cubicweb import Unauthorized, role as get_role, target as get_target
 from cubicweb.schema import display_name
 from cubicweb.selectors import (no_cnx, one_line_rset,  primary_view,
-                                match_context_prop, partial_has_related_entities)
+                                match_context_prop, partial_relation_possible,
+                                partial_has_related_entities)
 from cubicweb.view import View, ReloadableMixIn
-
-from cubicweb.web import INTERNAL_FIELD_VALUE
+from cubicweb.uilib import domid, js
+from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs
 from cubicweb.web.htmlwidgets import (BoxLink, BoxWidget, SideBoxWidget,
                                       RawBoxItem, BoxSeparator)
 from cubicweb.web.action import UnregisteredAction
@@ -241,3 +241,92 @@
                     entities.append(entity)
         return entities
 
+
+class AjaxEditRelationBoxTemplate(EntityBoxTemplate):
+    __select__ = EntityBoxTemplate.__select__ & partial_relation_possible()
+
+    # view used to display related entties
+    item_vid = 'incontext'
+    # values separator when multiple values are allowed
+    separator = ','
+    # msgid of the message to display when some new relation has been added/removed
+    added_msg = None
+    removed_msg = None
+
+    # class attributes below *must* be set in concret classes (additionaly to
+    # rtype / role [/ target_etype]. They should correspond to js_* methods on
+    # the json controller
+
+    # function(eid)
+    # -> expected to return a list of values to display as input selector
+    #    vocabulary
+    fname_vocabulary = None
+
+    # function(eid, value)
+    # -> handle the selector's input (eg create necessary entities and/or
+    # relations). If the relation is multiple, you'll get a list of value, else
+    # a single string value.
+    fname_validate = None
+
+    # function(eid, linked entity eid)
+    # -> remove the relation
+    fname_remove = None
+
+    def cell_call(self, row, col, **kwargs):
+        req = self._cw
+        entity = self.cw_rset.get_entity(row, col)
+        related = entity.related(self.rtype, self.role)
+        rdef = entity.e_schema.rdef(self.rtype, self.role, self.target_etype)
+        if self.role == 'subject':
+            mayadd = rdef.has_perm(req, 'add', fromeid=entity.eid)
+            maydel = rdef.has_perm(req, 'delete', fromeid=entity.eid)
+        else:
+            mayadd = rdef.has_perm(req, 'add', toeid=entity.eid)
+            maydel = rdef.has_perm(req, 'delete', toeid=entity.eid)
+        if not (related or mayadd):
+            return
+        if mayadd or maydel:
+            req.add_js(('cubicweb.ajax.js', 'cubicweb.ajax.box.js'))
+        _ = req._
+        w = self.w
+        divid = domid(self.__regid__) + unicode(entity.eid)
+        w(u'<div class="sideBox" id="%s%s">' % (domid(self.__regid__), entity.eid))
+        w(u'<div class="sideBoxTitle"><span>%s</span></div>' %
+               rdef.rtype.display_name(req, self.role))
+        w(u'<div class="sideBox"><div class="sideBoxBody">')
+        if related:
+            w(u'<table>')
+            for rentity in related.entities():
+                # for each related entity, provide a link to remove the relation
+                subview = rentity.view(self.item_vid)
+                if maydel:
+                    jscall = js.ajaxBoxRemoveLinkedEntity(
+                        self.__regid__, entity.eid, rentity.eid,
+                        self.fname_remove,
+                        self.removed_msg and _(self.removed_msg))
+                    w(u'<tr><td>[<a href="javascript: %s">-</a>]</td>'
+                      '<td class="tagged">%s</td></tr>' % (xml_escape(jscall),
+                                                           subview))
+                else:
+                    w(u'<tr><td class="tagged">%s</td></tr>' % (subview))
+            w(u'</table>')
+        else:
+            w(_('no related entity'))
+        if mayadd:
+            req.add_js('jquery.autocomplete.js')
+            req.add_css('jquery.autocomplete.css')
+            multiple = rdef.role_cardinality(self.role) in '*+'
+            w(u'<table><tr><td>')
+            jscall = js.ajaxBoxShowSelector(
+                self.__regid__, entity.eid, self.fname_vocabulary,
+                self.fname_validate, self.added_msg and _(self.added_msg),
+                _(stdmsgs.BUTTON_OK[0]), _(stdmsgs.BUTTON_CANCEL[0]),
+                multiple and self.separator)
+            w('<a class="button sglink" href="javascript: %s">%s</a>' % (
+                xml_escape(jscall),
+                multiple and _('add_relation') or _('update_relation')))
+            w(u'</td><td>')
+            w(u'<div id="%sHolder"></div>' % divid)
+            w(u'</td></tr></table>')
+        w(u'</div>\n')
+        w(u'</div></div>\n')