* Addressbook: Open CRM views into individual tabs

This commit is contained in:
Hadi Nategh 2020-10-30 17:21:37 +01:00
parent 84579ced53
commit f55fae6a3b
25 changed files with 354 additions and 120 deletions

View File

@ -38,7 +38,7 @@ class addressbook_hooks
{ {
display_sidebox($appname, lang('Contact data'), array( display_sidebox($appname, lang('Contact data'), array(
array( array(
'text' => '<div id="addressbook_view_sidebox"/>', 'text' => '<div id="'.self::getViewDOMID($_GET['contact_id'], $_GET['crm_list']).'" class="addressbook_view_sidebox"/>',
'no_lang' => true, 'no_lang' => true,
'link' => false, 'link' => false,
'icon' => false, 'icon' => false,
@ -102,6 +102,17 @@ class addressbook_hooks
} }
} }
/**
* Generate unique Id for addressbook view sidebox
* @param $contact_id
* @param $view
* @return string
*/
static function getViewDOMID($contact_id, $view)
{
return 'addressbook_'.$contact_id.'_'.$view.'_view_sidebox';
}
/** /**
* populates $settings for the Api\Preferences * populates $settings for the Api\Preferences
* *
@ -352,7 +363,9 @@ class addressbook_hooks
'titles' => 'api.EGroupware\\Api\\Contacts.link_titles', 'titles' => 'api.EGroupware\\Api\\Contacts.link_titles',
'view' => array( 'view' => array(
'menuaction' => 'addressbook.addressbook_ui.view', 'menuaction' => 'addressbook.addressbook_ui.view',
'ajax' => 'true' 'ajax' => 'true',
'target' => 'tab',
'crm_list' => 'infolog'
), ),
'view_id' => 'contact_id', 'view_id' => 'contact_id',
'list' => array( 'list' => array(

View File

@ -2742,7 +2742,7 @@ class addressbook_ui extends addressbook_bo
if(is_array($content)) if(is_array($content))
{ {
$button = key($content['button']); $button = key($content['button']);
switch ($content['toolbar'] ? $content['toolbar'] : $button) switch ($button)
{ {
case 'vcard': case 'vcard':
Egw::redirect_link('/index.php','menuaction=addressbook.uivcard.out&ab_id=' .$content['id']); Egw::redirect_link('/index.php','menuaction=addressbook.uivcard.out&ab_id=' .$content['id']);
@ -2844,8 +2844,7 @@ class addressbook_ui extends addressbook_bo
// make everything not explicit mentioned readonly // make everything not explicit mentioned readonly
$readonlys['__ALL__'] = true; $readonlys['__ALL__'] = true;
$readonlys['photo'] = $readonlys['button[cancel]'] = $readonlys['button[copy]'] = $readonlys['photo'] = $readonlys['button[copy]'] =false;
$readonlys['button[ok]'] = $readonlys['button[more]'] = $readonlys['toolbar'] = false;
foreach(array_keys($this->contact_fields) as $key) foreach(array_keys($this->contact_fields) as $key)
{ {
@ -2945,40 +2944,6 @@ class addressbook_ui extends addressbook_bo
// dont show an app-header // dont show an app-header
$GLOBALS['egw_info']['flags']['app_header'] = ''; $GLOBALS['egw_info']['flags']['app_header'] = '';
$actions = array(
'open' => array(
'caption' => 'Open',
'toolbarDefault' => true,
),
'copy' => 'Copy',
'delete' => array(
'caption' => 'Delete',
'confirm' => 'Delete this entry',
),
'cancel' => array(
'caption' => 'Cancel',
'toolbarDefault' => true,
'icon' => 'close'
),
'back' => array(
'caption' => 'Back',
'toolbarDefault' => true,
),
'next' => array(
'caption' => 'Next',
'toolbarDefault' => true,
),
);
if (!isset($content['index']) || !$content['index'])
{
unset($actions['back']);
}
if (!isset($content['index']) || $content['index'] >= $num_rows-1)
{
unset($actions['next']);
}
$this->tmpl->setElementAttribute('toolbar', 'actions', $actions);
// always show sidebox, as it contains contact-data // always show sidebox, as it contains contact-data
unset($GLOBALS['egw_info']['user']['preferences']['common']['auto_hide_sidebox']); unset($GLOBALS['egw_info']['user']['preferences']['common']['auto_hide_sidebox']);
@ -2987,7 +2952,7 @@ class addressbook_ui extends addressbook_bo
// Load CRM code // Load CRM code
Framework::includeJS('.','CRM','addressbook'); Framework::includeJS('.','CRM','addressbook');
$content['view_sidebox'] = addressbook_hooks::getViewDOMID($contact_id, $crm_list);
$this->tmpl->exec('addressbook.addressbook_ui.view',$content,$sel_options,$readonlys,array( $this->tmpl->exec('addressbook.addressbook_ui.view',$content,$sel_options,$readonlys,array(
'id' => $content['id'], 'id' => $content['id'],
'index' => $content['index'], 'index' => $content['index'],

View File

@ -90,9 +90,11 @@ var AddressbookApp = /** @class */ (function (_super) {
break; break;
} }
jQuery('select[id*="adr_one_countrycode"]').each(function () { jQuery('select[id*="adr_one_countrycode"]').each(function () {
if (app.addressbook)
app.addressbook.show_custom_country(this); app.addressbook.show_custom_country(this);
}); });
jQuery('select[id*="adr_two_countrycode"]').each(function () { jQuery('select[id*="adr_two_countrycode"]').each(function () {
if (app.addressbook)
app.addressbook.show_custom_country(this); app.addressbook.show_custom_country(this);
}); });
}; };
@ -198,11 +200,27 @@ var AddressbookApp = /** @class */ (function (_super) {
var extras = { var extras = {
index: index index: index
}; };
var data = egw.dataGetUIDdata(_senders[0].id)['data'];
// CRM list // CRM list
if (_action.id != 'view') { if (_action.id != 'view') {
extras.crm_list = _action.id.replace('view-', ''); extras.crm_list = _action.id.replace('view-', '');
} }
this.egw.open(id, 'addressbook', 'view', extras, '_self', 'addressbook'); this.egw.openTab(id, 'addressbook', 'view', extras, {
displayName: (_action.id.match(/\-organisation/) && data.org_name != "") ? data.org_name
: data.n_fn + " (" + egw.lang(extras.crm_list) + ")",
icon: data.photo,
refreshCallback: this.view_refresh,
id: id + '-' + extras.crm_list,
});
};
/**
* callback for refreshing relative crm view list
*/
AddressbookApp.prototype.view_refresh = function () {
var et2 = etemplate2_1.etemplate2.getById("addressbook-view-" + this.appName);
if (et2) {
et2.app_obj.addressbook.view_set_list();
}
}; };
/** /**
* Set link filter for the already open & rendered list * Set link filter for the already open & rendered list
@ -222,20 +240,22 @@ var AddressbookApp = /** @class */ (function (_super) {
* *
* @param {object} _action * @param {object} _action
*/ */
AddressbookApp.prototype.view_actions = function (_action) { AddressbookApp.prototype.view_actions = function (_action, _widget) {
var id = this.et2.getArrayMgr('content').data.id; var app_id = _widget.dom_id.split('_');
switch (_action.id) { var et2 = etemplate2_1.etemplate2.getById(app_id[0]);
case 'open': var id = et2.widgetContainer.getArrayMgr('content').data.id;
switch (_widget.id) {
case 'button[edit]':
this.egw.open(id, 'addressbook', 'edit'); this.egw.open(id, 'addressbook', 'edit');
break; break;
case 'copy': case 'button[copy]':
this.egw.open(id, 'addressbook', 'edit', { makecp: 1 }); this.egw.open(id, 'addressbook', 'edit', { makecp: 1 });
break; break;
case 'cancel': case 'button[delete]':
this.egw.open(null, 'addressbook', 'list', null, '_self', 'addressbook'); et2_dialog.confirm(_widget, egw.lang('Delete this contact?'), egw.lang('Delete'));
break; break;
default: // submit all other buttons back to server default: // submit all other buttons back to server
this.et2._inst.submit(); et2.widgetContainer._inst.submit();
break; break;
} }
}; };

View File

@ -92,10 +92,10 @@ class AddressbookApp extends EgwApp
} }
jQuery('select[id*="adr_one_countrycode"]').each(function() { jQuery('select[id*="adr_one_countrycode"]').each(function() {
app.addressbook.show_custom_country(this); if (app.addressbook) app.addressbook.show_custom_country(this);
}); });
jQuery('select[id*="adr_two_countrycode"]').each(function() { jQuery('select[id*="adr_two_countrycode"]').each(function() {
app.addressbook.show_custom_country(this); if (app.addressbook) app.addressbook.show_custom_country(this);
}); });
} }
@ -221,14 +221,32 @@ class AddressbookApp extends EgwApp
var extras : any = { var extras : any = {
index: index index: index
}; };
var data = egw.dataGetUIDdata(_senders[0].id)['data'];
// CRM list // CRM list
if(_action.id != 'view') if(_action.id != 'view')
{ {
extras.crm_list = _action.id.replace('view-',''); extras.crm_list = _action.id.replace('view-','');
} }
this.egw.open(id, 'addressbook', 'view', extras, '_self', 'addressbook'); this.egw.openTab(id, 'addressbook', 'view', extras, {
displayName: (_action.id.match(/\-organisation/) && data.org_name != "") ? data.org_name
: data.n_fn+" ("+egw.lang(extras.crm_list)+")",
icon: data.photo,
refreshCallback: this.view_refresh,
id: id+'-'+extras.crm_list,
});
}
/**
* callback for refreshing relative crm view list
*/
view_refresh()
{
let et2 = etemplate2.getById("addressbook-view-"+this.appName);
if (et2)
{
et2.app_obj.addressbook.view_set_list();
}
} }
/** /**
@ -254,23 +272,26 @@ class AddressbookApp extends EgwApp
* *
* @param {object} _action * @param {object} _action
*/ */
view_actions(_action) view_actions(_action, _widget)
{ {
var id = this.et2.getArrayMgr('content').data.id;
switch(_action.id) var app_id = _widget.dom_id.split('_');
var et2 = etemplate2.getById(app_id[0]);
var id = et2.widgetContainer.getArrayMgr('content').data.id;
switch(_widget.id)
{ {
case 'open': case 'button[edit]':
this.egw.open(id, 'addressbook', 'edit'); this.egw.open(id, 'addressbook', 'edit');
break; break;
case 'copy': case 'button[copy]':
this.egw.open(id, 'addressbook', 'edit', { makecp: 1}); this.egw.open(id, 'addressbook', 'edit', { makecp: 1});
break; break;
case 'cancel': case 'button[delete]':
this.egw.open(null, 'addressbook', 'list', null, '_self', 'addressbook'); et2_dialog.confirm(_widget, egw.lang('Delete this contact?'), egw.lang('Delete'));
break; break;
default: // submit all other buttons back to server default: // submit all other buttons back to server
this.et2._inst.submit(); et2.widgetContainer._inst.submit();
break; break;
} }
} }

