From 14f85509e1171eb0a6a5f8a3a668c17be943cc2e Mon Sep 17 00:00:00 2001 From: Gaetan Delannay Date: Wed, 30 Apr 2014 21:08:42 +0200 Subject: [PATCH] [gen] Added ref.render == 'titles' = a way to render linked objects as a simple list of comma-separated, non clickable titles; bugfix in inline-edit of string XHTML fields. --- fields/__init__.py | 1 + fields/pod.py | 8 +++--- fields/ref.py | 55 ++++++++++++++++------------------------ fields/string.py | 62 ++++++++++++++++++++++++++++++++++++---------- gen/model.py | 2 +- gen/tr/Appy.pot | 2 +- gen/tr/ar.po | 2 +- gen/tr/de.po | 2 +- gen/tr/en.po | 2 +- gen/tr/es.po | 2 +- gen/tr/fr.po | 4 +-- gen/tr/it.po | 4 +-- gen/tr/nl.po | 4 +-- gen/ui/appy.css | 4 +-- gen/ui/appy.js | 16 ++++++++++++ 15 files changed, 106 insertions(+), 64 deletions(-) diff --git a/fields/__init__.py b/fields/__init__.py index 5c281c4..6bcf415 100644 --- a/fields/__init__.py +++ b/fields/__init__.py @@ -40,6 +40,7 @@ class Field: cssFiles = {} jsFiles = {} dLayouts = 'lrv-d-f' + hLayouts = 'lhrv-f' wLayouts = Table('lrv-f') # Render a field. Optional vars: diff --git a/fields/pod.py b/fields/pod.py index 92e64c6..6cc3fc4 100644 --- a/fields/pod.py +++ b/fields/pod.py @@ -78,7 +78,7 @@ class Pod(Field): :_('unfreezeField') + class="smaller">:_('unfreezeField') @@ -87,7 +87,7 @@ class Pod(Field): :_('freezeField') + class="smaller">:_('freezeField') @@ -96,7 +96,7 @@ class Pod(Field): :_('uploadField') + class="smaller">:_('uploadField') @@ -107,7 +107,7 @@ class Pod(Field): template. For a single template, the field label already does the job. --> :field.getTemplateName(obj, info.template) + class="smaller">:field.getTemplateName(obj, info.template) diff --git a/fields/ref.py b/fields/ref.py index 576dff0..a874b3d 100644 --- a/fields/ref.py +++ b/fields/ref.py @@ -195,24 +195,10 @@ class Ref(Field): # PX that displays referred objects as a list. pxViewList = Px(''' - - - - - - - - - - - - - -
:_('no_ref'):field.pxAdd:field.pxObjectTitle
+ +
:_('no_ref')
- - +
:_(subLabel) (:totalNumber) @@ -231,10 +217,7 @@ class Ref(Field): :tool.pxNavigate - -

:_('no_ref')

