From 74cb73d2d284d3e62f6d2d529b31975976395f8d Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sat, 20 Jul 2013 13:45:22 +0000 Subject: [PATCH] moved most action "comfort" functions (with exception of nm_action and pagination) from server-side nextmatch class to client-side egwActions.updateActions() method, so they are available for all widgets --- etemplate/js/et2_core_DOMWidget.js | 62 +++--- .../js/et2_extension_nextmatch_actions.js | 42 ----- etemplate/js/et2_widget_split.js | 4 +- etemplate/js/et2_widget_tree.js | 27 +-- phpgwapi/js/egw_action/egw_action.js | 177 ++++++++++++++++-- phpgwapi/js/egw_action/egw_action_common.js | 5 + 6 files changed, 199 insertions(+), 118 deletions(-) diff --git a/etemplate/js/et2_core_DOMWidget.js b/etemplate/js/et2_core_DOMWidget.js index c84a686dd0..2a2f7954c1 100644 --- a/etemplate/js/et2_core_DOMWidget.js +++ b/etemplate/js/et2_core_DOMWidget.js @@ -409,51 +409,40 @@ var et2_DOMWidget = et2_widget.extend(et2_IDOMNode, this._actionManager = gam.addAction("actionManager", this.id); } } - // ActionManager wants an array - var parsed_actions = []; - if(typeof actions == "object" && actions) - { - var parse = function(actions) - { - var parsed = []; - for(var key in actions) - { - actions[key].id = key; - if(typeof actions[key].icon != "undefined" && actions[key].icon) - { - actions[key].iconUrl = this.egw().image(actions[key].icon); - } - if(typeof actions[key].children != "undefined") - { - actions[key].children = parse(actions[key].children); - } - parsed.push(actions[key]); - } - return parsed; - }; - // Don't make changes to original - var action = jQuery.extend(true, {}, actions); - parsed_actions = parse.call(this, action); - } - else - { - parsed_actions = actions; - } - this._actionManager.updateActions(parsed_actions); + this._actionManager.updateActions(actions); // Put a reference to the widget into the action stuff, so we can // easily get back to widget context from the action handler this._actionManager.data = {widget: this}; // Link the actions to the DOM - this._link_actions(parsed_actions); + this._link_actions(actions); + }, + + /** + * Get all action-links / id's of 1.-level actions from a given action object + * + * This can be overwritten to not allow all actions, by not returning them here. + * + * @param actions + * @returns {Array} + */ + _get_action_links: function(actions) + { + var action_links = []; + for(var i in actions) + { + var action = actions[i]; + action_links.push(typeof action.id != 'undefined' ? action.id : i); + } + return action_links; }, /** * Link the actions to the DOM nodes / widget bits. * */ - _link_actions: function(parsed_actions) + _link_actions: function(actions) { // Get the top level element for the tree var objectManager = egw_getAppObjectManager(true); @@ -472,12 +461,7 @@ var et2_DOMWidget = et2_widget.extend(et2_IDOMNode, // Go over the widget & add links - this is where we decide which actions are // 'allowed' for this widget at this time - var action_links = []; - for(var i = 0; i < parsed_actions.length; i++) - { - action_links.push(parsed_actions[i].id); - } - + var action_links = this._get_action_links(actions); widget_object.updateActionLinks(action_links); } }); diff --git a/etemplate/js/et2_extension_nextmatch_actions.js b/etemplate/js/et2_extension_nextmatch_actions.js index 944227d4ca..b83b8d51f1 100644 --- a/etemplate/js/et2_extension_nextmatch_actions.js +++ b/etemplate/js/et2_extension_nextmatch_actions.js @@ -195,48 +195,6 @@ function nm_action(_action, _senders, _target, _ids) } } -/** - * Callback to check if none of _senders rows has disableClass set - * - * @param _action egwAction object, we use _action.data.disableClass to check - * @param _senders array of egwActionObject objects - * @param _target egwActionObject object, get's called for every object in _senders - * @returns boolean true if none has disableClass, false otherwise - */ -function nm_not_disableClass(_action, _senders, _target) -{ - return !$j(_target.iface.getDOMNode()).hasClass(_action.data.disableClass); -} - -/** - * Callback to check if all of _senders rows have enableClass set - * - * @param _action egwAction object, we use _action.data.enableClass to check - * @param _senders array of egwActionObject objects - * @param _target egwActionObject object, get's called for every object in _senders - * @returns boolean true if none has disableClass, false otherwise - */ -function nm_enableClass(_action, _senders, _target) -{ - return $j(_target.iface.getDOMNode()).hasClass(_action.data.enableClass); -} - -/** - * Enable an _action, if it matches a given regular expresstion in _action.data.enableId - * - * @param _action egwAction object, we use _action.data.enableId to check - * @param _senders array of egwActionObject objects - * @param _target egwActionObject object, get's called for every object in _senders - * @returns boolean true if _target.id matches _action.data.enableId - */ -function nm_enableId(_action, _senders, _target) -{ - if (typeof _action.data.enableId == 'string') - _action.data.enableId = new RegExp(_action.data.enableId); - - return _target.id.match(_action.data.enableId); -} - /** * Callback to check if a certain field (_action.data.fieldId) is (not) equal to given value (_action.data.fieldValue) * diff --git a/etemplate/js/et2_widget_split.js b/etemplate/js/et2_widget_split.js index 5588b2a325..bf090ceea6 100644 --- a/etemplate/js/et2_widget_split.js +++ b/etemplate/js/et2_widget_split.js @@ -70,8 +70,8 @@ var et2_split = et2_DOMWidget.extend([et2_IResizeable], this.div = $j(document.createElement("div")); // Create the dynheight component which dynamically scales the inner - // container. - this.dynheight = new et2_dynheight(this.egw().window,this.div, 100); + // container. + this.dynheight = new et2_dynheight(this.egw().window,this.div, 100); // Add something so we can see it - will be replaced if there's children this.left = $j("
Top / Left
").appendTo(this.div); diff --git a/etemplate/js/et2_widget_tree.js b/etemplate/js/et2_widget_tree.js index 0593fbb4d0..cb7098ff0a 100644 --- a/etemplate/js/et2_widget_tree.js +++ b/etemplate/js/et2_widget_tree.js @@ -317,25 +317,11 @@ var et2_tree = et2_inputWidget.extend( }, /** - * Set Actions on the tree + * Links actions to tree nodes * - * Each action is defined as an object: - * move: { - * type: "drop", - * acceptedTypes: "mail", - * icon: "move", - * caption: "Move to" - * onExecute: javascript:mail_move" - * } - * - * This will turn all the tree nodes into drop targets. When "mail" drag types are dropped, - * the global function mail_move(egwAction action, egwActionObject sender) will be called. The ID of the - * dragged "mail" will be in sender.id, some information about the sender will be in sender.context. - * - * @param Array[ {ID, attributes..}+] array of egw action information - this is different from what is passed in to - * set_actions, which takes a more human-friendly Object map. + * @param Object[ {ID: attributes..}+] as for set_actions */ - _link_actions: function(parsed_actions) + _link_actions: function(actions) { // Get the top level element for the tree var objectManager = egw_getAppObjectManager(true); @@ -350,11 +336,8 @@ var et2_tree = et2_inputWidget.extend( treeObj.clear(); // Go over the tree parts & add links - var action_links = []; - for(var i = 0; i < parsed_actions.length; i++) - { - action_links.push(parsed_actions[i].id); - } + var action_links = this._get_action_links(actions); + if (typeof this.options.select_options != 'undefined') { diff --git a/phpgwapi/js/egw_action/egw_action.js b/phpgwapi/js/egw_action/egw_action.js index cf64e453bc..0075f897fe 100644 --- a/phpgwapi/js/egw_action/egw_action.js +++ b/phpgwapi/js/egw_action/egw_action.js @@ -120,7 +120,7 @@ function egwActionHandler(_executeEvent) * Associative array where action classes may register themselves */ if (typeof window._egwActionClasses == "undefined") - window._egwActionClasses = {} + window._egwActionClasses = {}; _egwActionClasses["default"] = { "actionConstructor": egwAction, @@ -151,7 +151,7 @@ function egwAction(_parent, _id, _caption, _iconUrl, _onExecute, _allowOnMultipl this.allowOnMultiple = _allowOnMultiple; this.enabled = new egwFnct(this, true); this.hideOnDisabled = false; - this.data = null; // Data which can be freely assigned to the action + this.data = {}; // Data which can be freely assigned to the action this.type = "default"; //All derived classes have to override this! this.canHaveChildren = false; //Has to be overwritten by inherited action classes @@ -274,23 +274,119 @@ egwAction.prototype.addAction = function(_type, _id, _caption, _iconUrl, } else { - throw "This action does not allow child elements!" + throw "This action does not allow child elements!"; } -} +}; + +/** + * Default icons for given id + */ +egwAction.prototype.defaultIcons = { + view: 'view', + edit: 'edit', + open: 'edit', // does edit if possible, otherwise view + add: 'new', + "new": 'new', + "delete": 'delete', + cat: 'attach', // add as category icon to api + document: 'etemplate/merge', + print: 'print', + copy: 'copy', + move: 'move', + cut: 'cut', + paste: 'editpaste', +}; /** * Updates the children of this element + * + * @param Object _actions { id: action, ...} + * @param string _app defaults to egw_getAppname() */ -egwAction.prototype.updateActions = function(_actions) +egwAction.prototype.updateActions = function(_actions, _app) { if (this.canHaveChildren) { - for (var i = 0 ; i < _actions.length; i++) + if (typeof _app == "undefined") _app = egw_getAppName(); // this can probably be queried from actionObjectManager ... + var egw = window.egw(_app); + + if (jQuery.isArray(_actions)) + { + _actions = jQuery.extend({}, _actions); + } + for (var i in _actions) { - //Check whether the given action is already part of this action manager instance var elem = _actions[i]; - if (typeof elem == "object" && typeof elem.id == "string" && elem.id) + + if (typeof elem == "string") elem = { caption: elem }; + + if (typeof elem == "object") { + // use attr name as id, if none given + if (typeof elem.id != "string") elem.id = i; + + // if no iconUrl given, check icon and default icons + if (typeof elem.iconUrl == "undefined") + { + if (typeof elem.icon == "undefined") elem.icon = this.defaultIcons[elem.id]; + if (typeof elem.icon != "undefined") + { + var parts = elem.icon.split('/', 2); // app/image syntax to search icon in different app + if (parts.length == 2) + { + elem.iconUrl = egw.image(parts[1], parts[0]); + } + else + { + elem.iconUrl = egw.image(elem.icon); + } + } + delete elem.icon; + } + + // allways add shortcut for delete + if (elem.id == "delete" && typeof elem.shortcut == "undefined") + { + elem.shortcut = { keyCode: 46, shift: false, ctrl: false, alt: false, caption: egw.lang('Del') }; + } + + // translate caption + if (typeof elem.no_lang == "undefined" || !elem.no_lang) + { + elem.caption = egw.lang(elem.caption); + if (typeof elem.hint == "string") elem.hint = egw.lang(elem.hint); + } + delete elem.no_lang; + + // translate confirm messages + for(var attr in {confirm: '', confirm_multiple: ''}) + { + if (typeof elem[attr] == "string") + { + elem[attr] = egw.lang(elem[attr])+(elem[attr].substr(-1) != '?' ? '?' : ''); + } + } + + if (typeof elem.confirm != "undefined") elem.confirm = egw.lang(elem.confirm); + if (typeof elem.confirm_multiple != "undefined") elem.confirm_multiple = egw.lang(elem.confirm_multiple); + + // set certain enabled functions + if (typeof elem.enabled == "undefined") + { + if (typeof elem.enableClass != "undefined") + { + elem.enabled = this.enableClass; + } + else if (typeof elem.disableClass != "undefined") + { + elem.enabled = this.not_disableClass; + } + else if (typeof elem.enableId != "undefined") + { + elem.enabled = this.enableId; + } + } + //Check whether the action already exists, and if no, add it to the //actions list var action = this.getActionById(elem.id); @@ -305,7 +401,7 @@ egwAction.prototype.updateActions = function(_actions) // array if (this.canHaveChildren !== true && this.canHaveChildren.indexOf(elem.type) == -1) { - throw "This child type '" + elem.type + "' is not allowed!" + throw "This child type '" + elem.type + "' is not allowed!"; } if (typeof _egwActionClasses[elem.type] != "undefined") @@ -324,7 +420,7 @@ egwAction.prototype.updateActions = function(_actions) // Add sub-actions to the action if (elem.children) { - action.updateActions(elem.children); + action.updateActions(elem.children, _app); } } } @@ -333,7 +429,49 @@ egwAction.prototype.updateActions = function(_actions) { throw "This action element cannot have children!"; } -} +}; + +/** + * Callback to check if none of _senders rows has disableClass set + * + * @param _action egwAction object, we use _action.data.disableClass to check + * @param _senders array of egwActionObject objects + * @param _target egwActionObject object, get's called for every object in _senders + * @returns boolean true if none has disableClass, false otherwise + */ +egwAction.prototype.not_disableClass = function(_action, _senders, _target) +{ + return !$j(_target.iface.getDOMNode()).hasClass(_action.data.disableClass); +}; + +/** + * Callback to check if all of _senders rows have enableClass set + * + * @param _action egwAction object, we use _action.data.enableClass to check + * @param _senders array of egwActionObject objects + * @param _target egwActionObject object, get's called for every object in _senders + * @returns boolean true if none has disableClass, false otherwise + */ +egwAction.prototype.enableClass = function(_action, _senders, _target) +{ + return $j(_target.iface.getDOMNode()).hasClass(_action.data.enableClass); +}; + +/** + * Enable an _action, if it matches a given regular expresstion in _action.data.enableId + * + * @param _action egwAction object, we use _action.data.enableId to check + * @param _senders array of egwActionObject objects + * @param _target egwActionObject object, get's called for every object in _senders + * @returns boolean true if _target.id matches _action.data.enableId + */ +egwAction.prototype.enableId = function(_action, _senders, _target) +{ + if (typeof _action.data.enableId == 'string') + _action.data.enableId = new RegExp(_action.data.enableId); + + return _target.id.match(_action.data.enableId); +}; /** * Applys the same onExecute handler to all actions which don't have an execute @@ -370,8 +508,21 @@ egwAction.prototype.execute = function(_senders, _target) _target = null; } + // check if actions needs to be confirmed first + if (this.data && this.data.confirm && this.onExecute.fcnt != window.nm_action) + { + var self = this; + et2_dialog.show_dialog(function(_button) + { + if (_button == et2_dialog.YES_BUTTON) + { + return self.onExecute.exec(self, _senders, _target); + } + }, self.data.confirm, self.data.hint, {}, et2_dialog.BUTTONS_YES_NO, et2_dialog.QUESTION_MESSAGE); + return; + } return this.onExecute.exec(this, _senders, _target); -} +}; /** * The set_onExecute function is the setter function for the onExecute event of @@ -427,7 +578,7 @@ egwAction.prototype.set_data = function(_value) egwAction.prototype.updateAction = function(_data) { - egwActionStoreJSON(_data, this, true); + egwActionStoreJSON(_data, this, "data"); } function _egwActionTreeContains(_tree, _elem) diff --git a/phpgwapi/js/egw_action/egw_action_common.js b/phpgwapi/js/egw_action/egw_action_common.js index ac97f36b9c..4d8db7149a 100644 --- a/phpgwapi/js/egw_action/egw_action_common.js +++ b/phpgwapi/js/egw_action/egw_action_common.js @@ -17,6 +17,7 @@ * @param object _data may be an object with data that will be stored inside the * given object. * @param object _obj is the object where the data will be stored. + * @param mixed _setterOnly false: store everything, true: only store when setter exists, "data" store rest in data property */ function egwActionStoreJSON(_data, _obj, _setterOnly) { @@ -33,6 +34,10 @@ function egwActionStoreJSON(_data, _obj, _setterOnly) { _obj[key] = _data[key]; } + else if (_setterOnly === 'data') + { + _obj.data[key] = _data[key]; + } } } }