View File

@ -13,7 +13,7 @@ td.addressbook_sidebox_header {
height: 20px; height: 20px;
vertical-align: bottom; vertical-align: bottom;
} }
#addressbook-view.et2_container { form[id^=addressbook-view].et2_container {
height: 0 !important; height: 0 !important;
} }
td.addressbook_sidebox_toolbar { td.addressbook_sidebox_toolbar {

View File

@ -3,7 +3,7 @@
<!-- $Id$ --> <!-- $Id$ -->
<overlay> <overlay>
<template id="addressbook.view" template="" lang="" group="0" version="1.9.001"> <template id="addressbook.view" template="" lang="" group="0" version="1.9.001">
<grid class="addressbook_view" parent_node="addressbook_view_sidebox" width="100%"> <grid class="addressbook_view" parent_node="@view_sidebox" width="100%">
<columns> <columns>
<column width="70"/> <column width="70"/>
<column/> <column/>
@ -51,7 +51,11 @@
<url id="url" readonly="true"/> <url id="url" readonly="true"/>
</row> </row>
<row> <row>
<toolbar class="addressbook_sidebox_toolbar" id="toolbar" span="2" view_range="1" default_execute="app.addressbook.view_actions"/> <hbox>
<button id="button[edit]" label="open" background_image="1" image="edit" onclick="app.addressbook.view_actions"/>
<button id="button[copy]" label="copy" background_image="1" image="copy" onclick="app.addressbook.view_actions"/>
<buttononly id="button[delete]" label="delete" image="delete" onclick="app.addressbook.view_actions"/>
</hbox>
</row> </row>
</rows> </rows>
</grid> </grid>

View File

@ -27,7 +27,7 @@ td.addressbook_sidebox_header {
height: 20px; height: 20px;
vertical-align: bottom; vertical-align: bottom;
} }
#addressbook-view.et2_container { form[id^=addressbook-view].et2_container {
height: 0 !important; height: 0 !important;
} }
td.addressbook_sidebox_toolbar { td.addressbook_sidebox_toolbar {
@ -193,29 +193,29 @@ select#addressbook-index_col_filter\[tid\] {
/*# # #*/ /*# # #*/
/*# # #*/ /*# # #*/
/*##############################################*/ /*##############################################*/
div#addressbook_view_sidebox img.photo { div.addressbook_view_sidebox img.photo {
width: 68px; width: 68px;
padding-right: 3px; padding-right: 3px;
height: auto; height: auto;
vertical-align: top; vertical-align: top;
margin-right: 5px; margin-right: 5px;
} }
div#addressbook_view_sidebox #addressbook-view_n_fn { div.addressbook_view_sidebox .addressbook_sidebox_name {
font-size: 14px; font-size: 14px;
font-weight: bold; font-weight: bold;
padding: 2px 0 2px 0; padding: 2px 0 2px 0;
width: 85%; width: 85%;
} }
div#addressbook_view_sidebox #addressbook-view_org_name { div.addressbook_view_sidebox .addressbook_sidebox_org {
font-size: 14px; font-size: 14px;
padding: 2px 0 2px 0; padding: 2px 0 2px 0;
width: 85%; width: 85%;
} }
div#addressbook_view_sidebox #addressbook-view_org_unit { div.addressbook_view_sidebox span[id^=addressbook-view-addressbook][id$=_org_unit] {
font-size: 11px; font-size: 11px;
padding: 2px 0 2px 0; padding: 2px 0 2px 0;
} }
div#addressbook_view_sidebox #addressbook-view_adr_one_locality { div.addressbook_view_sidebox span[id^=addressbook-view-addressbook][id$=_adr_one_locality] {
font-size: 11px; font-size: 11px;
padding: 2px 0 2px 0; padding: 2px 0 2px 0;
} }