- - + :', '.join(titles) or \ + _('no_ref')''') + # PX that displays referred objects through this field. In mode link="list", # if, in the request, key "scope" is present and holds value "objs", the # pick list (containing possible values) will not be rendered. pxView = Px(''' - @@ -410,12 +392,11 @@ class Ref(Field):
:field.pxViewList
:field.pxViewList
- :field.pxViewMenus + :getattr(field, 'pxView%s' % \ + render.capitalize())''') - # The "menus" render mode is only applicable in "cell", not in "view". - pxCell = Px(''':field.pxView''') - + pxCell = pxView pxEdit = Px(''' ''') @@ -298,7 +299,7 @@ class String(Field): label=None, sdefault='', scolspan=1, swidth=None, sheight=None, persist=True, transform='none', styles=('p','h1','h2','h3','h4'), allowImageUpload=True, - inlineEdit=False): + spellcheck=False, contentLanguage=None, inlineEdit=False): # According to format, the widget will be different: input field, # textarea, inline editor... Note that there can be only one String # field of format CAPTCHA by page, because the captcha challenge is @@ -310,6 +311,10 @@ class String(Field): self.styles = styles # When format is XHTML, do we allow the user to upload images in it ? self.allowImageUpload = allowImageUpload + # When format is XHTML, do we run the CK spellchecker ? + self.spellcheck = spellcheck + # What is the language of field content? + self.contentLanguage = contentLanguage # When format in XHTML, can the field be inline-edited (ckeditor)? self.inlineEdit = inlineEdit # The following field has a direct impact on the text entered by the @@ -409,6 +414,12 @@ class String(Field): type='warning') Field.store(self, obj, value) + def storeFromAjax(self, obj): + '''Stores the new field value from an Ajax request, or do nothing if + the action was canceled.''' + rq = obj.REQUEST + if rq.get('cancel') != 'True': self.store(obj, rq['fieldContent']) + def getDiffValue(self, obj, value): '''Returns a version of p_value that includes the cumulative diffs between successive versions.''' @@ -656,27 +667,52 @@ class String(Field): generator).''' return self.getCaptchaChallenge({})['text'] - def getJsInit(self, obj): - '''Gets the Javascript init code for displaying a rich editor for this - field (rich field only).''' - # Define the attributes that will initialize the ckeditor instance for - # this field. + ckLanguages = {'en': 'en_US', 'pt': 'pt_BR', 'da': 'da_DK', 'nl': 'nl_NL', + 'fi': 'fi_FI', 'fr': 'fr_FR', 'de': 'de_DE', 'el': 'el_GR', + 'it': 'it_IT', 'nb': 'nb_NO', 'pt': 'pt_PT', 'es': 'es_ES', + 'sv': 'sv_SE'} + def getCkLanguage(self): + '''Gets the language for CK editor SCAYT. We will use + self.contentLanguage. If it is not supported by CK, we use + english.''' + lang = self.contentLanguage + if lang and (lang in self.ckLanguages): return self.ckLanguages[lang] + return 'en_US' + + def getCkParams(self, obj): + '''Gets the base params to set on a rich text field.''' ckAttrs = {'toolbar': 'Appy', - 'format_tags': '%s' % ';'.join(self.styles)} + 'format_tags': ';'.join(self.styles), + 'scayt_sLang': self.getCkLanguage()} if self.width: ckAttrs['width'] = self.width + if self.spellcheck: ckAttrs['scayt_autoStartup'] = True if self.allowImageUpload: ckAttrs['filebrowserUploadUrl'] = '%s/upload' % obj.absolute_url() ck = [] for k, v in ckAttrs.iteritems(): if isinstance(v, int): sv = str(v) + if isinstance(v, bool): sv = str(v).lower() else: sv = '"%s"' % v ck.append('%s: %s' % (k, sv)) - return 'CKEDITOR.replace("%s", {%s})' % (self.name, ', '.join(ck)) + return ', '.join(ck) + + def getJsInit(self, obj): + '''Gets the Javascript init code for displaying a rich editor for this + field (rich field only).''' + return 'CKEDITOR.replace("%s", {%s})' % \ + (self.name, self.getCkParams(obj)) def getJsInlineInit(self, obj): '''Gets the Javascript init code for enabling inline edition of this field (rich text only).''' - uid = obj.UID() + uid = obj.id + return "CKEDITOR.disableAutoInline = true;\n" \ + "CKEDITOR.inline('%s_%s_ck', {%s, on: {blur: " \ + "function( event ) { var content = event.editor.getData(); " \ + "doInlineSave('%s', '%s', '%s', content)}}})" % \ + (uid, self.name, self.getCkParams(obj), uid, self.name, + obj.absolute_url()) + return "CKEDITOR.disableAutoInline = true;\n" \ "CKEDITOR.inline('%s_%s_ck', {on: {blur: " \ "function( event ) { var data = event.editor.getData(); " \ diff --git a/gen/model.py b/gen/model.py index d1881a5..da339b2 100644 --- a/gen/model.py +++ b/gen/model.py @@ -12,7 +12,7 @@ class Protos: # List of attributes that can't be given to a Type constructor notInit = ('id', 'type', 'pythonType', 'slaves', 'isSelect', 'hasLabel', 'hasDescr', 'hasHelp', 'required', 'filterable', 'validable', - 'isBack', 'pageName', 'masterName') + 'isBack', 'pageName', 'masterName', 'renderLabel') @classmethod def get(self, appyType): '''Returns a prototype instance for p_appyType.''' diff --git a/gen/tr/Appy.pot b/gen/tr/Appy.pot index ef47237..fc90a10 100644 --- a/gen/tr/Appy.pot +++ b/gen/tr/Appy.pot @@ -79,7 +79,7 @@ msgstr "" msgid "max_ref_violated" msgstr "" -#. Default: "No object." +#. Default: "-" msgid "no_ref" msgstr "" diff --git a/gen/tr/ar.po b/gen/tr/ar.po index cef7ded..db5a41f 100644 --- a/gen/tr/ar.po +++ b/gen/tr/ar.po @@ -79,7 +79,7 @@ msgstr "" msgid "max_ref_violated" msgstr "" -#. Default: "No object." +#. Default: "-" msgid "no_ref" msgstr "" diff --git a/gen/tr/de.po b/gen/tr/de.po index 84322f1..d09feaa 100644 --- a/gen/tr/de.po +++ b/gen/tr/de.po @@ -79,7 +79,7 @@ msgstr "Hier müssen Sie Elemente auswählen." msgid "max_ref_violated" msgstr "Sie haben zuviele Elemente ausgewählt." -#. Default: "No object." +#. Default: "-" msgid "no_ref" msgstr "Kein Element" diff --git a/gen/tr/en.po b/gen/tr/en.po index 3a5bd77..a3d8794 100644 --- a/gen/tr/en.po +++ b/gen/tr/en.po @@ -80,7 +80,7 @@ msgstr "You must choose more elements here." msgid "max_ref_violated" msgstr "Too much elements are selected here." -#. Default: "No object." +#. Default: "-" msgid "no_ref" msgstr "No object." diff --git a/gen/tr/es.po b/gen/tr/es.po index 05f20fc..1eb6ee1 100644 --- a/gen/tr/es.po +++ b/gen/tr/es.po @@ -79,7 +79,7 @@ msgstr "Debe elegir más elementos aquí." msgid "max_ref_violated" msgstr "Demasiados elementos son seleccionados aquí." -#. Default: "No object." +#. Default: "-" msgid "no_ref" msgstr "Ningún elemento." diff --git a/gen/tr/fr.po b/gen/tr/fr.po index de56336..bae3355 100644 --- a/gen/tr/fr.po +++ b/gen/tr/fr.po @@ -80,9 +80,9 @@ msgstr "Vous devez choisir plus d'éléments ici." msgid "max_ref_violated" msgstr "Trop d'éléments sont sélectionnés ici." -#. Default: "No object." +#. Default: "-" msgid "no_ref" -msgstr "Aucun élément." +msgstr "-" #. Default: "Add a new one" msgid "add_ref" diff --git a/gen/tr/it.po b/gen/tr/it.po index 75a5e43..5c1305e 100644 --- a/gen/tr/it.po +++ b/gen/tr/it.po @@ -79,9 +79,9 @@ msgstr "Qui deve scegliere un maggior numero di elementi" msgid "max_ref_violated" msgstr "Un numero eccessivo di elementi sono scelti" -#. Default: "No object." +#. Default: "-" msgid "no_ref" -msgstr "Nessun elemento" +msgstr "-" #. Default: "Add a new one" msgid "add_ref" diff --git a/gen/tr/nl.po b/gen/tr/nl.po index 320b5a8..b549eb1 100644 --- a/gen/tr/nl.po +++ b/gen/tr/nl.po @@ -79,9 +79,9 @@ msgstr "U moet hier meerdere elementen selecteren." msgid "max_ref_violated" msgstr "U hebt teveel elementen geselecteerd." -#. Default: "No object." +#. Default: "-" msgid "no_ref" -msgstr "Geen element." +msgstr "-" #. Default: "Add a new one" msgid "add_ref" diff --git a/gen/ui/appy.css b/gen/ui/appy.css index 218340f..38347b6 100644 --- a/gen/ui/appy.css +++ b/gen/ui/appy.css @@ -100,7 +100,7 @@ td.search { padding-top: 8px } border: 1px solid grey; box-shadow: 2px 2px 2px #888888} .dropdown { display:none; position: absolute; border: 1px solid #cccccc; background-color: white; padding: 3px 4px 0; font-size: 8pt; - font-weight: normal } + font-weight: normal; z-index: 2 } .dropdownMenu { cursor: pointer; padding-right: 4px; font-size: 93% } .dropdown a:hover { text-decoration: underline } .list { margin-bottom: 3px } @@ -155,7 +155,7 @@ td.search { padding-top: 8px } .homeTable th { padding-top: 5px; font-size: 105% } .first { margin-top: 0px } .error { margin: 5px } -.podName { font-size: 95% } +.smaller { font-size: 95% } .podTable { margin-left: 15px } .cbCell { width: 10px; text-align: center} .tabs { position:relative; bottom:-2px } diff --git a/gen/ui/appy.js b/gen/ui/appy.js index cd16a2d..ee3be6b 100644 --- a/gen/ui/appy.js +++ b/gen/ui/appy.js @@ -275,6 +275,22 @@ function askField(hookId, objectUrl, layoutType, showChanges, masterValues, askAjaxChunk(hookId, 'GET', objectUrl, px, params, null, evalInnerScripts); } +function doInlineSave(objectUid, name, objectUrl, content){ + /* Ajax-saves p_content of field named p_name on object whose id is + p_objectUid and whose URL is p_objectUrl. Asks a confirmation before + doing it. */ + var doIt = confirm('Do it?'); + var params = {'action': 'storeFromAjax', 'layoutType': 'view'}; + var hook = null; + if (!doIt) { + params['cancel'] = 'True'; + hook = objectUid + '_' + name; + } + else { params['fieldContent'] = encodeURIComponent(content) } + askAjaxChunk(hook, 'POST', objectUrl, name + ':pxRender', params, null, + evalInnerScripts); +} + // Used by checkbox widgets for having radio-button-like behaviour. function toggleCheckbox(visibleCheckbox, hiddenBoolean) { vis = document.getElementById(visibleCheckbox);