From fde0b3fa360f6ac7d20f58bcfe7d71cb690caaec Mon Sep 17 00:00:00 2001 From: Dylan Jay Date: Mon, 11 Jul 2016 16:55:18 +0700 Subject: [PATCH 01/24] initial checkin of fields being inserted visually into tinymce --- buildout.cfg | 2 +- .../CMFPlomino/browser/configure.zcml | 6 +- .../CMFPlomino/browser/static/css/tinymce.css | 75 ++++++++++++------- .../browser/static/js/tinymce.valid.js | 13 +++- .../browser/templates/tinymce/valid.pt | 14 ++-- src/Products/CMFPlomino/browser/tinymce.py | 29 +++++-- 6 files changed, 94 insertions(+), 45 deletions(-) diff --git a/buildout.cfg b/buildout.cfg index 7f7044392..c8e42c968 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -57,7 +57,7 @@ setuptools = 8.3 zc.buildout = 2.2.5 zc.recipe.egg = 2.0.1 -flake8 = 2.3.0 +flake8 = 2.4.0 robotframework = 2.8.4 robotframework-ride = 1.3 diff --git a/src/Products/CMFPlomino/browser/configure.zcml b/src/Products/CMFPlomino/browser/configure.zcml index b5aea3b7c..17176e917 100644 --- a/src/Products/CMFPlomino/browser/configure.zcml +++ b/src/Products/CMFPlomino/browser/configure.zcml @@ -146,7 +146,7 @@ @@ -154,7 +154,7 @@ @@ -162,7 +162,7 @@ diff --git a/src/Products/CMFPlomino/browser/static/css/tinymce.css b/src/Products/CMFPlomino/browser/static/css/tinymce.css index 384b5f88f..3d660b82f 100644 --- a/src/Products/CMFPlomino/browser/static/css/tinymce.css +++ b/src/Products/CMFPlomino/browser/static/css/tinymce.css @@ -1,32 +1,49 @@ .plominoFieldClass { - padding: 2px 10px; - color: #1b6899; - background-color: #ffffff; - border: 1px solid #1b6899; - border-radius: 4px; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + position:relative; } -.plominoActionClass { - padding: 2px 10px; - color: #ffffff; - background-color: #1b6899; - border: 1px solid #124465; - border-radius: 4px; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + +.plominoEditWidgetTab{ + background: #333; + background: rgba(0,0,0,.8); + border-radius: 5px; + color: #fff; + left: 20%; + top:100%; + padding: 5px 15px; + position: absolute; + z-index: 98; + width: 220px; } -.plominoHidewhenClass { - padding: 2px 10px; - color: #cccccc; - background-color: #ffffff; - border: 1px dashed #cccccc; - border-radius: 4px; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} -.plominoSubformClass { - padding: 2px 10px; - color: #ffffff; - background-color: #cccccc; - border: 1px solid #cccccc; - border-radius: 4px; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} \ No newline at end of file + +/*.plominoFieldClass {*/ + /*padding: 2px 10px;*/ + /*color: #1b6899;*/ + /*background-color: #ffffff;*/ + /*border: 1px solid #1b6899;*/ + /*border-radius: 4px;*/ + /*box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);*/ +/*}*/ +/*.plominoActionClass {*/ + /*padding: 2px 10px;*/ + /*color: #ffffff;*/ + /*background-color: #1b6899;*/ + /*border: 1px solid #124465;*/ + /*border-radius: 4px;*/ + /*box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);*/ +/*}*/ +/*.plominoHidewhenClass {*/ + /*padding: 2px 10px;*/ + /*color: #cccccc;*/ + /*background-color: #ffffff;*/ + /*border: 1px dashed #cccccc;*/ + /*border-radius: 4px;*/ + /*box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);*/ +/*}*/ +/*.plominoSubformClass {*/ + /*padding: 2px 10px;*/ + /*color: #ffffff;*/ + /*background-color: #cccccc;*/ + /*border: 1px solid #cccccc;*/ + /*border-radius: 4px;*/ + /*box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);*/ +/*}*/ \ No newline at end of file diff --git a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js index 0d8385b56..eeffb33c1 100644 --- a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js +++ b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js @@ -14,8 +14,17 @@ var PlominoDialog = { if (plominoClass !== undefined) { - // String to add in the editor - var span = '' + value + ''; + var example = document.getElementById("example_widget").innerHTML; + if (example) { + var span = ''+ + value+'' + + example + ''; + } + else { + // String to add in the editor + var span = '' + value + ''; + } // Insert or replace the selection diff --git a/src/Products/CMFPlomino/browser/templates/tinymce/valid.pt b/src/Products/CMFPlomino/browser/templates/tinymce/valid.pt index 79f67bb1f..d79ba9cec 100644 --- a/src/Products/CMFPlomino/browser/templates/tinymce/valid.pt +++ b/src/Products/CMFPlomino/browser/templates/tinymce/valid.pt @@ -1,16 +1,18 @@ - + Add/edit a Plomino Field - - + + +
@@ -36,5 +38,7 @@
+ + \ No newline at end of file diff --git a/src/Products/CMFPlomino/browser/tinymce.py b/src/Products/CMFPlomino/browser/tinymce.py index da962fc00..1767752dd 100644 --- a/src/Products/CMFPlomino/browser/tinymce.py +++ b/src/Products/CMFPlomino/browser/tinymce.py @@ -5,7 +5,7 @@ from plone.dexterity.utils import createContentInContainer from Products.CMFPlomino.config import FIELD_MODES -from Products.CMFPlomino.contents.field import get_field_types +from Products.CMFPlomino.contents.field import get_field_types, IPlominoField from Products.CMFPlomino.contents.action import ACTION_TYPES, ACTION_DISPLAY from Products.CMFPlomino.contents.action import PlominoAction from Products.CMFPlomino.contents.field import PlominoField @@ -64,8 +64,21 @@ def valid_page(self, **params): params = {} return self.valid_template(**params) + def example_widget(self): + widget_type = self.request.get('type') + id = self.request.get('value') + if not id: + return + if widget_type == "field": + field = getattr(self.context, id, None) + if isinstance(field.aq_base, PlominoField): #IPlominoField.implementedBy(field): + return field.getRenderedValue(fieldvalue=None, + editmode="EDITABLE", + target=self.context) -class PlominoForm(object): + + +class PlominoFormSettings(object): """ """ action_template = ViewPageTemplateFile('templates/tinymce/action.pt') @@ -146,7 +159,13 @@ def addField(self): formula=fieldformula, ) - self.request.RESPONSE.redirect(self.context.absolute_url() + "/@@tinymceplominoform/valid_page?type=field&value=" + fieldid + "&fieldurl=" + "/".join(field.getPhysicalPath())) + html = field.getRenderedValue("example", "EDITABLE", target=self.context) + + self.request.RESPONSE.redirect("{base}/@@tinymceplominoform/valid_page?type=field&value={fieldid}&fieldurl={url}".format( + base = self.context.absolute_url(), + fieldid = fieldid, + url = "/".join(field.getPhysicalPath()), + html = html)) else: self.request.RESPONSE.redirect(self.context.absolute_url() + "/@@tinymceplominoform/error_page?error=object_exists") @@ -375,7 +394,7 @@ def addCache(self): self.request.RESPONSE.redirect(self.context.absolute_url() + "/@@tinymceplominoform/error_page?error=no_cache") -class PlominoField(object): +class PlominoFieldSettings(object): """ """ @@ -405,7 +424,7 @@ def setFieldProperties(self): self.request.RESPONSE.redirect(self.context.absolute_url() + "/../@@tinymceplominoform/valid_page?type=field&value=" + self.context.id) -class PlominoAction(object): +class PlominoActionSettings(object): """ """ From 28ee077bc629a114d6c55875491b0c583b5cd368 Mon Sep 17 00:00:00 2001 From: Dylan Jay Date: Fri, 15 Jul 2016 12:56:34 +0700 Subject: [PATCH 02/24] store layout without example widgets --- setup.py | 1 + .../CMFPlomino/browser/static/css/tinymce.css | 34 +++++- .../CMFPlomino/browser/static/js/tinymce.js | 6 +- .../browser/static/js/tinymce.valid.js | 6 +- .../browser/templates/tinymce/valid.pt | 9 +- src/Products/CMFPlomino/browser/tinymce.py | 14 +-- src/Products/CMFPlomino/configure.zcml | 9 ++ src/Products/CMFPlomino/contents/form.py | 101 +++++++++++++++++- src/Products/CMFPlomino/fields/datetime.py | 1 + 9 files changed, 154 insertions(+), 27 deletions(-) diff --git a/setup.py b/setup.py index c35185f7a..52a16364e 100644 --- a/setup.py +++ b/setup.py @@ -46,6 +46,7 @@ 'plone.app.dexterity', 'jsonutil', 'collective.instancebehavior', + 'pyquery' ], extras_require={ 'test': [ diff --git a/src/Products/CMFPlomino/browser/static/css/tinymce.css b/src/Products/CMFPlomino/browser/static/css/tinymce.css index 34d74b026..fbb5b66ae 100644 --- a/src/Products/CMFPlomino/browser/static/css/tinymce.css +++ b/src/Products/CMFPlomino/browser/static/css/tinymce.css @@ -1,19 +1,43 @@ .plominoFieldClass { position:relative; + display: inline-block } +.plominoFieldClass::before { + background: #333; + background: rgba(0,0,0,.8); + border-radius: 5px 5px 0px 0px; + color: #fff; + bottom:100%; + right: 0%; + content: attr(data-plominoid); + padding: 2px 5px; + position: absolute; + z-index: 98; + font-size: 10px; +} + + .plominoEditWidgetTab{ background: #333; background: rgba(0,0,0,.8); - border-radius: 5px; + border-radius: 5px 5px 0px 0px; color: #fff; - left: 20%; - top:100%; - padding: 5px 15px; + left: 90%; + top:-50%; + padding: 2px 5px; position: absolute; z-index: 98; - width: 220px; + font-size: 10px; } + +.mce-content-body *[contentEditable=false][data-mce-selected] select, +.mce-content-body *[contentEditable=false][data-mce-selected] input, +{ + outline: 2px solid #2d8ac7; + padding: 3px; +} + .plominoLabelClass { padding: 2px 10px; color: #aaaaFF; diff --git a/src/Products/CMFPlomino/browser/static/js/tinymce.js b/src/Products/CMFPlomino/browser/static/js/tinymce.js index 0e6b9757e..7a611f84f 100644 --- a/src/Products/CMFPlomino/browser/static/js/tinymce.js +++ b/src/Products/CMFPlomino/browser/static/js/tinymce.js @@ -82,7 +82,11 @@ if (tinymce.DOM.hasClass(selection, elementClass)) { ed.selection.select(selection); - var elementId = selection.firstChild.nodeValue; + var elementId = selection.getAttribute('data-plominoid'); + if (elementId == null) { + elementId = selection.firstChild.nodeValue; + } + // hide-when and cache zones start with start:id and finish with end:id if (elementType === "hidewhen" || elementType === "cache") diff --git a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js index 89733683d..4d46848ed 100644 --- a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js +++ b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js @@ -23,9 +23,9 @@ var PlominoDialog = { var example = document.getElementById("example_widget").innerHTML; if (example) { var span = ''+ - value+'' + - example + ''; + + '" data-plominoid="'+value+'">' +// +''+ value+'' + + example + ''; } else { // String to add in the editor diff --git a/src/Products/CMFPlomino/browser/templates/tinymce/valid.pt b/src/Products/CMFPlomino/browser/templates/tinymce/valid.pt index 758e23a21..8b432b454 100644 --- a/src/Products/CMFPlomino/browser/templates/tinymce/valid.pt +++ b/src/Products/CMFPlomino/browser/templates/tinymce/valid.pt @@ -1,10 +1,11 @@ - + Add/edit a Plomino Field @@ -12,7 +13,8 @@ - + +
@@ -39,7 +41,6 @@
- \ No newline at end of file diff --git a/src/Products/CMFPlomino/browser/tinymce.py b/src/Products/CMFPlomino/browser/tinymce.py index 8a45877a7..5e2598d58 100644 --- a/src/Products/CMFPlomino/browser/tinymce.py +++ b/src/Products/CMFPlomino/browser/tinymce.py @@ -9,8 +9,10 @@ from Products.CMFPlomino.contents.action import ACTION_TYPES, ACTION_DISPLAY from Products.CMFPlomino.contents.action import PlominoAction from Products.CMFPlomino.contents.field import PlominoField +from Products.CMFPlomino.contents.form import PlominoForm from Products.CMFPlomino.contents.hidewhen import PlominoHidewhen #from Products.CMFPlomino.PlominoCache import PlominoCache +from Products.CMFPlomino.document import getTemporaryDocument class ITinyMCEPlominoFormView(interface.Interface): @@ -70,18 +72,6 @@ def valid_page(self, **params): params = {} return self.valid_template(**params) - def example_widget(self): - widget_type = self.request.get('type') - id = self.request.get('value') - if not id: - return - if widget_type == "field": - field = getattr(self.context, id, None) - if isinstance(field.aq_base, PlominoField): #IPlominoField.implementedBy(field): - return field.getRenderedValue(fieldvalue=None, - editmode="EDITABLE", - target=self.context) - class PlominoFormSettings(object): diff --git a/src/Products/CMFPlomino/configure.zcml b/src/Products/CMFPlomino/configure.zcml index a4ab8b35a..7ba381dc5 100644 --- a/src/Products/CMFPlomino/configure.zcml +++ b/src/Products/CMFPlomino/configure.zcml @@ -117,4 +117,13 @@ component=".fields.vocabularies.get_forms" /> + + + + diff --git a/src/Products/CMFPlomino/contents/form.py b/src/Products/CMFPlomino/contents/form.py index 4f7c70da3..1e429b3dc 100644 --- a/src/Products/CMFPlomino/contents/form.py +++ b/src/Products/CMFPlomino/contents/form.py @@ -7,6 +7,9 @@ from plone.autoform import directives as form from plone.dexterity.content import Container from plone.supermodel import directives, model +from z3c.form.datamanager import AttributeField, zope +from Products.CMFPlomino.contents.action import PlominoAction +from Products.CMFPlomino.contents.field import PlominoField import re from zope import schema from zope.interface import implements @@ -30,6 +33,7 @@ urlquote, ) from ..document import getTemporaryDocument +from pyquery import PyQuery as pq logger = logging.getLogger('Plomino') security = ClassSecurityInfo() @@ -41,8 +45,8 @@ class IPlominoForm(model.Schema): """ Plomino form schema """ - form.widget('form_layout', WysiwygFieldWidget) - form_layout = schema.Text( + form.widget('form_layout_visual', WysiwygFieldWidget) + form_layout_visual = schema.Text( title=_('CMFPlomino_label_FormLayout', default="Form layout"), description=_('CMFPlomino_help_FormLayout', default="Text with 'Plominofield' styles correspond to the" @@ -728,12 +732,74 @@ def childDocument(self, doc): 'fields': ''.join(field_items) } + #@property + # Using special datamanager because @property losses acquisition + def getForm_layout_visual(self): + #update all teh example widgets + # TODO: called twice during setter to check if changed + d = pq(self.form_layout) + s = ".plominoActionClass,.plominoSubformClass,.plominoFieldClass" + for element in d.find(s) + d.filter(s): + widget_type = element.attrib["class"][7:-5].lower() + id = element.text + example = self.example_widget(widget_type, id) + pq(element)\ + .html(example)\ + .add_class("mceNonEditable")\ + .attr("data-plominoid", id) + return str(d) + + #@form_layout_visual.setter + # Using special datamanager because @property losses acquisition + def setForm_layout_visual(self, layout): + #TODO strip out all the example widgets + d = pq(layout) + for element in d.find("*[data-plominoid]"): + pq(element)\ + .html(element.attrib["data-plominoid"])\ + .remove_class("mceNonEditable").\ + remove_attr("data-plominoid") + self.form_layout = str(d) + security.declarePrivate('_get_html_content') def _get_html_content(self): + # get the raw value for rendering the form html_content = self.form_layout or '' return html_content.replace('\n', '') + def example_widget(self, widget_type, id): + if not id: + return + db = self.getParentDatabase() + if widget_type == "field": + field = self.getFormField(id) + if field is not None: + return field.getRenderedValue(fieldvalue=None, + editmode="EDITABLE", + target=self) + elif widget_type == "subform": + subform = getattr(self, id, None) + if not isinstance(subform, PlominoForm): + return + doc = getTemporaryDocument(db, form=subform, + REQUEST={}).__of__(db) + return subform.displayDocument( + doc, editmode=True, creation=True, parent_form_id=self.id, + ) + elif widget_type == 'action': + action = getattr(self, id, None) + if not isinstance(action, PlominoAction): + return + pt = self.unrestrictedTraverse( + "@@plomino_actions").embedded_action + action_render = pt(display=action.action_display, + plominoaction=action, + plominotarget=self, + plomino_parent_id=self.id) + return action_render + + security.declareProtected(READ_PERMISSION, 'applyHideWhen') def applyHideWhen(self, doc=None, silent_error=True): @@ -1456,3 +1522,34 @@ def tojson(self, REQUEST=None, item=None): 'aaData': result} return json.dumps(result) + + + +class GetterSetterAttributeField(AttributeField): + """Special datamanager to get around loss on acqusition when using @property""" + zope.component.adapts( + IPlominoForm, zope.schema.interfaces.IField) + + def get(self): + """See z3c.form.interfaces.IDataManager""" + getter = "get%s"%self.field.__name__.capitalize() + if hasattr(self.adapted_context, getter): + return getattr(self.adapted_context, getter)() + else: + return getattr(self.adapted_context, self.field.__name__) + + def set(self, value): + """See z3c.form.interfaces.IDataManager""" + if self.field.readonly: + raise TypeError("Can't set values on read-only fields " + "(name=%s, class=%s.%s)" + % (self.field.__name__, + self.context.__class__.__module__, + self.context.__class__.__name__)) + setter = "set%s"%self.field.__name__.capitalize() + if hasattr(self.adapted_context, setter): + getattr(self.adapted_context, setter)(value) + else: + # get the right adapter or context + setattr(self.adapted_context, self.field.__name__, value) + diff --git a/src/Products/CMFPlomino/fields/datetime.py b/src/Products/CMFPlomino/fields/datetime.py index d037971f7..a923718c4 100644 --- a/src/Products/CMFPlomino/fields/datetime.py +++ b/src/Products/CMFPlomino/fields/datetime.py @@ -42,6 +42,7 @@ class IDatetimeField(model.Schema): startingyear = schema.TextLine( title=u"Starting year", description=u"Oldest year selectable in the calendar widget", + default=u"1975", required=False) From 5037fb161b7bd8c1c18913b7453ad577aa00a7da Mon Sep 17 00:00:00 2001 From: Dylan Jay Date: Sat, 16 Jul 2016 12:59:26 +0700 Subject: [PATCH 03/24] fix formatting of html on save stripping html widgets --- src/Products/CMFPlomino/contents/form.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Products/CMFPlomino/contents/form.py b/src/Products/CMFPlomino/contents/form.py index 1e429b3dc..bda07c0f9 100644 --- a/src/Products/CMFPlomino/contents/form.py +++ b/src/Products/CMFPlomino/contents/form.py @@ -2,6 +2,7 @@ import decimal from jsonutil import jsonutil as json import logging +from lxml.html import tostring from plone import api from plone.app.z3cform.wysiwyg import WysiwygFieldWidget from plone.autoform import directives as form @@ -737,29 +738,29 @@ def childDocument(self, doc): def getForm_layout_visual(self): #update all teh example widgets # TODO: called twice during setter to check if changed - d = pq(self.form_layout) + d = pq(self.form_layout, parser='html_fragments') s = ".plominoActionClass,.plominoSubformClass,.plominoFieldClass" for element in d.find(s) + d.filter(s): widget_type = element.attrib["class"][7:-5].lower() id = element.text example = self.example_widget(widget_type, id) pq(element)\ - .html(example)\ + .html(example, parser='html_fragments')\ .add_class("mceNonEditable")\ .attr("data-plominoid", id) - return str(d) + return ''.join([tostring(e) for e in d]) #@form_layout_visual.setter # Using special datamanager because @property losses acquisition def setForm_layout_visual(self, layout): #TODO strip out all the example widgets - d = pq(layout) + d = pq(layout, parser='html_fragments') for element in d.find("*[data-plominoid]"): pq(element)\ .html(element.attrib["data-plominoid"])\ .remove_class("mceNonEditable").\ remove_attr("data-plominoid") - self.form_layout = str(d) + self.form_layout = ''.join([tostring(e) for e in d]) security.declarePrivate('_get_html_content') From fce2bb0a763d3d61c3505b02b5b6154d2879ca43 Mon Sep 17 00:00:00 2001 From: Dylan Jay Date: Fri, 19 Aug 2016 11:02:29 +0700 Subject: [PATCH 04/24] cache,label and hidewhen should now work as noneditable dragable elements --- .../CMFPlomino/browser/static/css/tinymce.css | 90 ++++++++++++++---- .../CMFPlomino/browser/static/js/tinymce.js | 16 ++-- .../browser/static/js/tinymce.valid.js | 95 ++++--------------- src/Products/CMFPlomino/contents/form.py | 53 +++++++++-- 4 files changed, 148 insertions(+), 106 deletions(-) diff --git a/src/Products/CMFPlomino/browser/static/css/tinymce.css b/src/Products/CMFPlomino/browser/static/css/tinymce.css index fbb5b66ae..44a4cc8c3 100644 --- a/src/Products/CMFPlomino/browser/static/css/tinymce.css +++ b/src/Products/CMFPlomino/browser/static/css/tinymce.css @@ -1,9 +1,9 @@ -.plominoFieldClass { +.plominoFieldClass,.plominoHidewhenClass { position:relative; display: inline-block } -.plominoFieldClass::before { +.plominoFieldClass::before, .plominoHidewhenClass::before { background: #333; background: rgba(0,0,0,.8); border-radius: 5px 5px 0px 0px; @@ -38,14 +38,80 @@ padding: 3px; } -.plominoLabelClass { +/*.plominoLabelClass {*/ + /*padding: 2px 10px;*/ + /*color: #aaaaFF;*/ + /*background-color: #ffffff;*/ + /*border: 1px solid #aaaaFF;*/ + /*border-radius: 4px;*/ + /*box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);*/ +/*}*/ + +/*.plominoLabelClass::after {*/ +/*width: 0;*/ +/*height: 0;*/ +/*border-style: solid;*/ +/*border-width: 100px 0 100px 200px;*/ +/*border-color: transparent transparent transparent #007bff;*/ +/*}*/ + +*[data-plomino-position="start"] { padding: 2px 10px; - color: #aaaaFF; - background-color: #ffffff; - border: 1px solid #aaaaFF; - border-radius: 4px; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + color: #cccccc; + background-color: #cccccc; + background-repeat: no-repeat; + margin-right:0.7em; + display:inline; +} + +*[data-plomino-position="end"] { + padding: 2px 10px; + color: #cccccc; + background-color: #cccccc; + background-repeat: no-repeat; + margin-left:0.7em; + display:inline-block; +} + + +.plominoHidewhenClass[data-plomino-position] { + background-image: url('++resource++Products.CMFPlomino/img/PlominoHideWhen.png'); +} +.plominoLabelClass[data-plomino-position] { + background-image: url('++resource++Products.CMFPlomino/img/PlominoLabel.png'); } +.plominoCacheClass[data-plomino-position] { + background-image: url('++resource++Products.CMFPlomino/img/PlominoCache.png'); +} + +*[data-plomino-position]:after { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-color: rgba(187,0,0,0); + border-width: 0.7em; + margin-top: -0.7em; + z-index: 1; + top: 50%; +} + +*[data-plomino-position="start"]:after { + left: 100%; + border-left-width: 8px; + border-left-color: #cccccc; +} + +*[data-plomino-position="end"]:after { + right: 100%; + border-right-width: 8px; + border-right-color: #cccccc; +} + + + .plominoActionClass { padding: 2px 10px; color: #ffffff; @@ -63,14 +129,6 @@ /*border-radius: 4px;*/ /*box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);*/ /*}*/ -/*.plominoHidewhenClass {*/ - /*padding: 2px 10px;*/ - /*color: #cccccc;*/ - /*background-color: #ffffff;*/ - /*border: 1px dashed #cccccc;*/ - /*border-radius: 4px;*/ - /*box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);*/ -/*}*/ /*.plominoSubformClass {*/ /*padding: 2px 10px;*/ /*color: #ffffff;*/ diff --git a/src/Products/CMFPlomino/browser/static/js/tinymce.js b/src/Products/CMFPlomino/browser/static/js/tinymce.js index 7a611f84f..7e830b7e6 100644 --- a/src/Products/CMFPlomino/browser/static/js/tinymce.js +++ b/src/Products/CMFPlomino/browser/static/js/tinymce.js @@ -85,15 +85,13 @@ var elementId = selection.getAttribute('data-plominoid'); if (elementId == null) { elementId = selection.firstChild.nodeValue; - } - - - // hide-when and cache zones start with start:id and finish with end:id - if (elementType === "hidewhen" || elementType === "cache") - { - var splittedId = elementId.split(':'); - if (splittedId.length > 1) - elementId = splittedId[1]; + // hide-when and cache zones start with start:id and finish with end:id + if (elementType === "hidewhen" || elementType === "cache") + { + var splittedId = elementId.split(':'); + if (splittedId.length > 1) + elementId = splittedId[1]; + } } } else if (elementType !== "hidewhen" && elementType !== "cache") diff --git a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js index 4d46848ed..5f55928df 100644 --- a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js +++ b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js @@ -42,26 +42,26 @@ var PlominoDialog = { else ed.execCommand('mceInsertContent', false, span, {skip_undo : 1}); } - else if (type == "hidewhen") + else if (type == "hidewhen" || type == "label" || type == 'cache') { // Insert or replace the selection // TinyMCE 3, still needed ? //tinyMCEPopup.restoreSelection(); + var cssclass = 'plomino' + type.charAt(0).toUpperCase() + type.slice(1) + 'Class'; + // Select the parent node of the selection var selection = ed.selection.getNode(); // If the node is a , select all its content - if (top.tinymce.DOM.hasClass(selection, 'plominoHidewhenClass')) + if (top.tinymce.DOM.hasClass(selection, cssclass)) { // get the old hide-when id - var oldId = selection.firstChild.nodeValue; - var splittedId = oldId.split(':'); - if (splittedId.length > 1) - oldId = splittedId[1]; + var oldId = selection.firstChild.attr('data-plominoid'); + var pos = selection.firstChild.attr('data-plomino-position') // get a list of hide-when opening and closing spans - var hidewhens = ed.dom.select('span.plominoHidewhenClass'); + var hidewhens = ed.dom.select('span.'+cssclass); // find the selected span var i; for (i = 0; i < hidewhens.length; i++) { @@ -69,81 +69,26 @@ var PlominoDialog = { break; } - // change the corresponding end - if (splittedId[0] == 'start') { - selection.firstChild.nodeValue = 'start:' + value; + // change the corresponding start/end + if (pos == 'start') { + selection.firstChild.attr('data-plominoid', value); for (; i < hidewhens.length; i++) { - if (hidewhens[i].firstChild && hidewhens[i].firstChild.nodeValue == 'end:' + oldId) { - hidewhens[i].firstChild.nodeValue = 'end:' + value; - break; - } - } - } - // change the corresponding start - else { - selection.firstChild.nodeValue = 'end:' + value; - - for (; i >= 0; i--) { - if (hidewhens[i].firstChild && hidewhens[i].firstChild.nodeValue == 'start:' + oldId) { - hidewhens[i].firstChild.nodeValue = 'start:' + value; - break; - } - } - } - } - - else { - // String to add in the editor - var zone = 'start:' + value + '' + ed.selection.getContent() + 'end:' + value + ''; - ed.execCommand('mceInsertContent', false, zone, {skip_undo : 1}); - } - } - else if (type == "cache") - { - // Insert or replace the selection - - // TinyMCE 3, still needed ? - //tinyMCEPopup.restoreSelection(); - - // Select the parent node of the selection - var selection = ed.selection.getNode(); - // If the node is a , select all its content - if (top.tinymce.DOM.hasClass(selection, 'plominoCacheClass')) - { - // get the old cache id - var oldId = selection.firstChild.nodeValue; - var splittedId = oldId.split(':'); - if (splittedId.length > 1) - oldId = splittedId[1]; - - // get a list of cache opening and closing spans - var caches = ed.dom.select('span.plominoCacheClass'); - // find the selected span - var i; - for (i = 0; i < caches.length; i++) { - if (caches[i] == selection) - break; - } - - // change the corresponding end - if (splittedId[0] == 'start') { - selection.firstChild.nodeValue = 'start:' + value; - - for (; i < caches.length; i++) { - if (caches[i].firstChild && caches[i].firstChild.nodeValue == 'end:' + oldId) { - caches[i].firstChild.nodeValue = 'end:' + value; + if (hidewhens[i].firstChild && hidewhens[i].firstChild.attr('data-plominoid') == oldId && + hidewhens[i].firstChild.attr('data-plominoid') == 'end') { + hidewhens[i].firstChild.attr('data-plominoid', value); break; } } } - // change the corresponding start + // change the corresponding start by going backwards else { - selection.firstChild.nodeValue = 'end:' + value; + selection.firstChild.attr('data-plominoid', value); for (; i >= 0; i--) { - if (caches[i].firstChild && caches[i].firstChild.nodeValue == 'start:' + oldId) { - caches[i].firstChild.nodeValue = 'start:' + value; + if (hidewhens[i].firstChild && hidewhens[i].firstChild.attr('data-plominoid') == oldId && + hidewhens[i].firstChild.attr('data-plominoid') == 'start') { + hidewhens[i].firstChild.attr('data-plominoid', value); break; } } @@ -152,7 +97,9 @@ var PlominoDialog = { else { // String to add in the editor - var zone = 'start:' + value + '' + ed.selection.getContent() + 'end:' + value + ''; + var zone = '' + + ed.selection.getContent() + + ''; ed.execCommand('mceInsertContent', false, zone, {skip_undo : 1}); } } diff --git a/src/Products/CMFPlomino/contents/form.py b/src/Products/CMFPlomino/contents/form.py index bda07c0f9..4cb7528de 100644 --- a/src/Products/CMFPlomino/contents/form.py +++ b/src/Products/CMFPlomino/contents/form.py @@ -744,22 +744,61 @@ def getForm_layout_visual(self): widget_type = element.attrib["class"][7:-5].lower() id = element.text example = self.example_widget(widget_type, id) + # .html has a bug - https://github.com/gawel/pyquery/issues/102 + # so can't use it. below will strip off initial text but that's ok pq(element)\ - .html(example, parser='html_fragments')\ + .empty()\ + .append(pq(example, parser='html_fragments'))\ .add_class("mceNonEditable")\ .attr("data-plominoid", id) + + s = ".plominoHidewhenClass,.plominoCacheClass,.plominoLabelClass" + for element in d.find(s) + d.filter(s): + widget_type = element.attrib["class"][7:-5].lower() + if ':' not in element.text: + continue + pos,id = element.text.split(':') + + # .html has a bug - https://github.com/gawel/pyquery/issues/102 + tail = element.tail + pq(element)\ + .html(" ")\ + .add_class("mceNonEditable")\ + .attr("data-plomino-position", pos)\ + .attr("data-plominoid", id) + element.tail = tail + + return ''.join([tostring(e) for e in d]) #@form_layout_visual.setter # Using special datamanager because @property losses acquisition def setForm_layout_visual(self, layout): - #TODO strip out all the example widgets d = pq(layout, parser='html_fragments') - for element in d.find("*[data-plominoid]"): - pq(element)\ - .html(element.attrib["data-plominoid"])\ - .remove_class("mceNonEditable").\ - remove_attr("data-plominoid") + + # restore start: end: type elements + s = ".plominoHidewhenClass,.plominoCacheClass,.plominoLabelClass" + (d.find(s) + d.filter(s)).each( + lambda i, e: pq(e)\ + # .html has a bug - https://github.com/gawel/pyquery/issues/102 + .text("{pos}:{id}".format(pos=pq(e).attr("data-plomino-position"), + id=pq(e).attr("data-plominoid")))\ + .remove_class("mceNonEditable")\ + .remove_attr("data-plominoid")\ + .remove_attr("data-plomino-position") + ) + + # strip out all the example widgets + d.find("*[data-plominoid]").each( + lambda i, e: pq(e)\ + .text(pq(e).attr("data-plominoid"))\ + .remove_class("mceNonEditable")\ + .remove_attr("data-plominoid") + ) + + + + self.form_layout = ''.join([tostring(e) for e in d]) security.declarePrivate('_get_html_content') From ddd99ff5f37c9889d0b7a5332e21235361364f9b Mon Sep 17 00:00:00 2001 From: Dylan Jay Date: Tue, 23 Aug 2016 17:34:50 +0700 Subject: [PATCH 05/24] allow adding a field without typing the id first --- .../browser/static/js/tinymce.valid.js | 6 +-- .../browser/templates/tinymce/field.pt | 46 ++++++++++--------- src/Products/CMFPlomino/browser/tinymce.py | 12 ++--- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js index 5f55928df..3f49af90a 100644 --- a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js +++ b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js @@ -22,8 +22,8 @@ var PlominoDialog = { { var example = document.getElementById("example_widget").innerHTML; if (example) { - var span = '' + var span = '' // +''+ value+'' + example + ''; } @@ -38,7 +38,7 @@ var PlominoDialog = { //tinyMCEPopup.restoreSelection(); var selection = ed.selection.getNode(); if (top.tinymce.DOM.hasClass(selection, 'plominoActionClass') || top.tinymce.DOM.hasClass(selection, 'plominoFieldClass') || top.tinymce.DOM.hasClass(selection, 'plominoLabelClass') || top.tinymce.DOM.hasClass(selection, 'plominoSubformClass')) - ed.dom.setOuterHTML(selection, span); + ed.execCommand('mceInsertContent', false, span, {skip_undo : 1}); else ed.execCommand('mceInsertContent', false, span, {skip_undo : 1}); } diff --git a/src/Products/CMFPlomino/browser/templates/tinymce/field.pt b/src/Products/CMFPlomino/browser/templates/tinymce/field.pt index 9816f31f9..6afb6fe96 100644 --- a/src/Products/CMFPlomino/browser/templates/tinymce/field.pt +++ b/src/Products/CMFPlomino/browser/templates/tinymce/field.pt @@ -7,12 +7,23 @@ Add/edit a Plomino Field +
@@ -20,22 +31,20 @@

Add/edit a Plomino Field

-
+
Select a Plomino Field - +

- - + +

- +
Advanced edition

@@ -44,18 +53,13 @@ target="_blank" id="fieldSettingsLink" i18n:translate="plominofield_specificsettings">Specific settings

- - + + Form which edits the selected field
- -
- Create a Plomino Field - - - -
- + tal:attributes="action python:field and field.absolute_url() + '/tinyfield/setFieldProperties' or fieldid and form.absolute_url() + '/tinyform/addField'"> + + +
Edit Field Properties diff --git a/src/Products/CMFPlomino/browser/tinymce.py b/src/Products/CMFPlomino/browser/tinymce.py index 5e2598d58..245588894 100644 --- a/src/Products/CMFPlomino/browser/tinymce.py +++ b/src/Products/CMFPlomino/browser/tinymce.py @@ -112,12 +112,12 @@ def getField(self): return field else: return None - - fieldsList = self.context.getFormFields() - if len(fieldsList) > 0: - return fieldsList[0] - else: - return None + # + # fieldsList = self.context.getFormFields() + # if len(fieldsList) > 0: + # return fieldsList[0] + # else: + # return None def getFieldProperties(self): """Return properties of an action, or , if no action is given, properties filled with default values. From b629fef5fb6ee6244e67fca0f95b1626a6680177 Mon Sep 17 00:00:00 2001 From: Dylan Jay Date: Wed, 24 Aug 2016 11:11:03 +0700 Subject: [PATCH 06/24] fix up popups for hidewhen and actions --- .../CMFPlomino/browser/configure.zcml | 9 +--- .../browser/static/js/tinymce.valid.js | 20 ++++----- .../browser/templates/tinymce/action.pt | 29 +++++++------ .../browser/templates/tinymce/field.pt | 1 - .../browser/templates/tinymce/hidewhen.pt | 34 ++++++++------- src/Products/CMFPlomino/browser/tinymce.py | 42 ++++++------------- 6 files changed, 59 insertions(+), 76 deletions(-) diff --git a/src/Products/CMFPlomino/browser/configure.zcml b/src/Products/CMFPlomino/browser/configure.zcml index 9f612d734..77ebdf6a8 100644 --- a/src/Products/CMFPlomino/browser/configure.zcml +++ b/src/Products/CMFPlomino/browser/configure.zcml @@ -162,7 +162,7 @@ @@ -176,13 +176,6 @@ /> -
+
Select a Plomino Action

- +

@@ -41,12 +49,7 @@
-
- Create a Plomino Action - - - -
+
Edit Action Properties diff --git a/src/Products/CMFPlomino/browser/templates/tinymce/field.pt b/src/Products/CMFPlomino/browser/templates/tinymce/field.pt index 6afb6fe96..7b9505b7f 100644 --- a/src/Products/CMFPlomino/browser/templates/tinymce/field.pt +++ b/src/Products/CMFPlomino/browser/templates/tinymce/field.pt @@ -23,7 +23,6 @@ adapted form/tinyform|nothing; fieldproperties adapted/getFieldProperties; field adapted/getField; - insert view/request/insert|nothing; ">
diff --git a/src/Products/CMFPlomino/browser/templates/tinymce/hidewhen.pt b/src/Products/CMFPlomino/browser/templates/tinymce/hidewhen.pt index 38762a6eb..1d77161c1 100644 --- a/src/Products/CMFPlomino/browser/templates/tinymce/hidewhen.pt +++ b/src/Products/CMFPlomino/browser/templates/tinymce/hidewhen.pt @@ -6,7 +6,17 @@ Add/edit a Plomino Hide-when - + + Add/edit a Plomino Hide-when
-
+
Select a Plomino Hide-when

- +

@@ -38,13 +46,9 @@
-
- Create a Plomino Hide-when - - - -
- + + +
Edit Hide-when Formula diff --git a/src/Products/CMFPlomino/browser/tinymce.py b/src/Products/CMFPlomino/browser/tinymce.py index 245588894..e587aa33a 100644 --- a/src/Products/CMFPlomino/browser/tinymce.py +++ b/src/Products/CMFPlomino/browser/tinymce.py @@ -112,12 +112,6 @@ def getField(self): return field else: return None - # - # fieldsList = self.context.getFormFields() - # if len(fieldsList) > 0: - # return fieldsList[0] - # else: - # return None def getFieldProperties(self): """Return properties of an action, or , if no action is given, properties filled with default values. @@ -212,12 +206,7 @@ def getAction(self): return action else: return None - - actionsList = self.context.getActions(None, False) - if len(actionsList) > 0: - return actionsList[0][0] - else: - return None + return None def getActionProperties(self): """Return properties of an action, or , if no action is given, properties filled with default values. @@ -305,8 +294,8 @@ def getHidewhen(self): """ hidewhenid = self.request.get("hidewhenid", None) - if self.request.get("create", False): - return None + #if self.request.get("create", False): + # return None if hidewhenid: hidewhen = getattr(self.context, hidewhenid, None) @@ -314,12 +303,7 @@ def getHidewhen(self): return hidewhen else: return None - - hidewhenList = self.context.getHidewhenFormulas() - if len(hidewhenList) > 0: - return hidewhenList[0] - else: - return None + return None def getHidewhenProperties(self): """Returns properties of a hide-when formula, or, if no hide-when formula is given, properties filled with default values. @@ -448,7 +432,7 @@ def setFieldProperties(self): self.request.RESPONSE.redirect(self.context.absolute_url() + "/../@@tinymceplominoform/valid_page?type=field&value=" + self.context.id) -class PlominoHidewhen(object): +class PlominoHidewhenSettings(object): """ """ @@ -502,13 +486,13 @@ def setActionProperties(self): inActionBar = self.request.get("actioninactionbar", None) == 'on' # self.context is the current field - self.context.setTitle(title) - self.context.setActionType(actionType) - self.context.setActionDisplay(actionDisplay) - self.context.setContent(content) - self.context.setHidewhen(hideWhen) - self.context.setInActionBar(inActionBar) - - self.context.aq_inner.at_post_edit_script() + self.context.title = title + self.context.action_type = actionType + self.context.action_display = actionDisplay + self.context.content = content + self.context.hidewhen = hideWhen + self.context.in_action_bar = inActionBar + + #self.context.aq_inner.at_post_edit_script() self.request.RESPONSE.redirect(self.context.absolute_url() + "/../@@tinymceplominoform/valid_page?type=action&value=" + self.context.id) From bc57a05d9acbd03cea9aadfab6b4ce10a841b5b0 Mon Sep 17 00:00:00 2001 From: Dylan Jay Date: Wed, 24 Aug 2016 11:52:18 +0700 Subject: [PATCH 07/24] clean up css for visual elements in tinymce --- .../CMFPlomino/browser/static/css/tinymce.css | 61 ++++++++++++++----- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/src/Products/CMFPlomino/browser/static/css/tinymce.css b/src/Products/CMFPlomino/browser/static/css/tinymce.css index 44a4cc8c3..1fe0d9ff3 100644 --- a/src/Products/CMFPlomino/browser/static/css/tinymce.css +++ b/src/Products/CMFPlomino/browser/static/css/tinymce.css @@ -1,15 +1,28 @@ -.plominoFieldClass,.plominoHidewhenClass { +.plominoFieldClass, +.plominoHidewhenClass, +.plominoActionClass, +.plominoSubformClass{ position:relative; - display: inline-block + display: inline-block; + /*color: #1b6899;*/ + /*background-color: #ffffff;*/ + /*border: 1px solid #1b6899;*/ + /*border-radius: 4px;*/ + /*box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);*/ + } -.plominoFieldClass::before, .plominoHidewhenClass::before { - background: #333; + + +.plominoFieldClass:hover:before, +.plominoHidewhenClass:hover:before, +.plominoActionClass:hover:before, +.plominoSubformClass:hover:before{ background: rgba(0,0,0,.8); border-radius: 5px 5px 0px 0px; color: #fff; bottom:100%; - right: 0%; + left: 0%; content: attr(data-plominoid); padding: 2px 5px; position: absolute; @@ -17,6 +30,21 @@ font-size: 10px; } +.plominoFieldClass { + outline: 1px dashed #1b6899; +} +.plominoFieldClass:hover:before { + color: #ffffff; + background-color: #1b6899; +} + +.plominoActionClass { + outline: 1px dashed #124465; +} +.plominoActionClass:hover:before { + color: #ffffff; + background-color:#124465; +} .plominoEditWidgetTab{ background: #333; @@ -33,9 +61,14 @@ .mce-content-body *[contentEditable=false][data-mce-selected] select, .mce-content-body *[contentEditable=false][data-mce-selected] input, +.mce-content-body *[contentEditable=false][data-mce-selected] button, +.mce-content-body *[contentEditable=false][data-mce-selected] a, +.mce-content-body *[contentEditable=false][data-mce-selected] textarea { outline: 2px solid #2d8ac7; - padding: 3px; + /*padding: 3px;*/ + pointer-events: none; + cursor: default; } /*.plominoLabelClass {*/ @@ -112,14 +145,14 @@ -.plominoActionClass { - padding: 2px 10px; - color: #ffffff; - background-color: #1b6899; - border: 1px solid #124465; - border-radius: 4px; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -} +/*.plominoActionClass {*/ + /*padding: 2px 10px;*/ + /*color: #ffffff;*/ + /*background-color: #1b6899;*/ + /*border: 1px solid #124465;*/ + /*border-radius: 4px;*/ + /*box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);*/ +/*}*/ /*.plominoFieldClass {*/ /*padding: 2px 10px;*/ From d912880d2e4cf745af95072e0d3e11552e0cccf7 Mon Sep 17 00:00:00 2001 From: Dylan Jay Date: Thu, 25 Aug 2016 13:26:17 +0700 Subject: [PATCH 08/24] dnd subforms now work --- .../CMFPlomino/browser/static/css/tinymce.css | 36 +++++------ .../browser/static/js/tinymce.valid.js | 15 +++-- .../browser/templates/tinymce/action.pt | 2 +- .../browser/templates/tinymce/field.pt | 2 +- .../browser/templates/tinymce/hidewhen.pt | 2 +- src/Products/CMFPlomino/contents/form.py | 63 ++++++++++++------- 6 files changed, 71 insertions(+), 49 deletions(-) diff --git a/src/Products/CMFPlomino/browser/static/css/tinymce.css b/src/Products/CMFPlomino/browser/static/css/tinymce.css index 1fe0d9ff3..695ce5f09 100644 --- a/src/Products/CMFPlomino/browser/static/css/tinymce.css +++ b/src/Products/CMFPlomino/browser/static/css/tinymce.css @@ -46,6 +46,22 @@ background-color:#124465; } +.plominoSubformClass { + outline: 1px dashed #cccccc; + /*padding: 2px 10px;*/ + /*color: #ffffff;*/ + /*background-color: #cccccc;*/ + /*border: 1px solid #cccccc;*/ + /*border-radius: 4px;*/ + /*box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);*/ + +} +.plominoSubformClass:hover:before { + color: #ffffff; + background-color:#cccccc; +} + + .plominoEditWidgetTab{ background: #333; background: rgba(0,0,0,.8); @@ -65,29 +81,11 @@ .mce-content-body *[contentEditable=false][data-mce-selected] a, .mce-content-body *[contentEditable=false][data-mce-selected] textarea { - outline: 2px solid #2d8ac7; + /*outline: 2px solid #2d8ac7;*/ /*padding: 3px;*/ pointer-events: none; cursor: default; } - -/*.plominoLabelClass {*/ - /*padding: 2px 10px;*/ - /*color: #aaaaFF;*/ - /*background-color: #ffffff;*/ - /*border: 1px solid #aaaaFF;*/ - /*border-radius: 4px;*/ - /*box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);*/ -/*}*/ - -/*.plominoLabelClass::after {*/ -/*width: 0;*/ -/*height: 0;*/ -/*border-style: solid;*/ -/*border-width: 100px 0 100px 200px;*/ -/*border-color: transparent transparent transparent #007bff;*/ -/*}*/ - *[data-plomino-position="start"] { padding: 2px 10px; color: #cccccc; diff --git a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js index 56f5c1b26..c81567868 100644 --- a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js +++ b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js @@ -4,13 +4,18 @@ var PlominoDialog = { submit : function(type, value, option) { var ed = top.tinymce.activeEditor; + var container = "span"; - if (type == 'action') + if (type == 'action') { var plominoClass = 'plominoActionClass'; - else if (type == 'field') + } + else if (type == 'field') { var plominoClass = 'plominoFieldClass'; - else if (type == 'subform') + } + else if (type == 'subform') { var plominoClass = 'plominoSubformClass'; + container = "div"; + } else if (type == 'label') { var plominoClass = 'plominoLabelClass'; if (option != null && option.length > 0) { @@ -22,10 +27,10 @@ var PlominoDialog = { { var example = document.getElementById("example_widget").innerHTML; if (example) { - var span = '' // +''+ value+'' - + example + ''; + + example + ''; } else { // String to add in the editor diff --git a/src/Products/CMFPlomino/browser/templates/tinymce/action.pt b/src/Products/CMFPlomino/browser/templates/tinymce/action.pt index 914d95a27..ce1731558 100644 --- a/src/Products/CMFPlomino/browser/templates/tinymce/action.pt +++ b/src/Products/CMFPlomino/browser/templates/tinymce/action.pt @@ -40,7 +40,7 @@

diff --git a/src/Products/CMFPlomino/browser/templates/tinymce/field.pt b/src/Products/CMFPlomino/browser/templates/tinymce/field.pt index 7b9505b7f..1d688f134 100644 --- a/src/Products/CMFPlomino/browser/templates/tinymce/field.pt +++ b/src/Products/CMFPlomino/browser/templates/tinymce/field.pt @@ -38,7 +38,7 @@

diff --git a/src/Products/CMFPlomino/browser/templates/tinymce/hidewhen.pt b/src/Products/CMFPlomino/browser/templates/tinymce/hidewhen.pt index 1d77161c1..c2552e1ec 100644 --- a/src/Products/CMFPlomino/browser/templates/tinymce/hidewhen.pt +++ b/src/Products/CMFPlomino/browser/templates/tinymce/hidewhen.pt @@ -37,7 +37,7 @@

diff --git a/src/Products/CMFPlomino/contents/form.py b/src/Products/CMFPlomino/contents/form.py index 8fcf1effe..63fdd6744 100644 --- a/src/Products/CMFPlomino/contents/form.py +++ b/src/Products/CMFPlomino/contents/form.py @@ -739,6 +739,7 @@ def getForm_layout_visual(self): #update all teh example widgets # TODO: called twice during setter to check if changed d = pq(self.form_layout, parser='html_fragments') + root = d[0].getparent() s = ".plominoActionClass,.plominoSubformClass,.plominoFieldClass" for element in d.find(s) + d.filter(s): widget_type = element.attrib["class"][7:-5].lower() @@ -746,11 +747,18 @@ def getForm_layout_visual(self): example = self.example_widget(widget_type, id) # .html has a bug - https://github.com/gawel/pyquery/issues/102 # so can't use it. below will strip off initial text but that's ok - pq(element)\ - .empty()\ - .append(pq(example, parser='html_fragments'))\ - .add_class("mceNonEditable")\ - .attr("data-plominoid", id) + # pq(element)\ + # .empty()\ + # .append(pq(example, parser='html_fragments'))\ + # .add_class("mceNonEditable")\ + # .attr("data-plominoid", id) + html = '<{tag} class="{pclass} mceNonEditable" data-plominoid="{id}">{example}'.format( + id=id, + example=example, + tag='div' if pq(element).has_class('plominoSubformClass') else 'span', + pclass=pq(element).attr('class') + ) + pq(element).replace_with(html) s = ".plominoHidewhenClass,.plominoCacheClass,.plominoLabelClass" for element in d.find(s) + d.filter(s): @@ -769,37 +777,38 @@ def getForm_layout_visual(self): element.tail = tail - return ''.join([tostring(e) for e in d]) + return tostring_innerhtml(root) #@form_layout_visual.setter # Using special datamanager because @property losses acquisition def setForm_layout_visual(self, layout): d = pq(layout, parser='html_fragments') + root = d[0].getparent() # restore start: end: type elements s = ".plominoHidewhenClass,.plominoCacheClass,.plominoLabelClass" - (d.find(s) + d.filter(s)).each( - lambda i, e: pq(e)\ - # .html has a bug - https://github.com/gawel/pyquery/issues/102 + for e in d.find(s) + d.filter(s): + # .html has a bug - https://github.com/gawel/pyquery/issues/102 + pq(e)\ .text("{pos}:{id}".format(pos=pq(e).attr("data-plomino-position"), id=pq(e).attr("data-plominoid")))\ .remove_class("mceNonEditable")\ .remove_attr("data-plominoid")\ .remove_attr("data-plomino-position") - ) # strip out all the example widgets - d.find("*[data-plominoid]").each( - lambda i, e: pq(e)\ - .text(pq(e).attr("data-plominoid"))\ - .remove_class("mceNonEditable")\ - .remove_attr("data-plominoid") - ) - - - - - self.form_layout = ''.join([tostring(e) for e in d]) + s="*[data-plominoid]" + for e in d.find(s) + d.filter(s): + # lambda i, e: pq(e)\ + # .text(pq(e).attr("data-plominoid"))\ + # .remove_class("mceNonEditable")\ + # .remove_attr("data-plominoid") + span = '{id}'.format( + id=pq(e).attr("data-plominoid"), + pclass=pq(e).remove_class("mceNonEditable").attr('class') + ) + pq(e).replace_with(span) + self.form_layout = tostring_innerhtml(root) security.declarePrivate('_get_html_content') @@ -824,9 +833,11 @@ def example_widget(self, widget_type, id): return doc = getTemporaryDocument(db, form=subform, REQUEST={}).__of__(db) - return subform.displayDocument( + rendering = subform.displayDocument( doc, editmode=True, creation=True, parent_form_id=self.id, ) + return rendering + elif widget_type == 'action': action = getattr(self, id, None) if not isinstance(action, PlominoAction): @@ -1593,3 +1604,11 @@ def set(self, value): # get the right adapter or context setattr(self.adapted_context, self.field.__name__, value) + +def tostring_innerhtml(root): + """ pyquery doesn't handle remove or replace_with well when you are dealing + with fragments + """ + if not root: + return '' + return (root.text or '') + ''.join([tostring(child) for child in root.iterchildren()]) \ No newline at end of file From 5b4994342ad018cff2efa90fde179c4e1ed2f175 Mon Sep 17 00:00:00 2001 From: Dylan Jay Date: Fri, 26 Aug 2016 13:56:06 +0700 Subject: [PATCH 09/24] handle inserting dnd datagrids using very basic progressive table. Can't yet disable tinymces table controls. --- .../CMFPlomino/browser/static/css/tinymce.css | 21 +++++++------- .../browser/static/js/tinymce.valid.js | 13 ++++++--- .../browser/templates/tinymce/action.pt | 3 +- .../browser/templates/tinymce/field.pt | 5 ++-- .../browser/templates/tinymce/hidewhen.pt | 3 +- .../CMFPlomino/fields/datagrid_edit.pt | 28 ++++++++++++++----- 6 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/Products/CMFPlomino/browser/static/css/tinymce.css b/src/Products/CMFPlomino/browser/static/css/tinymce.css index 695ce5f09..215fe2aaa 100644 --- a/src/Products/CMFPlomino/browser/static/css/tinymce.css +++ b/src/Products/CMFPlomino/browser/static/css/tinymce.css @@ -1,15 +1,16 @@ -.plominoFieldClass, -.plominoHidewhenClass, -.plominoActionClass, -.plominoSubformClass{ +span.plominoFieldClass, +span.plominoHidewhenClass, +span.plominoActionClass, +span.plominoSubformClass{ position:relative; display: inline-block; - /*color: #1b6899;*/ - /*background-color: #ffffff;*/ - /*border: 1px solid #1b6899;*/ - /*border-radius: 4px;*/ - /*box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);*/ - +} +div.plominoFieldClass, +div.plominoHidewhenClass, +div.plominoActionClass, +div.plominoSubformClass{ + position:relative; + display:block; } diff --git a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js index c81567868..74f7418cb 100644 --- a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js +++ b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js @@ -11,6 +11,7 @@ var PlominoDialog = { } else if (type == 'field') { var plominoClass = 'plominoFieldClass'; + container = "div"; } else if (type == 'subform') { var plominoClass = 'plominoSubformClass'; @@ -25,10 +26,14 @@ var PlominoDialog = { if (plominoClass !== undefined) { - var example = document.getElementById("example_widget").innerHTML; + var eblock = document.getElementById("example_widget"); + var example = eblock.innerHTML; + if (eblock.getElementsByTagName("div") || e.block.getElementsByTagName("table")) { + container = "div"; + } if (example) { var span = '<'+container+' class="'+plominoClass - + ' mceNonEditable" data-plominoid="'+value+'">' + + ' mceNonEditable" data-mce-resize="false" data-plominoid="'+value+'">' // +''+ value+'' + example + ''; } @@ -43,9 +48,9 @@ var PlominoDialog = { //tinyMCEPopup.restoreSelection(); var selection = ed.selection.getNode(); if (top.tinymce.DOM.hasClass(selection, 'plominoActionClass') || top.tinymce.DOM.hasClass(selection, 'plominoFieldClass') || top.tinymce.DOM.hasClass(selection, 'plominoLabelClass') || top.tinymce.DOM.hasClass(selection, 'plominoSubformClass')) - ed.execCommand('mceInsertContent', false, span, {skip_undo : 1}); + ed.execCommand('mceInsertRawHTML', false, span, {skip_undo : 1}); else - ed.execCommand('mceInsertContent', false, span, {skip_undo : 1}); + ed.execCommand('mceInsertRawHTML', false, span, {skip_undo : 1}); } else if (type == "hidewhen" || type == "label" || type == 'cache') { diff --git a/src/Products/CMFPlomino/browser/templates/tinymce/action.pt b/src/Products/CMFPlomino/browser/templates/tinymce/action.pt index ce1731558..f8ce5cb4a 100644 --- a/src/Products/CMFPlomino/browser/templates/tinymce/action.pt +++ b/src/Products/CMFPlomino/browser/templates/tinymce/action.pt @@ -23,6 +23,7 @@ adapted form/tinyform|nothing; actionProperties adapted/getActionProperties; action adapted/getAction; + actions adapted/getActions; ">
@@ -40,7 +41,7 @@

diff --git a/src/Products/CMFPlomino/browser/templates/tinymce/field.pt b/src/Products/CMFPlomino/browser/templates/tinymce/field.pt index 1d688f134..c74791188 100644 --- a/src/Products/CMFPlomino/browser/templates/tinymce/field.pt +++ b/src/Products/CMFPlomino/browser/templates/tinymce/field.pt @@ -23,6 +23,7 @@ adapted form/tinyform|nothing; fieldproperties adapted/getFieldProperties; field adapted/getField; + fields form/getFormFields; ">
@@ -38,8 +39,8 @@ + tal:define="tags python:','.join(f.id for f in fields) if fields else '[]'" + tal:attributes="value fieldid; data-pat-select2 string:allowNewItems:true;; tags:${tags};; maximumSelectionSize:1;;}"/>

diff --git a/src/Products/CMFPlomino/browser/templates/tinymce/hidewhen.pt b/src/Products/CMFPlomino/browser/templates/tinymce/hidewhen.pt index c2552e1ec..fd2f893d3 100644 --- a/src/Products/CMFPlomino/browser/templates/tinymce/hidewhen.pt +++ b/src/Products/CMFPlomino/browser/templates/tinymce/hidewhen.pt @@ -22,6 +22,7 @@ adapted form/tinyform|nothing; hidewhenproperties adapted/getHidewhenProperties; hidewhen adapted/getHidewhen; + hidewhens adapted/getHidewhenFormulas; ">
@@ -37,7 +38,7 @@

diff --git a/src/Products/CMFPlomino/fields/datagrid_edit.pt b/src/Products/CMFPlomino/fields/datagrid_edit.pt index 37d3eb004..8991a2427 100644 --- a/src/Products/CMFPlomino/fields/datagrid_edit.pt +++ b/src/Products/CMFPlomino/fields/datagrid_edit.pt @@ -2,17 +2,31 @@ field python:options['field']; fieldname python:field.id; associated_form python:field.getSettings().getAssociatedForm(); + add_url associated_form/absolute_url|string:.; parent_form field/getParentNode; - fieldvalue python:options['fieldvalue'];" + fieldvalue python:options['fieldvalue']; + columns field/getSettings/getColumnLabels|python:['A','B','C']" tal:attributes=" - data-associated-form python:associated_form.id; + data-associated-form associated_form/id|nothing; data-fields python:field.field_mapping; - data-columns python:','.join(field.getSettings().getColumnLabels()); - data-form-url string:${associated_form/absolute_url}/OpenForm?ajax_load=1&Plomino_Parent_Field=${fieldname}&Plomino_Parent_Form=${parent_form/id}; + data-columns python:','.join(columns); + data-form-url string:${add_url}/OpenForm?ajax_load=1&Plomino_Parent_Field=${fieldname}&Plomino_Parent_Form=${parent_form/id}; "> -
+ + + + + + + + + + +
col
cell
+
\ No newline at end of file From b0661e80da3fc9b0dbde331926d11459cfdce050 Mon Sep 17 00:00:00 2001 From: Dylan Jay Date: Mon, 29 Aug 2016 11:12:42 +0700 Subject: [PATCH 10/24] fix bug in creating empty form --- .../CMFPlomino/browser/static/js/tinymce.valid.js | 7 +++++-- src/Products/CMFPlomino/contents/form.py | 10 +++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js index 74f7418cb..dbb61986d 100644 --- a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js +++ b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js @@ -28,10 +28,13 @@ var PlominoDialog = { { var eblock = document.getElementById("example_widget"); var example = eblock.innerHTML; - if (eblock.getElementsByTagName("div") || e.block.getElementsByTagName("table")) { + // tinymce will remove a span around a block element since its invalid + if (eblock.getElementsByTagName("div") || + eblock.getElementsByTagName("table") || + eblock.getElementsByTagName("p")) { container = "div"; } - if (example) { + if (example != undefined) { var span = '<'+container+' class="'+plominoClass + ' mceNonEditable" data-mce-resize="false" data-plominoid="'+value+'">' // +''+ value+'' diff --git a/src/Products/CMFPlomino/contents/form.py b/src/Products/CMFPlomino/contents/form.py index 63fdd6744..ca1b591c3 100644 --- a/src/Products/CMFPlomino/contents/form.py +++ b/src/Products/CMFPlomino/contents/form.py @@ -41,11 +41,19 @@ label_re = re.compile('((?P\S+):){0,1}\s*(?P.+?)') +class IHelper(model.Schema): + schema.Choice(values=[]) class IPlominoForm(model.Schema): """ Plomino form schema """ + # helpers = schema.List(value_type=schema.Object(IHelper), + # title=u"Helpers", + # description=u"Helpers applied", + # required=False + # ) + form.widget('form_layout_visual', WysiwygFieldWidget) form_layout_visual = schema.Text( title=_('CMFPlomino_label_FormLayout', default="Form layout"), @@ -783,7 +791,7 @@ def getForm_layout_visual(self): # Using special datamanager because @property losses acquisition def setForm_layout_visual(self, layout): d = pq(layout, parser='html_fragments') - root = d[0].getparent() + root = d[0].getparent() if d else d # restore start: end: type elements s = ".plominoHidewhenClass,.plominoCacheClass,.plominoLabelClass" From af4fc278aebffb1a8bc2ff4b4dfabf62735733ef Mon Sep 17 00:00:00 2001 From: Dylan Jay Date: Mon, 29 Aug 2016 12:45:31 +0700 Subject: [PATCH 11/24] fix for display fields with no content --- src/Products/CMFPlomino/contents/form.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Products/CMFPlomino/contents/form.py b/src/Products/CMFPlomino/contents/form.py index ca1b591c3..e03ad0f39 100644 --- a/src/Products/CMFPlomino/contents/form.py +++ b/src/Products/CMFPlomino/contents/form.py @@ -747,7 +747,7 @@ def getForm_layout_visual(self): #update all teh example widgets # TODO: called twice during setter to check if changed d = pq(self.form_layout, parser='html_fragments') - root = d[0].getparent() + root = d[0].getparent() if d else d s = ".plominoActionClass,.plominoSubformClass,.plominoFieldClass" for element in d.find(s) + d.filter(s): widget_type = element.attrib["class"][7:-5].lower() @@ -807,10 +807,6 @@ def setForm_layout_visual(self, layout): # strip out all the example widgets s="*[data-plominoid]" for e in d.find(s) + d.filter(s): - # lambda i, e: pq(e)\ - # .text(pq(e).attr("data-plominoid"))\ - # .remove_class("mceNonEditable")\ - # .remove_attr("data-plominoid") span = '{id}'.format( id=pq(e).attr("data-plominoid"), pclass=pq(e).remove_class("mceNonEditable").attr('class') @@ -832,9 +828,19 @@ def example_widget(self, widget_type, id): if widget_type == "field": field = self.getFormField(id) if field is not None: - return field.getRenderedValue(fieldvalue=None, + html = field.getRenderedValue(fieldvalue=None, editmode="EDITABLE", target=self) + # need to determine if the html will get wiped + field_pq = pq(html) + blocks = 'input,select,table,textarea,button,img,video' + if not field_pq.text() and not field_pq.filter(blocks) and not field_pq.find(blocks): + #TODO: bit of a hack. perhaps need somethign better + return id + else: + return html + + elif widget_type == "subform": subform = getattr(self, id, None) if not isinstance(subform, PlominoForm): From a10799301d57d44d3864d1223a29fed833e09670 Mon Sep 17 00:00:00 2001 From: Daniel Marks Date: Tue, 13 Sep 2016 15:16:06 +0930 Subject: [PATCH 12/24] Fix typo in registry --- src/Products/CMFPlomino/profiles/default/registry.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Products/CMFPlomino/profiles/default/registry.xml b/src/Products/CMFPlomino/profiles/default/registry.xml index 759e992b8..f6b319753 100644 --- a/src/Products/CMFPlomino/profiles/default/registry.xml +++ b/src/Products/CMFPlomino/profiles/default/registry.xml @@ -161,8 +161,8 @@ plone.app.vocabularies.ReallyUserFriendlyTypes - > - PlominoDatabase> + + PlominoDatabase From 6ed55356ae3c4c3be3c601d93d06b34a34ee0e48 Mon Sep 17 00:00:00 2001 From: Daniel Marks Date: Tue, 13 Sep 2016 15:16:19 +0930 Subject: [PATCH 13/24] Fix typo in template --- src/Products/CMFPlomino/browser/templates/tinymce/action.pt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Products/CMFPlomino/browser/templates/tinymce/action.pt b/src/Products/CMFPlomino/browser/templates/tinymce/action.pt index f8ce5cb4a..abde2fbaf 100644 --- a/src/Products/CMFPlomino/browser/templates/tinymce/action.pt +++ b/src/Products/CMFPlomino/browser/templates/tinymce/action.pt @@ -41,7 +41,7 @@

From e03de26df2f8bb240fd889c426cecc0480b7da1b Mon Sep 17 00:00:00 2001 From: Daniel Marks Date: Tue, 13 Sep 2016 15:32:26 +0930 Subject: [PATCH 14/24] Fix splitting bug --- src/Products/CMFPlomino/contents/form.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Products/CMFPlomino/contents/form.py b/src/Products/CMFPlomino/contents/form.py index 76478b132..166c34eaf 100644 --- a/src/Products/CMFPlomino/contents/form.py +++ b/src/Products/CMFPlomino/contents/form.py @@ -781,7 +781,7 @@ def getForm_layout_visual(self): widget_type = element.attrib["class"][7:-5].lower() if ':' not in element.text: continue - pos,id = element.text.split(':') + pos, id = element.text.split(':', 1) # .html has a bug - https://github.com/gawel/pyquery/issues/102 tail = element.tail From dc125e98227eac977d8ff2ebef2e7c6a5a89333e Mon Sep 17 00:00:00 2001 From: Daniel Marks Date: Tue, 6 Sep 2016 17:05:58 +0930 Subject: [PATCH 15/24] Don't change plominoLabels, and handle hidewhens if they haven't been styled --- src/Products/CMFPlomino/browser/static/js/tinymce.valid.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js index dbb61986d..afe05363a 100644 --- a/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js +++ b/src/Products/CMFPlomino/browser/static/js/tinymce.valid.js @@ -55,13 +55,14 @@ var PlominoDialog = { else ed.execCommand('mceInsertRawHTML', false, span, {skip_undo : 1}); } - else if (type == "hidewhen" || type == "label" || type == 'cache') + else if (type == "hidewhen" || type == 'cache') { // Insert or replace the selection // TinyMCE 3, still needed ? //tinyMCEPopup.restoreSelection(); + var cssclass = 'plomino' + type.charAt(0).toUpperCase() + type.slice(1) + 'Class'; // Select the parent node of the selection From 33d01342e3f7b99200b9d1f709237279818d7958 Mon Sep 17 00:00:00 2001 From: Daniel Marks Date: Tue, 13 Sep 2016 15:55:27 +0930 Subject: [PATCH 16/24] Stop handling labels for the moment --- src/Products/CMFPlomino/contents/form.py | 25 ++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Products/CMFPlomino/contents/form.py b/src/Products/CMFPlomino/contents/form.py index 166c34eaf..9580bc3fd 100644 --- a/src/Products/CMFPlomino/contents/form.py +++ b/src/Products/CMFPlomino/contents/form.py @@ -776,7 +776,7 @@ def getForm_layout_visual(self): ) pq(element).replace_with(html) - s = ".plominoHidewhenClass,.plominoCacheClass,.plominoLabelClass" + s = ".plominoHidewhenClass,.plominoCacheClass" for element in d.find(s) + d.filter(s): widget_type = element.attrib["class"][7:-5].lower() if ':' not in element.text: @@ -802,12 +802,14 @@ def setForm_layout_visual(self, layout): root = d[0].getparent() if d else d # restore start: end: type elements - s = ".plominoHidewhenClass,.plominoCacheClass,.plominoLabelClass" + s = ".plominoHidewhenClass,.plominoCacheClass" for e in d.find(s) + d.filter(s): # .html has a bug - https://github.com/gawel/pyquery/issues/102 + position = pq(e).attr("data-plomino-position") + hwid = pq(e).attr("data-plominoid") + if position and hwid: + pq(e).text("{pos}:{id}".format(pos=position, id=hwid)) pq(e)\ - .text("{pos}:{id}".format(pos=pq(e).attr("data-plomino-position"), - id=pq(e).attr("data-plominoid")))\ .remove_class("mceNonEditable")\ .remove_attr("data-plominoid")\ .remove_attr("data-plomino-position") @@ -848,6 +850,21 @@ def example_widget(self, widget_type, id): else: return html + elif widget_type == 'label': + if ':' in id: + fieldid, labeltext = id.split(':', 1) + else: + fieldid = id + labeltext = None + field = self.getFormField(fieldid) + + if labeltext: + html = '' % labeltext + elif field is not None: + html = '' % field.Title() + else: + html = '' % id + return html elif widget_type == "subform": subform = getattr(self, id, None) From d3923b4291ba2976d29cde35a88c696756e600ae Mon Sep 17 00:00:00 2001 From: Daniel Marks Date: Wed, 14 Sep 2016 13:57:14 +0930 Subject: [PATCH 17/24] Add a span before a hidden field so it can be dragged --- src/Products/CMFPlomino/contents/form.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Products/CMFPlomino/contents/form.py b/src/Products/CMFPlomino/contents/form.py index 4ac98f03a..55b7ec119 100644 --- a/src/Products/CMFPlomino/contents/form.py +++ b/src/Products/CMFPlomino/contents/form.py @@ -848,7 +848,12 @@ def example_widget(self, widget_type, id): #TODO: bit of a hack. perhaps need somethign better return id else: - return html + # Handle hidden fields + hidden = field_pq.find('input[type="hidden"]') + if hidden: + pq('Hidden: %s' % id).insertBefore(hidden) + + return field_pq.outer_html() elif widget_type == 'label': if ':' in id: From 25222de83aecaf8544adbbc4f119ff121bbdef2d Mon Sep 17 00:00:00 2001 From: Daniel Marks Date: Wed, 14 Sep 2016 14:05:27 +0930 Subject: [PATCH 18/24] Number fields should use the appropriate input type --- src/Products/CMFPlomino/fields/number_edit.pt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Products/CMFPlomino/fields/number_edit.pt b/src/Products/CMFPlomino/fields/number_edit.pt index 24a6dcf52..d609dbd0d 100644 --- a/src/Products/CMFPlomino/fields/number_edit.pt +++ b/src/Products/CMFPlomino/fields/number_edit.pt @@ -1,3 +1,3 @@ - + From 020daa0e3b0d7f87a964cd8ff1bb79370fba50b6 Mon Sep 17 00:00:00 2001 From: Daniel Marks Date: Wed, 14 Sep 2016 14:17:30 +0930 Subject: [PATCH 19/24] Add test for the rendering of number fields --- src/Products/CMFPlomino/tests/plomino.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Products/CMFPlomino/tests/plomino.txt b/src/Products/CMFPlomino/tests/plomino.txt index b0b7a5a4c..db9705848 100644 --- a/src/Products/CMFPlomino/tests/plomino.txt +++ b/src/Products/CMFPlomino/tests/plomino.txt @@ -466,6 +466,8 @@ Number field:: >>> db.cleanRequestCache() >>> db.frm2.validateInputs(REQUEST) ['price must be a float (submitted value was: zero)'] + >>> db.frm2.displayDocument(None, True, True).replace('\n', '').replace('\t', '') + u'6

Price:

' Rich-text field:: From d3c5ddce3d60f0246f83da23f7a2ac244c85ad15 Mon Sep 17 00:00:00 2001 From: Daniel Marks Date: Wed, 14 Sep 2016 15:05:11 +0930 Subject: [PATCH 20/24] Merge in Dylan's label grouping code --- src/Products/CMFPlomino/contents/form.py | 211 ++++++++++++++++------- 1 file changed, 150 insertions(+), 61 deletions(-) diff --git a/src/Products/CMFPlomino/contents/form.py b/src/Products/CMFPlomino/contents/form.py index 55b7ec119..d182a8e8b 100644 --- a/src/Products/CMFPlomino/contents/form.py +++ b/src/Products/CMFPlomino/contents/form.py @@ -505,79 +505,168 @@ def _handleLabels(self, html_content_orig, editmode): - if the referenced field does not exist, leave the layout markup as is (as for missing field markup). """ - html_content_processed = html_content_orig - match_iter = label_re.finditer(html_content_orig) - for match_label in match_iter: - d = match_label.groupdict() - if d['optional_fieldname']: - fn = d['optional_fieldname'] - field = self.getFormField(fn) - if field: - label = d['fieldname_or_label'] - else: + # Don't try to process an empty HTML doc + if not html_content_orig: + return html_content_orig + + html_content_processed = html_content_orig # We edit the copy + # from lxml import etree + + # dom = etree.HTML(html_content_processed) + # d = pq(html_content_processed, parser='html_fragments') + d = pq(html_content_processed) + + # interate over all the labels + # if there is stuff inbetween its field then grab it too + # create fieldset around teh field and put in the label and inbetween stuff + # we will build up a map of field_id -> (field, nodes_to_group) + # so we can check we group all labels for a given field + field2group = {} + + for label_node in d("span.plominoLabelClass"): + + # work out the fieldid the label is for, and its text + # we could have hide whens or other nodes in our label. filter them out + #TODO: we really want to remove "fieldid:" and leave rest unchanged + #label_text = pq(label_node).clone().children().remove().end().text() + #label_text = pq(label_node).children().html() + #label_re = re.compile('((?P\S+):){0,1}\s*(?P.+?)') + # see if we have a label with fieldid buried in it somewhere. + # we want to keep any html inside it intact. + field = None + for child in [label_node] + pq(label_node).find("*"): + for text_attribute in ['text', 'tail']: + label_text = getattr(child, text_attribute) + if label_text and ':' in label_text: + field_id, label_text = label_text.split(':',1) + field_id = field_id.strip() + field = self.getFormField(field_id) + if field is not None: + # do we replace if we don't know if the field exists in teh layout? yes for ow + setattr(child, text_attribute, label_text) + break + if field is not None: + break + if field is None: + # we aren't using custom label text. In that case we can blow any internal html away + field_id = pq(label_node).clone().children().remove().end().text() + field_id = field_id.strip() + if not field_id: + # label is empty? #TODO: get rid of label? continue - else: - fn = d['fieldname_or_label'] - field = self.getFormField(fn) - if field: - label = asUnicode(field.Title()) + field = self.getFormField(field_id) + if field is not None: + pq(label_node).text(asUnicode(field.Title())) else: + #TODO? should we produce a label anyway? + # We could also look and see if the next field doesn't have a label and use that. continue - field_re = re.compile( - '%s' % fn) - match_field = field_re.search(html_content_processed) + if field_id in field2group: + # we have more than one label. We will try to form a group around both the other labels + # and the field. + (field, togroup, labels) = field2group[field_id] + labels = labels + [label_node] + else: + labels = [label_node] + + + #field_node.first(":parent") + # do a breadth first search but starting at the label and going up + togroup = [] + for parent in [label_node] + [n for n in reversed(pq(label_node).parents())]: + #parent.next("span.plominoFieldClass") + togroup = [] + to_find = set(labels+[field]) + # go through siblings until to find first and last target + for sibling in pq(parent).parent().children(): + found_in_sibling = False + field_node = pq(sibling)("span.plominoFieldClass").eq(0) + if field_node and field_node.text().strip() == field_id: + # found our field + found_in_sibling = True + to_find.remove(field) + elif field_node and togroup: + # found a field in our group thats not ours + # If it's a dynamic field we want this in our groping + group_field_id = field_node.text().strip() + group_field = self.getFormField(group_field_id) + if group_field is not None and group_field.isDynamicField: + found_in_sibling = True + # otherwise disolve grouping + else: + togroup = found = [] + break + + for label in set(to_find): + if sibling in pq(label).parents() or label == sibling: + to_find.remove(label) + found_in_sibling = True + if found_in_sibling or togroup: + togroup.append(sibling) + if not to_find: + # we found everything already + break + + if not to_find: + # we found everything already + break + + if togroup: + field2group[field_id] = (field, togroup, labels) + + for field_id, (field, togroup, labels) in field2group.items(): + field_type = field.field_type if hasattr(field, 'widget'): widget_name = field.widget - # Handle input groups: - if field_type in ('DATETIME', 'SELECTION', ) and widget_name in ( - 'CHECKBOX', 'RADIO', 'SERVER', - ): - # Delete processed label - html_content_processed = label_re.sub( - '', html_content_processed, count=1) - # Is the field in the layout? - if match_field: - # Markup the field - if editmode: - mandatory = ( - field.mandatory - and " class='required'" - or '') - html_content_processed = field_re.sub( - "
%s%s
" % ( - mandatory, label, match_field.group() - ), - html_content_processed - ) - else: - html_content_processed = field_re.sub( - "
%s%s
" % ( - fn, label, match_field.group() - ), html_content_processed) + compound_widget = (field_type == 'DATETIME' or + field_type == 'SELECTION' and + widget_name in ['CHECKBOX', 'RADIO', 'PICKLIST']) - # Handle single inputs: + # groupit: take label, inbetween, field and wrap it in a container + # if its a compound widget like datetime or selection then use a fieldset and legend + # if a simple widget then just a div + if editmode: + legend = "