View File

@ -43,7 +43,7 @@
// Image + Data // Image + Data
div#addressbook_view_sidebox{ div.addressbook_view_sidebox{
//img //img
img.photo { img.photo {
width: 68px; width: 68px;
@ -53,25 +53,25 @@
margin-right: 5px; margin-right: 5px;
} }
// name // name
#addressbook-view_n_fn { .addressbook_sidebox_name {
.fontsize_xl; .fontsize_xl;
font-weight: bold; font-weight: bold;
padding: 2px 0 2px 0; padding: 2px 0 2px 0;
width: 85%; width: 85%;
} }
// org // org
#addressbook-view_org_name { .addressbook_sidebox_org {
.fontsize_xl; .fontsize_xl;
padding: 2px 0 2px 0; padding: 2px 0 2px 0;
width: 85%; width: 85%;
} }
// Unit // Unit
#addressbook-view_org_unit { span[id^=addressbook-view-addressbook][id $=_org_unit] {
.fontsize_m; .fontsize_m;
padding: 2px 0 2px 0; padding: 2px 0 2px 0;
} }
// Ort // Ort
#addressbook-view_adr_one_locality { span[id^=addressbook-view-addressbook][id $=_adr_one_locality] {
.fontsize_m; .fontsize_m;
padding: 2px 0 2px 0; padding: 2px 0 2px 0;
} }

View File

