--- a/src/pyams_security/widget/__init__.py Mon Apr 20 09:50:51 2015 +0200
+++ b/src/pyams_security/widget/__init__.py Mon Apr 20 09:51:14 2015 +0200
@@ -18,15 +18,18 @@
# import interfaces
from pyams_form.interfaces.form import IFormLayer
-from pyams_security.interfaces import ISecurityManager
-from pyams_security.schema import IPrincipal, IPrincipalsSet
-from pyams_security.widget.interfaces import IPrincipalWidget, IPrincipalsSetWidget
-from z3c.form.interfaces import IDataConverter, IFieldWidget
+from pyams_security.interfaces import ISecurityManager, IPermission
+from pyams_security.schema import IPrincipal, IPrincipalsSet, IPermissionField, IPermissionsSetField
+from pyams_security.widget.interfaces import IPrincipalWidget, IPrincipalsSetWidget, IPermissionWidget, \
+ IPermissionsSetWidget
+from z3c.form.interfaces import IDataConverter, IFieldWidget, NO_VALUE
+from zope.schema.interfaces import ITitledTokenizedTerm
# import packages
from pyams_form.widget import widgettemplate_config
from pyams_utils.adapter import adapter_config
-from pyams_utils.registry import query_utility
+from pyams_utils.registry import query_utility, get_all_utilities_registered_for, get_utilities_for
+from z3c.form.browser.select import SelectWidget
from z3c.form.browser.widget import HTMLInputWidget
from z3c.form.converter import BaseDataConverter
from z3c.form.widget import Widget, FieldWidget
@@ -34,6 +37,148 @@
#
+# Permission widget
+#
+
+@adapter_config(context=(IPermissionField, IPermissionWidget), provides=IDataConverter)
+class PermissionDataConverter(BaseDataConverter):
+ """Permission data converter"""
+
+ def toWidgetValue(self, value):
+ # Permission widget is waiting for a Permission object
+ if not value:
+ return None
+ return query_utility(IPermission, name=value)
+
+ def toFieldValue(self, value):
+ # Permission widget only returns selected permission ID
+ return value
+
+
+@widgettemplate_config(mode='input', template='templates/permission-input.pt', layer=IFormLayer)
+@widgettemplate_config(mode='display', template='templates/permission-display.pt', layer=IFormLayer)
+@implementer_only(IPermissionWidget)
+class PermissionWidget(SelectWidget):
+ """Principal widget"""
+
+ def isSelected(self, term):
+ return term.token == self.value.id
+
+ def items(self):
+ if self.terms is None: # update() has not been called yet
+ return ()
+ items = []
+ if (not self.required or self.prompt) and self.multiple is None:
+ message = self.noValueMessage
+ if self.prompt:
+ message = self.promptMessage
+ items.append({'id': self.id + '-novalue',
+ 'value': self.noValueToken,
+ 'content': message,
+ 'selected': self.value == []})
+
+ ignored = {self.value.id} if self.value else set()
+
+ def addItem(idx, term, prefix=''):
+ selected = self.isSelected(term)
+ if selected and term.token in ignored:
+ ignored.remove(term.token)
+ id = '%s-%s%i' % (self.id, prefix, idx)
+ content = term.token
+ if ITitledTokenizedTerm.providedBy(term):
+ content = self.request.localizer.translate(term.title)
+ items.append({'id': id, 'value': term.token, 'content': content, 'selected': selected})
+
+ for idx, term in enumerate(self.terms):
+ addItem(idx, term)
+
+ if ignored:
+ # some values are not displayed, probably they went away from the vocabulary
+ for idx, token in enumerate(sorted(ignored)):
+ try:
+ term = self.terms.getTermByToken(token)
+ except LookupError:
+ # just in case the term really went away
+ continue
+
+ addItem(idx, term, prefix='missing-')
+ return items
+
+ @property
+ def value_map(self):
+ if not self.value:
+ return ''
+ translate = self.request.localizer.translate
+ return json.dumps({self.value.id: translate(self.value.title)})
+
+ def extract(self, default=NO_VALUE):
+ result = super(PermissionWidget, self).extract(default)
+ if result != default:
+ result = result[0]
+ return result
+
+
+@adapter_config(context=(IPermissionField, IFormLayer), provides=IFieldWidget)
+def PermissionFieldWidget(field, request):
+ """Permission field widget factory"""
+ return FieldWidget(field, PermissionWidget(request))
+
+
+#
+# Permissions set widget
+#
+
+@adapter_config(context=(IPermissionsSetField, IPermissionsSetWidget), provides=IDataConverter)
+class PermissionsSetDataConverter(BaseDataConverter):
+ """Permissions set data converter"""
+
+ def toWidgetValue(self, value):
+ # Permissions set widget is waiting for a set of Permission object
+ if not value:
+ return ()
+ return sorted((query_utility(IPermission, name=permission_id) for permission_id in value),
+ key=lambda x: x.title)
+
+ def toFieldValue(self, value):
+ # Permissions set widget only returns selected permissions IDs in a single input
+ if not value:
+ return set()
+ return set(value.split('|'))
+
+
+@widgettemplate_config(mode='input', template='templates/permissions-set-input.pt', layer=IFormLayer)
+@widgettemplate_config(mode='display', template='templates/permissions-set-display.pt', layer=IFormLayer)
+@implementer_only(IPermissionsSetWidget)
+class PermissionsSetWidget(HTMLInputWidget, Widget):
+ """Permissions set widget"""
+
+ @property
+ def values_data(self):
+ translate = self.request.localizer.translate
+ registry = self.request.registry
+ return json.dumps(sorted([{'id': permission.id, 'text': translate(permission.title)}
+ for name, permission in registry.getUtilitiesFor(IPermission)],
+ key=lambda x: x['text']))
+
+ @property
+ def values_str(self):
+ return '|'.join((permission.id for permission in (self.value or ())))
+
+ @property
+ def values_map(self):
+ result = {}
+ translate = self.request.localizer.translate
+ [result.update({value.id: translate(value.title)}) for value in (self.value or ())]
+ return json.dumps(result)
+
+
+@adapter_config(context=(IPermissionsSetField, IFormLayer), provides=IFieldWidget)
+def PermissionsSetFieldWidget(field, request):
+ """Permissions set field widget factory"""
+ return FieldWidget(field, PermissionsSetWidget(request))
+
+
+#
# Principal widget
#
--- a/src/pyams_security/widget/interfaces.py Mon Apr 20 09:50:51 2015 +0200
+++ b/src/pyams_security/widget/interfaces.py Mon Apr 20 09:51:14 2015 +0200
@@ -21,6 +21,14 @@
# import packages
+class IPermissionWidget(IWidget):
+ """Permission widget"""
+
+
+class IPermissionsSetWidget(IWidget):
+ """Permissions set widget"""
+
+
class IPrincipalWidget(IWidget):
"""Principal widget"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_security/widget/templates/permission-display.pt Mon Apr 20 09:51:14 2015 +0200
@@ -0,0 +1,24 @@
+<input type="hidden" autocomplete="off" readonly
+ tal:attributes="id view/id;
+ name view/name;
+ class string:select2 ${view/klass};
+ style view/style;
+ title view/title;
+ value view/value/id | nothing;
+ lang view/lang;
+ onclick view/onclick;
+ ondblclick view/ondblclick;
+ onmousedown view/onmousedown;
+ onmouseup view/onmouseup;
+ onmouseover view/onmouseover;
+ onmousemove view/onmousemove;
+ onmouseout view/onmouseout;
+ onkeypress view/onkeypress;
+ onkeydown view/onkeydown;
+ onkeyup view/onkeyup;
+ disabled view/disabled;
+ tabindex view/tabindex;
+ onfocus view/onfocus;
+ onblur view/onblur;
+ onchange view/onchange;
+ data-ams-select2-values view/value_map;" />
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_security/widget/templates/permission-input.pt Mon Apr 20 09:51:14 2015 +0200
@@ -0,0 +1,40 @@
+<label class="input bordered with-icon"
+ tal:omit-tag="view/required" i18n:domain="pyams_security">
+ <i class="icon-append fa fa-trash-o hint opaque"
+ title="Clear selected values" i18n:attributes="title"
+ tal:omit-tag="view/required"
+ data-ams-click-handler="MyAMS.helpers.select2ClearSelection"
+ tal:attributes="data-ams-select2-target view/name"></i>
+ <div class="select2-parent"
+ tal:omit-tag="view/required">
+ <select class="select2"
+ tal:attributes="id view/id;
+ name string:${view/name};
+ class string:select2 ${view/klass};
+ style view/style;
+ title view/title;
+ lang view/lang;
+ onclick view/onclick;
+ ondblclick view/ondblclick;
+ onmousedown view/onmousedown;
+ onmouseup view/onmouseup;
+ onmouseover view/onmouseover;
+ onmousemove view/onmousemove;
+ onmouseout view/onmouseout;
+ onkeypress view/onkeypress;
+ onkeydown view/onkeydown;
+ onkeyup view/onkeyup;
+ disabled view/disabled;
+ tabindex view/tabindex;
+ onfocus view/onfocus;
+ onblur view/onblur;
+ onchange view/onchange;
+ multiple view/multiple;
+ size view/size;">
+ <option tal:repeat="entry view/items"
+ tal:attributes="value entry/value;
+ selected entry/selected;"
+ tal:content="entry/content"></option>
+ </select>
+ </div>
+</label>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_security/widget/templates/permissions-set-display.pt Mon Apr 20 09:51:14 2015 +0200
@@ -0,0 +1,26 @@
+<input type="hidden" autocomplete="off" readonly
+ data-ams-select2-multiple="true"
+ data-ams-select2-separator="|"
+ tal:attributes="id view/id;
+ name view/name;
+ class string:select2 ${view/klass};
+ style view/style;
+ title view/title;
+ value python:'|'.join((value.id for value in view.value));
+ lang view/lang;
+ onclick view/onclick;
+ ondblclick view/ondblclick;
+ onmousedown view/onmousedown;
+ onmouseup view/onmouseup;
+ onmouseover view/onmouseover;
+ onmousemove view/onmousemove;
+ onmouseout view/onmouseout;
+ onkeypress view/onkeypress;
+ onkeydown view/onkeydown;
+ onkeyup view/onkeyup;
+ disabled view/disabled;
+ tabindex view/tabindex;
+ onfocus view/onfocus;
+ onblur view/onblur;
+ onchange view/onchange;
+ data-ams-select2-values view/values_map;" />
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_security/widget/templates/permissions-set-input.pt Mon Apr 20 09:51:14 2015 +0200
@@ -0,0 +1,44 @@
+<label class="input bordered with-icon"
+ tal:omit-tag="view/required" i18n:domain="pyams_security">
+ <i class="icon-append fa fa-trash-o hint opaque"
+ title="Clear selected values" i18n:attributes="title"
+ tal:omit-tag="view/required"
+ data-ams-click-handler="MyAMS.helpers.select2ClearSelection"
+ tal:attributes="data-ams-select2-target view/name"></i>
+ <div class="select2-parent"
+ tal:omit-tag="view/required">
+ <input type="hidden" autocomplete="off" class="select2"
+ data-ams-select2-placeholder="Select permissions..."
+ data-ams-select2-allow-clear="true"
+ data-ams-select2-multiple="true"
+ data-ams-select2-separator="|"
+ tal:attributes="id view/id;
+ name view/name;
+ class string:select2 ${view/klass};
+ style view/style;
+ title view/title;
+ value view/values_str;
+ lang view/lang;
+ onclick view/onclick;
+ ondblclick view/ondblclick;
+ onmousedown view/onmousedown;
+ onmouseup view/onmouseup;
+ onmouseover view/onmouseover;
+ onmousemove view/onmousemove;
+ onmouseout view/onmouseout;
+ onkeypress view/onkeypress;
+ onkeydown view/onkeydown;
+ onkeyup view/onkeyup;
+ disabled view/disabled;
+ tabindex view/tabindex;
+ onfocus view/onfocus;
+ onblur view/onblur;
+ onchange view/onchange;
+ readonly view/readonly;
+ accesskey view/accesskey;
+ onselect view/onselect;
+ data-ams-select2-data view/values_data;
+ data-ams-select2-values view/values_map;"
+ i18n:attributes="data-ams-select2-placeholder" />
+ </div>
+</label>