From 1876a5faa1fb2f6ce49055ca99276d282e33dceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20St=C3=B6ckel?= Date: Thu, 1 Mar 2012 16:24:29 +0000 Subject: [PATCH] Splitted the egw object into multiple files, added system for allowing a application specific client-side api (use 'egw' as a function) --- phpgwapi/js/jsapi/egw.js | 896 +------------------ phpgwapi/js/jsapi/egw_app.js | 26 + phpgwapi/js/jsapi/egw_config.js | 56 ++ phpgwapi/js/jsapi/egw_core.js | 237 +++++ phpgwapi/js/jsapi/egw_images.js | 93 ++ phpgwapi/js/jsapi/egw_jsonq.js | 147 +++ phpgwapi/js/jsapi/egw_lang.js | 79 ++ phpgwapi/js/jsapi/egw_links.js | 472 ++++++++++ phpgwapi/js/jsapi/egw_preferences.js | 98 ++ phpgwapi/js/jsapi/egw_user.js | 69 ++ phpgwapi/templates/idots/css/traditional.css | 1 + 11 files changed, 1288 insertions(+), 886 deletions(-) create mode 100644 phpgwapi/js/jsapi/egw_app.js create mode 100644 phpgwapi/js/jsapi/egw_config.js create mode 100644 phpgwapi/js/jsapi/egw_core.js create mode 100644 phpgwapi/js/jsapi/egw_images.js create mode 100644 phpgwapi/js/jsapi/egw_jsonq.js create mode 100644 phpgwapi/js/jsapi/egw_lang.js create mode 100644 phpgwapi/js/jsapi/egw_links.js create mode 100644 phpgwapi/js/jsapi/egw_preferences.js create mode 100644 phpgwapi/js/jsapi/egw_user.js diff --git a/phpgwapi/js/jsapi/egw.js b/phpgwapi/js/jsapi/egw.js index 4675c94882..e926bfdc9f 100644 --- a/phpgwapi/js/jsapi/egw.js +++ b/phpgwapi/js/jsapi/egw.js @@ -12,890 +12,14 @@ "use strict"; -var egw; +/*egw:uses + egw_core; + egw_preferences; + egw_lang; + egw_links; + egw_user; + egw_config; + egw_images; + egw_jsonq; +*/ -/** - * Central object providing all kinds of api services on clientside: - * - preferences: egw.preferences(_name, _app) - * - translation: egw.lang("%1 entries deleted", 5) - * - link registry: egw.open(_id, _app), egw.link_get_registry(_app, _name), egw.link_app_list(_must_support) - * - configuration: egw.config(_name[, _app='phpgwapi']) - * - image urls: egw.image(_name[, _app='phpgwapi']) - * - user data: egw.user(_field) - * - user app data: egw.app(_app[, _name]) - */ -if (window.opener && typeof window.opener.egw == 'object') -{ - egw = window.opener.egw; -} -else if (window.top && typeof window.top.egw == 'object') -{ - egw = window.top.egw; -} -else -{ - egw = { - /** - * Object holding the prefences as 2-dim. associative array, use egw.preference(name[,app]) to access it - * - * @access: private, use egw.preferences() or egw.set_perferences() - */ - prefs: { - common: { - dateformat: "Y-m-d", - timeformat: 24, - lang: "en" - } - }, - - /** - * base-URL of the EGroupware installation - * - * get set via egw_framework::header() - */ - webserverUrl: "/egroupware", - - /** - * Setting prefs for an app or 'common' - * - * @param object _data object with name: value pairs to set - * @param string _app application name, 'common' or undefined to prefes of all apps at once - */ - set_preferences: function(_data, _app) - { - if (typeof _app == 'undefined') - { - this.prefs = _data; - } - else - { - this.prefs[_app] = _data; - } - }, - - /** - * Query an EGroupware user preference - * - * If a prefernce is not already loaded (only done for "common" by default), it is synchroniosly queryed from the server! - * - * @param string _name name of the preference, eg. 'dateformat' - * @param string _app='common' - * @return string preference value - * @todo add a callback to query it asynchron - */ - preference: function(_name, _app) - { - if (typeof _app == 'undefined') _app = 'common'; - - if (typeof this.prefs[_app] == 'undefined') - { - xajax_doXMLHTTPsync('home.egw_framework.ajax_get_preference.template', _app); - - if (typeof this.prefs[_app] == 'undefined') this.prefs[_app] = {}; - } - return this.prefs[_app][_name]; - }, - - /** - * Set a preference and sends it to the server - * - * Server will silently ignore setting preferences, if user has no right to do so! - * - * @param string _app application name or "common" - * @param string _name name of the pref - * @param string _val value of the pref - */ - set_preference: function(_app, _name, _val) - { - xajax_doXMLHTTP('home.egw_framework.ajax_set_preference.template', _app, _name, _val); - - // update own preference cache, if _app prefs are loaded (dont update otherwise, as it would block loading of other _app prefs!) - if (typeof this.prefs[_app] != 'undefined') this.prefs[_app][_name] = _val; - }, - - /** - * Translations - * - * @access: private, use egw.lang() or egw.set_lang_arr() - */ - lang_arr: {}, - - /** - * Set translation for a given application - * - * @param string _app - * @param object _message message => translation pairs - */ - set_lang_arr: function(_app, _messages) - { - this.lang_arr[_app] = _messages; - }, - - /** - * Translate a given phrase replacing optional placeholders - * - * @param string _msg message to translate - * @param string _arg1 ... _argN - */ - lang: function(_msg, _arg1) - { - var translation = _msg; - _msg = _msg.toLowerCase(); - - // search apps in given order for a replacement - var apps = [window.egw_appName, 'etemplate', 'common']; - for(var i = 0; i < apps.length; ++i) - { - if (typeof this.lang_arr[apps[i]] != "undefined" && - typeof this.lang_arr[apps[i]][_msg] != 'undefined') - { - translation = this.lang_arr[apps[i]][_msg]; - break; - } - } - if (arguments.length == 1) return translation; - - if (arguments.length == 2) return translation.replace('%1', arguments[1]); - - // to cope with arguments containing '%2' (eg. an urlencoded path like a referer), - // we first replace all placeholders '%N' with '|%N|' and then we replace all '|%N|' with arguments[N] - translation = translation.replace(/%([0-9]+)/g, '|%$1|'); - for(var i = 1; i < arguments.length; ++i) - { - translation = translation.replace('|%'+i+'|', arguments[i]); - } - return translation; - }, - - /** - * View an EGroupware entry: opens a popup of correct size or redirects window.location to requested url - * - * Examples: - * - egw.open(123,'infolog') or egw.open('infolog:123') opens popup to edit or view (if no edit rights) infolog entry 123 - * - egw.open('infolog:123','timesheet','add') opens popup to add new timesheet linked to infolog entry 123 - * - egw.open(123,'addressbook','view') opens addressbook view for entry 123 (showing linked infologs) - * - egw.open('','addressbook','view_list',{ search: 'Becker' }) opens list of addresses containing 'Becker' - * - * @param string|int id either just the id or "app:id" if app=="" - * @param string app app-name or empty (app is part of id) - * @param string type default "edit", possible "view", "view_list", "edit" (falls back to "view") and "add" - * @param object|string extra extra url parameters to append as object or string - * @param string target target of window to open - */ - open: function(id, app, type, extra, target) - { - if (typeof this.link_registry != 'object') - { - alert('egw.open() link registry is NOT defined!'); - return; - } - if (!app) - { - var app_id = id.split(':',2); - app = app_id[0]; - id = app_id[1]; - } - if (!app || typeof this.link_registry[app] != 'object') - { - alert('egw.open() app "'+app+'" NOT defined in link registry!'); - return; - } - var app_registry = this.link_registry[app]; - if (typeof type == 'undefined') type = 'edit'; - if (type == 'edit' && typeof app_registry.edit == 'undefined') type = 'view'; - if (typeof app_registry[type] == 'undefined') - { - alert('egw.open() type "'+type+'" is NOT defined in link registry for app "'+app+'"!'); - return; - } - var url = this.webserverUrl+'/index.php'; - var delimiter = '?'; - var params = app_registry[type]; - if (type == 'view' || type == 'edit') // add id parameter for type view or edit - { - params[app_registry[type+'_id']] = id; - } - else if (type == 'add' && id) // add add_app and app_id parameters, if given for add - { - var app_id = id.split(':',2); - params[app_registry.add_app] = app_id[0]; - params[app_registry.add_id] = app_id[1]; - } - for(var attr in params) - { - url += delimiter+attr+'='+encodeURIComponent(params[attr]); - delimiter = '&'; - } - if (typeof extra == 'object') - { - for(var attr in extra) - { - url += delimiter+attr+'='+encodeURIComponent(extra[attr]); - } - } - else if (typeof extra == 'string') - { - url += delimiter + extra; - } - if (typeof app_registry[type+'_popup'] == 'undefined') - { - if (target) - { - window.open(url, target); - } - else - { - egw_appWindowOpen(app, url); - } - } - else - { - var w_h = app_registry[type+'_popup'].split('x'); - if (w_h[1] == 'egw_getWindowOuterHeight()') w_h[1] = egw_getWindowOuterHeight(); - egw_openWindowCentered2(url, target, w_h[0], w_h[1], 'yes', app, false); - } - }, - - /** - * Check if $app is in the registry and has an entry for $name - * - * @param string $app app-name - * @param string $name name / key in the registry, eg. 'view' - * @return boolean|string false if $app is not registered, otherwise string with the value for $name - */ - link_get_registry: function(_app, _name) - { - if (typeof this.link_registry[_app] == 'undefined') - { - return false; - } - var reg = this.link_registry[_app]; - - // some defaults (we set them directly in the registry, to do this only once) - if (typeof reg[_name] == 'undefined') - { - switch(_name) - { - case 'name': - reg.name = _app; - break; - case 'icon': - var app_data = this.app(_app); - if (typeof app_data != 'undefined' && typeof app_data.icon != 'undefined') - { - reg.icon = (typeof app_data.icon_app != 'undefined' ? app_data.icon_app : _app)+'/'+app_data.icon; - } - else - { - reg.icon = _app+'/navbar'; - } - break; - } - } - return typeof reg[_name] == 'undefined' ? false : reg[_name]; - }, - - /** - * Get list of link-aware apps the user has rights to use - * - * @param string $must_support capability the apps need to support, eg. 'add', default ''=list all apps - * @return array with app => title pairs - */ - link_app_list: function(_must_support) - { - var apps = []; - for (var type in this.link_registry) - { - var reg = this.link_registry[type]; - - if (typeof _must_support != 'undefined' && _must_support && typeof reg[_must_support] == 'undefined') continue; - - var app_sub = type.split('-'); - if (this.app(app_sub[0])) - { - apps.push({"type": type, "label": this.lang(this.link_get_registry(type,'name'))}); - } - } - // sort labels (caseinsensitive) alphabetic - apps = apps.sort(function(_a,_b) { - var al = _a.label.toUpperCase(); - var bl = _b.label.toUpperCase(); - return al == bl ? 0 : (al > bl ? 1 : -1); - }); - // create sorted associative array / object - var sorted = {}; - for(var i = 0; i < apps.length; ++i) - { - sorted[apps[i].type] = apps[i].label; - } - return sorted; - }, - - /** - * Link registry - * - * @access: private, use egw.open() or egw.set_link_registry() - */ - link_registry: null, - - /** - * Set link registry - * - * @param object _registry whole registry or entries for just one app - * @param string _app - */ - set_link_registry: function (_registry, _app) - { - if (typeof _app == 'undefined') - { - this.link_registry = _registry; - } - else - { - this.link_registry[_app] = _registry; - } - }, - - /** - * Clientside config - * - * @access: private, use egw.config(_name, _app="phpgwapi") - */ - configs: {}, - - /** - * Query clientside config - * - * @param string _name name of config variable - * @param string _app default "phpgwapi" - * @return mixed - */ - config: function (_name, _app) - { - if (typeof _app == 'undefined') _app = 'phpgwapi'; - - if (typeof this.configs[_app] == 'undefined') return null; - - return this.configs[_app][_name]; - }, - - /** - * Set clientside configuration for all apps - * - * @param array/object - */ - set_configs: function(_configs) - { - this.configs = _configs; - }, - - /** - * Map to serverside available images for users template-set - * - * @access: private, use egw.image(_name, _app) - */ - images: {}, - - /** - * Set imagemap, called from /phpgwapi/images.php - * - * @param array/object _images - */ - set_images: function (_images) - { - this.images = _images; - }, - - /** - * Get image URL for a given image-name and application - * - * @param string _name image-name without extension - * @param string _app application name, default current app of window - * @return string with URL of image - */ - image: function (_name, _app) - { - // For logging all paths tried - var tries = {}; - - if (typeof _app == 'undefined') - { - if(_name.indexOf('/') > 0) - { - var split = et2_csvSplit(_value, 2,"/"); - var _app = split[0]; - _name = split[1]; - } - else - { - _app = this.getAppName(); - } - } - - // own instance specific images in vfs have highest precedence - tries['vfs']=_name; - if (typeof this.images['vfs'] != 'undefined' && typeof this.images['vfs'][_name] != 'undefined') - { - return this.webserverUrl+this.images['vfs'][_name]; - } - tries[_app + (_app == 'phpgwapi' ? " (current app)" : "")] = _name; - if (typeof this.images[_app] != 'undefined' && typeof this.images[_app][_name] != 'undefined') - { - return this.webserverUrl+this.images[_app][_name]; - } - tries['phpgwapi'] = _name; - if (typeof this.images['phpgwapi'] != 'undefined' && typeof this.images['phpgwapi'][_name] != 'undefined') - { - return this.webserverUrl+this.images['phpgwapi'][_name]; - } - // if no match, check if it might contain an extension - var matches = []; - if (matches = _name.match(/\.(png|gif|jpg)$/i)) - { - return this.image(_name.replace(/.(png|gif|jpg)$/i,''), _app); - } - if(matches != null) tries[_app + " (matched)"]= matches; - console.log('egw.image("'+_name+'", "'+_app+'") image NOT found! Tried ', tries); - return null; - }, - - /** - * Returns the name of the currently active application - * - * @ToDo: fixme: does not work, as egw object runs in framework for jdots - */ - getAppName: function () - { - if (typeof egw_appName == 'undefined') - { - return 'egroupware'; - } - else - { - return egw_appName; - } - }, - - /** - * Data about current user - * - * @access: private, use egw.user(_field) or egw.app(_app) - */ - userData: {}, - - /** - * Set data of current user - * - * @param object _data - */ - set_user: function(_data) - { - this.userData = _data; - }, - - /** - * Get data about current user - * - * @param string _field - * - 'account_id','account_lid','person_id','account_status', - * - 'account_firstname','account_lastname','account_email','account_fullname','account_phone' - * - 'apps': object with app => data pairs the user has run-rights for - * @return string|array|null - */ - user: function (_field) - { - return this.userData[_field]; - }, - - /** - * Return data of apps the user has rights to run - * - * Can be used the check of run rights like: if (egw.app('addressbook')) { do something if user has addressbook rights } - * - * @param string _app - * @param string _name attribute to return, default return whole app-data-object - * @return object|string|null null if not found - */ - app: function(_app, _name) - { - return typeof _name == 'undefined' || typeof this.userData.apps[_app] == 'undefined' ? - this.userData.apps[_app] : this.userData.apps[_app][_name]; - }, - - /** - * Call a link, which can be either a menuaction, a EGroupware relative url or a full url - * - * @param string _link menuaction, EGroupware relative url or a full url (incl. "mailto:" or "javascript:") - * @param string _target optional target - * @param string _popup widthxheight, if a popup should be used - */ - call_link: function(_link, _target, _popup) - { - var url = _link; - if (url.indexOf('javascript:') == 0) - { - eval(url.substr(11)); - return; - } - // link is not necessary an url, it can also be a menuaction! - if (url.indexOf('/') == -1 && - url.split('.').length >= 3 && - url.indexOf('mailto:') == -1 || - url.indexOf('://') == -1) - { - url = "/index.php?menuaction="+url; - } - if (url[0] == '/') // link relative to eGW - { - url = this.webserverUrl + url; - } - if (_popup) - { - var w_h = _popup.split('x'); - if (w_h[1] == 'egw_getWindowOuterHeight()') w_h[1] = egw_getWindowOuterHeight(); - egw_openWindowCentered2(url, _target, w_h[0], w_h[1]); - } - else - { - window.open(url, _target); - } - }, - - /** - * Generate a url which supports url or cookies based sessions - * - * Please note, the values of the query get url encoded! - * - * @param string _url a url relative to the egroupware install root, it can contain a query too - * @param array|string _extravars query string arguements as string or array (prefered) - * if string is used ambersands in vars have to be already urlencoded as '%26', function ensures they get NOT double encoded - * @return string generated url - */ - link: function(_url, _extravars) - { - if (_url[0] != '/') - { - var app = window.egw_appName; - if (app != 'login' && app != 'logout') _url = app+'/'+_url; - } - // append the url to the webserver url, but avoid more then one slash between the parts of the url - var webserver_url = this.webserverUrl; - // patch inspired by vladimir kolobkov -> we should not try to match the webserver url against the url without '/' as delimiter, - // as webserver_url may be part of _url (as /egw is part of phpgwapi/js/egw_instant_load.html) - if ((_url[0] != '/' || webserver_url != '/') && (!webserver_url || _url.indexOf(webserver_url+'/') == -1)) - { - if(_url[0] != '/' && webserver_url.lastIndexOf('/') != webserver_url.length-1) - { - _url = webserver_url +'/'+ _url; - } - else - { - _url = webserver_url + _url; - } - } - - var vars = {}; - /* not sure we still need to support that - // add session params if not using cookies - if (!$GLOBALS['egw_info']['server']['usecookies']) - { - $vars['sessionid'] = $GLOBALS['egw']->session->sessionid; - $vars['kp3'] = $GLOBALS['egw']->session->kp3; - $vars['domain'] = $GLOBALS['egw']->session->account_domain; - }*/ - - // check if the url already contains a query and ensure that vars is an array and all strings are in extravars - var url_othervars = _url.split('?',2); - _url = url_othervars[0]; - var othervars = url_othervars[1]; - if (_extravars && typeof _extravars == 'object') - { - $j.extend(vars, _extravars); - _extravars = othervars; - } - else - { - if (othervars) _extravars += (_extravars?'&':'').othervars; - } - - // parse extravars string into the vars array - if (_extravars) - { - _extravars = _extravars.split('&'); - for(var i=0; i < _extravars.length; ++i) - { - var name_val = _extravars[i].split('=',2); - var name = name_val[0]; - var val = name_val[1]; - if (val.indexOf('%26') != -1) val = val.replace(/%26/g,'&'); // make sure to not double encode & - if (name.lastIndexOf('[]') == name.length-2) - { - name = name.substr(0,name.length-2); - if (typeof vars[name] == 'undefined') vars[name] = []; - vars[name].push(val); - } - else - { - vars[name] = val; - } - } - } - - // if there are vars, we add them urlencoded to the url - var query = []; - for(var name in vars) - { - var val = vars[name]; - if (typeof val == 'object') - { - for(var i=0; i < val.length; ++i) - { - query.push(name+'[]='+encodeURIComponent(val[i])); - } - } - else - { - query.push(name+'='+encodeURIComponent(val)); - } - } - return query.length ? _url+'?'+query.join('&') : _url; - }, - - /** - * Queued json requests (objects with attributes menuaction, parameters, context, callback, sender and callbeforesend) - * - * @access private, use jsonq method to queue requests - */ - jsonq_queue: {}, - - /** - * Next uid (index) in queue - */ - jsonq_uid: 0, - - /** - * Running timer for next send of queued items - */ - jsonq_timer: null, - - /** - * Send a queued JSON call to the server - * - * @param string _menuaction the menuaction function which should be called and - * which handles the actual request. If the menuaction is a full featured - * url, this one will be used instead. - * @param array _parameters which should be passed to the menuaction function. - * @param function _callback callback function which should be called upon a "data" response is received - * @param object _sender is the reference object the callback function should get - * @param function _callbeforesend optional callback function which can modify the parameters, eg. to do some own queuing - * @return string uid of the queued request - */ - jsonq: function(_menuaction, _parameters, _callback, _sender, _callbeforesend) - { - var uid = 'u'+(this.jsonq_uid++); - this.jsonq_queue[uid] = { - menuaction: _menuaction, - parameters: _parameters, - callback: _callback, - sender: _sender, - callbeforesend: _callbeforesend - }; - - if (this.jsonq_time == null) - { - // check / send queue every N ms - var self = this; - this.jsonq_timer = window.setInterval(function(){ self.jsonq_send();}, 100); - } - return uid; - }, - - /** - * Send the whole job-queue to the server in a single json request with menuaction=queue - */ - jsonq_send: function() - { - if (this.jsonq_uid > 0 && typeof this.jsonq_queue['u'+(this.jsonq_uid-1)] == 'object') - { - var jobs_to_send = {}; - var something_to_send = false; - for(var uid in this.jsonq_queue) - { - var job = this.jsonq_queue[uid]; - - if (job.menuaction == 'send') continue; // already send to server - - // if job has a callbeforesend callback, call it to allow it to modify pararmeters - if (typeof job.callbeforesend == 'function') - { - job.callbeforesend.call(job.sender, job.parameters); - } - jobs_to_send[uid] = { - menuaction: job.menuaction, - parameters: job.parameters - }; - job.menuaction = 'send'; - job.parameters = null; - something_to_send = true; - } - if (something_to_send) - { - new egw_json_request('home.queue', jobs_to_send, this).sendRequest(true, this.jsonq_callback, this); - } - } - }, - - /** - * Dispatch responses received - * - * @param object _data uid => response pairs - */ - jsonq_callback: function(_data) - { - if (typeof _data != 'object') throw "jsonq_callback called with NO object as parameter!"; - - var json = new egw_json_request('none'); - for(var uid in _data) - { - if (typeof this.jsonq_queue[uid] == 'undefined') - { - console.log("jsonq_callback received response for not existing queue uid="+uid+"!"); - console.log(_data[uid]); - continue; - } - var job = this.jsonq_queue[uid]; - var response = _data[uid]; - - // fake egw_json_request object, to call it with the current response - json.callback = job.callback; - json.sender = job.sender; - json.handleResponse({response: response}); - - delete this.jsonq_queue[uid]; - } - // if nothing left in queue, stop interval-timer to give browser a rest - if (this.jsonq_timer && typeof this.jsonq_queue['u'+(this.jsonq_uid-1)] != 'object') - { - window.clearInterval(this.jsonq_timer); - this.jsonq_timer = null; - } - }, - - /** - * Local cache for link-titles - * - * @access private, use egw.link_title(_app, _id[, _callback, _context]) - */ - title_cache: {}, - /** - * Queue for link_title requests - * - * @access private, use egw.link_title(_app, _id[, _callback, _context]) - * @var object _app._id.[{callback: _callback, context: _context}[, ...]] - */ - title_queue: {}, - /** - * Uid of active jsonq request, to not start an other one, as we get notified - * before it's actually send to the server via our link_title_before_send callback. - * @access private - */ - title_uid: null, - - /** - * Query a title of _app/_id - * - * @param string _app - * @param string|int _id - * @param function _callback optinal callback, required if for responses from the server - * @param object _context context for the callback - * @return string|boolean|null string with title if it exist in local cache or null if not - */ - link_title: function(_app, _id, _callback, _context) - { - // check if we have a cached title --> return it direct - if (typeof this.title_cache[_app] != 'undefined' && typeof this.title_cache[_app][_id] != 'undefined') - { - if (typeof _callback == 'function') - { - _callback.call(_context, this.title_cache[_app][_id]); - } - return this.title_cache[_app][_id]; - } - // no callback --> return null - if (typeof _callback != 'function') - { - return null; // not found in local cache and cant do a synchronious request - } - // queue the request - if (typeof this.title_queue[_app] == 'undefined') - { - this.title_queue[_app] = {}; - } - if (typeof this.title_queue[_app][_id] == 'undefined') - { - this.title_queue[_app][_id] = []; - } - this.title_queue[_app][_id].push({callback: _callback, context: _context}); - // if there's no active jsonq request, start a new one - if (this.title_uid == null) - { - this.title_uid = this.jsonq(_app+'.etemplate_widget_link.ajax_link_titles.etemplate',[{}], this.link_title_callback, this, this.link_title_before_send); - } - }, - - /** - * Callback to add all current title requests - * - * @param array of parameters, only first parameter is used - */ - link_title_before_send: function(_params) - { - // add all current title-requests - for(var app in this.title_queue) - { - if (typeof _params[0][app] == 'undefined') - { - _params[0][app] = []; - } - for(var id in this.title_queue[app]) - { - _params[0][app].push(id); - } - } - this.title_uid = null; // allow next request to jsonq - }, - - /** - * Callback for server response - * - * @param object _response _app => _id => title - */ - link_title_callback: function(_response) - { - if (typeof _response != 'object') - { - throw "Wrong parameter for egw.link_title_callback!"; - } - for(var app in _response) - { - if (typeof this.title_cache[app] != 'object') - { - this.title_cache[app] = {}; - } - for (var id in _response[app]) - { - var title = _response[app][id]; - // cache locally - this.title_cache[app][id] = title; - // call callbacks waiting for title of app/id - for(var i=0; i < this.title_queue[app][id].length; ++i) - { - var callback = this.title_queue[app][id][i]; - callback.callback.call(callback.context, title); - } - delete this.title_queue[app][id]; - } - } - } - }; -} diff --git a/phpgwapi/js/jsapi/egw_app.js b/phpgwapi/js/jsapi/egw_app.js new file mode 100644 index 0000000000..617f19e801 --- /dev/null +++ b/phpgwapi/js/jsapi/egw_app.js @@ -0,0 +1,26 @@ +/** + * EGroupware clientside API object + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package etemplate + * @subpackage api + * @link http://www.egroupware.org + * @author Andreas Stöckel (as AT stylite.de) + * @author Ralf Becker + * @version $Id$ + */ + +"use strict"; + +/*egw:uses + egw_core; +*/ + +egw.extend('jsonq', function() { + + return { + + }; + +}); + diff --git a/phpgwapi/js/jsapi/egw_config.js b/phpgwapi/js/jsapi/egw_config.js new file mode 100644 index 0000000000..421786e92d --- /dev/null +++ b/phpgwapi/js/jsapi/egw_config.js @@ -0,0 +1,56 @@ +/** + * EGroupware clientside API object + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package etemplate + * @subpackage api + * @link http://www.egroupware.org + * @author Andreas Stöckel (as AT stylite.de) + * @author Ralf Becker + * @version $Id$ + */ + +"use strict"; + +/*egw:uses + egw_core; +*/ + +egw().extend('config', function() { + + /** + * Clientside config + * + * @access: private, use egw.config(_name, _app="phpgwapi") + */ + var configs = {}; + + return { + /** + * Query clientside config + * + * @param string _name name of config variable + * @param string _app default "phpgwapi" + * @return mixed + */ + config: function (_name, _app) + { + if (typeof _app == 'undefined') _app = 'phpgwapi'; + + if (typeof configs[_app] == 'undefined') return null; + + return configs[_app][_name]; + }, + + /** + * Set clientside configuration for all apps + * + * @param array/object + */ + set_configs: function(_configs) + { + this.configs = _configs; + } + }; +}); + diff --git a/phpgwapi/js/jsapi/egw_core.js b/phpgwapi/js/jsapi/egw_core.js new file mode 100644 index 0000000000..f3d85242af --- /dev/null +++ b/phpgwapi/js/jsapi/egw_core.js @@ -0,0 +1,237 @@ +/** + * EGroupware clientside API object + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package etemplate + * @subpackage api + * @link http://www.egroupware.org + * @author Andreas Stöckel (as AT stylite.de) + * @author Ralf Becker + * @version $Id$ + */ + +"use strict"; + +var egw; + +/** + * IE Fix for array.indexOf + */ +if (typeof Array.prototype.indexOf == "undefined") +{ + Array.prototype.indexOf = function(_elem) { + for (var i = 0; i < this.length; i++) + { + if (this[i] === _elem) + return i; + } + return -1; + }; +} + +/** + * This code setups the egw namespace and adds the "extend" function, which is + * used by extension modules to inject their content into the egw object. + */ +(function(_parent) { + + // Some local functions for cloning and merging javascript objects + function cloneObject(_obj) { + var result = {}; + + for (var key in _obj) + { + result[key] = _obj[key]; + } + + return result; + } + + function mergeObjects(_to, _from) { + // Extend the egw object + for (var key in _from) + { + if (typeof _to[key] === 'undefined') + { + _to[key] = _from[key]; + } + } + } + + if (window.opener && typeof window.opener.egw !== 'undefined') + { + egw = window.opener.egw; + } + else if (window.top && typeof window.top.egw !== 'undefined') + { + egw = window.top.egw; + } + else + { + /** + * EGW_DEBUGLEVEL specifies which messages are printed to the console. + * Decrease the value of EGW_DEBUGLEVEL to get less messages. + */ + var EGW_DEBUGLEVEL = 4; + + /** + * Modules contains all currently loaded egw extension modules. + */ + var modules = []; + + var localEgw = {}; + + var globalEgw = { + + /** + * Name of the application the egw object belongs to. + */ + appName: null, + + /** + * Returns the current application name. The current application + * name equals the name, which was given when calling the egw + * function. If the getAppName function is called on the global + * instance, 'etemplate' is returned. + */ + getAppName: function() { + // Return the default application name if this function is + // called on the global egw instance. + if (!this.appName) { + return 'etemplate'; + } + + // Otherwise return the correct application name. + return this.appName; + }, + + /** + * base-URL of the EGroupware installation + * + * get set via egw_framework::header() + */ + webserverUrl: "/egroupware", + + /** + * The extend function can be used to extend the egw object. + * + * @param _module should be a string containing the name of the new + * module. + * @param _code should be a function, which returns an object that + * should extend the egw object. + */ + extend: function(_module, _code) { + + // Check whether the given module has already been loaded. + if (modules.indexOf(_module) < 0) { + + // Call the function specified by "_code" which returns + // nothing but an object containing the extension. + var content = _code.call(this); + + // Merge the extension into the global egw object (used for + // cloning) + mergeObjects(globalEgw, content); + + // Merge the extension into the egw function + mergeObjects(egw, content); + + // Merge the extension into the local egw object + for (var key in localEgw) { + mergeObjects(localEgw[key], content); + } + + // Register the module as loaded + modules.push(_module); + } + }, + + /** + * The debug function can be used to send a debug message to the + * java script console. The first parameter specifies the debug + * level, all other parameters are passed to the corresponding + * console function. + */ + debug: function(_level) { + if (typeof console != "undefined") + { + // Get the passed parameters and remove the first entry + var args = []; + for (var i = 1; i < arguments.length; i++) + { + args.push(arguments[i]); + } + + if (_level == "log" && EGW_DEBUGLEVEL >= 4 && + typeof console.log == "function") + { + console.log.apply(console, args); + } + + if (_level == "info" && EGW_DEBUGLEVEL >= 3 && + typeof console.info == "function") + { + console.info.apply(console, args); + } + + if (_level == "warn" && EGW_DEBUGLEVEL >= 2 && + typeof console.warn == "function") + { + console.warn.apply(console, args); + } + + if (_level == "error" && EGW_DEBUGLEVEL >= 1 && + typeof console.error == "function") + { + console.error.apply(console, args); + } + } + } + }; + + /** + * The egw function returns an instance of the client side api. If no + * parameter is given, an egw istance, which is not bound to a certain + * application is returned. + */ + egw = function(_app) { + + // If no argument is given, simply return the global egw object, or + // check whether 'window.egw_appName' is set correctly. + if (typeof _app === 'undefined') + { + // TODO: Remove this code, window.egw_appName will be removed + // in the future. + if (typeof window.egw_appName == 'string') + { + _app = window.egw_appName; + } + else + { + return egw; + } + } + + if (typeof _app == 'string') + { + // If a argument is given, this represents the current application + // name. Check whether we already have a copy of the egw object for + // that application. If yes, return it. + if (typeof localEgw[_app] === 'undefined') + { + // Otherwise clone the global egw object, set the application + // name and return it + localEgw[_app] = cloneObject(globalEgw); + localEgw[_app].appName = _app; + } + + return localEgw[_app]; + } + + this.debug("error", "Non-string argument given to the egw function."); + } + + mergeObjects(egw, globalEgw); + } +})(); + diff --git a/phpgwapi/js/jsapi/egw_images.js b/phpgwapi/js/jsapi/egw_images.js new file mode 100644 index 0000000000..6a169c0a74 --- /dev/null +++ b/phpgwapi/js/jsapi/egw_images.js @@ -0,0 +1,93 @@ +/** + * EGroupware clientside API object + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package etemplate + * @subpackage api + * @link http://www.egroupware.org + * @author Andreas Stöckel (as AT stylite.de) + * @author Ralf Becker + * @version $Id$ + */ + +"use strict"; + +/*egw:uses + egw_core; +*/ + +egw().extend('images', function() { + + /** + * Map to serverside available images for users template-set + * + * @access: private, use egw.image(_name, _app) + */ + var images = {}; + + return { + /** + * Set imagemap, called from /phpgwapi/images.php + * + * @param array/object _images + */ + set_images: function (_images) + { + images = _images; + }, + + /** + * Get image URL for a given image-name and application + * + * @param string _name image-name without extension + * @param string _app application name, default current app of window + * @return string with URL of image + */ + image: function (_name, _app) + { + // For logging all paths tried + var tries = {}; + + if (typeof _app == 'undefined') + { + if(_name.indexOf('/') > 0) + { + var split = et2_csvSplit(_value, 2,"/"); + var _app = split[0]; + _name = split[1]; + } + else + { + _app = this.getAppName(); + } + } + + // own instance specific images in vfs have highest precedence + tries['vfs']=_name; + if (typeof images['vfs'] != 'undefined' && typeof images['vfs'][_name] != 'undefined') + { + return this.webserverUrl+images['vfs'][_name]; + } + tries[_app + (_app == 'phpgwapi' ? " (current app)" : "")] = _name; + if (typeof images[_app] != 'undefined' && typeof images[_app][_name] != 'undefined') + { + return this.webserverUrl+images[_app][_name]; + } + tries['phpgwapi'] = _name; + if (typeof images['phpgwapi'] != 'undefined' && typeof images['phpgwapi'][_name] != 'undefined') + { + return this.webserverUrl+images['phpgwapi'][_name]; + } + // if no match, check if it might contain an extension + var matches = []; + if (matches = _name.match(/\.(png|gif|jpg)$/i)) + { + return this.image(_name.replace(/.(png|gif|jpg)$/i,''), _app); + } + if(matches != null) tries[_app + " (matched)"]= matches; + console.log('egw.image("'+_name+'", "'+_app+'") image NOT found! Tried ', tries); + return null; + } + } +}); + diff --git a/phpgwapi/js/jsapi/egw_jsonq.js b/phpgwapi/js/jsapi/egw_jsonq.js new file mode 100644 index 0000000000..ec33ef55ab --- /dev/null +++ b/phpgwapi/js/jsapi/egw_jsonq.js @@ -0,0 +1,147 @@ +/** + * EGroupware clientside API object + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package etemplate + * @subpackage api + * @link http://www.egroupware.org + * @author Andreas Stöckel (as AT stylite.de) + * @author Ralf Becker + * @version $Id$ + */ + +"use strict"; + +/*egw:uses + egw_core; +*/ + +egw().extend('jsonq', function() { + + /** + * Queued json requests (objects with attributes menuaction, parameters, context, callback, sender and callbeforesend) + * + * @access private, use jsonq method to queue requests + */ + var jsonq_queue = {}; + + /** + * Next uid (index) in queue + */ + var jsonq_uid = 0; + + /** + * Running timer for next send of queued items + */ + var jsonq_timer = null; + + return { + /** + * Send a queued JSON call to the server + * + * @param string _menuaction the menuaction function which should be called and + * which handles the actual request. If the menuaction is a full featured + * url, this one will be used instead. + * @param array _parameters which should be passed to the menuaction function. + * @param function _callback callback function which should be called upon a "data" response is received + * @param object _sender is the reference object the callback function should get + * @param function _callbeforesend optional callback function which can modify the parameters, eg. to do some own queuing + * @return string uid of the queued request + */ + jsonq: function(_menuaction, _parameters, _callback, _sender, _callbeforesend) + { + var uid = 'u'+(jsonq_uid++); + jsonq_queue[uid] = { + menuaction: _menuaction, + parameters: _parameters, + callback: _callback, + sender: _sender, + callbeforesend: _callbeforesend + }; + + if (jsonq_timer == null) + { + // check / send queue every N ms + var self = this; + jsonq_timer = window.setInterval(function(){ + self.jsonq_send(); + }, 100); + } + return uid; + }, + + /** + * Send the whole job-queue to the server in a single json request with menuaction=queue + */ + jsonq_send: function() + { + if (jsonq_uid > 0 && typeof jsonq_queue['u'+(jsonq_uid-1)] == 'object') + { + var jobs_to_send = {}; + var something_to_send = false; + for(var uid in jsonq_queue) + { + var job = jsonq_queue[uid]; + + if (job.menuaction == 'send') continue; // already send to server + + // if job has a callbeforesend callback, call it to allow it to modify pararmeters + if (typeof job.callbeforesend == 'function') + { + job.callbeforesend.call(job.sender, job.parameters); + } + jobs_to_send[uid] = { + menuaction: job.menuaction, + parameters: job.parameters + }; + job.menuaction = 'send'; + job.parameters = null; + something_to_send = true; + } + if (something_to_send) + { + new egw_json_request('home.queue', jobs_to_send, this).sendRequest(true, this.jsonq_callback, this); + } + } + }, + + /** + * Dispatch responses received + * + * @param object _data uid => response pairs + */ + jsonq_callback: function(_data) + { + if (typeof _data != 'object') throw "jsonq_callback called with NO object as parameter!"; + + var json = new egw_json_request('none'); + for(var uid in _data) + { + if (typeof jsonq_queue[uid] == 'undefined') + { + console.log("jsonq_callback received response for not existing queue uid="+uid+"!"); + console.log(_data[uid]); + continue; + } + var job = jsonq_queue[uid]; + var response = _data[uid]; + + // fake egw_json_request object, to call it with the current response + json.callback = job.callback; + json.sender = job.sender; + json.handleResponse({response: response}); + + delete jsonq_queue[uid]; + } + // if nothing left in queue, stop interval-timer to give browser a rest + if (jsonq_timer && typeof jsonq_queue['u'+(jsonq_uid-1)] != 'object') + { + window.clearInterval(jsonq_timer); + jsonq_timer = null; + } + }, + }; + +}); + + diff --git a/phpgwapi/js/jsapi/egw_lang.js b/phpgwapi/js/jsapi/egw_lang.js new file mode 100644 index 0000000000..86174deb1e --- /dev/null +++ b/phpgwapi/js/jsapi/egw_lang.js @@ -0,0 +1,79 @@ +/** + * EGroupware clientside API object + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package etemplate + * @subpackage api + * @link http://www.egroupware.org + * @author Andreas Stöckel (as AT stylite.de) + * @author Ralf Becker + * @version $Id$ + */ + +"use strict"; + +/*egw:uses + egw_core; +*/ + +egw().extend('lang', function() { + + /** + * Translations + * + * @access: private, use egw.lang() or egw.set_lang_arr() + */ + var lang_arr = {}; + + // Return the actual extension + return { + /** + * Set translation for a given application + * + * @param string _app + * @param object _message message => translation pairs + */ + set_lang_arr: function(_app, _messages) + { + lang_arr[_app] = _messages; + }, + + /** + * Translate a given phrase replacing optional placeholders + * + * @param string _msg message to translate + * @param string _arg1 ... _argN + */ + lang: function(_msg, _arg1) + { + var translation = _msg; + _msg = _msg.toLowerCase(); + + // search apps in given order for a replacement + var apps = [this.getAppName(), 'etemplate', 'common']; + for(var i = 0; i < apps.length; ++i) + { + if (typeof lang_arr[apps[i]] != "undefined" && + typeof lang_arr[apps[i]][_msg] != 'undefined') + { + translation = lang_arr[apps[i]][_msg]; + break; + } + } + if (arguments.length == 1) return translation; + + if (arguments.length == 2) return translation.replace('%1', arguments[1]); + + // to cope with arguments containing '%2' (eg. an urlencoded path like a referer), + // we first replace all placeholders '%N' with '|%N|' and then we replace all '|%N|' with arguments[N] + translation = translation.replace(/%([0-9]+)/g, '|%$1|'); + for(var i = 1; i < arguments.length; ++i) + { + translation = translation.replace('|%'+i+'|', arguments[i]); + } + return translation; + } + }; + +}); + diff --git a/phpgwapi/js/jsapi/egw_links.js b/phpgwapi/js/jsapi/egw_links.js new file mode 100644 index 0000000000..621cf45336 --- /dev/null +++ b/phpgwapi/js/jsapi/egw_links.js @@ -0,0 +1,472 @@ +/** + * EGroupware clientside API object + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package etemplate + * @subpackage api + * @link http://www.egroupware.org + * @author Andreas Stöckel (as AT stylite.de) + * @author Ralf Becker + * @version $Id$ + */ + +"use strict"; + +/*egw:uses + egw_core; + egw_link; +*/ + +egw().extend('links', function() { + + /** + * Link registry + * + * @access: private, use egw.open() or egw.set_link_registry() + */ + var link_registry = null; + + /** + * Local cache for link-titles + * + * @access private, use egw.link_title(_app, _id[, _callback, _context]) + */ + var title_cache = {}; + + /** + * Queue for link_title requests + * + * @access private, use egw.link_title(_app, _id[, _callback, _context]) + * @var object _app._id.[{callback: _callback, context: _context}[, ...]] + */ + var title_queue = {}; + + /** + * Uid of active jsonq request, to not start an other one, as we get notified + * before it's actually send to the server via our link_title_before_send callback. + * @access private + */ + var title_uid = null; + + return { + /** + * View an EGroupware entry: opens a popup of correct size or redirects window.location to requested url + * + * Examples: + * - egw.open(123,'infolog') or egw.open('infolog:123') opens popup to edit or view (if no edit rights) infolog entry 123 + * - egw.open('infolog:123','timesheet','add') opens popup to add new timesheet linked to infolog entry 123 + * - egw.open(123,'addressbook','view') opens addressbook view for entry 123 (showing linked infologs) + * - egw.open('','addressbook','view_list',{ search: 'Becker' }) opens list of addresses containing 'Becker' + * + * @param string|int id either just the id or "app:id" if app=="" + * @param string app app-name or empty (app is part of id) + * @param string type default "edit", possible "view", "view_list", "edit" (falls back to "view") and "add" + * @param object|string extra extra url parameters to append as object or string + * @param string target target of window to open + */ + open: function(id, app, type, extra, target) + { + if (typeof link_registry != 'object') + { + alert('egw.open() link registry is NOT defined!'); + return; + } + if (!app) + { + var app_id = id.split(':',2); + app = app_id[0]; + id = app_id[1]; + } + if (!app || typeof link_registry[app] != 'object') + { + alert('egw.open() app "'+app+'" NOT defined in link registry!'); + return; + } + var app_registry = link_registry[app]; + if (typeof type == 'undefined') type = 'edit'; + if (type == 'edit' && typeof app_registry.edit == 'undefined') type = 'view'; + if (typeof app_registry[type] == 'undefined') + { + alert('egw.open() type "'+type+'" is NOT defined in link registry for app "'+app+'"!'); + return; + } + var url = this.webserverUrl+'/index.php'; + var delimiter = '?'; + var params = app_registry[type]; + if (type == 'view' || type == 'edit') // add id parameter for type view or edit + { + params[app_registry[type+'_id']] = id; + } + else if (type == 'add' && id) // add add_app and app_id parameters, if given for add + { + var app_id = id.split(':',2); + params[app_registry.add_app] = app_id[0]; + params[app_registry.add_id] = app_id[1]; + } + for(var attr in params) + { + url += delimiter+attr+'='+encodeURIComponent(params[attr]); + delimiter = '&'; + } + if (typeof extra == 'object') + { + for(var attr in extra) + { + url += delimiter+attr+'='+encodeURIComponent(extra[attr]); + } + } + else if (typeof extra == 'string') + { + url += delimiter + extra; + } + if (typeof app_registry[type+'_popup'] == 'undefined') + { + if (target) + { + window.open(url, target); + } + else + { + egw_appWindowOpen(app, url); + } + } + else + { + var w_h = app_registry[type+'_popup'].split('x'); + if (w_h[1] == 'egw_getWindowOuterHeight()') w_h[1] = egw_getWindowOuterHeight(); + egw_openWindowCentered2(url, target, w_h[0], w_h[1], 'yes', app, false); + } + }, + + /** + * Check if $app is in the registry and has an entry for $name + * + * @param string $app app-name + * @param string $name name / key in the registry, eg. 'view' + * @return boolean|string false if $app is not registered, otherwise string with the value for $name + */ + link_get_registry: function(_app, _name) + { + if (typeof link_registry[_app] == 'undefined') + { + return false; + } + var reg = link_registry[_app]; + + // some defaults (we set them directly in the registry, to do this only once) + if (typeof reg[_name] == 'undefined') + { + switch(_name) + { + case 'name': + reg.name = _app; + break; + case 'icon': + var app_data = this.app(_app); + if (typeof app_data != 'undefined' && typeof app_data.icon != 'undefined') + { + reg.icon = (typeof app_data.icon_app != 'undefined' ? app_data.icon_app : _app)+'/'+app_data.icon; + } + else + { + reg.icon = _app+'/navbar'; + } + break; + } + } + return typeof reg[_name] == 'undefined' ? false : reg[_name]; + }, + + /** + * Get list of link-aware apps the user has rights to use + * + * @param string $must_support capability the apps need to support, eg. 'add', default ''=list all apps + * @return array with app => title pairs + */ + link_app_list: function(_must_support) + { + var apps = []; + for (var type in link_registry) + { + var reg = link_registry[type]; + + if (typeof _must_support != 'undefined' && _must_support && typeof reg[_must_support] == 'undefined') continue; + + var app_sub = type.split('-'); + if (this.app(app_sub[0])) + { + apps.push({"type": type, "label": this.lang(this.link_get_registry(type,'name'))}); + } + } + // sort labels (caseinsensitive) alphabetic + apps = apps.sort(function(_a,_b) { + var al = _a.label.toUpperCase(); + var bl = _b.label.toUpperCase(); + return al == bl ? 0 : (al > bl ? 1 : -1); + }); + // create sorted associative array / object + var sorted = {}; + for(var i = 0; i < apps.length; ++i) + { + sorted[apps[i].type] = apps[i].label; + } + return sorted; + }, + + /** + * Set link registry + * + * @param object _registry whole registry or entries for just one app + * @param string _app + */ + set_link_registry: function (_registry, _app) + { + if (typeof _app == 'undefined') + { + link_registry = _registry; + } + else + { + link_registry[_app] = _registry; + } + }, + + /** + * Call a link, which can be either a menuaction, a EGroupware relative url or a full url + * + * @param string _link menuaction, EGroupware relative url or a full url (incl. "mailto:" or "javascript:") + * @param string _target optional target + * @param string _popup widthxheight, if a popup should be used + */ + call_link: function(_link, _target, _popup) + { + var url = _link; + if (url.indexOf('javascript:') == 0) + { + eval(url.substr(11)); + return; + } + // link is not necessary an url, it can also be a menuaction! + if (url.indexOf('/') == -1 && + url.split('.').length >= 3 && + url.indexOf('mailto:') == -1 || + url.indexOf('://') == -1) + { + url = "/index.php?menuaction="+url; + } + if (url[0] == '/') // link relative to eGW + { + url = this.webserverUrl + url; + } + if (_popup) + { + var w_h = _popup.split('x'); + if (w_h[1] == 'egw_getWindowOuterHeight()') w_h[1] = egw_getWindowOuterHeight(); + egw_openWindowCentered2(url, _target, w_h[0], w_h[1]); + } + else + { + window.open(url, _target); + } + }, + + /** + * Generate a url which supports url or cookies based sessions + * + * Please note, the values of the query get url encoded! + * + * @param string _url a url relative to the egroupware install root, it can contain a query too + * @param array|string _extravars query string arguements as string or array (prefered) + * if string is used ambersands in vars have to be already urlencoded as '%26', function ensures they get NOT double encoded + * @return string generated url + */ + link: function(_url, _extravars) + { + if (_url[0] != '/') + { + var app = window.egw_appName; + if (app != 'login' && app != 'logout') _url = app+'/'+_url; + } + // append the url to the webserver url, but avoid more then one slash between the parts of the url + var webserver_url = this.webserverUrl; + // patch inspired by vladimir kolobkov -> we should not try to match the webserver url against the url without '/' as delimiter, + // as webserver_url may be part of _url (as /egw is part of phpgwapi/js/egw_instant_load.html) + if ((_url[0] != '/' || webserver_url != '/') && (!webserver_url || _url.indexOf(webserver_url+'/') == -1)) + { + if(_url[0] != '/' && webserver_url.lastIndexOf('/') != webserver_url.length-1) + { + _url = webserver_url +'/'+ _url; + } + else + { + _url = webserver_url + _url; + } + } + + var vars = {}; + /* not sure we still need to support that + // add session params if not using cookies + if (!$GLOBALS['egw_info']['server']['usecookies']) + { + $vars['sessionid'] = $GLOBALS['egw']->session->sessionid; + $vars['kp3'] = $GLOBALS['egw']->session->kp3; + $vars['domain'] = $GLOBALS['egw']->session->account_domain; + }*/ + + // check if the url already contains a query and ensure that vars is an array and all strings are in extravars + var url_othervars = _url.split('?',2); + _url = url_othervars[0]; + var othervars = url_othervars[1]; + if (_extravars && typeof _extravars == 'object') + { + $j.extend(vars, _extravars); + _extravars = othervars; + } + else + { + if (othervars) _extravars += (_extravars?'&':'').othervars; + } + + // parse extravars string into the vars array + if (_extravars) + { + _extravars = _extravars.split('&'); + for(var i=0; i < _extravars.length; ++i) + { + var name_val = _extravars[i].split('=',2); + var name = name_val[0]; + var val = name_val[1]; + if (val.indexOf('%26') != -1) val = val.replace(/%26/g,'&'); // make sure to not double encode & + if (name.lastIndexOf('[]') == name.length-2) + { + name = name.substr(0,name.length-2); + if (typeof vars[name] == 'undefined') vars[name] = []; + vars[name].push(val); + } + else + { + vars[name] = val; + } + } + } + + // if there are vars, we add them urlencoded to the url + var query = []; + for(var name in vars) + { + var val = vars[name]; + if (typeof val == 'object') + { + for(var i=0; i < val.length; ++i) + { + query.push(name+'[]='+encodeURIComponent(val[i])); + } + } + else + { + query.push(name+'='+encodeURIComponent(val)); + } + } + return query.length ? _url+'?'+query.join('&') : _url; + }, + + /** + * Query a title of _app/_id + * + * @param string _app + * @param string|int _id + * @param function _callback optinal callback, required if for responses from the server + * @param object _context context for the callback + * @return string|boolean|null string with title if it exist in local cache or null if not + */ + link_title: function(_app, _id, _callback, _context) + { + // check if we have a cached title --> return it direct + if (typeof title_cache[_app] != 'undefined' && typeof title_cache[_app][_id] != 'undefined') + { + if (typeof _callback == 'function') + { + _callback.call(_context, title_cache[_app][_id]); + } + return title_cache[_app][_id]; + } + // no callback --> return null + if (typeof _callback != 'function') + { + return null; // not found in local cache and cant do a synchronious request + } + // queue the request + if (typeof title_queue[_app] == 'undefined') + { + title_queue[_app] = {}; + } + if (typeof title_queue[_app][_id] == 'undefined') + { + title_queue[_app][_id] = []; + } + title_queue[_app][_id].push({callback: _callback, context: _context}); + // if there's no active jsonq request, start a new one + if (title_uid == null) + { + title_uid = this.jsonq(_app+'.etemplate_widget_link.ajax_link_titles.etemplate',[{}], this.link_title_callback, this, this.link_title_before_send); + } + }, + + /** + * Callback to add all current title requests + * + * @param array of parameters, only first parameter is used + */ + link_title_before_send: function(_params) + { + // add all current title-requests + for(var app in title_queue) + { + if (typeof _params[0][app] == 'undefined') + { + _params[0][app] = []; + } + for(var id in title_queue[app]) + { + _params[0][app].push(id); + } + } + title_uid = null; // allow next request to jsonq + }, + + /** + * Callback for server response + * + * @param object _response _app => _id => title + */ + link_title_callback: function(_response) + { + if (typeof _response != 'object') + { + throw "Wrong parameter for egw.link_title_callback!"; + } + for(var app in _response) + { + if (typeof title_cache[app] != 'object') + { + title_cache[app] = {}; + } + for (var id in _response[app]) + { + var title = _response[app][id]; + // cache locally + title_cache[app][id] = title; + // call callbacks waiting for title of app/id + for(var i=0; i < title_queue[app][id].length; ++i) + { + var callback = title_queue[app][id][i]; + callback.callback.call(callback.context, title); + } + delete title_queue[app][id]; + } + } + } + }; + +}); + diff --git a/phpgwapi/js/jsapi/egw_preferences.js b/phpgwapi/js/jsapi/egw_preferences.js new file mode 100644 index 0000000000..6d07943a87 --- /dev/null +++ b/phpgwapi/js/jsapi/egw_preferences.js @@ -0,0 +1,98 @@ +/** + * EGroupware clientside API object + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package etemplate + * @subpackage api + * @link http://www.egroupware.org + * @author Andreas Stöckel (as AT stylite.de) + * @author Ralf Becker + * @version $Id$ + */ + +"use strict"; + +/*egw:uses + egw_core; +*/ + +egw().extend('preferences', function() { + + /** + * Object holding the prefences as 2-dim. associative array, use + * egw.preference(name[,app]) to access it. + * + * @access: private, use egw.preferences() or egw.set_perferences() + */ + var prefs = { + common: { + dateformat: "Y-m-d", + timeformat: 24, + lang: "en" + } + }; + + // Return the actual extension + return { + /** + * Setting prefs for an app or 'common' + * + * @param object _data object with name: value pairs to set + * @param string _app application name, 'common' or undefined to prefes of all apps at once + */ + set_preferences: function(_data, _app) + { + if (typeof _app == 'undefined') + { + prefs = _data; + } + else + { + prefs[_app] = _data; + } + }, + + /** + * Query an EGroupware user preference + * + * If a prefernce is not already loaded (only done for "common" by default), it is synchroniosly queryed from the server! + * + * @param string _name name of the preference, eg. 'dateformat' + * @param string _app='common' + * @return string preference value + * @todo add a callback to query it asynchron + */ + preference: function(_name, _app) + { + if (typeof _app == 'undefined') _app = 'common'; + + if (typeof prefs[_app] == 'undefined') + { + xajax_doXMLHTTPsync('home.egw_framework.ajax_get_preference.template', _app); + + if (typeof prefs[_app] == 'undefined') prefs[_app] = {}; + } + return prefs[_app][_name]; + }, + + /** + * Set a preference and sends it to the server + * + * Server will silently ignore setting preferences, if user has no right to do so! + * + * @param string _app application name or "common" + * @param string _name name of the pref + * @param string _val value of the pref + */ + set_preference: function(_app, _name, _val) + { + // TODO: Queue + xajax_doXMLHTTP('home.egw_framework.ajax_set_preference.template', _app, _name, _val); + + // update own preference cache, if _app prefs are loaded (dont update otherwise, as it would block loading of other _app prefs!) + if (typeof prefs[_app] != 'undefined') prefs[_app][_name] = _val; + } + }; + +}); + diff --git a/phpgwapi/js/jsapi/egw_user.js b/phpgwapi/js/jsapi/egw_user.js new file mode 100644 index 0000000000..8994f66f6d --- /dev/null +++ b/phpgwapi/js/jsapi/egw_user.js @@ -0,0 +1,69 @@ +/** + * EGroupware clientside API object + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package etemplate + * @subpackage api + * @link http://www.egroupware.org + * @author Andreas Stöckel (as AT stylite.de) + * @author Ralf Becker + * @version $Id$ + */ + +"use strict"; + +/*egw:uses + egw_core; +*/ + +egw().extend('user', function() { + + /** + * Data about current user + * + * @access: private, use egw.user(_field) or egw.app(_app) + */ + var userData = {}; + + return { + /** + * Set data of current user + * + * @param object _data + */ + set_user: function(_data) + { + this.userData = _data; + }, + + /** + * Get data about current user + * + * @param string _field + * - 'account_id','account_lid','person_id','account_status', + * - 'account_firstname','account_lastname','account_email','account_fullname','account_phone' + * - 'apps': object with app => data pairs the user has run-rights for + * @return string|array|null + */ + user: function (_field) + { + return this.userData[_field]; + }, + + /** + * Return data of apps the user has rights to run + * + * Can be used the check of run rights like: if (egw.app('addressbook')) { do something if user has addressbook rights } + * + * @param string _app + * @param string _name attribute to return, default return whole app-data-object + * @return object|string|null null if not found + */ + app: function(_app, _name) + { + return typeof _name == 'undefined' || typeof this.userData.apps[_app] == 'undefined' ? + this.userData.apps[_app] : this.userData.apps[_app][_name]; + } + }; + +}); diff --git a/phpgwapi/templates/idots/css/traditional.css b/phpgwapi/templates/idots/css/traditional.css index f0ca47c496..715bf5fe96 100755 --- a/phpgwapi/templates/idots/css/traditional.css +++ b/phpgwapi/templates/idots/css/traditional.css @@ -857,3 +857,4 @@ td.lettersearch { border-left: 2px solid black; border-right: 2px solid black; } +