Commit 19dd7ce6 authored by Romain Courteaud's avatar Romain Courteaud

erp5_forge: action to commit from erp5js

Add a gadget to display and select the list of changes.

The gadget will show the selected diff to change the selection if needed.

Changelog can also be directly entered in the gadget to quickly switch the view while writing it.
parent 61eb9d0f
......@@ -16,13 +16,13 @@
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
<string>action_type/object_onlyxhtml_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
<value> <string>object_onlyxhtml_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_onlyjio_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_onlyjio_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>git_commit_action</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>Manage portal</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>4.5</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Git</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/BusinessTemplate_viewVcsStatusDialog</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: here.getInstallationState() not in (\'installed\', \'replaced\') and here.isVcsType(\'git\')</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
import json
vcs_tool = context.getVcsTool()
result_dict = {
'added_list': [],
'modified_list': [],
'deleted_list': []
}
for path in modified:
result_dict['modified_list'].append({
'path': path,
# 'edit_path': vcs_tool.editPath(path, True),
'diff': vcs_tool.diff(path)
})
for path in added:
result_dict['added_list'].append({
'path': path
})
for path in deleted:
result_dict['deleted_list'].append({
'path': path
})
if REQUEST is not None:
REQUEST.RESPONSE.setHeader('Content-Type', 'application/hal+json')
return json.dumps(result_dict, indent=2)
else:
return result_dict
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>added=(), modified=(), deleted=(), REQUEST=None</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>BusinessTemplate_doVcsDiffAsJson</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>BusinessTemplate_doVcsCommit</string> </value>
</item>
<item>
<key> <string>action_title</string> </key>
<value> <string>Commit</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list>
<string>your_commit_json</string>
</list>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>BusinessTemplate_viewVcsStatusDialog</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>BusinessTemplate_viewVcsStatusDialog</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>ERP5 Form</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_dialog</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>VCS Status</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>gadget_url</string>
<string>renderjs_extra</string>
<string>title</string>
<string>validator_field_id</string>
<string>validator_form_id</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_commit_json</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>gadget_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>renderjs_extra</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>validator_field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>validator_form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_gadget_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>gadget_url</string> </key>
<value> <string>gadget_vcs_status.html</string> </value>
</item>
<item>
<key> <string>renderjs_extra</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Gadget</string> </value>
</item>
<item>
<key> <string>validator_field_id</string> </key>
<value> <string>my_core_mode_text_content_validator</string> </value>
</item>
<item>
<key> <string>validator_form_id</string> </key>
<value> <string>erp5_core/Base_viewFieldLibrary</string> </value>
</item>
</dictionary>
</value>
</item>
</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>python: [(\'default_push\', preferences.getPreferredVcsPushMode() and not here.getVcsTool().getAheadCount()), (\'default_changelog\', "{}: ".format(context.getTitle())), (\'diff_url\', \'%s/BusinessTemplate_doVcsDiffAsJson\' % here.absolute_url()), (\'get_tree_url\', \'%s/tree.xml\' % here.absolute_url()), (\'remote_url\', here.getVcsTool().getRemoteUrl()), (\'remote_comment\', here.getVcsTool().getRemoteComment())]</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>string:</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
div[data-gadget-url$="gadget_vcs_status.html"] {
background-color: #FFFFFF;
}
div[data-gadget-url$="gadget_vcs_status.html"] li li {
padding-left: 1em;
}
div[data-gadget-url$="gadget_vcs_status.html"] li input.showhide {
display: none;
}
div[data-gadget-url$="gadget_vcs_status.html"] li input.showhide:checked ~ label.show {
display: none;
}
div[data-gadget-url$="gadget_vcs_status.html"] li input.showhide:checked ~ ul {
display: none;
}
div[data-gadget-url$="gadget_vcs_status.html"] li input.showhide:not(:checked) ~ label.hide {
display: none;
}
div[data-gadget-url$="gadget_vcs_status.html"] li label {
display: inline-block;
}
div[data-gadget-url$="gadget_vcs_status.html"] li label.showhide {
width: 1em;
}
div[data-gadget-url$="gadget_vcs_status.html"] li label input {
margin: 0 3pt;
}
div[data-gadget-url$="gadget_vcs_status.html"] li label.added {
color: #22CC22;
}
div[data-gadget-url$="gadget_vcs_status.html"] li label.modified {
color: #FF6600;
}
div[data-gadget-url$="gadget_vcs_status.html"] li label.removed {
color: red;
}
div[data-gadget-url$="gadget_vcs_status.html"] button {
color: #212529;
padding: 3pt;
border: 1px solid rgba(0, 0, 0, 0.14);
border-radius: 0.325em;
display: inline-block;
margin-right: 6pt;
}
div[data-gadget-url$="gadget_vcs_status.html"] button:disabled,
div[data-gadget-url$="gadget_vcs_status.html"] button[disabled] {
color: #999999;
}
div[data-gadget-url$="gadget_vcs_status.html"] button:before {
padding-right: 0.2em;
}
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_vcs_status.css</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/css</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<!DOCTYPE html>
<html>
<head>
<!--
data-i18n=Added Files
data-i18n=Modified Files
data-i18n=Removed Files
data-i18n=Tree
data-i18n=Diff
data-i18n=Changelog
data-i18n=Expand
data-i18n=Push
-->
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<title>VCS status gadget</title>
<link rel=stylesheet href="gadget_vcs_status.css">
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<script src="jio.js" type="text/javascript"></script>
<script src="domsugar.js" type="text/javascript"></script>
<script src="gadget_vcs_status.js" type="text/javascript"></script>
</head>
<body>
<div class="vcsheader"></div>
<div class="vcsbody"></div>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_vcs_status.html</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
/*global window, rJS, RSVP, domsugar, jIO, console, document, NodeFilter,
FormData */
/*jslint nomen: true, maxlen:80, indent:2*/
(function (window, rJS, RSVP, domsugar, jIO, console, document, NodeFilter) {
"use strict";
var whitelist = {
node_list: {
P: true,
UL: true,
LI: true,
INPUT: true,
LABEL: true
},
attribute_list: {
type: true,
class: true,
id: true,
for: true,
name: true,
value: true
}
},
DISPLAY_TREE = 'display_tree',
DISPLAY_DIFF = 'display_diff',
DISPLAY_CHANGELOG = 'display_changelog';
function getTranslationDict(gadget) {
var word_list = ['Added Files', 'Modified Files', 'Removed Files',
'Tree', 'Diff', 'Changelog', 'Expand', 'Push'];
return gadget.getTranslationList(word_list)
.push(function (result_list) {
var result = {},
i;
for (i = 0; i < word_list.length; i += 1) {
result[word_list[i]] = result_list[i];
}
return result;
});
}
function keepOnlyChildren(current_node) {
var fragment = document.createDocumentFragment();
while (current_node.firstChild) {
fragment.appendChild(current_node.firstChild);
}
current_node.parentNode.replaceChild(
fragment,
current_node
);
}
function renderTreeXml(gadget, tree_xml_element) {
var iterator,
current_node,
next_node,
parent_node,
value,
ul_node,
attribute,
attribute_list,
child_list,
len,
finished = false,
id = -1,
name,
state_value = JSON.parse(gadget.state.value);
// Replace the tree element by a fragment
next_node = domsugar('ul');
while (tree_xml_element.firstChild) {
next_node.appendChild(tree_xml_element.firstChild);
}
tree_xml_element = next_node;
iterator = document.createNodeIterator(
tree_xml_element,
NodeFilter.SHOW_ELEMENT,
function () {
return NodeFilter.FILTER_ACCEPT;
}
);
while (!finished) {
current_node = iterator.nextNode();
finished = (current_node === null);
if (!finished) {
if (current_node.nodeName === 'item') {
child_list = [];
// Open/hide element
if (current_node.firstChild) {
id += 1;
child_list.push(
domsugar('input', {type: 'checkbox', id: 'showhide' + id,
class: 'showhide'}),
domsugar('label', {for: 'showhide' + id, class: 'showhide show',
text: '-'}),
domsugar('label', {for: 'showhide' + id, class: 'showhide hide',
text: '+'})
);
} else {
child_list.push(
domsugar('label', {class: 'showhide', text: ' '})
);
}
// Select element
parent_node = current_node.closest('ul');
if (parent_node !== null) {
parent_node = parent_node.parentNode;
}
if (parent_node !== null) {
parent_node = parent_node.querySelector('input.vcs_to_commit');
}
value = current_node.getAttribute('text');
if ((parent_node !== null) && (parent_node !== current_node)) {
if (parent_node.value) {
value = parent_node.value + '/' + value;
}
} else {
value = '';
}
name = {
'green': 'added',
'orange': 'modified',
'red': 'removed'
}[current_node.getAttribute('aCol')];
child_list.push(domsugar('label', {
class: name
}, [
domsugar('input', {
type: 'checkbox',
class: 'vcs_to_commit',
value: value,
name: name,
checked: ((state_value[name] === undefined) ||
(state_value[name].indexOf(value) === -1)) ? '' :
'checked'
}),
current_node.getAttribute('text')
]));
// Child items
if (current_node.firstChild) {
ul_node = domsugar('ul');
while (current_node.firstChild) {
ul_node.appendChild(current_node.firstChild);
}
child_list.push(ul_node);
}
current_node.parentNode.replaceChild(
domsugar('li', child_list),
current_node
);
} else if (!whitelist.node_list[current_node.nodeName]) {
keepOnlyChildren(current_node);
} else {
// Cleanup attributes
attribute_list = current_node.attributes;
len = attribute_list.length;
while (len !== 0) {
len = len - 1;
attribute = attribute_list[len].name;
if (!whitelist.attribute_list[attribute]) {
current_node.removeAttribute(attribute);
}
}
}
}
}
return tree_xml_element;
}
function renderGadgetHeader(gadget, loading, translation_dict) {
var element_list = [
domsugar('p', [
'Repository: ',
domsugar('a', {
text: gadget.state.remote_url,
href: gadget.state.remote_url
}),
' (' + gadget.state.remote_comment + ')'
])
],
tree_icon = 'ui-icon-check-square',
diff_icon = 'ui-icon-search-plus',
changelog_icon = 'ui-icon-git';
if (loading) {
if (gadget.state.display_step === DISPLAY_TREE) {
tree_icon = 'ui-icon-spinner';
} else if (gadget.state.display_step === DISPLAY_DIFF) {
diff_icon = 'ui-icon-spinner';
} else if (gadget.state.display_step === DISPLAY_CHANGELOG) {
changelog_icon = 'ui-icon-spinner';
} else {
throw new Error("Can't render header state " +
gadget.state.display_step);
}
}
element_list.push(
domsugar('button', {
type: 'button',
text: translation_dict.Tree,
disabled: (gadget.state.display_step === DISPLAY_TREE),
class: 'display-tree-btn ui-btn-icon-left ' + tree_icon
}),
domsugar('button', {
type: 'button',
text: translation_dict.Diff,
disabled: (gadget.state.display_step === DISPLAY_DIFF),
class: 'diff-tree-btn ui-btn-icon-left ' + diff_icon
}),
domsugar('button', {
type: 'button',
text: translation_dict.Changelog,
disabled: (gadget.state.display_step === DISPLAY_CHANGELOG),
class: 'changelog-btn ui-btn-icon-left ' + changelog_icon
})
);
if ((!loading) && (gadget.state.display_step === DISPLAY_TREE)) {
element_list.push(
domsugar('button', {
type: 'button',
text: translation_dict.Expand,
class: 'expand-tree-btn ui-btn-icon-left ui-icon-arrows-v'
})
);
}
domsugar(gadget.element.querySelector('div.vcsheader'), element_list);
}
function updateFullTreeCheckbox(checkbox) {
var i,
element_list,
parent_checkbox,
is_checked,
is_indeterminate;
// https://css-tricks.com/indeterminate-checkboxes/
// Check/uncheck all children checkboxes
element_list = checkbox.parentElement
.parentElement
.querySelectorAll('input.vcs_to_commit');
for (i = 0; i < element_list.length; i += 1) {
element_list[i].checked = checkbox.checked;
element_list[i].indeterminate = false;
}
// Check/uncheck/undefine all parent checkboxes
while (checkbox !== null) {
parent_checkbox = checkbox.closest('ul')
.parentNode
.querySelector('input.vcs_to_commit');
if (parent_checkbox === checkbox) {
// Top checkbox
checkbox = null;
} else {
// check the state of all child chexbox
element_list = parent_checkbox.closest('li')
.querySelector('ul')
.children;
is_checked = true;
is_indeterminate = false;
for (i = 0; i < element_list.length; i += 1) {
is_checked = is_checked &&
element_list[i].querySelector('input.vcs_to_commit')
.checked;
is_indeterminate =
is_indeterminate ||
element_list[i].querySelector('input.vcs_to_commit')
.checked ||
element_list[i].querySelector('input.vcs_to_commit')
.indeterminate;
}
parent_checkbox.checked = is_checked;
parent_checkbox.indeterminate = (!is_checked) && is_indeterminate;
checkbox = parent_checkbox;
}
}
}
function renderTreeView(gadget, extract) {
var translation_dict;
return getTranslationDict(gadget)
.push(function (result) {
translation_dict = result;
renderGadgetHeader(gadget, true, translation_dict);
var form_data = new FormData();
form_data.append('show_unmodified:int', 0);
// form_data.append('bt_id', 'erp5');
form_data.append('do_extract:int', extract ? 1 : 0);
return jIO.util.ajax({
"type": "POST",
"url": gadget.state.get_tree_url,
"xhrFields": {
withCredentials: true
},
"dataType": "document",
"data": form_data
});
})
.push(function (evt) {
renderGadgetHeader(gadget, false, translation_dict);
domsugar(gadget.element.querySelector('div.vcsbody'), [
renderTreeXml(gadget, evt.target.response.querySelector('tree'))
]);
// Update the tree parent
var element_list = gadget.element
.querySelectorAll('input.vcs_to_commit'),
i;
for (i = 0; i < element_list.length; i += 1) {
if (element_list[i].checked) {
updateFullTreeCheckbox(element_list[i]);
}
}
});
}
function getContentFromTreeView(gadget) {
var result = JSON.parse(gadget.state.value),
checkbox_list = gadget.element.querySelectorAll('input.vcs_to_commit'),
i,
name;
result.added = [];
result.modified = [];
result.removed = [];
for (i = 0; i < checkbox_list.length; i += 1) {
name = checkbox_list[i].name;
if (name && checkbox_list[i].checked) {
result[name].push(checkbox_list[i].value);
}
}
gadget.state.value = JSON.stringify(result);
}
function expandTreeView(gadget) {
var element_list = gadget.element.querySelectorAll('input.showhide'),
// Do not crash if no checkbox is displayed
is_checked = (element_list.length !== 0) && (!element_list[0].checked),
i;
for (i = 0; i < element_list.length; i += 1) {
element_list[0].checked = is_checked;
}
}
function declareAndRenderDiff(gadget, path, diff) {
return gadget.declareGadget('gadget_erp5_side_by_side_diff.html')
.push(function (diff_gadget) {
return RSVP.all([
diff_gadget.element,
path,
diff_gadget.render({value: diff})
]);
});
}
function renderDiffView(gadget) {
var result = JSON.parse(gadget.state.value),
ajax_result,
diff_count = result.modified.length,
translation_dict;
return getTranslationDict(gadget)
.push(function (result2) {
translation_dict = result2;
renderGadgetHeader(gadget, true, translation_dict);
var form_data = new FormData(),
key_list = ['modified', 'added', 'removed'],
key,
i,
j;
for (i = 0; i < key_list.length; i += 1) {
key = key_list[i];
for (j = 0; j < result[key].length; j += 1) {
form_data.append(key + ':list', result[key][j]);
}
}
return jIO.util.ajax({
"type": "POST",
"url": gadget.state.diff_url,
"xhrFields": {
withCredentials: true
},
"dataType": "json",
"data": form_data
});
})
.push(function (evt) {
ajax_result = evt.target.response;
var promise_list = [],
i;
for (i = 0; i < diff_count; i += 1) {
promise_list.push(
declareAndRenderDiff(
gadget,
ajax_result.modified_list[i].path,
ajax_result.modified_list[i].diff
)
);
}
return RSVP.all(promise_list);
})
.push(function (result_list) {
var i,
element_list = [];
element_list.push(domsugar('h3', {
text: translation_dict['Modified Files']
}));
for (i = 0; i < result_list.length; i += 1) {
element_list.push(
domsugar('label', [
domsugar('input', {
type: 'checkbox',
class: 'vcs_to_commit',
value: result_list[i][1],
name: 'modified',
checked: 'checked'
}),
result_list[i][1]
]),
result_list[i][0]
);
}
element_list.push(domsugar('h3', {
text: translation_dict['Added Files']
}));
for (i = 0; i < ajax_result.added_list.length; i += 1) {
element_list.push(
domsugar('label', [
domsugar('input', {
type: 'checkbox',
class: 'vcs_to_commit',
value: ajax_result.added_list[i].path,
name: 'added',
checked: 'checked'
}),
ajax_result.added_list[i].path
])
);
}
element_list.push(domsugar('h3', {
text: translation_dict['Removed Files']
}));
for (i = 0; i < result.removed.length; i += 1) {
element_list.push(
domsugar('label', [
domsugar('input', {
type: 'checkbox',
class: 'vcs_to_commit',
value: result.removed[i],
name: 'removed',
checked: 'checked'
}),
result.removed[i]
])
);
}
renderGadgetHeader(gadget, false, translation_dict);
domsugar(gadget.element.querySelector('div.vcsbody'), element_list);
});
}
function renderChangelogView(gadget) {
var result = JSON.parse(gadget.state.value);
return getTranslationDict(gadget)
.push(function (translation_dict) {
renderGadgetHeader(gadget, false, translation_dict);
domsugar(gadget.element.querySelector('div.vcsbody'), [
domsugar('label', {text: translation_dict.Push}, [
' ',
domsugar('input', {
type: 'checkbox',
checked: result.push || gadget.state.default_push || false
})
]),
domsugar('h3', {text: translation_dict.Changelog}),
domsugar('textarea', {
value: result.changelog || gadget.state.default_changelog || ''
}),
domsugar('h3', {text: translation_dict['Added Files']}),
domsugar('pre', {text: result.added.join('\n')}),
domsugar('h3', {text: translation_dict['Modified Files']}),
domsugar('pre', {text: result.modified.join('\n')}),
domsugar('h3', {text: translation_dict['Removed Files']}),
domsugar('pre', {text: result.removed.join('\n')})
]);
});
}
function getContentFromChangelogView(gadget) {
var result = JSON.parse(gadget.state.value);
result.changelog = gadget.element.querySelector('textarea').value;
// Ensure user set a changelog
if (result.changelog === gadget.state.default_changelog) {
result.changelog = '';
}
result.push = gadget.element.querySelector('input').checked;
gadget.state.value = JSON.stringify(result);
}
rJS(window)
.declareAcquiredMethod("getTranslationList", "getTranslationList")
.declareMethod('render', function (options) {
return this.changeState({
display_step: DISPLAY_TREE,
// Only build the bt5 during the first query
extract: 1,
diff_url: options.diff_url,
get_tree_url: options.get_tree_url,
remote_comment: options.remote_comment,
remote_url: options.remote_url,
key: options.key,
default_changelog: options.default_changelog,
default_push: options.default_push,
value: options.value || JSON.stringify({
added: [],
modified: [],
removed: [],
changelog: '',
push: false
}),
editable: (options.editable === undefined) ? true : options.editable
});
})
.onStateChange(function (modification_dict) {
var gadget = this;
if (gadget.state.display_step === DISPLAY_TREE) {
console.log(modification_dict);
if (modification_dict.hasOwnProperty('display_step')) {
return renderTreeView(gadget,
modification_dict.hasOwnProperty('extract'));
}
if (modification_dict.hasOwnProperty('expand_tree')) {
return expandTreeView(gadget);
}
}
if (modification_dict.display_step === DISPLAY_DIFF) {
return renderDiffView(gadget);
}
if (modification_dict.display_step === DISPLAY_CHANGELOG) {
return renderChangelogView(gadget);
}
if (modification_dict.hasOwnProperty('display_step')) {
throw new Error('Unhandled display step: ' + gadget.state.display_step);
}
})
.onEvent("change", function (evt) {
var gadget = this,
tag_name = evt.target.tagName;
// Only handle vcs_to_commit checkbox
if ((tag_name !== 'INPUT') ||
(evt.target.className !== 'vcs_to_commit')) {
return;
}
if (gadget.state.display_step !== DISPLAY_TREE) {
return;
}
updateFullTreeCheckbox(evt.target);
}, false, false)
.onEvent("click", function (evt) {
// Only handle click on BUTTON and IMG element
var gadget = this,
tag_name = evt.target.tagName,
queue;
if (tag_name !== 'BUTTON') {
return;
}
// Disable any button. It must be managed by this gadget
evt.preventDefault();
// Always get content to ensure the possible displayed form
// is checked and content propagated to the gadget state value
queue = gadget.getContent();
if (evt.target.className.indexOf("expand-tree-btn") !== -1) {
return queue
.push(function () {
return gadget.changeState({
display_step: DISPLAY_TREE,
expand_tree: new Date()
});
});
}
if (evt.target.className.indexOf("display-tree-btn") !== -1) {
return queue
.push(function () {
return gadget.changeState({
display_step: DISPLAY_TREE
});
});
}
if (evt.target.className.indexOf("diff-tree-btn") !== -1) {
return queue
.push(function () {
return gadget.changeState({
display_step: DISPLAY_DIFF
});
});
}
if (evt.target.className.indexOf("changelog-btn") !== -1) {
return queue
.push(function () {
return gadget.changeState({
display_step: DISPLAY_CHANGELOG
});
});
}
throw new Error('Unhandled button: ' + evt.target.textContent);
}, false, false)
//////////////////////////////////////////////////
// Used when submitting the form
//////////////////////////////////////////////////
.declareMethod('getContent', function () {
var gadget = this,
display_step = gadget.state.display_step,
queue;
if (gadget.state.display_step === DISPLAY_TREE) {
queue = new RSVP.Queue(getContentFromTreeView(gadget));
} else if (gadget.state.display_step === DISPLAY_DIFF) {
queue = new RSVP.Queue(getContentFromTreeView(gadget));
} else if (gadget.state.display_step === DISPLAY_CHANGELOG) {
queue = new RSVP.Queue(getContentFromChangelogView(gadget));
} else {
throw new Error('getContent form not handled: ' + display_step);
}
return queue
.push(function () {
var result = {};
if (gadget.state.editable) {
result[gadget.state.key] = gadget.state.value;
}
return result;
});
}, {mutex: 'changestate'})
.declareMethod('checkValidity', function () {
return true;
}, {mutex: 'changestate'})
.declareAcquiredMethod("notifyChange", "notifyChange")
.onEvent('change', function change() {
return RSVP.all([
this.checkValidity(),
this.notifyChange("change")
]);
}, false, false)
.onEvent('input', function input() {
return RSVP.all([
this.checkValidity(),
this.notifyChange("input")
]);
}, false, false)
.declareAcquiredMethod("notifyFocus", "notifyFocus")
.onEvent('focus', function focus() {
return this.notifyFocus();
}, true, false)
.declareAcquiredMethod("notifyBlur", "notifyBlur")
.onEvent('blur', function blur() {
return this.notifyBlur();
}, true, false);
}(window, rJS, RSVP, domsugar, jIO, console, document, NodeFilter));
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Cacheable__manager_id</string> </key>
<value> <string>must_revalidate_http_cache</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_vcs_status.js</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>application/javascript</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
@orange: #FF6600;
@white: #FFFFFF;
@green: #22CC22;
@red: red;
div[data-gadget-url$="gadget_vcs_status.html"] {
background-color: @white;
li {
li {
padding-left: 1em;
}
input.showhide {
display: none;
&:checked ~ label.show {
display: none;
}
&:checked ~ ul {
display: none;
}
&:not(:checked) ~ label.hide {
display: none;
}
}
label {
&.showhide {
width: 1em;
}
display: inline-block;
input {
margin: 0 3pt;
}
&.added {
color: @green;
}
&.modified {
color: @orange;
}
&.removed {
color: @red;
}
}
}
button {
color: #212529;
padding: 3pt;
border: 1px solid rgba(0, 0, 0, 0.14);
border-radius: 0.325em;
display: inline-block;
margin-right: 6pt;
&:disabled, &[disabled] {
color: #999999;
}
&:before {
padding-right: 0.2em;
}
}
}
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_vcs_status.less</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/less</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -15,6 +15,7 @@ Business Template | create_report
Business Template | create_skin_folder
Business Template | create_working_copy
Business Template | git_commit
Business Template | git_commit_action
Business Template | manage_field_library
Business Template | rename_proxy_field
Business Template | svn_cleanup_locks
......
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