Hey guys...
I’ve posted the code for the modActionButtons.class.js here, fully-documented. Please review this, so you can understand how they work.
For those of you unfamiliar with the modActionButtons, those are the toolbar buttons at the top of every page (save, delete, etc). They are dynamically added in each .tpl file (see the code at the bottom for an example). They’re very flexible, and are powerful in how they can be used in coordination with connectors and controllers.
So, without further delay:
var modActionButtons = new Class({
id: '',
buttons: [],
options: { a_close: 2 }, // default close action goes to the home page
stay: 'stay', // default action is to continue editing
initialize: function(id,options) {
// if specified form, designate all toolbar keyhandlers to scope of form
fr = options.form_id ? {for:options.form_id} : {};
this.toolbar = new Ext.Toolbar(id,null,fr);
this.id = id;
this.options = options; // assign global options
},
add: function(options) {
// if - sent, create a toolbar delimiter
if (options == '-') {
this.toolbar.add('-');
return false;
}
// the default Ext class for Toolbar.Button
options.cls = 'x-btn-text bmenu';
// reference self for inline functions to have accurate scope
options.self = this;
// if handler is specified for a button, execute that instead
// this can be used for doing document-specific actions
// such as using a Ext.menu.DateMenu in the action buttons
// or some other item that opens up more options...you get the idea
if (options.handler == null) options.handler = this.handleClick;
// create the button
b = new Ext.Toolbar.Button(options);
// if javascript is specified, run it when button is click, before this.handleClick is run
if (options.javascript) {
b.addListener('click',function() {
eval(options.javascript);
});
}
// add button to toolbar
this.toolbar.add(b);
return false;
},
handleClick: function(itm,e) {
o = itm.self.options;
if (o == null) return false;
// action buttons handlers, abstracted to all get-out
switch (itm.type) {
case 'save': // if saving anything
_resetProgress(); // reset the Progress Bar
Ext.MessageBox.show({
title: 'Please wait...',
msg: 'Saving...',
width: 240,
progress:true, // make it a progress bar
closable:false
});
// now send form data through FormHandler to the connector
FormHandler.send($(o.form_id),o.type,function(res) {
// if response is an integer, proceed.
// The only other thing that connectors/processors should send
// is JSON errors using modJSONError
if (isInteger(res)) {
// update the progress bar
_progressTime(5,_progressID,'Refreshing tree...');
// refresh the tree, then pass the handling onto the redirectStay func
parent.modTree.refresh(itm.self.redirectStay(o,itm.self.stay,res));
} else {
// if error, pass handling to FormHandler.js
FormHandler.errorJSON(res);
}
});
break;
default: // this is any other action besides save
id = o['id'] || 0; // append the ID of the element if specified
loc = 'index.php?a='+o.actions[itm.type]+'&id='+id;
// if any confirm dialogs are specified, show them
// else just redirect to the action
if (o['confirms'] != null) {
// confirms must be parallel to the type setting
// e.g. type: 'delete' must have:
// confirms: { 'delete': 'confirm message here' }
if (o['confirms'][itm.type] != null) {
itm.self.confirm(itm,o['confirms'][itm.type],loc);
} else location.href = loc;
} else location.href = loc;
break;
}
return false;
},
// abstract this so that you can choose whether or not to have confirm dialogs
// you can also pass in functions as loc, if you dont want to redirect
// if you pass a function, the only argument will be the action button
confirm: function(itm,m,loc) {
// if no message go ahead and redirect...we dont like blank questions
if (m == null) return true;
Ext.MessageBox.confirm('',m,function(e) {
// if the user is okay with the action
if (e == 'yes') {
if (loc == null) return true;
if (typeof(loc) == 'function') { // if loc is a function, run it, and pass Button
loc(itm);
} else location.href = loc;
}
});
},
loadStay: function(options) {
// you can override the default text for the Stay options.
// (you can also only override one option, if you wish, for page-specific text)
// otherwise it's the normal Options, Create Another, Continue editing, etc
if (options == null) options = modStayOptions;
this.toolbar.add({
text: options['txt_menu_header'] || modStayOptions.txt_menu_header,
menu: new Ext.menu.Menu({
id: 'stayMenu',
items: [
new Ext.menu.CheckItem({
text: options['txt_new'] || modStayOptions.txt_new,
value: 'new',
group: 'stay',
self: this,
handler: this.checkStay
}),
new Ext.menu.CheckItem({
text: options['txt_continue'] || modStayOptions.txt_continue,
value: 'stay',
group: 'stay',
self: this,
checked: true,
handler: this.checkStay
}),
new Ext.menu.CheckItem({
text: options['txt_close'] || modStayOptions.txt_close,
value: 'close',
group: 'stay',
self: this,
handler: this.checkStay
}),
]
})
});
},
checkStay: function(itm,e) {
itm.self.stay = itm.value; // set the modActionButton toolbar stay value
},
redirectStay: function(o,s,res) {
// update progress dialog
_progressTime(10,_progressID,'Redirecting...');
switch (s) {
case 'new': // if user selected 'new', then always redirect
location.href = 'index.php?a='+o.actions['new'];
break;
case 'stay':
// if Continue Editing, then don't reload the page - just hide the Progress bar
// unless the user is on a 'Create' page...if so, then redirect
// to the proper Edit page
if (o.type == 'create') {
id = o.type == 'create' ? res : o.id;
location.href = 'index.php?a='+o.actions['edit']+'&id='+id;
} else Ext.MessageBox.hide();
break;
case 'close': // redirect to the cancel action
location.href = 'index.php?a='+o.actions['cancel'];
break;
}
}
});
Now, to use this in a page, you would do something like this (in Smarty format):
{literal}
<script type="text/javascript">
// <![CDATA[
Ext.onReady(function() {
ab = new modActionButtons('modAB',{
form_id: 'mutate_document',
type: 'update',
actions: { // all the action IDs to move to
'new': 4,
'edit': 27,
'delete': 6,
'duplicate': 94,
'publish': 61,
'unpublish': 62,
'cancel': 2
},
id: {/literal}{$document->id}{literal}, // pass in the document ID
confirms: { // these are confirm dialogs for each button
{/literal}
'duplicate': '{$_lang.confirm_duplicate_document}',
'delete': '{$_lang.confirm_delete_document}'
{literal}
}
});
ab.add({{/literal}
type: 'save',
// note i'm running dynamic javascript before the button executes...
// this cleans up the RTE and puts the value into something FormHandler can transmit
{if $which_editor NEQ 'none'}
javascript: "cleanupRTE('{$which_editor}');",
{/if}
text: '{$_lang.save}'
{literal}});
// different buttons based upon whether or not a doc is published
{/literal}{if $document->published}{literal}
ab.add({{/literal}type: 'unpublish', text: '{$_lang.unpublish}'{literal}});
{/literal}{else}{literal}
ab.add({{/literal}type: 'publish', text: '{$_lang.publish}'{literal}});
{/literal}{/if}{literal}
ab.add({{/literal}type: 'duplicate', text: '{$_lang.duplicate}'{literal}});
{/literal}{if $document->deleted}{literal}
ab.add({{/literal}type: 'undelete', text: '{$_lang.undelete}'{literal}});
{/literal}{else}{literal}
ab.add({{/literal}type: 'delete', text: '{$_lang.delete}'{literal}});
{/literal}{/if}{literal}
ab.add({{/literal}type: 'cancel', text: '{$_lang.cancel}'{literal}});
ab.add('-'); // create a menu delimiter
ab.loadStay(); // load the stay options
});
// ]]>
</script>
{/literal}
It’s that simple!
Please ask questions or give suggestions if you have them. Thanks guys!