From 7bf233bbee18d3869a44d4aa27b049abd77a49bc Mon Sep 17 00:00:00 2001 From: Arnaud Fontaine <arnaud.fontaine@nexedi.com> Date: Fri, 19 Jul 2013 18:57:44 +0900 Subject: [PATCH] Add save button to Ace Editor to save source code while staying on the same page. This has several benefits: * No need to exit maximize/fullscreen modes to save. * The cursor position in the editor does not change. This implementation is hackish because it is too Component-specific, but until RenderJS is being used, there is no probably no better way. --- .../erp5_ace_editor/ace_editor_support.xml | 165 +++++++++++++++++- bt5/erp5_ace_editor/bt/change_log | 3 + bt5/erp5_ace_editor/bt/revision | 2 +- .../portal_skins/erp5_core/Base_edit.xml | 5 +- .../Component_view/my_error_message_list.xml | 24 --- .../my_translated_validation_state_title.xml | 8 +- .../ERP5/bootstrap/erp5_core/bt/change_log | 3 + product/ERP5/bootstrap/erp5_core/bt/revision | 2 +- product/ERP5Type/mixin/component.py | 14 +- 9 files changed, 190 insertions(+), 36 deletions(-) diff --git a/bt5/erp5_ace_editor/SkinTemplateItem/portal_skins/erp5_ace_editor/ace_editor_support.xml b/bt5/erp5_ace_editor/SkinTemplateItem/portal_skins/erp5_ace_editor/ace_editor_support.xml index 7671fb77c8..1c66056b58 100644 --- a/bt5/erp5_ace_editor/SkinTemplateItem/portal_skins/erp5_ace_editor/ace_editor_support.xml +++ b/bt5/erp5_ace_editor/SkinTemplateItem/portal_skins/erp5_ace_editor/ace_editor_support.xml @@ -69,11 +69,46 @@ .ace_line {\n color: black !important;\n }\n +\n + .ace_editor_save_button {\n + position: absolute;\n + top: 5px;\n + right: 20px;\n + width: 30px;\n + height: 30px;\n + border: none;\n + background-color: transparent;\n + cursor: pointer;\n + }\n +\n + .ace_editor_maximize_fullscreen_message {\n + display: table;\n + position: absolute;\n + bottom: 0;\n + right: 20px;\n + z-index: 424242;\n + padding: 20px;\n + background-color: #DAE6F6;\n + border: 1px solid #97B0D1;\n + opacity: 0.3;\n + cursor: pointer;\n + font-weight: bold;\n + }\n +\n + .ace_editor_maximize_fullscreen_error_message {\n + background-color: red;\n + }\n +\n + .ace_editor_maximize_fullscreen_message > div {\n + font-size: 14px;\n + display: table-cell;\n + vertical-align: middle;\n + }\n \n #maximize_message {\n display: block !important;\n position: absolute !important;\n - top: 0 !important;\n + bottom: 0 !important;\n right: 0px !important;\n z-index: 4243 !important;\n padding: 10px;\n @@ -139,10 +174,16 @@ <script type="text/javascript"\n tal:attributes="src string:${portal_url}/ace/mode-python.js"></script>\n <script type="text/javascript"\n - tal:define=\'fullscreen_button string:<input type="button" value="Fullscreen" onclick="switchToFullScreen()" class="ace_editor_action_button" />\'\n + tal:define=\'fullscreen_button string:<input type="button" value="Fullscreen" onclick="switchToFullScreen()" class="ace_editor_action_button" />;\n + save_button string:<button class="ace_editor_save_button" onclick="saveDocument(event)"><img src="images/save2.png" width="30" height="30" border="0" /></button>;\'\n +\n tal:content="structure string:\n ace_editor_container_div = null;\n ace_editor = null;\n +\n + function maximizeFullscreenRemoveSaveMessage() {\n + $(\'.ace_editor_maximize_fullscreen_message\').remove();\n + }\n \n function switchToFullScreen(id) {\n element = document.getElementById(\'${container_div_id}\');\n @@ -151,10 +192,10 @@ if (element.requestFullScreen) {\n element.requestFullScreen();\n }\n - else if (element.mozRequestFullScreen) {\n + else if(element.mozRequestFullScreen) {\n element.mozRequestFullScreen();\n }\n - else if (element.webkitRequestFullScreen) {\n + else if(element.webkitRequestFullScreen) {\n element.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);\n }\n ace_editor.resize();\n @@ -181,6 +222,7 @@ \n function unmaximize(event) {\n if(event.keyCode == 27) {\n + maximizeFullscreenRemoveSaveMessage();\n $(\'body\').css(\'overflow\', \'visible\');\n ace_editor_container_div.removeClass(\'maximize\');\n ace_editor_container_div.appendTo(ace_editor_container_div_parent_before_maximized);\n @@ -207,6 +249,104 @@ $(document).keyup(unmaximize);\n ace_editor.resize();\n }\n +\n + // Save source code only through an AJAX request\n + function saveDocument(event) {\n + event.stopPropagation();\n + event.preventDefault();\n +\n + clickSaveButton(\'Base_edit\');\n +\n + /* If the save is successful, then update validation state field (requires\n + * ace_editor_validation_state CSS class to be set on the field) and error\n + * message (requires error CSS class to be set on the field) on the main\n + * page. If inside maximize/fullscreen mode, display an box with the\n + * result as well\n + */\n + function successHandler(data) {\n + transition_message = $(\'#transition_message\');\n + transition_message.css(\'opacity\', 0.0);\n + transition_message.html(data);\n + transition_message.animate({opacity: 1.0},\n + {duration: 3000, queue: false});\n +\n + var maximize_fullscreen_message = data;\n + var error_arr = [];\n +\n + var validation_state_span = $(\'div.input > .ace_editor_validation_state\');\n + if(validation_state_span.length) {\n + // Animate field to emphasize the change\n + function getTranslatedValidationStateTitleHandler(data) {\n + validation_state_span.css(\'opacity\', 0.0);\n + validation_state_span.html(data);\n + validation_state_span.animate({opacity: 1.0},\n + {duration: 3000, queue: false});\n + }\n +\n + $.ajax({type: \'GET\',\n + url: \'getTranslatedValidationStateTitle\',\n + success: getTranslatedValidationStateTitleHandler});\n + }\n +\n + var error_element = $(\'div.input > .error\');\n + if(error_element.length) {\n + // Animate field to emphasize the change\n + function getErrorMessageListHandler(data) {\n + error_arr = $.parseJSON(data);\n + error_element.css(\'opacity\', 0.0);\n + error_element.html(error_arr.join(\'<br />\'));\n + error_element.animate({opacity: 1.0},\n + {duration: 3000, queue: false});\n + }\n +\n + $.ajax({type: \'GET\',\n + async: false,\n + url: \'getErrorMessageList\',\n + data: \'as_json:int=1\',\n + success: getErrorMessageListHandler});\n + }\n +\n + if($(\'.maximize\').length ||\n + (document.fullScreenElement && document.fullScreenElement !== null &&\n + (document.mozFullScreen || document.webkitIsFullScreen))) {\n + var msg_elem_classes = \'ace_editor_maximize_fullscreen_message\';\n + if(error_arr.length) {\n + maximize_fullscreen_message = error_arr.join(\'<br />\');\n + msg_elem_classes += \' ace_editor_maximize_fullscreen_error_message\';\n + }\n +\n + // Clear previous saving message if any\n + maximizeFullscreenRemoveSaveMessage();\n +\n + msg_elem = $(\'<div class="\' + msg_elem_classes + \'">\' +\n + \'<div>\' + maximize_fullscreen_message + \'</div></div>\');\n +\n + msg_elem.appendTo($(\'#${div_id}\'));\n +\n + function animateMessageComplete() {\n + if(!error_arr.length)\n + $(this).remove();\n + else\n + $(this).bind(\'click\', function() { $(this).remove() });\n + }\n + msg_elem.animate({opacity: 1.0}, 1500, animateMessageComplete);\n + }\n + }\n +\n + function errorHandler(data, textStatus) {\n + alert(\'Saving failed: \' + textStatus);\n + }\n +\n + var edit_data = $(\'form#main_form\').serialize();\n + edit_data += \'&message_only:int=1\';\n + $.ajax({type: \'POST\',\n + url: \'Base_edit\',\n + data: edit_data,\n + success: successHandler,\n + error: errorHandler});\n +\n + return false;\n + }\n \n window.onload = function() {\n ace_editor_container_div = $(\'#${container_div_id}\');\n @@ -225,11 +365,26 @@ ace_editor.getSession().on(\'change\', function() {\n textarea.val(ace_editor.getSession().getValue());\n });\n +\n + /* Only display the source code saving button if the main save button is\n + * displayed. This specific save button allows to save without reloading the\n + * page (and thus keep the cursor position and mode (maximize/fullscreen)\n + * through an AJAX request.\n + *\n + * TODO: Use RenderJS instead to avoid this ugly hack as only some fields\n + * are reloaded and this is not generic at all.\n + */\n + if($$(\'div.actions > button.save[name=Base_edit:method]\').length)\n + $$(\'${save_button}\').appendTo($(\'#${div_id}\'));\n \n if(typeof document.cancelFullScreen != \'undefined\' ||\n (typeof document.mozFullScreenEnabled != \'undefined\' && document.mozFullScreenEnabled) ||\n - typeof document.webkitCancelFullScreen != \'undefined\')\n + typeof document.webkitCancelFullScreen != \'undefined\') {\n + $$(document).bind(\'webkitfullscreenchange mozfullscreenchange fullscreenchange\',\n + maximizeFullscreenRemoveSaveMessage);\n +\n $$(\'${fullscreen_button}\').insertAfter($$(\'input.ace_editor_action_button\'));\n + }\n };">\n </script>\n </tal:block> diff --git a/bt5/erp5_ace_editor/bt/change_log b/bt5/erp5_ace_editor/bt/change_log index c2fcbe3b66..5b7d8abd04 100644 --- a/bt5/erp5_ace_editor/bt/change_log +++ b/bt5/erp5_ace_editor/bt/change_log @@ -1,3 +1,6 @@ +2013-07-19 arnaud.fontaine +* Add save button to Ace Editor to save source code while staying on the same page. + 2013-07-09 arnaud.fontaine * ZODB Components: Follow ERP5 Python indentation style in Ace editor. diff --git a/bt5/erp5_ace_editor/bt/revision b/bt5/erp5_ace_editor/bt/revision index f11c82a4cb..9a037142aa 100644 --- a/bt5/erp5_ace_editor/bt/revision +++ b/bt5/erp5_ace_editor/bt/revision @@ -1 +1 @@ -9 \ No newline at end of file +10 \ No newline at end of file diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_edit.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_edit.xml index caf66afaed..af33d0d256 100644 --- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_edit.xml +++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_edit.xml @@ -287,6 +287,9 @@ try:\n except ActivityPendingError,e:\n message = Base_translateString("%s" % e)\n \n +if message_only:\n + return message\n +\n ignore_layout = int(ignore_layout)\n editable_mode = int(editable_mode)\n spp = context.getPhysicalPath()\n @@ -332,7 +335,7 @@ return result\n </item> <item> <key> <string>_params</string> </key> - <value> <string>form_id, selection_index=0, selection_name=\'\', dialog_id=\'\', ignore_layout=0, editable_mode=1, silent_mode=0, field_prefix=\'my_\', key_prefix=None, listbox_edit=None</string> </value> + <value> <string>form_id, selection_index=0, selection_name=\'\', dialog_id=\'\', ignore_layout=0, editable_mode=1, silent_mode=0, field_prefix=\'my_\', key_prefix=None, listbox_edit=None, message_only=False</string> </value> </item> <item> <key> <string>id</string> </key> diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Component_view/my_error_message_list.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Component_view/my_error_message_list.xml index 9db83bc78f..f2d81ac436 100644 --- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Component_view/my_error_message_list.xml +++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Component_view/my_error_message_list.xml @@ -12,7 +12,6 @@ <list> <string>css_class</string> <string>editable</string> - <string>enabled</string> <string>height</string> <string>title</string> </list> @@ -64,12 +63,6 @@ <key> <string>editable</string> </key> <value> <string></string> </value> </item> - <item> - <key> <string>enabled</string> </key> - <value> - <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent> - </value> - </item> <item> <key> <string>field_id</string> </key> <value> <string></string> </value> @@ -105,10 +98,6 @@ <key> <string>editable</string> </key> <value> <int>0</int> </value> </item> - <item> - <key> <string>enabled</string> </key> - <value> <int>1</int> </value> - </item> <item> <key> <string>field_id</string> </key> <value> <string>my_lines_field</string> </value> @@ -135,17 +124,4 @@ </dictionary> </pickle> </record> - <record id="2" aka="AAAAAAAAAAI="> - <pickle> - <global name="TALESMethod" module="Products.Formulator.TALESField"/> - </pickle> - <pickle> - <dictionary> - <item> - <key> <string>_text</string> </key> - <value> <string>here/hasErrorMessageList</string> </value> - </item> - </dictionary> - </pickle> - </record> </ZopeData> diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Component_view/my_translated_validation_state_title.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Component_view/my_translated_validation_state_title.xml index f8bf5216b5..d756aff23c 100644 --- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Component_view/my_translated_validation_state_title.xml +++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Component_view/my_translated_validation_state_title.xml @@ -9,7 +9,9 @@ <item> <key> <string>delegated_list</string> </key> <value> - <list/> + <list> + <string>css_class</string> + </list> </value> </item> <item> @@ -69,6 +71,10 @@ <key> <string>values</string> </key> <value> <dictionary> + <item> + <key> <string>css_class</string> </key> + <value> <string>ace_editor_validation_state</string> </value> + </item> <item> <key> <string>field_id</string> </key> <value> <string>my_view_mode_translated_workflow_state_title</string> </value> diff --git a/product/ERP5/bootstrap/erp5_core/bt/change_log b/product/ERP5/bootstrap/erp5_core/bt/change_log index 39c8c33d2d..48c7976a65 100644 --- a/product/ERP5/bootstrap/erp5_core/bt/change_log +++ b/product/ERP5/bootstrap/erp5_core/bt/change_log @@ -1,3 +1,6 @@ +2013-07-19 arnaud.fontaine +* Add save button to Ace Editor to save source code while staying on the same page. + 2013-07-09 arnaud.fontaine * ZODB COmponents: Cosmetic: Fix Component Validation Workflow description. diff --git a/product/ERP5/bootstrap/erp5_core/bt/revision b/product/ERP5/bootstrap/erp5_core/bt/revision index 9585095e65..fb4a45ee67 100644 --- a/product/ERP5/bootstrap/erp5_core/bt/revision +++ b/product/ERP5/bootstrap/erp5_core/bt/revision @@ -1 +1 @@ -41117 \ No newline at end of file +41118 \ No newline at end of file diff --git a/product/ERP5Type/mixin/component.py b/product/ERP5Type/mixin/component.py index f13694f518..3357641a95 100644 --- a/product/ERP5Type/mixin/component.py +++ b/product/ERP5Type/mixin/component.py @@ -282,14 +282,22 @@ class ComponentMixin(PropertyRecordableMixin, Base): security.declareProtected(Permissions.AccessContentsInformation, 'getErrorMessageList') - def getErrorMessageList(self): + def getErrorMessageList(self, as_json=False): """ Return the checkConsistency errors which may have occurred when the Component has been modified after being validated once """ current_workflow = self.workflow_history['component_validation_workflow'][-1] - return [error.translate() - for error in current_workflow.get('error_message', [])] + error_list = [error.translate() + for error in current_workflow.get('error_message', [])] + + # Dirty hack until RenderJS is used to save the source code + # (erp5_ace_editor/ace_editor_support) + if as_json: + import json + return json.dumps(error_list) + + return error_list security.declareProtected(Permissions.ModifyPortalContent, 'load') def load(self, namespace_dict, validated_only=False, text_content=None): -- 2.30.9