mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
3ee8f505b7
The main benefit of this is so that authors who make use of .editorconfig don't end up with whitespace changes in their PRs. Spaces vs. tabs has been left alone, although that could do with a tidy-up in SS4 after the switch to PSR-1/2. The command used was this: for match in '*.ss' '*.css' '*.scss' '*.html' '*.yml' '*.php' '*.js' '*.csv' '*.inc' '*.php5'; do find . -path ./thirdparty -not -prune -o -path ./admin/thirdparty -not -prune -o -type f -name "$match" -exec sed -E -i '' 's/[[:space:]]+$//' {} \+ find . -path ./thirdparty -not -prune -o -path ./admin/thirdparty -not -prune -o -type f -name "$match" | xargs perl -pi -e 's/ +$//' done
397 lines
11 KiB
JavaScript
397 lines
11 KiB
JavaScript
/**
|
|
* File: LeftAndMain.BatchActions.js
|
|
*/
|
|
(function($) {
|
|
$.entwine('ss.tree', function($){
|
|
|
|
/**
|
|
* Class: #Form_BatchActionsForm
|
|
*
|
|
* Batch actions which take a bunch of selected pages,
|
|
* usually from the CMS tree implementation, and perform serverside
|
|
* callbacks on the whole set. We make the tree selectable when the jQuery.UI tab
|
|
* enclosing this form is opened.
|
|
*
|
|
* Events:
|
|
* register - Called before an action is added.
|
|
* unregister - Called before an action is removed.
|
|
*/
|
|
$('#Form_BatchActionsForm').entwine({
|
|
|
|
/**
|
|
* Variable: Actions
|
|
* (Array) Stores all actions that can be performed on the collected IDs as
|
|
* function closures. This might trigger filtering of the selected IDs,
|
|
* a confirmation message, etc.
|
|
*/
|
|
Actions: [],
|
|
|
|
getTree: function() {
|
|
return $('.cms-tree');
|
|
},
|
|
|
|
fromTree: {
|
|
oncheck_node: function(e, data){
|
|
this.serializeFromTree();
|
|
},
|
|
onuncheck_node: function(e, data){
|
|
this.serializeFromTree();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @func registerDefault
|
|
* @desc Register default bulk confirmation dialogs
|
|
*/
|
|
registerDefault: function() {
|
|
// Publish selected pages action
|
|
this.register('admin/pages/batchactions/publish', function(ids) {
|
|
var confirmed = confirm(
|
|
ss.i18n.inject(
|
|
ss.i18n._t(
|
|
"CMSMAIN.BATCH_PUBLISH_PROMPT",
|
|
"You have {num} page(s) selected.\n\nDo you really want to publish?"
|
|
),
|
|
{'num': ids.length}
|
|
)
|
|
);
|
|
return (confirmed) ? ids : false;
|
|
});
|
|
|
|
// Unpublish selected pages action
|
|
this.register('admin/pages/batchactions/unpublish', function(ids) {
|
|
var confirmed = confirm(
|
|
ss.i18n.inject(
|
|
ss.i18n._t(
|
|
"CMSMAIN.BATCH_UNPUBLISH_PROMPT",
|
|
"You have {num} page(s) selected.\n\nDo you really want to unpublish"
|
|
),
|
|
{'num': ids.length}
|
|
)
|
|
);
|
|
return (confirmed) ? ids : false;
|
|
});
|
|
|
|
// Delete selected pages action
|
|
// @deprecated since 4.0 Use archive instead
|
|
this.register('admin/pages/batchactions/delete', function(ids) {
|
|
var confirmed = confirm(
|
|
ss.i18n.inject(
|
|
ss.i18n._t(
|
|
"CMSMAIN.BATCH_DELETE_PROMPT",
|
|
"You have {num} page(s) selected.\n\nDo you really want to delete?"
|
|
),
|
|
{'num': ids.length}
|
|
)
|
|
);
|
|
return (confirmed) ? ids : false;
|
|
});
|
|
|
|
// Delete selected pages action
|
|
this.register('admin/pages/batchactions/archive', function(ids) {
|
|
var confirmed = confirm(
|
|
ss.i18n.inject(
|
|
ss.i18n._t(
|
|
"CMSMAIN.BATCH_ARCHIVE_PROMPT",
|
|
"You have {num} page(s) selected.\n\nAre you sure you want to archive these pages?\n\nThese pages and all of their children pages will be unpublished and sent to the archive."
|
|
),
|
|
{'num': ids.length}
|
|
)
|
|
);
|
|
return (confirmed) ? ids : false;
|
|
});
|
|
|
|
// Restore selected archived pages
|
|
this.register('admin/pages/batchactions/restore', function(ids) {
|
|
var confirmed = confirm(
|
|
ss.i18n.inject(
|
|
ss.i18n._t(
|
|
"CMSMAIN.BATCH_RESTORE_PROMPT",
|
|
"You have {num} page(s) selected.\n\nDo you really want to restore to stage?\n\nChildren of archived pages will be restored to the root level, unless those pages are also being restored."
|
|
),
|
|
{'num': ids.length}
|
|
)
|
|
);
|
|
return (confirmed) ? ids : false;
|
|
});
|
|
|
|
// Delete selected pages from live action
|
|
this.register('admin/pages/batchactions/deletefromlive', function(ids) {
|
|
var confirmed = confirm(
|
|
ss.i18n.inject(
|
|
ss.i18n._t(
|
|
"CMSMAIN.BATCH_DELETELIVE_PROMPT",
|
|
"You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?"
|
|
),
|
|
{'num': ids.length}
|
|
)
|
|
);
|
|
return (confirmed) ? ids : false;
|
|
});
|
|
},
|
|
|
|
onadd: function() {
|
|
this.registerDefault();
|
|
this._super();
|
|
},
|
|
|
|
/**
|
|
* @func register
|
|
* @param {string} type
|
|
* @param {function} callback
|
|
*/
|
|
register: function(type, callback) {
|
|
this.trigger('register', {type: type, callback: callback});
|
|
var actions = this.getActions();
|
|
actions[type] = callback;
|
|
this.setActions(actions);
|
|
},
|
|
|
|
/**
|
|
* @func unregister
|
|
* @param {string} type
|
|
* @desc Remove an existing action.
|
|
*/
|
|
unregister: function(type) {
|
|
this.trigger('unregister', {type: type});
|
|
|
|
var actions = this.getActions();
|
|
if(actions[type]) delete actions[type];
|
|
this.setActions(actions);
|
|
},
|
|
|
|
/**
|
|
* @func refreshSelected
|
|
* @param {object} rootNode
|
|
* @desc Ajax callbacks determine which pages is selectable in a certain batch action.
|
|
*/
|
|
refreshSelected : function(rootNode) {
|
|
var self = this,
|
|
st = this.getTree(),
|
|
ids = this.getIDs(),
|
|
allIds = [],
|
|
viewMode = $('.cms-content-batchactions-button'),
|
|
actionUrl = this.find(':input[name=Action]').val();
|
|
|
|
// Default to refreshing the entire tree
|
|
if(rootNode == null) rootNode = st;
|
|
|
|
for(var idx in ids) {
|
|
$($(st).getNodeByID(idx)).addClass('selected').attr('selected', 'selected');
|
|
}
|
|
|
|
// If no action is selected, enable all nodes
|
|
if(!actionUrl || actionUrl == -1 || !viewMode.hasClass('active')) {
|
|
$(rootNode).find('li').each(function() {
|
|
$(this).setEnabled(true);
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Disable the nodes while the ajax request is being processed
|
|
$(rootNode).find('li').each(function() {
|
|
allIds.push($(this).data('id'));
|
|
$(this).addClass('treeloading').setEnabled(false);
|
|
});
|
|
|
|
// Post to the server to ask which pages can have this batch action applied
|
|
// Retain existing query parameters in URL before appending path
|
|
var actionUrlParts = $.path.parseUrl(actionUrl);
|
|
var applicablePagesUrl = actionUrlParts.hrefNoSearch + '/applicablepages/';
|
|
applicablePagesUrl = $.path.addSearchParams(applicablePagesUrl, actionUrlParts.search);
|
|
applicablePagesUrl = $.path.addSearchParams(applicablePagesUrl, {csvIDs: allIds.join(',')});
|
|
jQuery.getJSON(applicablePagesUrl, function(applicableIDs) {
|
|
// Set a CSS class on each tree node indicating which can be batch-actioned and which can't
|
|
jQuery(rootNode).find('li').each(function() {
|
|
$(this).removeClass('treeloading');
|
|
|
|
var id = $(this).data('id');
|
|
if(id == 0 || $.inArray(id, applicableIDs) >= 0) {
|
|
$(this).setEnabled(true);
|
|
} else {
|
|
// De-select the node if it's non-applicable
|
|
$(this).removeClass('selected').setEnabled(false);
|
|
$(this).prop('selected', false);
|
|
}
|
|
});
|
|
|
|
self.serializeFromTree();
|
|
});
|
|
},
|
|
|
|
/**
|
|
* @func serializeFromTree
|
|
* @return {boolean}
|
|
*/
|
|
serializeFromTree: function() {
|
|
var tree = this.getTree(), ids = tree.getSelectedIDs();
|
|
|
|
// write IDs to the hidden field
|
|
this.setIDs(ids);
|
|
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* @func setIDS
|
|
* @param {array} ids
|
|
*/
|
|
setIDs: function(ids) {
|
|
this.find(':input[name=csvIDs]').val(ids ? ids.join(',') : null);
|
|
},
|
|
|
|
/**
|
|
* @func getIDS
|
|
* @return {array}
|
|
*/
|
|
getIDs: function() {
|
|
// Map empty value to empty array
|
|
var value = this.find(':input[name=csvIDs]').val();
|
|
return value
|
|
? value.split(',')
|
|
: [];
|
|
},
|
|
|
|
onsubmit: function(e) {
|
|
var self = this, ids = this.getIDs(), tree = this.getTree(), actions = this.getActions();
|
|
|
|
// if no nodes are selected, return with an error
|
|
if(!ids || !ids.length) {
|
|
alert(ss.i18n._t('CMSMAIN.SELECTONEPAGE', 'Please select at least one page'));
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
|
|
// apply callback, which might modify the IDs
|
|
var type = this.find(':input[name=Action]').val();
|
|
if(actions[type]) {
|
|
ids = this.getActions()[type].apply(this, [ids]);
|
|
}
|
|
|
|
// Discontinue processing if there are no further items
|
|
if(!ids || !ids.length) {
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
|
|
// write (possibly modified) IDs back into to the hidden field
|
|
this.setIDs(ids);
|
|
|
|
// Reset failure states
|
|
tree.find('li').removeClass('failed');
|
|
|
|
var button = this.find(':submit:first');
|
|
button.addClass('loading');
|
|
|
|
jQuery.ajax({
|
|
// don't use original form url
|
|
url: type,
|
|
type: 'POST',
|
|
data: this.serializeArray(),
|
|
complete: function(xmlhttp, status) {
|
|
button.removeClass('loading');
|
|
|
|
// Refresh the tree.
|
|
// Makes sure all nodes have the correct CSS classes applied.
|
|
tree.jstree('refresh', -1);
|
|
self.setIDs([]);
|
|
|
|
// Reset action
|
|
self.find(':input[name=Action]').val('').change();
|
|
|
|
// status message (decode into UTF-8, HTTP headers don't allow multibyte)
|
|
var msg = xmlhttp.getResponseHeader('X-Status');
|
|
if(msg) statusMessage(decodeURIComponent(msg), (status == 'success') ? 'good' : 'bad');
|
|
},
|
|
success: function(data, status) {
|
|
var id, node;
|
|
|
|
if(data.modified) {
|
|
var modifiedNodes = [];
|
|
for(id in data.modified) {
|
|
node = tree.getNodeByID(id);
|
|
tree.jstree('set_text', node, data.modified[id]['TreeTitle']);
|
|
modifiedNodes.push(node);
|
|
}
|
|
$(modifiedNodes).effect('highlight');
|
|
}
|
|
if(data.deleted) {
|
|
for(id in data.deleted) {
|
|
node = tree.getNodeByID(id);
|
|
if(node.length) tree.jstree('delete_node', node);
|
|
}
|
|
}
|
|
if(data.error) {
|
|
for(id in data.error) {
|
|
node = tree.getNodeByID(id);
|
|
$(node).addClass('failed');
|
|
}
|
|
}
|
|
},
|
|
dataType: 'json'
|
|
});
|
|
|
|
// Never process this action; Only invoke via ajax
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
|
|
});
|
|
|
|
$('.cms-content-batchactions-button').entwine({
|
|
onmatch: function () {
|
|
this._super();
|
|
this.updateTree();
|
|
},
|
|
onunmatch: function () {
|
|
this._super();
|
|
},
|
|
onclick: function (e) {
|
|
this.updateTree();
|
|
},
|
|
updateTree: function () {
|
|
var tree = $('.cms-tree'),
|
|
form = $('#Form_BatchActionsForm');
|
|
|
|
this._super();
|
|
|
|
if(this.data('active')) {
|
|
tree.addClass('multiple');
|
|
tree.removeClass('draggable');
|
|
form.serializeFromTree();
|
|
} else {
|
|
tree.removeClass('multiple');
|
|
tree.addClass('draggable');
|
|
}
|
|
|
|
$('#Form_BatchActionsForm').refreshSelected();
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Class: #Form_BatchActionsForm :select[name=Action]
|
|
*/
|
|
$('#Form_BatchActionsForm select[name=Action]').entwine({
|
|
onchange: function(e) {
|
|
var form = $(e.target.form),
|
|
btn = form.find(':submit'),
|
|
selected = $(e.target).val();
|
|
if(!selected || selected == -1) {
|
|
btn.attr('disabled', 'disabled').button('refresh');
|
|
} else {
|
|
btn.removeAttr('disabled').button('refresh');
|
|
}
|
|
|
|
// Refresh selected / enabled nodes
|
|
$('#Form_BatchActionsForm').refreshSelected();
|
|
|
|
// TODO Should work by triggering change() along, but doesn't - entwine event bubbling?
|
|
this.trigger("liszt:updated");
|
|
|
|
this._super(e);
|
|
}
|
|
});
|
|
});
|
|
|
|
})(jQuery);
|