@ -84,7 +84,7 @@ var egw_app_1 = require("../jsapi/egw_app");
* @param _menuaction is the URL to which the form data should be submitted. * @param _menuaction is the URL to which the form data should be submitted.
*/ */
var etemplate2 = /** @class */ (function () { var etemplate2 = /** @class */ (function () {
function etemplate2(_container, _menuaction) { function etemplate2(_container, _menuaction, _uniqueId) {
if (typeof _menuaction == "undefined") { if (typeof _menuaction == "undefined") {
_menuaction = "EGroupware\\Api\\Etemplate::ajax_process_content"; _menuaction = "EGroupware\\Api\\Etemplate::ajax_process_content";
} }
@ -92,7 +92,7 @@ var etemplate2 = /** @class */ (function () {
this._DOMContainer = _container; this._DOMContainer = _container;
this.menuaction = _menuaction; this.menuaction = _menuaction;
// Unique ID to prevent DOM collisions across multiple templates // Unique ID to prevent DOM collisions across multiple templates
this.uniqueId = _container.getAttribute("id") ? _container.getAttribute("id").replace('.', '-') : ''; this.uniqueId = _uniqueId ? _uniqueId : (_container.getAttribute("id") ? _container.getAttribute("id").replace('.', '-') : '');
/** /**
* Preset the object variable * Preset the object variable
* @type {et2_container} * @type {et2_container}
@ -345,8 +345,9 @@ var etemplate2 = /** @class */ (function () {
* @param {function} _callback called after template is loaded * @param {function} _callback called after template is loaded
* @param {object} _app local app object * @param {object} _app local app object
* @param {boolean} _no_et2_ready true: do not send et2_ready, used by et2_dialog to not overwrite app.js et2 object * @param {boolean} _no_et2_ready true: do not send et2_ready, used by et2_dialog to not overwrite app.js et2 object
* @param {string} _open_target flag of string to distinguishe between tab target and normal app object
*/ */
etemplate2.prototype.load = function (_name, _url, _data, _callback, _app, _no_et2_ready) { etemplate2.prototype.load = function (_name, _url, _data, _callback, _app, _no_et2_ready, _open_target) {
var app = _app || window.app; var app = _app || window.app;
this.name = _name; // store top-level template name to have it available in widgets this.name = _name; // store top-level template name to have it available in widgets
// store template base url, in case initial template is loaded via webdav, to use that for further loads too // store template base url, in case initial template is loaded via webdav, to use that for further loads too
@ -364,7 +365,7 @@ var etemplate2 = /** @class */ (function () {
var appname = _name.split('.')[0]; var appname = _name.split('.')[0];
// if no app object provided and template app is not currentapp (eg. infolog CRM view) // if no app object provided and template app is not currentapp (eg. infolog CRM view)
// create private app object / closure with just classes / prototypes // create private app object / closure with just classes / prototypes
if (!_app && appname && appname != currentapp) { if (!_app && appname && appname != currentapp || _open_target) {
app = { classes: window.app.classes }; app = { classes: window.app.classes };
} }
// remember used app object, to eg. use: onchange="widget.getInstanceMgr().app_object[app].callback()" // remember used app object, to eg. use: onchange="widget.getInstanceMgr().app_object[app].callback()"
@ -864,9 +865,18 @@ var etemplate2 = /** @class */ (function () {
*/ */
etemplate2.app_refresh = function (_msg, _app, _id, _type) { etemplate2.app_refresh = function (_msg, _app, _id, _type) {
var refresh_done = false; var refresh_done = false;
var et2 = etemplate2.getByApplication(_app); var app = _app.split('-');
var et2 = etemplate2.getByApplication(app[0]);
for (var i = 0; i < et2.length; i++) { for (var i = 0; i < et2.length; i++) {
refresh_done = et2[i].refresh(_msg, _app, _id, _type) || refresh_done; if (app[1]) {
if (et2[i]['uniqueId'].match(_app)) {
refresh_done = et2[i].refresh(_msg, app[0], _id, _type) || refresh_done;
break;
}
}
else {
refresh_done = et2[i].refresh(_msg, app[0], _id, _type) || refresh_done;
}
} }
return refresh_done; return refresh_done;
}; };
@ -994,6 +1004,8 @@ var etemplate2 = /** @class */ (function () {
} }
// handle framework.setSidebox calls // handle framework.setSidebox calls
if (window.framework && jQuery.isArray(data.setSidebox)) { if (window.framework && jQuery.isArray(data.setSidebox)) {
if (data['fw-target'])
data.setSidebox[0] = data['fw-target'];
window.framework.setSidebox.apply(window.framework, data.setSidebox); window.framework.setSidebox.apply(window.framework, data.setSidebox);
} }
// regular et2 re-load // regular et2 re-load
@ -1010,6 +1022,7 @@ var etemplate2 = /** @class */ (function () {
else { else {
// Not etemplate // Not etemplate
var node = document.getElementById(data.DOMNodeID); var node = document.getElementById(data.DOMNodeID);
var uniqueId = data.DOMNodeID;
if (node) { if (node) {
if (node.children.length) { if (node.children.length) {
// Node has children already? Check for loading over an // Node has children already? Check for loading over an
@ -1018,8 +1031,11 @@ var etemplate2 = /** @class */ (function () {
if (old) if (old)
old.clear(); old.clear();
} }
var et2 = new etemplate2(node, data.menuaction); if (data['open_target'] && !uniqueId.match(data['open_target'])) {
et2.load(data.name, data.url, data.data); uniqueId = data.DOMNodeID.replace('.', '-') + '-' + data['open_target'];
}
var et2 = new etemplate2(node, data.menuaction, uniqueId);
et2.load(data.name, data.url, data.data, null, null, null, data['fw-target']);
return true; return true;
} }
else { else {

View File

@ -112,7 +112,7 @@ export class etemplate2
private app_obj: EgwApp; private app_obj: EgwApp;
app: string; app: string;
constructor(_container : HTMLElement, _menuaction? : string) constructor(_container : HTMLElement, _menuaction? : string, _uniqueId?: string)
{ {
if (typeof _menuaction == "undefined") if (typeof _menuaction == "undefined")
{ {
@ -124,7 +124,7 @@ export class etemplate2
this.menuaction = _menuaction; this.menuaction = _menuaction;
// Unique ID to prevent DOM collisions across multiple templates // Unique ID to prevent DOM collisions across multiple templates
this.uniqueId = _container.getAttribute("id") ? _container.getAttribute("id").replace('.', '-') : ''; this.uniqueId = _uniqueId ? _uniqueId : (_container.getAttribute("id") ? _container.getAttribute("id").replace('.', '-') : '');
/** /**
* Preset the object variable * Preset the object variable
@ -438,8 +438,9 @@ export class etemplate2
* @param {function} _callback called after template is loaded * @param {function} _callback called after template is loaded
* @param {object} _app local app object * @param {object} _app local app object
* @param {boolean} _no_et2_ready true: do not send et2_ready, used by et2_dialog to not overwrite app.js et2 object * @param {boolean} _no_et2_ready true: do not send et2_ready, used by et2_dialog to not overwrite app.js et2 object
* @param {string} _open_target flag of string to distinguishe between tab target and normal app object
*/ */
load(_name, _url, _data, _callback?, _app?, _no_et2_ready?) load(_name, _url, _data, _callback?, _app?, _no_et2_ready?, _open_target?)
{ {
let app = _app || window.app; let app = _app || window.app;
this.name = _name; // store top-level template name to have it available in widgets this.name = _name; // store top-level template name to have it available in widgets
@ -461,7 +462,7 @@ export class etemplate2
const appname = _name.split('.')[0]; const appname = _name.split('.')[0];
// if no app object provided and template app is not currentapp (eg. infolog CRM view) // if no app object provided and template app is not currentapp (eg. infolog CRM view)
// create private app object / closure with just classes / prototypes // create private app object / closure with just classes / prototypes
if (!_app && appname && appname != currentapp) if (!_app && appname && appname != currentapp || _open_target)
{ {
app = {classes: window.app.classes}; app = {classes: window.app.classes};
} }
@ -1107,10 +1108,22 @@ export class etemplate2
static app_refresh (_msg, _app, _id, _type) static app_refresh (_msg, _app, _id, _type)
{ {
let refresh_done = false; let refresh_done = false;
const et2 = etemplate2.getByApplication(_app); let app = _app.split('-');
const et2 = etemplate2.getByApplication(app[0]);
for (let i = 0; i < et2.length; i++) for (let i = 0; i < et2.length; i++)
{ {
refresh_done = et2[i].refresh(_msg, _app, _id, _type) || refresh_done; if (app[1])
{
if (et2[i]['uniqueId'].match(_app))
{
refresh_done = et2[i].refresh(_msg, app[0], _id, _type) || refresh_done;
break;
}
}
else
{
refresh_done = et2[i].refresh(_msg, app[0], _id, _type) || refresh_done;
}
} }
return refresh_done; return refresh_done;
} }
@ -1279,6 +1292,8 @@ export class etemplate2
// handle framework.setSidebox calls // handle framework.setSidebox calls
if (window.framework && jQuery.isArray(data.setSidebox)) if (window.framework && jQuery.isArray(data.setSidebox))
{ {
if (data['fw-target']) data.setSidebox[0] = data['fw-target'];
window.framework.setSidebox.apply(window.framework, data.setSidebox); window.framework.setSidebox.apply(window.framework, data.setSidebox);
} }
@ -1299,6 +1314,7 @@ export class etemplate2
{ {
// Not etemplate // Not etemplate
const node = document.getElementById(data.DOMNodeID); const node = document.getElementById(data.DOMNodeID);
let uniqueId = data.DOMNodeID;
if (node) if (node)
{ {
if (node.children.length) if (node.children.length)
@ -1308,8 +1324,12 @@ export class etemplate2
const old = etemplate2.getById(node.id); const old = etemplate2.getById(node.id);
if (old) old.clear(); if (old) old.clear();
} }
const et2 = new etemplate2(node, data.menuaction); if (data['open_target'] && !uniqueId.match(data['open_target']))
et2.load(data.name, data.url, data.data); {
uniqueId = data.DOMNodeID.replace('.', '-') + '-' + data['open_target'];
}
const et2 = new etemplate2(node, data.menuaction, uniqueId);
et2.load(data.name, data.url, data.data, null, null, null, data['fw-target']);
return true; return true;
} }
else else

View File

@ -153,7 +153,7 @@ var fw_base = (function(){ "use strict"; return Class.extend(
* *
* @param {egw_fw_class_application} _app * @param {egw_fw_class_application} _app
* @param {string} _url optional url, default index page of app * @param {string} _url optional url, default index page of app
* @param {bool} _hidden specifies, whether the application should be set active * @param {boolean} _hidden specifies, whether the application should be set active
* after opening the tab * after opening the tab
* @param {int} _pos * @param {int} _pos
* @param {status} _status * @param {status} _status
@ -308,7 +308,7 @@ var fw_base = (function(){ "use strict"; return Class.extend(
//Lookup whether this entry was opened before. If no data is //Lookup whether this entry was opened before. If no data is
//stored about this, use the information we got from the server //stored about this, use the information we got from the server
var opened = egw.preference('jdots_sidebox_'+_data[i].menu_name, _app.appName); var opened = egw.preference('jdots_sidebox_'+_data[i].menu_name, _app.internalName);
if (typeof opened == 'undefined') if (typeof opened == 'undefined')
{ {
opened = _data[i].opened; opened = _data[i].opened;
@ -446,7 +446,7 @@ var fw_base = (function(){ "use strict"; return Class.extend(
*/ */
categoryOpenCloseCallback: function(_opened) categoryOpenCloseCallback: function(_opened)
{ {
egw.set_preference(this.tag.appName, 'jdots_sidebox_'+this.catName, _opened); if (!framework.isAnInternalApp(this.tag)) egw.set_preference(this.tag.internalName, 'jdots_sidebox_'+this.catName, _opened);
}, },
categoryAnimationCallback: function() categoryAnimationCallback: function()
@ -492,7 +492,7 @@ var fw_base = (function(){ "use strict"; return Class.extend(
_app.tab = this.tabsUi.addTab(_app.icon, this.tabClickCallback, this.tabCloseClickCallback, _app.tab = this.tabsUi.addTab(_app.icon, this.tabClickCallback, this.tabCloseClickCallback,
_app, _pos, _status); _app, _pos, _status);
_app.tab.setTitle(_app.displayName); _app.tab.setTitle(_app.displayName);
_app.tab.setHint(_app.hint ? _app.hint : '');
//Set the tab closeable if there's more than one tab //Set the tab closeable if there's more than one tab
this.tabsUi.setCloseable(this.tabsUi._isNotTheLastTab()); this.tabsUi.setCloseable(this.tabsUi._isNotTheLastTab());
// Do not show tab header if the app is with status 5, means run in background // Do not show tab header if the app is with status 5, means run in background
@ -563,6 +563,10 @@ var fw_base = (function(){ "use strict"; return Class.extend(
//As a new tab might remove a row from the tab header, we have to resize all tab content browsers //As a new tab might remove a row from the tab header, we have to resize all tab content browsers
this.tag.parentFw.resizeHandler(); this.tag.parentFw.resizeHandler();
if (app.isFrameworkTab)
{
app.destroy();
}
}, },
/** /**
@ -671,6 +675,46 @@ var fw_base = (function(){ "use strict"; return Class.extend(
} }
}, },
tabLinkHandler: function(_link, _extra)
{
var app = this.parseAppFromUrl(_link);
if (app)
{
var appname = app.appName+"-"+btoa(_extra.id ? _extra.id : _link);
this.applications[appname] = this.getApplicationByName(appname);
if (this.applications[appname])
{
this.setActiveApp(this.applications[appname]);
return;
}
var self = this;
// add target flag
_link += '&fw_target='+appname;
// create an actual clone of existing app object
this.applications[appname] = jQuery.extend(true, {}, app);
this.applications[appname]['isFrameworkTab'] = true;
// merge extra framework app data into the new one
this.applications[appname] = jQuery.extend(true, this.applications[appname], _extra);
this.applications[appname]['appName'] = appname; // better to control it here
this.applications[appname]['indexUrl'] = _link;
this.applications[appname]['tab'] = null; // must be rest to create a new tab
this.applications[appname]['browser'] = null; // must be rest to create a new browser content
this.applications[appname]['sidemenuEntry'] = this.sidemenuUi.addEntry(
this.applications[appname].displayName, this.applications[appname].icon,
function(){
self.applicationTabNavigate(self.applications[appname], _link, false, -1, null);
}, this.applications[appname], appname);
this.applicationTabNavigate(this.applications[appname], _link, false, -1, null);
}
else
{
egw_alertHandler("No appropriate target application has been found.",
"Target link: " + _link);
}
},
/** /**
* *
* @param {type} _link * @param {type} _link
@ -697,6 +741,19 @@ var fw_base = (function(){ "use strict"; return Class.extend(
if (app) if (app)
{ {
if (_app == '_tab')
{
// add target flag
_link += '&target=_tab';
var appname = app.appName+":"+btoa(_link);
this.applications[appname] = jQuery.extend(true, {},app);
this.applications[appname]['appName'] = appname;
this.applications[appname]['indexUrl'] = _link;
this.applications[appname]['tab'] = null;
this.applications[appname]['browser'] = null;
this.applications[appname]['title'] = 'view';
app = this.getApplicationByName(appname);
}
this.applicationTabNavigate(app, _link); this.applicationTabNavigate(app, _link);
} }
else else
@ -1033,7 +1090,12 @@ var fw_base = (function(){ "use strict"; return Class.extend(
refresh: function(_msg, _app, _id, _type, _targetapp, _replace, _with, _msg_type) refresh: function(_msg, _app, _id, _type, _targetapp, _replace, _with, _msg_type)
{ {
//alert("egw_refresh(\'"+_msg+"\',\'"+_app+"\',\'"+_id+"\',\'"+_type+"\')"); //alert("egw_refresh(\'"+_msg+"\',\'"+_app+"\',\'"+_id+"\',\'"+_type+"\')");
let app_object = this.getApplicationByName(_app);
if (this.isAnInternalApp(app_object) && typeof app_object.refreshCallback == 'function')
{
app_object.refreshCallback();
return;
}
if (!_app) // force reload of entire framework, eg. when template-set changes if (!_app) // force reload of entire framework, eg. when template-set changes
{ {
window.location.href = window.egw_webserverUrl+'/index.php?cd=yes'+(_msg ? '&msg='+encodeURIComponent(_msg) : ''); window.location.href = window.egw_webserverUrl+'/index.php?cd=yes'+(_msg ? '&msg='+encodeURIComponent(_msg) : '');
@ -1198,5 +1260,15 @@ var fw_base = (function(){ "use strict"; return Class.extend(
} }
} }
}); });
},
/**
* Check if the app is an internal app object like multitab views
* @param _app app object
* @return {boolean}
*/
isAnInternalApp: function(_app)
{
return _app && _app.appName != _app.internalName;
} }
});}).call(this); });}).call(this);

View File

@ -51,6 +51,18 @@ function egw_fw_class_application(_parentFw, _appName, _displayName, _icon,
this.browser = null; this.browser = null;
} }
/**
* destroy application object and its relative parts
*/
egw_fw_class_application.prototype.destroy = function()
{
delete this.tab;
if (this.sidemenuEntry) this.sidemenuEntry.remove();
delete this.sidemenuEntry;
delete this.browser;
delete (framework.applications[this.appName]);
};
/** /**
* Returns an menuaction inside the jdots_framework for this application. * Returns an menuaction inside the jdots_framework for this application.
* without a "this" context (by directly calling egw_fw_class_application.prototype.getAjaxUrl) * without a "this" context (by directly calling egw_fw_class_application.prototype.getAjaxUrl)

View File

@ -276,7 +276,7 @@
//Set the sidebox width if a application specific sidebox width is set //Set the sidebox width if a application specific sidebox width is set
// do not trigger resize if the sidebar is already in toggle on mode and // do not trigger resize if the sidebar is already in toggle on mode and
// the next set state is the same // the next set state is the same
if (_app.sideboxWidth !== false && egw.preference('toggleSidebar',_app.appName) == 'off') if (_app.sideboxWidth !== false && egw.preference('toggleSidebar',_app.internalName) == 'off')
{ {
this.sideboxSizeCallback(_app.sideboxWidth); this.sideboxSizeCallback(_app.sideboxWidth);
this.splitterUi.constraints[0].size = _app.sideboxWidth; this.splitterUi.constraints[0].size = _app.sideboxWidth;
@ -330,7 +330,7 @@
if (_toggleMode !== "toggle") if (_toggleMode !== "toggle")
{ {
egw.set_preference(this.tag.activeApp.internalName, 'jdotssideboxwidth', _width); if (!framework.isAnInternalApp(this.tag.activeApp)) egw.set_preference(this.tag.activeApp.internalName, 'jdotssideboxwidth', _width);
//If there are no global application width values, set the sidebox width of //If there are no global application width values, set the sidebox width of
//the application every time the splitter is resized //the application every time the splitter is resized
@ -441,7 +441,7 @@
*/ */
categoryOpenCloseCallback: function(_opened) categoryOpenCloseCallback: function(_opened)
{ {
egw.set_preference(this.tag.appName, 'jdots_sidebox_'+this.catName, _opened); if (!framework.isAnInternalApp(this.tag)) egw.set_preference(this.tag.internalName, 'jdots_sidebox_'+this.catName, _opened);
}, },
categoryAnimationCallback: function() categoryAnimationCallback: function()
@ -455,16 +455,16 @@
*/ */
_toggleSidebarCallback: function (_state) _toggleSidebarCallback: function (_state)
{ {
var splitterWidth = egw.preference('jdotssideboxwidth',this.activeApp.appName) || this.activeApp.sideboxWidth; var splitterWidth = egw.preference('jdotssideboxwidth',this.activeApp.internalName) || this.activeApp.sideboxWidth;
if (_state === "on") if (_state === "on")
{ {
this.splitterUi.resizeCallback(70,'toggle'); this.splitterUi.resizeCallback(70,'toggle');
egw.set_preference(this.activeApp.appName, 'toggleSidebar', 'on'); if (!framework.isAnInternalApp(this.activeApp)) egw.set_preference(this.activeApp.internalName, 'toggleSidebar', 'on');
} }
else else
{ {
this.splitterUi.resizeCallback(splitterWidth); this.splitterUi.resizeCallback(splitterWidth);
egw.set_preference(this.activeApp.appName, 'toggleSidebar', 'off'); if (!framework.isAnInternalApp(this.activeApp)) egw.set_preference(this.activeApp.internalName, 'toggleSidebar', 'off');
} }
}, },
@ -473,7 +473,7 @@
*/ */
getToggleSidebarState: function() getToggleSidebarState: function()
{ {
var toggleSidebar = egw.preference('toggleSidebar',this.activeApp.appName); var toggleSidebar = egw.preference('toggleSidebar',this.activeApp.internalName);
this.toggleSidebarUi.set_toggle(toggleSidebar?toggleSidebar:"off", this._toggleSidebarCallback, this); this.toggleSidebarUi.set_toggle(toggleSidebar?toggleSidebar:"off", this._toggleSidebarCallback, this);
}, },

View File

@ -308,6 +308,7 @@ function egw_fw_ui_tab(_parent, _contHeaderDiv, _contDiv, _icon, _callback,
this.position = _pos; this.position = _pos;
this.status = _status; this.status = _status;
this.notification = 0; this.notification = 0;
this.hint = '';
//Create the header div and set its "click" function and "hover" event //Create the header div and set its "click" function and "hover" event
this.headerDiv = document.createElement("span"); this.headerDiv = document.createElement("span");
@ -439,6 +440,17 @@ egw_fw_ui_tab.prototype.setTitle = function(_title)
jQuery(this.headerH1).text(_title); jQuery(this.headerH1).text(_title);
}; };
/**
* setHint sets tooltip of this tab. An existing tooltip will be removed.
*
* @param {string} _hint Text which should be displayed.
*/
egw_fw_ui_tab.prototype.setHint = function(_hint)
{
this.hint = _hint;
egw().tooltipBind(jQuery(this.headerDiv), _hint);
};
/** /**
* setTitle sets the content of this tab. Existing content is removed. * setTitle sets the content of this tab. Existing content is removed.
* *

View File

@ -993,6 +993,17 @@ declare interface IegwWndLocal extends IegwGlobal
*/ */
openPopup(_url : string, _width : number, _height : number|"availHeight", _windowName? : string, _app? : string|boolean, openPopup(_url : string, _width : number, _height : number|"availHeight", _windowName? : string, _app? : string|boolean,
_returnID? : boolean, _status? : "yes"|"no", _skip_framework? : boolean) : Window|void; _returnID? : boolean, _status? : "yes"|"no", _skip_framework? : boolean) : Window|void;
/**
* View an EGroupware entry: opens a framework tab for the given app entry
*
* @param {string}|int|object _id either just the id or if app=="" "app:id" or object with all data
* @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 {object} _framework_app framework app attributes e.g. title or displayName
*/
openTab(_id, _app, _type, _extra, _framework_app) : void;
/** /**
* Get available height of screen * Get available height of screen
*/ */

View File

@ -331,10 +331,6 @@ egw.extend('links', egw.MODULE_GLOBAL, function()
// if there are vars, we add them urlencoded to the url // if there are vars, we add them urlencoded to the url
var query = []; var query = [];
// If ajax flag is there, it must be the last one
var ajax = vars.ajax || false;
delete vars.ajax;
for(var name in vars) for(var name in vars)
{ {
var val = vars[name] || ''; // fix error for eg. null, which is an object! var val = vars[name] || ''; // fix error for eg. null, which is an object!
@ -351,11 +347,6 @@ egw.extend('links', egw.MODULE_GLOBAL, function()
} }
} }
// Add ajax flag at the end
if(ajax)
{
query.push('ajax='+encodeURIComponent(ajax));
}
return query.length ? _url+'?'+query.join('&') : _url; return query.length ? _url+'?'+query.join('&') : _url;
}, },

View File

@ -96,6 +96,8 @@ egw.extend('open', egw.MODULE_WND_LOCAL, function(_egw, _wnd)
* @param {boolean} _check_popup_blocker TRUE check if browser pop-up blocker is on/off, FALSE no check * @param {boolean} _check_popup_blocker TRUE check if browser pop-up blocker is on/off, FALSE no check
* - This option only makes sense to be enabled when the open_link requested without user interaction * - This option only makes sense to be enabled when the open_link requested without user interaction
* @memberOf egw * @memberOf egw
*
* @return {object|void} returns object for given specific target like '_tab'
*/ */
open: function(id_data, app, type, extra, target, target_app, _check_popup_blocker) open: function(id_data, app, type, extra, target, target_app, _check_popup_blocker)
{ {
@ -221,9 +223,40 @@ egw.extend('open', egw.MODULE_WND_LOCAL, function(_egw, _wnd)
{ {
url = this.link(url, params); url = this.link(url, params);
} }
if (target == '_tab') return {url: url};
if (type == 'view' && params.target == 'tab') {
return this.openTab(params[app_registry['view_id']], app, type, params, {
id: params[app_registry['view_id']] + '-' + this.appName,
icon: params['icon'],
displayName: id_data['title'] + " (" + egw.lang(this.appName) + ")",
});
}
return this.open_link(url, target, popup, target_app, _check_popup_blocker); return this.open_link(url, target, popup, target_app, _check_popup_blocker);
}, },
/**
* View an EGroupware entry: opens a framework tab for the given app entry
*
* @param {string}|int|object _id either just the id or if app=="" "app:id" or object with all data
* @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 {object} _framework_app framework app attributes e.g. title or displayName
*/
openTab: function(_id, _app, _type, _extra, _framework_app)
{
if (_wnd.framework && _wnd.framework.tabLinkHandler)
{
var data = this.open(_id, _app, _type, _extra, "_tab", false);
// Use framework's link handler
_wnd.framework.tabLinkHandler(data.url, _framework_app);
}
else
{
this.open(_id, _app, _type, _extra);
}
},
/** /**
* Open a link, which can be either a menuaction, a EGroupware relative url or a full url * Open a link, which can be either a menuaction, a EGroupware relative url or a full url
* *

View File

@ -527,7 +527,7 @@ class Etemplate extends Etemplate\Widget\Template
$this->version=$version, $this->laod_via = $load_via); $this->version=$version, $this->laod_via = $load_via);
//error_log(__METHOD__."('$name', '$template_set', '$lang', $group, '$version', '$load_via') rel_path=".array2string($this->rel_path)); //error_log(__METHOD__."('$name', '$template_set', '$lang', $group, '$version', '$load_via') rel_path=".array2string($this->rel_path));
$this->dom_id = $name; $this->dom_id = isset($_GET['fw_target']) ? $name.'-'.$_GET['fw_target'] : $name;
return (boolean)$this->rel_path; return (boolean)$this->rel_path;
} }

View File

@ -1006,7 +1006,8 @@ abstract class Ajax extends Api\Framework
// send Api\Preferences, so we dont need to request them in a second ajax request // send Api\Preferences, so we dont need to request them in a second ajax request
$GLOBALS['egw']->framework->response->call('egw.set_preferences', $GLOBALS['egw']->framework->response->call('egw.set_preferences',
(array)$GLOBALS['egw_info']['user']['preferences'][$app], $app); (array)$GLOBALS['egw_info']['user']['preferences'][$app], $app);
// flag to indicate target of output e.g. _tab
if ($_GET['fw_target']) $GLOBALS['egw']->framework->set_extra('fw','target',$_GET['fw_target']);
// call application menuaction // call application menuaction
ob_start(); ob_start();
$obj->$method(); $obj->$method();

View File

@ -4319,6 +4319,7 @@ td.message span.message {
cursor: pointer; cursor: pointer;
background-repeat: repeat-x; background-repeat: repeat-x;
height: 100%; height: 100%;
max-width: 200px;
} }
#egw_fw_main #egw_fw_tabs .egw_fw_ui_tabs_header .egw_fw_ui_tab_header .notifyTabDiv { #egw_fw_main #egw_fw_tabs .egw_fw_ui_tabs_header .egw_fw_ui_tab_header .notifyTabDiv {
position: absolute; position: absolute;
@ -4511,6 +4512,14 @@ td.message span.message {
color: #000000; color: #000000;
padding-top: 0; padding-top: 0;
line-height: 33px; line-height: 33px;
height: 33px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding-left: 10px;
}
#egw_fw_sidebar #egw_fw_sidemenu .egw_fw_ui_scrollarea_outerdiv .egw_fw_ui_sidemenu_entry_header_active h1:hover {
padding-left: 35px;
} }
#egw_fw_sidebar #egw_fw_sidemenu .egw_fw_ui_scrollarea_outerdiv .egw_fw_ui_sidemenu_entry_header_active:hover { #egw_fw_sidebar #egw_fw_sidemenu .egw_fw_ui_scrollarea_outerdiv .egw_fw_ui_sidemenu_entry_header_active:hover {
background-image: url(../images/reload.png); background-image: url(../images/reload.png);

View File

@ -4308,6 +4308,7 @@ td.message span.message {
cursor: pointer; cursor: pointer;
background-repeat: repeat-x; background-repeat: repeat-x;
height: 100%; height: 100%;
max-width: 200px;
} }
#egw_fw_main #egw_fw_tabs .egw_fw_ui_tabs_header .egw_fw_ui_tab_header .notifyTabDiv { #egw_fw_main #egw_fw_tabs .egw_fw_ui_tabs_header .egw_fw_ui_tab_header .notifyTabDiv {
position: absolute; position: absolute;
@ -4500,6 +4501,14 @@ td.message span.message {
color: #000000; color: #000000;
padding-top: 0; padding-top: 0;
line-height: 33px; line-height: 33px;
height: 33px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding-left: 10px;
}
#egw_fw_sidebar #egw_fw_sidemenu .egw_fw_ui_scrollarea_outerdiv .egw_fw_ui_sidemenu_entry_header_active h1:hover {
padding-left: 35px;
} }
#egw_fw_sidebar #egw_fw_sidemenu .egw_fw_ui_scrollarea_outerdiv .egw_fw_ui_sidemenu_entry_header_active:hover { #egw_fw_sidebar #egw_fw_sidemenu .egw_fw_ui_scrollarea_outerdiv .egw_fw_ui_sidemenu_entry_header_active:hover {
background-image: url(../images/reload.png); background-image: url(../images/reload.png);

View File

@ -4319,6 +4319,7 @@ td.message span.message {
cursor: pointer; cursor: pointer;
background-repeat: repeat-x; background-repeat: repeat-x;
height: 100%; height: 100%;
max-width: 200px;
} }
#egw_fw_main #egw_fw_tabs .egw_fw_ui_tabs_header .egw_fw_ui_tab_header .notifyTabDiv { #egw_fw_main #egw_fw_tabs .egw_fw_ui_tabs_header .egw_fw_ui_tab_header .notifyTabDiv {
position: absolute; position: absolute;
@ -4511,6 +4512,14 @@ td.message span.message {
color: #000000; color: #000000;
padding-top: 0; padding-top: 0;
line-height: 33px; line-height: 33px;
height: 33px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding-left: 10px;
}
#egw_fw_sidebar #egw_fw_sidemenu .egw_fw_ui_scrollarea_outerdiv .egw_fw_ui_sidemenu_entry_header_active h1:hover {
padding-left: 35px;
} }
#egw_fw_sidebar #egw_fw_sidemenu .egw_fw_ui_scrollarea_outerdiv .egw_fw_ui_sidemenu_entry_header_active:hover { #egw_fw_sidebar #egw_fw_sidemenu .egw_fw_ui_scrollarea_outerdiv .egw_fw_ui_sidemenu_entry_header_active:hover {
background-image: url(../images/reload.png); background-image: url(../images/reload.png);

View File

@ -86,6 +86,7 @@
cursor: pointer; cursor: pointer;
background-repeat:repeat-x; background-repeat:repeat-x;
height: 100%; height: 100%;
max-width: 200px;
.notifyTabDiv { .notifyTabDiv {
position: absolute; position: absolute;
background-color: #c14343; background-color: #c14343;

View File

@ -102,6 +102,12 @@
.color_100_gray; .color_100_gray;
padding-top: 0; padding-top: 0;
line-height: 33px; line-height: 33px;
height: 33px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding-left: 10px;
&:hover {padding-left: 35px;}
} }
&:active {} &:active {}
&:hover { &:hover {

View File

@ -4330,6 +4330,7 @@ td.message span.message {
cursor: pointer; cursor: pointer;
background-repeat: repeat-x; background-repeat: repeat-x;
height: 100%; height: 100%;
max-width: 200px;
} }
#egw_fw_main #egw_fw_tabs .egw_fw_ui_tabs_header .egw_fw_ui_tab_header .notifyTabDiv { #egw_fw_main #egw_fw_tabs .egw_fw_ui_tabs_header .egw_fw_ui_tab_header .notifyTabDiv {
position: absolute; position: absolute;
@ -4522,6 +4523,14 @@ td.message span.message {
color: #000000; color: #000000;
padding-top: 0; padding-top: 0;
line-height: 33px; line-height: 33px;
height: 33px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding-left: 10px;
}
#egw_fw_sidebar #egw_fw_sidemenu .egw_fw_ui_scrollarea_outerdiv .egw_fw_ui_sidemenu_entry_header_active h1:hover {
padding-left: 35px;
} }
#egw_fw_sidebar #egw_fw_sidemenu .egw_fw_ui_scrollarea_outerdiv .egw_fw_ui_sidemenu_entry_header_active:hover { #egw_fw_sidebar #egw_fw_sidemenu .egw_fw_ui_scrollarea_outerdiv .egw_fw_ui_sidemenu_entry_header_active:hover {
background-image: url(../images/reload.png); background-image: url(../images/reload.png);