Commit 7bf233bb authored by Arnaud Fontaine's avatar Arnaud Fontaine

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.
parent 1dd9753c
...@@ -69,11 +69,46 @@ ...@@ -69,11 +69,46 @@
.ace_line {\n .ace_line {\n
color: black !important;\n color: black !important;\n
}\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 \n
#maximize_message {\n #maximize_message {\n
display: block !important;\n display: block !important;\n
position: absolute !important;\n position: absolute !important;\n
top: 0 !important;\n bottom: 0 !important;\n
right: 0px !important;\n right: 0px !important;\n
z-index: 4243 !important;\n z-index: 4243 !important;\n
padding: 10px;\n padding: 10px;\n
...@@ -139,10 +174,16 @@ ...@@ -139,10 +174,16 @@
<script type="text/javascript"\n <script type="text/javascript"\n
tal:attributes="src string:${portal_url}/ace/mode-python.js"></script>\n tal:attributes="src string:${portal_url}/ace/mode-python.js"></script>\n
<script type="text/javascript"\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 tal:content="structure string:\n
ace_editor_container_div = null;\n ace_editor_container_div = null;\n
ace_editor = null;\n ace_editor = null;\n
\n
function maximizeFullscreenRemoveSaveMessage() {\n
$(\'.ace_editor_maximize_fullscreen_message\').remove();\n
}\n
\n \n
function switchToFullScreen(id) {\n function switchToFullScreen(id) {\n
element = document.getElementById(\'${container_div_id}\');\n element = document.getElementById(\'${container_div_id}\');\n
...@@ -151,10 +192,10 @@ ...@@ -151,10 +192,10 @@
if (element.requestFullScreen) {\n if (element.requestFullScreen) {\n
element.requestFullScreen();\n element.requestFullScreen();\n
}\n }\n
else if (element.mozRequestFullScreen) {\n else if(element.mozRequestFullScreen) {\n
element.mozRequestFullScreen();\n element.mozRequestFullScreen();\n
}\n }\n
else if (element.webkitRequestFullScreen) {\n else if(element.webkitRequestFullScreen) {\n
element.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);\n element.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);\n
}\n }\n
ace_editor.resize();\n ace_editor.resize();\n
...@@ -181,6 +222,7 @@ ...@@ -181,6 +222,7 @@
\n \n
function unmaximize(event) {\n function unmaximize(event) {\n
if(event.keyCode == 27) {\n if(event.keyCode == 27) {\n
maximizeFullscreenRemoveSaveMessage();\n
$(\'body\').css(\'overflow\', \'visible\');\n $(\'body\').css(\'overflow\', \'visible\');\n
ace_editor_container_div.removeClass(\'maximize\');\n ace_editor_container_div.removeClass(\'maximize\');\n
ace_editor_container_div.appendTo(ace_editor_container_div_parent_before_maximized);\n ace_editor_container_div.appendTo(ace_editor_container_div_parent_before_maximized);\n
...@@ -207,6 +249,104 @@ ...@@ -207,6 +249,104 @@
$(document).keyup(unmaximize);\n $(document).keyup(unmaximize);\n
ace_editor.resize();\n ace_editor.resize();\n
}\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=&quot;\' + msg_elem_classes + \'&quot;>\' +\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 \n
window.onload = function() {\n window.onload = function() {\n
ace_editor_container_div = $(\'#${container_div_id}\');\n ace_editor_container_div = $(\'#${container_div_id}\');\n
...@@ -225,11 +365,26 @@ ...@@ -225,11 +365,26 @@
ace_editor.getSession().on(\'change\', function() {\n ace_editor.getSession().on(\'change\', function() {\n
textarea.val(ace_editor.getSession().getValue());\n textarea.val(ace_editor.getSession().getValue());\n
});\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 \n
if(typeof document.cancelFullScreen != \'undefined\' ||\n if(typeof document.cancelFullScreen != \'undefined\' ||\n
(typeof document.mozFullScreenEnabled != \'undefined\' && document.mozFullScreenEnabled) ||\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 $$(\'${fullscreen_button}\').insertAfter($$(\'input.ace_editor_action_button\'));\n
}\n
};">\n };">\n
</script>\n </script>\n
</tal:block> </tal:block>
......
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 2013-07-09 arnaud.fontaine
* ZODB Components: Follow ERP5 Python indentation style in Ace editor. * ZODB Components: Follow ERP5 Python indentation style in Ace editor.
......
9 10
\ No newline at end of file \ No newline at end of file
...@@ -287,6 +287,9 @@ try:\n ...@@ -287,6 +287,9 @@ try:\n
except ActivityPendingError,e:\n except ActivityPendingError,e:\n
message = Base_translateString("%s" % e)\n message = Base_translateString("%s" % e)\n
\n \n
if message_only:\n
return message\n
\n
ignore_layout = int(ignore_layout)\n ignore_layout = int(ignore_layout)\n
editable_mode = int(editable_mode)\n editable_mode = int(editable_mode)\n
spp = context.getPhysicalPath()\n spp = context.getPhysicalPath()\n
...@@ -332,7 +335,7 @@ return result\n ...@@ -332,7 +335,7 @@ return result\n
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <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>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
<list> <list>
<string>css_class</string> <string>css_class</string>
<string>editable</string> <string>editable</string>
<string>enabled</string>
<string>height</string> <string>height</string>
<string>title</string> <string>title</string>
</list> </list>
...@@ -64,12 +63,6 @@ ...@@ -64,12 +63,6 @@
<key> <string>editable</string> </key> <key> <string>editable</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
</item> </item>
<item>
<key> <string>enabled</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item> <item>
<key> <string>field_id</string> </key> <key> <string>field_id</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
...@@ -105,10 +98,6 @@ ...@@ -105,10 +98,6 @@
<key> <string>editable</string> </key> <key> <string>editable</string> </key>
<value> <int>0</int> </value> <value> <int>0</int> </value>
</item> </item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item> <item>
<key> <string>field_id</string> </key> <key> <string>field_id</string> </key>
<value> <string>my_lines_field</string> </value> <value> <string>my_lines_field</string> </value>
...@@ -135,17 +124,4 @@ ...@@ -135,17 +124,4 @@
</dictionary> </dictionary>
</pickle> </pickle>
</record> </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> </ZopeData>
...@@ -9,7 +9,9 @@ ...@@ -9,7 +9,9 @@
<item> <item>
<key> <string>delegated_list</string> </key> <key> <string>delegated_list</string> </key>
<value> <value>
<list/> <list>
<string>css_class</string>
</list>
</value> </value>
</item> </item>
<item> <item>
...@@ -69,6 +71,10 @@ ...@@ -69,6 +71,10 @@
<key> <string>values</string> </key> <key> <string>values</string> </key>
<value> <value>
<dictionary> <dictionary>
<item>
<key> <string>css_class</string> </key>
<value> <string>ace_editor_validation_state</string> </value>
</item>
<item> <item>
<key> <string>field_id</string> </key> <key> <string>field_id</string> </key>
<value> <string>my_view_mode_translated_workflow_state_title</string> </value> <value> <string>my_view_mode_translated_workflow_state_title</string> </value>
......
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 2013-07-09 arnaud.fontaine
* ZODB COmponents: Cosmetic: Fix Component Validation Workflow description. * ZODB COmponents: Cosmetic: Fix Component Validation Workflow description.
......
41117 41118
\ No newline at end of file \ No newline at end of file
...@@ -282,14 +282,22 @@ class ComponentMixin(PropertyRecordableMixin, Base): ...@@ -282,14 +282,22 @@ class ComponentMixin(PropertyRecordableMixin, Base):
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getErrorMessageList') 'getErrorMessageList')
def getErrorMessageList(self): def getErrorMessageList(self, as_json=False):
""" """
Return the checkConsistency errors which may have occurred when Return the checkConsistency errors which may have occurred when
the Component has been modified after being validated once the Component has been modified after being validated once
""" """
current_workflow = self.workflow_history['component_validation_workflow'][-1] current_workflow = self.workflow_history['component_validation_workflow'][-1]
return [error.translate() error_list = [error.translate()
for error in current_workflow.get('error_message', [])] 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') security.declareProtected(Permissions.ModifyPortalContent, 'load')
def load(self, namespace_dict, validated_only=False, text_content=None): def load(self, namespace_dict, validated_only=False, text_content=None):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment