silverstripe-cms/javascript/CMSMain.BatchActions.js
Ingo Schommer 44e16b9620 API CHANGE Removed CMSBatchAction->getDoingText(), is now using button loading indicators in the UI
API CHANGE Removed LeftAndMain->BatchActionList(), no longer necessary as we're creating the batch actions form in the same class
ENHANCEMENT Changed CMSBatchAction logic to return JSON status changes rather than eval'ed JavaScript via FormResponse
ENHANCEMENT Ported batchactions in CMSMain_left.js to concrete javascript, and moved to new CMSMain.BatchActions.js file
ENHANCEMENT Using native CMSMain->BatchActionsForm() to render form instead of custom template markup in CMSMain_TreeTools.ss

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@92722 467b73ca-7a2a-4603-9d3b-597d59a354a9
2009-11-21 03:15:40 +00:00

278 lines
7.9 KiB
JavaScript

(function($) {
/**
* @class 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.
* @name ss.Form_BatchActionsForm
*
* Events:
* - register: Called before an action is added.
* - unregister: Called before an action is removed.
*/
$('#Form_BatchActionsForm').concrete('ss', function($){
return/** @lends ss.Form_BatchActionsForm */{
/**
* @type {DOMElement}
*/
Tree: null,
/**
* @type {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: [],
onmatch: function() {
var self = this;
this.setTree($('#sitetree')[0]);
$(this.Tree()).bind('selectionchanged', function(e, data) {
self._treeSelectionChanged(data.node);
});
// if tab which contains this form is shown, make the tree selectable
$('#TreeActions').bind('tabsselect', function(e, ui) {
if($(ui.panel).attr('id') != 'TreeActions-batchactions') return;
// if the panel is visible (meaning about to be closed),
// disable tree selection and reset any values. Otherwise enable it.
if($(ui.panel).is(':visible')) {
$(self.Tree()).removeClass('multiselect');
} else {
self._multiselectTransform();
}
});
this.bind('submit', function(e) {return self._submit(e);});
},
/**
* @param {String} type
* @param {Function} callback
*/
register: function(type, callback) {
this.trigger('register', {type: type, callback: callback});
var actions = this.Actions();
actions[type] = callback;
this.setActions(actions);
},
/**
* Remove an existing action.
*
* @param {String} type
*/
unregister: function(type) {
this.trigger('unregister', {type: type});
var actions = this.Actions();
if(actions[type]) delete actions[type];
this.setActions(actions);
},
/**
* Determines if we should allow and track tree selections.
*
* @todo Too much coupling with tabset
* @return boolean
*/
_isActive: function() {
return $('#TreeActions-batchactions').is(':visible');
},
_submit: function(e) {
var ids = [];
var tree = this.Tree();
// find all explicitly selected IDs
$(tree).find('li.selected').each(function() {
ids.push(tree.getIdxOf(this));
// find implicitly selected children
$(this).find('li').each(function() {
ids.push(tree.getIdxOf(this));
});
});
// if no nodes are selected, return with an error
if(!ids || !ids.length) {
alert(ss.i18n._t('CMSMAIN.SELECTONEPAGE'));
return false;
}
// apply callback, which might modify the IDs
var type = this.find(':input[name=Action]').val();
if(this.Actions()[type]) ids = this.Actions()[type].apply(this, [ids]);
// if no IDs are selected, stop here. This is an implict way for the
// callback to cancel the actions
if(!ids || !ids.length) {
return false;
}
// write IDs to the hidden field
this.find(':input[name=csvIDs]').val(ids.join(','));
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');
// status message
var msg = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.statusText;
statusMessage(msg, (status == 'success') ? 'good' : 'bad');
},
success: function(data, status) {
// TODO This should use a more common serialization in a new tree library
if(data.modified) {
for(var id in data.modified) {
tree.setNodeTitle(id, data.modified[id]['TreeTitle']);
}
}
if(data.deleted) {
for(var id in data.deleted) {
var node = tree.getTreeNodeByIdx(id);
if(node && node.parentTreeNode) node.parentTreeNode.removeTreeNode(node);
}
}
// reset selection state
// TODO Should unselect all selected nodes as well
jQuery(tree).removeClass('multiselect');
// Check if current page still exists, and refresh it.
// Otherwise remove the current form
var selectedNode = tree.firstSelected();
if(selectedNode) {
var selectedNodeId = tree.getIdxOf(selectedNode);
if(data.modified[selectedNode.getIdx()]) {
// only if the current page was modified
selectedNode.selectTreeNode();
} else if(data.deleted[selectedNode.getIdx()]) {
$('#Form_EditForm').concrete('ss').removeForm();
}
} else {
$('#Form_EditForm').concrete('ss').removeForm();
}
// close panel
// TODO Coupling with tabs
$('#TreeActions').tabs('select', -1);
},
dataType: 'json'
});
return false;
},
/**
* @todo This is simulating MultiselectTree functionality, and shouldn't be necessary.
*/
_multiselectTransform : function() {
// make tree selectable
jQuery(this.Tree()).addClass('multiselect');
// auto-select the current node
var node = this.Tree().firstSelected();
if(node){
node.removeNodeClass('current');
node.addNodeClass('selected');
node.open();
// Open all existing children, which might trigger further
// ajaxExansion calls to ensure all nodes are selectable
var children = $(node).find('li').each(function() {
this.open();
});
}
},
/**
* Only triggers if the field is considered 'active'.
* @todo Most of this is basically simulating broken behaviour of the MultiselectTree mixin,
* and should be removed.
*/
_treeSelectionChanged: function(node) {
if(!this._isActive()) return;
if(node.selected) {
node.removeNodeClass('selected');
node.selected = false;
} else {
// Select node
node.addNodeClass('selected');
node.selected = true;
// Open node in order to allow proper selection of children
if($(node).hasClass('unexpanded')) {
node.open();
}
// Open all existing children, which might trigger further
// ajaxExansion calls to ensure all nodes are selectable
var children = $(node).find('li').each(function() {
this.open();
});
}
}
};
});
$(document).ready(function() {
/**
* Publish selected pages action
*/
$('#Form_BatchActionsForm').concrete('ss').register('admin/batchactions/publish', function(ids) {
var confirmed = confirm(
"You have " + ids.length + " pages selected.\n\n"
+ "Do your really want to publish?"
);
return (confirmed) ? ids : false;
});
/**
* Unpublish selected pages action
*/
$('#Form_BatchActionsForm').concrete('ss').register('admin/batchactions/unpublish', function(ids) {
var confirmed = confirm(
"You have " + ids.length + " pages selected.\n\n"
+ "Do your really want to unpublish?"
);
return (confirmed) ? ids : false;
});
/**
* Delete selected pages action
*/
$('#Form_BatchActionsForm').concrete('ss').register('admin/batchactions/delete', function(ids) {
var confirmed = confirm(
"You have " + ids.length + " pages selected.\n\n"
+ "Do your really want to delete?"
);
return (confirmed) ? ids : false;
});
/**
* Delete selected pages from live action
*/
$('#Form_BatchActionsForm').concrete('ss').register('admin/batchactions/deletefromlive', function(ids) {
var confirmed = confirm(
"You have " + ids.length + " pages selected.\n\n"
+ "Do your really want to delete these pages from live?"
);
return (confirmed) ? ids : false;
});
});
})(jQuery);