Skip to content

Commit

Permalink
Add new and enhanced link widget.
Browse files Browse the repository at this point in the history
  • Loading branch information
tomgross authored and thet committed Jun 8, 2017
1 parent f620da8 commit a32acaf
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ New features:

Bug fixes:

- Add new and enhanced link widget.
[tomgross, thet]

- Fix broken ``get_tinymce_options`` when called with non-contentish contexts like form or field contexts.
[thet]

Expand Down
12 changes: 12 additions & 0 deletions plone/app/z3cform/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@
template="templates/object_input.pt"
/>

<z3c:widgetTemplate
mode="input"
widget=".interfaces.ILinkWidget"
layer=".interfaces.IPloneFormLayer"
template="templates/link_input.pt"
/>

<browser:page
name="z3cform_validate_field"
for="*"
Expand All @@ -136,6 +143,7 @@
<adapter factory=".converters.QueryStringDataConverter" />
<adapter factory=".converters.RelationChoiceRelatedItemsWidgetConverter" />
<adapter factory=".converters.RelatedItemsDataConverter" />
<adapter factory=".converters.LinkWidgetDataConverter" />

<!-- widget registration stuff -->
<class class=".widget.DateWidget">
Expand Down Expand Up @@ -184,4 +192,8 @@
for="plone.app.textfield.interfaces.IRichText
plone.app.z3cform.interfaces.IPloneFormLayer"/>

<adapter factory=".widget.LinkFieldWidget"
for="z3c.form.interfaces.ITextWidget
plone.app.z3cform.interfaces.IPloneFormLayer"/>

</configure>
38 changes: 38 additions & 0 deletions plone/app/z3cform/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
from plone.app.z3cform.interfaces import IAjaxSelectWidget
from plone.app.z3cform.interfaces import IDatetimeWidget
from plone.app.z3cform.interfaces import IDateWidget
from plone.app.z3cform.interfaces import ILinkWidget
from plone.app.z3cform.interfaces import IQueryStringWidget
from plone.app.z3cform.interfaces import IRelatedItemsWidget
from plone.app.z3cform.interfaces import ISelectWidget
from plone.app.z3cform.utils import replace_link_variables_by_paths
from plone.uuid.interfaces import IUUID
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.utils import safe_callable
Expand Down Expand Up @@ -303,3 +305,39 @@ def toFieldValue(self, value):
if not value:
return self.field.missing_value
return value


@adapter(IField, ILinkWidget)
class LinkWidgetDataConverter(BaseDataConverter):
"""Data converter for the enhanced link widget."""

def toWidgetValue(self, value):
value = super(LinkWidgetDataConverter, self).toWidgetValue(value)
result = {'internal': u'',
'external': u'',
'email': u'',
'email_subject': u''}
uuid = None
if value.startswith('mailto:'):
value = value[7:] # strip mailto from beginning
if '?subject=' in value:
email, email_subject = value.split('?subject=')
result['email'] = email
result['email_subject'] = email_subject
else:
result['email'] = value
else:
if '/resolveuid/' in value:
result['internal'] = value.rsplit('/', 1)[-1]
else:
portal = getSite()
path = replace_link_variables_by_paths(portal, value)
path = path[len(portal.absolute_url())+1:].encode('ascii', 'ignore') # noqa
obj = portal.unrestrictedTraverse(path=path, default=None)
if obj is not None:
uuid = IUUID(obj, None)
if uuid is not None:
result['internal'] = uuid
else:
result['external'] = value
return result
4 changes: 4 additions & 0 deletions plone/app/z3cform/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@ class IRelatedItemsWidget(ITextWidget):

class IRichTextWidget(patextfield_IRichTextWidget):
"""Marker interface for the TinyMCEWidget."""


class ILinkWidget(ITextWidget):
"""Marker interface for the enhanced link widget."""
51 changes: 51 additions & 0 deletions plone/app/z3cform/templates/link_input.pt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<div>
<div class="linkModal">

<div class="linkTypes pat-autotoc autotabs"
data-pat-autotoc="section:span.linkType;levels:span.linkLabel;">

<span class="linkType internal" data-linkType="internal">
<span class="linkLabel" i18n:translate="label_internal_url">Internal</span>
<div>
<div class="form-group main">
<!-- this gives the name to the "linkType" -->
<input type="text" name="internal" class="pat-relateditems"
tal:attributes="data-pat-relateditems view/pattern_data;
value view/value/internal | nothing;
name string:${view/name}.internal" />
</div>
</div>
</span>

<span class="linkType external" data-linkType="external">
<span class="linkLabel" i18n:translate="label_external_url">External</span>
<div class="form-group main">
<label for="external" i18n:translate="help_external_url">External URL (can be relative within this site or absolute if it starts with http:// or https://)</label>
<input type="text" name="external"
tal:attributes="name string:${view/name}.external;
value view/value/external | nothing" />
</div>
</span>

<span class="linkType email" data-linkType="email">
<span class="linkLabel" i18n:translate="label_email_url">Email</span>
<div class="form-inline">
<div class="form-group main">
<label i18n:translate="help_email_url">Email Address</label>
<input type="text" name="email"
tal:attributes="name string:${view/name}.email;
value view/value/email | nothing" />
</div>
<div class="form-group">
<label i18n:translate="help_email_url_subject">Email Subject (optional)</label>
<input type="text" name="subject"
tal:attributes="name string:${view/name}.subject;
value view/value/email_subject | nothing" />
</div>
</div>
</span>

</div><!-- / tabs -->

</div>
</div>
32 changes: 32 additions & 0 deletions plone/app/z3cform/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,35 @@ def call_callables(value, *args, **kwargs):
for k, v in value.items()
}
return ret


def replace_link_variables_by_paths(context, url):
"""Take an `url` and replace the variables "${navigation_root_url}" and
"${portal_url}" by the corresponding paths. `context` is the acquisition
context.
"""

def _replace_variable_by_path(url, variable, obj):
path = '/'.join(obj.getPhysicalPath())
return url.replace(variable, path)

if not url:
return url

portal_state = context.restrictedTraverse('@@plone_portal_state')

if '${navigation_root_url}' in url:
url = _replace_variable_by_path(
url,
'${navigation_root_url}',
portal_state.navigation_root()
)

if '${portal_url}' in url:
url = _replace_variable_by_path(
url,
'${portal_url}',
portal_state.portal()
)

return url
46 changes: 46 additions & 0 deletions plone/app/z3cform/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from plone.app.z3cform.interfaces import IAjaxSelectWidget
from plone.app.z3cform.interfaces import IDatetimeWidget
from plone.app.z3cform.interfaces import IDateWidget
from plone.app.z3cform.interfaces import ILinkWidget
from plone.app.z3cform.interfaces import IQueryStringWidget
from plone.app.z3cform.interfaces import IRelatedItemsWidget
from plone.app.z3cform.interfaces import IRichTextWidget
Expand All @@ -30,6 +31,7 @@
from plone.registry.interfaces import IRegistry
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.interfaces import IEditingSchema
from Products.CMFPlone.utils import safe_unicode
from UserDict import UserDict
from z3c.form.browser.select import SelectWidget as z3cform_SelectWidget
from z3c.form.browser.text import TextWidget as z3cform_TextWidget
Expand All @@ -42,6 +44,7 @@
from z3c.form.widget import Widget
from zope.component import ComponentLookupError
from zope.component import getUtility
from zope.component.hooks import getSite
from zope.i18n import translate
from zope.interface import implementer
from zope.interface import implementer_only
Expand Down Expand Up @@ -686,6 +689,44 @@ def render(self):
return super(RichTextWidget, self).render()


@implementer_only(ILinkWidget)
class LinkWidget(z3cform_TextWidget):
"""Implementation of enhanced link widget.
.. note::
Unlike the others here, this is not a plone.app.widgets based widget
and it uses it's own template.
"""

def pattern_data(self):
pattern_data = {
'vocabularyUrl': '{0}/@@getVocabulary?name=plone.app.vocabularies.Catalog'.format( # noqa
getSite().absolute_url(0)
),
'maximumSelectionSize': 1
}
return json.dumps(pattern_data)

def extract(self, default=NO_VALUE):
form = self.request.form
internal = form.get(self.name + '.internal')
external = form.get(self.name + '.external')
email = form.get(self.name + '.email')
if internal:
url = '${portal_url}/resolveuid/' + internal
elif email:
subject = form.get(self.name + '.subject')
if not subject:
url = 'mailto:' + email
else:
url = 'mailto:{}?subject={}'.format(email, subject)
else:
url = external # the default is `http://` so we land here
if url:
self.request[self.name] = safe_unicode(url)
return super(LinkWidget, self).extract(default=default)


@implementer(IFieldWidget)
def DateFieldWidget(field, request):
widget = FieldWidget(field, DateWidget(request))
Expand Down Expand Up @@ -731,3 +772,8 @@ def QueryStringFieldWidget(field, request, extra=None):
if extra is not None:
request = extra
return FieldWidget(field, QueryStringWidget(request))


@implementer(IFieldWidget)
def LinkFieldWidget(field, request):
return FieldWidget(field, LinkWidget(request))

0 comments on commit a32acaf

Please sign in to comment.