|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* EGroupware - Framework for Ajax based templates: jdots & Pixelegg |
|
4
|
|
|
* |
|
5
|
|
|
* @link http://www.stylite.de |
|
6
|
|
|
* @package api |
|
7
|
|
|
* @subpackage framework |
|
8
|
|
|
* @author Andreas Stöckel <[email protected]> |
|
9
|
|
|
* @author Ralf Becker <[email protected]> |
|
10
|
|
|
* @author Nathan Gray <[email protected]> |
|
11
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
|
12
|
|
|
* @version $Id$ |
|
13
|
|
|
*/ |
|
14
|
|
|
|
|
15
|
|
|
namespace EGroupware\Api\Framework; |
|
16
|
|
|
|
|
17
|
|
|
use EGroupware\Api; |
|
18
|
|
|
use EGroupware\Api\Egw; |
|
19
|
|
|
|
|
20
|
|
|
/** |
|
21
|
|
|
* Stylite jdots template |
|
22
|
|
|
*/ |
|
23
|
|
|
abstract class Ajax extends Api\Framework |
|
24
|
|
|
{ |
|
25
|
|
|
/** |
|
26
|
|
|
* Appname used to include javascript code |
|
27
|
|
|
*/ |
|
28
|
|
|
const JS_INCLUDE_APP = ''; |
|
29
|
|
|
/** |
|
30
|
|
|
* Appname used for everything else |
|
31
|
|
|
*/ |
|
32
|
|
|
const APP = ''; |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* Minimum width of sidebar eg. from German 2-letter daynames in Calendar |
|
36
|
|
|
* or Calendar's navigation buttons need to be seen |
|
37
|
|
|
* |
|
38
|
|
|
* Need to be changed in js/fw_[template].js. |
|
39
|
|
|
*/ |
|
40
|
|
|
const MIN_SIDEBAR_WIDTH = 245; |
|
41
|
|
|
/** |
|
42
|
|
|
* Default width need to be tested with English 3-letter day-names and Pixelegg template in Calendar |
|
43
|
|
|
* |
|
44
|
|
|
* Need to be changed in js/egw_fw.js around line 1536 too! |
|
45
|
|
|
*/ |
|
46
|
|
|
const DEFAULT_SIDEBAR_WIDTH = 255; |
|
47
|
|
|
/** |
|
48
|
|
|
* Whether javascript:egw_link_handler calls (including given app) should be returned by the "link" function |
|
49
|
|
|
* or just the link |
|
50
|
|
|
* |
|
51
|
|
|
* @var string |
|
52
|
|
|
*/ |
|
53
|
|
|
private static $link_app; |
|
54
|
|
|
|
|
55
|
|
|
/** |
|
56
|
|
|
* Constructor |
|
57
|
|
|
* |
|
58
|
|
|
* @param string $template = '' name of the template |
|
59
|
|
|
*/ |
|
60
|
|
|
function __construct($template=self::APP) |
|
61
|
|
|
{ |
|
62
|
|
|
parent::__construct($template); // call the constructor of the extended class |
|
63
|
|
|
|
|
64
|
|
|
$this->template_dir = '/'.$template; // we are packaged as an application |
|
65
|
|
|
} |
|
66
|
|
|
|
|
67
|
|
|
/** |
|
68
|
|
|
* Check if current user agent is supported |
|
69
|
|
|
* |
|
70
|
|
|
* Currently we do NOT support: |
|
71
|
|
|
* - iPhone, iPad, Android, SymbianOS due to iframe scrolling problems of Webkit |
|
72
|
|
|
* - IE < 7 |
|
73
|
|
|
* |
|
74
|
|
|
* @return boolean |
|
75
|
|
|
*/ |
|
76
|
|
|
public static function is_supported_user_agent() |
|
77
|
|
|
{ |
|
78
|
|
|
if (Api\Header\UserAgent::type() == 'msie' && Api\Header\UserAgent::version() < 7) |
|
79
|
|
|
{ |
|
80
|
|
|
return false; |
|
81
|
|
|
} |
|
82
|
|
|
return true; |
|
83
|
|
|
} |
|
84
|
|
|
|
|
85
|
|
|
/** |
|
86
|
|
|
* Reads an returns the width of the sidebox or false if the width is not set |
|
87
|
|
|
*/ |
|
88
|
|
|
private static function get_sidebar_width($app) |
|
89
|
|
|
{ |
|
90
|
|
|
$width = self::DEFAULT_SIDEBAR_WIDTH; |
|
91
|
|
|
|
|
92
|
|
|
//Check whether the width had been stored explicitly for the jdots template, use that value |
|
93
|
|
|
if ($GLOBALS['egw_info']['user']['preferences'][$app]['jdotssideboxwidth']) |
|
94
|
|
|
{ |
|
95
|
|
|
$width = (int)$GLOBALS['egw_info']['user']['preferences'][$app]['jdotssideboxwidth']; |
|
96
|
|
|
// error_log(__METHOD__.__LINE__."($app):$width --> reading jdotssideboxwidth"); |
|
97
|
|
|
} |
|
98
|
|
|
//Otherwise use the legacy "idotssideboxwidth" value |
|
99
|
|
View Code Duplication |
else if ($GLOBALS['egw_info']['user']['preferences'][$app]['idotssideboxwidth']) |
|
100
|
|
|
{ |
|
101
|
|
|
$width = (int)$GLOBALS['egw_info']['user']['preferences'][$app]['idotssideboxwidth']; |
|
102
|
|
|
// error_log(__METHOD__.__LINE__."($app):$width --> reading idotssideboxwidth"); |
|
103
|
|
|
} |
|
104
|
|
|
|
|
105
|
|
|
//Width may not be smaller than MIN_SIDEBAR_WIDTH |
|
106
|
|
|
if ($width < self::MIN_SIDEBAR_WIDTH) |
|
107
|
|
|
$width = self::MIN_SIDEBAR_WIDTH; |
|
108
|
|
|
|
|
109
|
|
|
return $width; |
|
110
|
|
|
} |
|
111
|
|
|
|
|
112
|
|
|
/** |
|
113
|
|
|
* Returns the global width of the sidebox. If the app_specific_sidebar_width had been switched |
|
114
|
|
|
* on, the default width will be returned |
|
115
|
|
|
*/ |
|
116
|
|
|
private static function get_global_sidebar_width() |
|
117
|
|
|
{ |
|
118
|
|
|
return self::DEFAULT_SIDEBAR_WIDTH; |
|
119
|
|
|
} |
|
120
|
|
|
|
|
121
|
|
|
|
|
122
|
|
|
|
|
123
|
|
|
/** |
|
124
|
|
|
* Extract applicaton name from given url (incl. GET parameters) |
|
125
|
|
|
* |
|
126
|
|
|
* @param string $url |
|
127
|
|
|
* @return string appname or NULL if it could not be detected (eg. constructing javascript urls) |
|
128
|
|
|
*/ |
|
129
|
|
|
public static function app_from_url($url) |
|
130
|
|
|
{ |
|
131
|
|
|
$matches = null; |
|
132
|
|
|
if (preg_match('/menuaction=([a-z0-9_-]+)\./i',$url,$matches)) |
|
133
|
|
|
{ |
|
134
|
|
|
return $matches[1]; |
|
135
|
|
|
} |
|
136
|
|
|
if ($GLOBALS['egw_info']['server']['webserver_url'] && |
|
137
|
|
|
($webserver_path = parse_url($GLOBALS['egw_info']['server']['webserver_url'],PHP_URL_PATH))) |
|
138
|
|
|
{ |
|
139
|
|
|
list(,$url) = explode($webserver_path, parse_url($url,PHP_URL_PATH),2); |
|
140
|
|
|
} |
|
141
|
|
|
if (preg_match('/\/([^\/]+)\/([^\/]+\.php)?(\?|\/|$)/',$url,$matches)) |
|
142
|
|
|
{ |
|
143
|
|
|
return $matches[1]; |
|
144
|
|
|
} |
|
145
|
|
|
//error_log(__METHOD__."('$url') could NOT detect application!"); |
|
146
|
|
|
return null; |
|
147
|
|
|
} |
|
148
|
|
|
|
|
149
|
|
|
/** |
|
150
|
|
|
* Link url generator |
|
151
|
|
|
* |
|
152
|
|
|
* @param string $url The url the link is for |
|
153
|
|
|
* @param string|array $extravars Extra params to be passed to the url |
|
154
|
|
|
* @param string $link_app = null if appname or true, some templates generate a special link-handler url |
|
155
|
|
|
* @return string The full url after processing |
|
156
|
|
|
*/ |
|
157
|
|
|
static function link($url = '', $extravars = '', $link_app=null) |
|
158
|
|
|
{ |
|
159
|
|
|
if (is_null($link_app)) $link_app = self::$link_app; |
|
160
|
|
|
$link = parent::link($url, $extravars); |
|
161
|
|
|
|
|
162
|
|
|
// $link_app === true --> detect application, otherwise use given application |
|
163
|
|
|
if ($link_app && (is_string($link_app) || ($link_app = self::app_from_url($link)))) |
|
164
|
|
|
{ |
|
165
|
|
|
// Link gets handled in JS, so quotes need slashes as well as url-encoded |
|
166
|
|
|
// encoded ampersands in get parameters (%26) need to be encoded twise, |
|
167
|
|
|
// so they are still encoded when assigned to window.location |
|
168
|
|
|
$link_with_slashes = str_replace(array('%27','%26'), array('\%27','%2526'), $link); |
|
169
|
|
|
|
|
170
|
|
|
//$link = "javascript:window.egw_link_handler?egw_link_handler('$link','$link_app'):parent.egw_link_handler('$link','$link_app');"; |
|
171
|
|
|
$link = "javascript:egw_link_handler('$link_with_slashes','$link_app')"; |
|
172
|
|
|
} |
|
173
|
|
|
return $link; |
|
174
|
|
|
} |
|
175
|
|
|
|
|
176
|
|
|
/** |
|
177
|
|
|
* Query additional CSP frame-src from current app |
|
178
|
|
|
* |
|
179
|
|
|
* We have to query all apps, as we dont reload frameset! |
|
180
|
|
|
* |
|
181
|
|
|
* @return array |
|
182
|
|
|
*/ |
|
183
|
|
|
protected function _get_csp_frame_src() |
|
184
|
|
|
{ |
|
185
|
|
|
$srcs = array(); |
|
186
|
|
|
foreach(Api\Hooks::process('csp-frame-src') as $src) |
|
187
|
|
|
{ |
|
188
|
|
|
if ($src) $srcs = array_merge($srcs, $src); |
|
189
|
|
|
} |
|
190
|
|
|
return $srcs; |
|
191
|
|
|
} |
|
192
|
|
|
|
|
193
|
|
|
/** |
|
194
|
|
|
* Returns the html-header incl. the opening body tag |
|
195
|
|
|
* |
|
196
|
|
|
* @param array $extra = array() extra attributes passed as data-attribute to egw.js |
|
197
|
|
|
* @return string with Api\Html |
|
198
|
|
|
*/ |
|
199
|
|
|
function header(array $extra=array()) |
|
200
|
|
|
{ |
|
201
|
|
|
// make sure header is output only once |
|
202
|
|
|
if (self::$header_done) return ''; |
|
203
|
|
|
self::$header_done = true; |
|
204
|
|
|
|
|
205
|
|
|
$this->send_headers(); |
|
206
|
|
|
|
|
207
|
|
|
// catch error echo'ed before the header, ob_start'ed in the header.inc.php |
|
208
|
|
|
$content = ob_get_contents(); |
|
209
|
|
|
ob_end_clean(); |
|
210
|
|
|
//error_log(__METHOD__.'('.array2string($extra).') called from:'.function_backtrace()); |
|
211
|
|
|
|
|
212
|
|
|
// the instanciation of the template has to be here and not in the constructor, |
|
213
|
|
|
// as the old template class has problems if restored from the session (php-restore) |
|
214
|
|
|
// todo: check if this is still true |
|
215
|
|
|
$this->tpl = new Template(EGW_SERVER_ROOT.$this->template_dir); |
|
216
|
|
|
if (Api\Header\UserAgent::mobile() || $GLOBALS['egw_info']['user']['preferences']['common']['theme'] == 'mobile') |
|
217
|
|
|
{ |
|
218
|
|
|
$this->tpl->set_file(array('_head' => 'head_mobile.tpl')); |
|
219
|
|
|
} |
|
220
|
|
|
else |
|
221
|
|
|
{ |
|
222
|
|
|
$this->tpl->set_file(array('_head' => 'head.tpl')); |
|
223
|
|
|
} |
|
224
|
|
|
$this->tpl->set_block('_head','head'); |
|
225
|
|
|
$this->tpl->set_block('_head','framework'); |
|
226
|
|
|
|
|
227
|
|
|
// should we draw the framework, or just a header |
|
228
|
|
|
$do_framework = isset($_GET['cd']) && $_GET['cd'] === 'yes'; |
|
229
|
|
|
|
|
230
|
|
|
// load clientside link registry to framework only |
|
231
|
|
|
if (!isset($GLOBALS['egw_info']['flags']['js_link_registry'])) |
|
232
|
|
|
{ |
|
233
|
|
|
$GLOBALS['egw_info']['flags']['js_link_registry'] = $do_framework; |
|
234
|
|
|
} |
|
235
|
|
|
// Loader |
|
236
|
|
|
$this->tpl->set_var('loader_text', lang('please wait...')); |
|
237
|
|
|
|
|
238
|
|
|
if ($do_framework) |
|
239
|
|
|
{ |
|
240
|
|
|
//echo __METHOD__.__LINE__.' do framework ...'.'<br>'; |
|
241
|
|
|
// framework javascript classes only need for framework |
|
242
|
|
|
if (Api\Header\UserAgent::mobile() || $GLOBALS['egw_info']['user']['preferences']['common']['theme'] == 'mobile') |
|
243
|
|
|
{ |
|
244
|
|
|
self::includeJS('.', 'fw_mobile', static::JS_INCLUDE_APP); |
|
245
|
|
|
} |
|
246
|
|
|
else |
|
247
|
|
|
{ |
|
248
|
|
|
self::includeJS('.', 'fw_'.static::APP, static::JS_INCLUDE_APP); |
|
249
|
|
|
} |
|
250
|
|
|
Api\Cache::unsetSession(__CLASS__,'sidebox_md5'); // sideboxes need to be send again |
|
251
|
|
|
|
|
252
|
|
|
$extra['navbar-apps'] = $this->get_navbar_apps($_SERVER['REQUEST_URI']); |
|
253
|
|
|
} |
|
254
|
|
|
// for an url WITHOUT cd=yes --> load framework if not yet loaded: |
|
255
|
|
|
// - if top has framework object, we are all right |
|
256
|
|
|
// - if not we need to check if we have an opener (are a popup window) |
|
257
|
|
|
// - as popups can open further popups, we need to decend all the way down until we find a framework |
|
258
|
|
|
// - only if we cant find a framework in all openers, we redirect to create a new framework |
|
259
|
|
|
if(!$do_framework) |
|
260
|
|
|
{ |
|
261
|
|
|
// fetch sidebox from application and set it in extra data, if we are no popup |
|
262
|
|
|
if (!$GLOBALS['egw_info']['flags']['nonavbar']) |
|
263
|
|
|
{ |
|
264
|
|
|
$this->do_sidebox(); |
|
265
|
|
|
} |
|
266
|
|
|
// for remote manual never check/create framework |
|
267
|
|
|
if (!in_array($GLOBALS['egw_info']['flags']['currentapp'], array('manual', 'login', 'logout', 'sitemgr'))) |
|
268
|
|
|
{ |
|
269
|
|
|
if (empty($GLOBALS['egw_info']['flags']['java_script'])) $GLOBALS['egw_info']['flags']['java_script']=''; |
|
270
|
|
|
$extra['check-framework'] = $_GET['cd'] !== 'no'; |
|
271
|
|
|
} |
|
272
|
|
|
} |
|
273
|
|
|
$this->tpl->set_var($this->_get_header($extra)); |
|
274
|
|
|
$content = $this->tpl->fp('out','head').$content; |
|
275
|
|
|
|
|
276
|
|
|
if (!$do_framework) |
|
277
|
|
|
{ |
|
278
|
|
|
return $content; |
|
279
|
|
|
} |
|
280
|
|
|
|
|
281
|
|
|
// topmenu |
|
282
|
|
|
$vars = $this->_get_navbar($apps = $this->_get_navbar_apps()); |
|
283
|
|
|
$this->tpl->set_var($this->topmenu($vars,$apps)); |
|
284
|
|
|
|
|
285
|
|
|
// hook after_navbar (eg. notifications) |
|
286
|
|
|
$this->tpl->set_var('hook_after_navbar',$this->_get_after_navbar()); |
|
287
|
|
|
|
|
288
|
|
|
//Global sidebar width |
|
289
|
|
|
$this->tpl->set_var('sidebox_width', self::get_global_sidebar_width()); |
|
290
|
|
|
$this->tpl->set_var('sidebox_min_width', self::MIN_SIDEBAR_WIDTH); |
|
291
|
|
|
|
|
292
|
|
|
if (!(Api\Header\UserAgent::mobile() || $GLOBALS['egw_info']['user']['preferences']['common']['theme'] == 'mobile')) |
|
293
|
|
|
{ |
|
294
|
|
|
// logout button |
|
295
|
|
|
$this->tpl->set_var('title_logout', lang("Logout")); |
|
296
|
|
|
$this->tpl->set_var('link_logout', Egw::link('/logout.php')); |
|
297
|
|
|
//Print button title |
|
298
|
|
|
$this->tpl->set_var('title_print', lang("Print current view")); |
|
299
|
|
|
} |
|
300
|
|
|
|
|
301
|
|
|
// add framework div's |
|
302
|
|
|
$this->tpl->set_var($this->_get_footer()); |
|
303
|
|
|
$content .= $this->tpl->fp('out','framework'); |
|
304
|
|
|
$content .= self::footer(false); |
|
305
|
|
|
|
|
306
|
|
|
echo $content; |
|
307
|
|
|
exit(); |
|
308
|
|
|
} |
|
309
|
|
|
|
|
310
|
|
|
private $topmenu_items; |
|
311
|
|
|
private $topmenu_info_items; |
|
312
|
|
|
|
|
313
|
|
|
/** |
|
314
|
|
|
* Compile entries for topmenu: |
|
315
|
|
|
* - regular items: links |
|
316
|
|
|
* - info items |
|
317
|
|
|
* |
|
318
|
|
|
* @param array $vars |
|
319
|
|
|
* @param array $apps |
|
320
|
|
|
* @return array |
|
321
|
|
|
*/ |
|
322
|
|
|
function topmenu(array $vars,array $apps) |
|
323
|
|
|
{ |
|
324
|
|
|
$this->topmenu_items = $this->topmenu_info_items = array(); |
|
325
|
|
|
|
|
326
|
|
|
parent::topmenu($vars,$apps); |
|
327
|
|
|
$vars['topmenu_items'] = "<ul>\n<li>".implode("</li>\n<li>",$this->topmenu_items)."</li>\n</ul>"; |
|
328
|
|
|
$vars['topmenu_info_items'] = ''; |
|
329
|
|
|
foreach($this->topmenu_info_items as $id => $item) |
|
330
|
|
|
{ |
|
331
|
|
|
$vars['topmenu_info_items'] .= '<div class="topmenu_info_item"'. |
|
332
|
|
|
(is_numeric($id) ? '' : ' id="topmenu_info_'.$id.'"').'>'.$item."</div>\n"; |
|
333
|
|
|
} |
|
334
|
|
|
$this->topmenu_items = $this->topmenu_info_items = null; |
|
335
|
|
|
|
|
336
|
|
|
return $vars; |
|
337
|
|
|
} |
|
338
|
|
|
|
|
339
|
|
|
/** |
|
340
|
|
|
* called by hooks to add an icon in the topmenu info location |
|
341
|
|
|
* |
|
342
|
|
|
* @param string $id unique element id |
|
343
|
|
|
* @param string $icon_src src of the icon image. Make sure this nog height then 18pixels |
|
344
|
|
|
* @param string $iconlink where the icon links to |
|
345
|
|
|
* @param booleon $blink set true to make the icon blink |
|
346
|
|
|
* @param mixed $tooltip string containing the tooltip Api\Html, or null of no tooltip |
|
347
|
|
|
* @todo implement in a reasonable way for jdots |
|
348
|
|
|
* @return void |
|
349
|
|
|
*/ |
|
350
|
|
|
function topmenu_info_icon($id,$icon_src,$iconlink,$blink=false,$tooltip=null) |
|
351
|
|
|
{ |
|
352
|
|
|
unset($id,$icon_src,$iconlink,$blink,$tooltip); // not used |
|
353
|
|
|
// not yet implemented, only used in admin/inc/hook_topmenu_info.inc.php to notify about pending updates |
|
354
|
|
|
} |
|
355
|
|
|
|
|
356
|
|
|
/** |
|
357
|
|
|
* Add menu items to the topmenu template class to be displayed |
|
358
|
|
|
* |
|
359
|
|
|
* @param array $app application data |
|
360
|
|
|
* @param mixed $alt_label string with alternative menu item label default value = null |
|
361
|
|
|
* @param string $urlextra string with alternate additional code inside <a>-tag |
|
362
|
|
|
* @access protected |
|
363
|
|
|
* @return void |
|
364
|
|
|
*/ |
|
365
|
|
|
function _add_topmenu_item(array $app_data,$alt_label=null) |
|
366
|
|
|
{ |
|
367
|
|
|
switch($app_data['name']) |
|
368
|
|
|
{ |
|
369
|
|
|
case 'logout': |
|
370
|
|
View Code Duplication |
if (Api\Header\UserAgent::mobile() || $GLOBALS['egw_info']['user']['preferences']['common']['theme'] == 'mobile') |
|
|
|
|
|
|
371
|
|
|
{ |
|
372
|
|
|
|
|
373
|
|
|
} |
|
374
|
|
|
else |
|
375
|
|
|
{ |
|
376
|
|
|
return; // no need for logout in topmenu on jdots |
|
377
|
|
|
} |
|
378
|
|
|
break; |
|
379
|
|
|
|
|
380
|
|
|
case 'manual': |
|
381
|
|
|
$app_data['url'] = "javascript:callManual();"; |
|
382
|
|
|
break; |
|
383
|
|
|
|
|
384
|
|
|
default: |
|
385
|
|
|
if (strpos($app_data['url'],'logout.php') === false && substr($app_data['url'], 0, 11) != 'javascript:') |
|
386
|
|
|
{ |
|
387
|
|
|
$app_data['url'] = "javascript:egw_link_handler('".$app_data['url']."','". |
|
388
|
|
|
(isset($GLOBALS['egw_info']['user']['apps'][$app_data['name']]) ? |
|
389
|
|
|
$app_data['name'] : 'about')."')"; |
|
390
|
|
|
} |
|
391
|
|
|
} |
|
392
|
|
|
$id = $app_data['id'] ? $app_data['id'] : ($app_data['name'] ? $app_data['name'] : $app_data['title']); |
|
393
|
|
|
$title = htmlspecialchars($alt_label ? $alt_label : $app_data['title']); |
|
394
|
|
|
$this->topmenu_items[] = '<a id="topmenu_' . $id . '" href="'.htmlspecialchars($app_data['url']).'" title="'.$app_data['title'].'">'.$title.'</a>'; |
|
395
|
|
|
} |
|
396
|
|
|
|
|
397
|
|
|
/** |
|
398
|
|
|
* Add info items to the topmenu template class to be displayed |
|
399
|
|
|
* |
|
400
|
|
|
* @param string $content Api\Html of item |
|
401
|
|
|
* @param string $id = null |
|
402
|
|
|
* @access protected |
|
403
|
|
|
* @return void |
|
404
|
|
|
*/ |
|
405
|
|
|
function _add_topmenu_info_item($content, $id=null) |
|
406
|
|
|
{ |
|
407
|
|
|
if(strpos($content,'menuaction=admin.admin_accesslog.sessions') !== false) |
|
408
|
|
|
{ |
|
409
|
|
|
$content = preg_replace('/href="([^"]+)"/',"href=\"javascript:egw_link_handler('\\1','admin')\"",$content); |
|
410
|
|
|
} |
|
411
|
|
|
if ($id) |
|
412
|
|
|
{ |
|
413
|
|
|
$this->topmenu_info_items[$id] = $content; |
|
414
|
|
|
} |
|
415
|
|
|
else |
|
416
|
|
|
{ |
|
417
|
|
|
$this->topmenu_info_items[] = $content; |
|
418
|
|
|
} |
|
419
|
|
|
} |
|
420
|
|
|
|
|
421
|
|
|
/** |
|
422
|
|
|
* Change timezone |
|
423
|
|
|
* |
|
424
|
|
|
* @param string $tz |
|
425
|
|
|
*/ |
|
426
|
|
|
static function ajax_tz_selection($tz) |
|
427
|
|
|
{ |
|
428
|
|
|
Api\DateTime::setUserPrefs($tz); // throws exception, if tz is invalid |
|
429
|
|
|
|
|
430
|
|
|
$GLOBALS['egw']->preferences->read_repository(); |
|
431
|
|
|
$GLOBALS['egw']->preferences->add('common','tz',$tz); |
|
432
|
|
|
$GLOBALS['egw']->preferences->save_repository(); |
|
433
|
|
|
} |
|
434
|
|
|
|
|
435
|
|
|
/** |
|
436
|
|
|
* Flag if do_sidebox() was called |
|
437
|
|
|
* |
|
438
|
|
|
* @var boolean |
|
439
|
|
|
*/ |
|
440
|
|
|
protected $sidebox_done = false; |
|
441
|
|
|
|
|
442
|
|
|
/** |
|
443
|
|
|
* Returns the Api\Html from the body-tag til the main application area (incl. opening div tag) |
|
444
|
|
|
* |
|
445
|
|
|
* jDots does NOT use a navbar, but it tells us that application might want a sidebox! |
|
446
|
|
|
* |
|
447
|
|
|
* @return string |
|
448
|
|
|
*/ |
|
449
|
|
|
function navbar() |
|
450
|
|
|
{ |
|
451
|
|
|
$header = ''; |
|
452
|
|
|
if (!self::$header_done) |
|
453
|
|
|
{ |
|
454
|
|
|
$header = $this->header(); |
|
455
|
|
|
} |
|
456
|
|
|
$GLOBALS['egw_info']['flags']['nonavbar'] = false; |
|
457
|
|
|
|
|
458
|
|
|
if (!$this->sidebox_done && self::$header_done) |
|
459
|
|
|
{ |
|
460
|
|
|
$this->do_sidebox(); |
|
461
|
|
|
return $header.'<span id="late-sidebox" data-setSidebox="'.htmlspecialchars(json_encode(self::$extra['setSidebox'])).'"/>'; |
|
462
|
|
|
} |
|
463
|
|
|
|
|
464
|
|
|
return $header; |
|
465
|
|
|
} |
|
466
|
|
|
|
|
467
|
|
|
/** |
|
468
|
|
|
* Set sidebox content in self::$data['setSidebox'] |
|
469
|
|
|
* |
|
470
|
|
|
* We store in the session the md5 of each sidebox menu already send to client. |
|
471
|
|
|
* If the framework get reloaded, that list gets cleared in header(); |
|
472
|
|
|
* Most apps never change sidebox, so we not even need to generate it more then once. |
|
473
|
|
|
*/ |
|
474
|
|
|
function do_sidebox() |
|
475
|
|
|
{ |
|
476
|
|
|
$this->sidebox_done = true; |
|
477
|
|
|
|
|
478
|
|
|
$app = $GLOBALS['egw_info']['flags']['currentapp']; |
|
479
|
|
|
|
|
480
|
|
|
// only send admin sidebox, for admin index url (when clicked on admin), |
|
481
|
|
|
// not for other admin pages, called eg. from sidebox menu of other apps |
|
482
|
|
|
// --> that way we always stay in the app, and NOT open admin sidebox for an app tab!!! |
|
483
|
|
|
if ($app == 'admin' && substr($_SERVER['PHP_SELF'],-16) != '/admin/index.php' && |
|
484
|
|
|
$_GET['menuaction'] != 'admin.admin_ui.index') |
|
485
|
|
|
{ |
|
486
|
|
|
//error_log(__METHOD__."() app=$app, menuaction=$_GET[menuaction], PHP_SELF=$_SERVER[PHP_SELF] --> sidebox request ignored"); |
|
487
|
|
|
return; |
|
488
|
|
|
} |
|
489
|
|
|
$md5_session =& Api\Cache::getSession(__CLASS__,'sidebox_md5'); |
|
490
|
|
|
|
|
491
|
|
|
//Set the sidebox content |
|
492
|
|
|
$sidebox = $this->get_sidebox($app); |
|
493
|
|
|
$md5 = md5(json_encode($sidebox)); |
|
494
|
|
|
|
|
495
|
|
|
if ($md5_session[$app] !== $md5) |
|
496
|
|
|
{ |
|
497
|
|
|
//error_log(__METHOD__."() header changed md5_session[$app]!=='$md5' --> setting it on self::\$extra[setSidebox]"); |
|
498
|
|
|
$md5_session[$app] = $md5; // update md5 in session |
|
499
|
|
|
self::$extra['setSidebox'] = array($app, $sidebox, $md5); |
|
500
|
|
|
} |
|
501
|
|
|
//else error_log(__METHOD__."() md5_session[$app]==='$md5' --> nothing to do"); |
|
502
|
|
|
} |
|
503
|
|
|
|
|
504
|
|
|
/** |
|
505
|
|
|
* Return true if we are rendering the top-level EGroupware window |
|
506
|
|
|
* |
|
507
|
|
|
* A top-level EGroupware window has a navbar: eg. no popup and for a framed template (jdots) only frameset itself |
|
508
|
|
|
* |
|
509
|
|
|
* @return boolean $consider_navbar_not_yet_called_as_true=true ignored by jdots, we only care for cd=yes GET param |
|
510
|
|
|
* @return boolean |
|
511
|
|
|
*/ |
|
512
|
|
|
public function isTop($consider_navbar_not_yet_called_as_true=true) |
|
513
|
|
|
{ |
|
514
|
|
|
unset($consider_navbar_not_yet_called_as_true); // not used |
|
515
|
|
|
return isset($_GET['cd']) && $_GET['cd'] === 'yes'; |
|
516
|
|
|
} |
|
517
|
|
|
|
|
518
|
|
|
/** |
|
519
|
|
|
* Array containing sidebox menus by applications and menu-name |
|
520
|
|
|
* |
|
521
|
|
|
* @var array |
|
522
|
|
|
*/ |
|
523
|
|
|
protected $sideboxes; |
|
524
|
|
|
|
|
525
|
|
|
/** |
|
526
|
|
|
* Should calls the first call to self::sidebox create an opened menu |
|
527
|
|
|
* |
|
528
|
|
|
* @var boolean |
|
529
|
|
|
*/ |
|
530
|
|
|
protected $sidebox_menu_opened = true; |
|
531
|
|
|
|
|
532
|
|
|
/** |
|
533
|
|
|
* Callback for sideboxes hooks, collects the data in a private var |
|
534
|
|
|
* |
|
535
|
|
|
* @param string $appname |
|
536
|
|
|
* @param string $menu_title |
|
537
|
|
|
* @param array $file |
|
538
|
|
|
* @param string $type = null 'admin', 'preferences', 'favorites', ... |
|
539
|
|
|
*/ |
|
540
|
|
|
public function sidebox($appname,$menu_title,$file,$type=null) |
|
541
|
|
|
{ |
|
542
|
|
|
if (!isset($file['menuOpened'])) $file['menuOpened'] = (boolean)$this->sidebox_menu_opened; |
|
543
|
|
|
//error_log(__METHOD__."('$appname', '$menu_title', file[menuOpened]=$file[menuOpened], ...) this->sidebox_menu_opened=$this->sidebox_menu_opened"); |
|
544
|
|
|
$this->sidebox_menu_opened = false; |
|
545
|
|
|
|
|
546
|
|
|
// fix app admin menus to use admin.admin_ui.index loader |
|
547
|
|
|
if (($type == 'admin' || $menu_title == lang('Admin')) && $appname != 'admin') |
|
548
|
|
|
{ |
|
549
|
|
|
$file = preg_replace("/^(javascript:egw_link_handler\(')(.*)menuaction=([^&]+)(.*)(','[^']+'\))$/", |
|
550
|
|
|
'$1$2menuaction=admin.admin_ui.index&load=$3$4&ajax=true\',\'admin\')', $file_was=$file); |
|
551
|
|
|
} |
|
552
|
|
|
|
|
553
|
|
|
$this->sideboxes[$appname][$menu_title] = $file; |
|
554
|
|
|
} |
|
555
|
|
|
|
|
556
|
|
|
/** |
|
557
|
|
|
* Return sidebox data for an application |
|
558
|
|
|
* |
|
559
|
|
|
* @param $appname |
|
560
|
|
|
* @return array of array( |
|
561
|
|
|
* 'menu_name' => (string), // menu name, currently md5(title) |
|
562
|
|
|
* 'title' => (string), // translated title to display |
|
563
|
|
|
* 'opened' => (boolean), // menu opend or closed |
|
564
|
|
|
* 'entries' => array( |
|
565
|
|
|
* array( |
|
566
|
|
|
* 'lang_item' => translated menu item or Api\Html, i item_link === false |
|
567
|
|
|
* 'icon_or_star' => url of bullet images, or false for none |
|
568
|
|
|
* 'item_link' => url or false (lang_item contains complete html) |
|
569
|
|
|
* 'target' => target attribute fragment, ' target="..."' |
|
570
|
|
|
* ), |
|
571
|
|
|
* // more entries |
|
572
|
|
|
* ), |
|
573
|
|
|
* ), |
|
574
|
|
|
* array ( |
|
575
|
|
|
* // next menu |
|
576
|
|
|
* ) |
|
577
|
|
|
*/ |
|
578
|
|
|
public function get_sidebox($appname) |
|
579
|
|
|
{ |
|
580
|
|
|
if (!isset($this->sideboxes[$appname])) |
|
581
|
|
|
{ |
|
582
|
|
|
self::$link_app = $appname; |
|
583
|
|
|
// allow other apps to hook into sidebox menu of an app, hook-name: sidebox_$appname |
|
584
|
|
|
$this->sidebox_menu_opened = true; |
|
585
|
|
|
Api\Hooks::process('sidebox_'.$appname,array($appname),true); // true = call independent of app-permissions |
|
586
|
|
|
|
|
587
|
|
|
// calling the old hook |
|
588
|
|
|
$this->sidebox_menu_opened = true; |
|
589
|
|
|
Api\Hooks::single('sidebox_menu',$appname); |
|
590
|
|
|
self::$link_app = null; |
|
591
|
|
|
|
|
592
|
|
|
// allow other apps to hook into sidebox menu of every app: sidebox_all |
|
593
|
|
|
Api\Hooks::process('sidebox_all',array($GLOBALS['egw_info']['flags']['currentapp']),true); |
|
594
|
|
|
} |
|
595
|
|
|
//If there still is no sidebox content, return null here |
|
596
|
|
|
if (!isset($this->sideboxes[$appname])) |
|
597
|
|
|
{ |
|
598
|
|
|
return null; |
|
599
|
|
|
} |
|
600
|
|
|
|
|
601
|
|
|
$data = array(); |
|
602
|
|
|
$sendToBottom = array(); |
|
603
|
|
|
foreach($this->sideboxes[$appname] as $menu_name => &$file) |
|
604
|
|
|
{ |
|
605
|
|
|
$current_menu = array( |
|
606
|
|
|
'menu_name' => md5($menu_name), // can contain Api\Html tags and javascript! |
|
607
|
|
|
'title' => $menu_name, |
|
608
|
|
|
'entries' => array(), |
|
609
|
|
|
'opened' => (boolean)$file['menuOpened'], |
|
610
|
|
|
); |
|
611
|
|
|
foreach($file as $item_text => $item_link) |
|
612
|
|
|
{ |
|
613
|
|
|
if ($item_text === 'menuOpened' || $item_text === 'sendToBottom' ||// flag, not menu entry |
|
614
|
|
|
$item_text === '_NewLine_' || $item_link === '_NewLine_') |
|
615
|
|
|
{ |
|
616
|
|
|
continue; |
|
617
|
|
|
} |
|
618
|
|
|
if (strtolower($item_text) == 'grant access' && $GLOBALS['egw_info']['server']['deny_user_grants_access']) |
|
619
|
|
|
{ |
|
620
|
|
|
continue; |
|
621
|
|
|
} |
|
622
|
|
|
|
|
623
|
|
|
$var = array(); |
|
624
|
|
|
$var['icon_or_star'] = $GLOBALS['egw_info']['server']['webserver_url'] . $this->template_dir.'/images/bullet.png'; |
|
625
|
|
|
$var['target'] = ''; |
|
626
|
|
|
if(is_array($item_link)) |
|
627
|
|
|
{ |
|
628
|
|
|
if(isset($item_link['icon'])) |
|
629
|
|
|
{ |
|
630
|
|
|
$app = isset($item_link['app']) ? $item_link['app'] : $appname; |
|
631
|
|
|
$var['icon_or_star'] = $item_link['icon'] ? Api\Image::find($app,$item_link['icon']) : False; |
|
632
|
|
|
} |
|
633
|
|
|
$var['lang_item'] = isset($item_link['no_lang']) && $item_link['no_lang'] ? $item_link['text'] : lang($item_link['text']); |
|
634
|
|
|
$var['item_link'] = $item_link['link']; |
|
635
|
|
|
if ($item_link['target']) |
|
636
|
|
|
{ |
|
637
|
|
|
// we only support real targets not Api\Html markup with target in it |
|
638
|
|
|
if (strpos($item_link['target'], 'target=') === false && |
|
639
|
|
|
strpos($item_link['target'], '"') === false) |
|
640
|
|
|
{ |
|
641
|
|
|
$var['target'] = $item_link['target']; |
|
642
|
|
|
} |
|
643
|
|
|
} |
|
644
|
|
|
} |
|
645
|
|
|
else |
|
646
|
|
|
{ |
|
647
|
|
|
$var['lang_item'] = lang($item_text); |
|
648
|
|
|
$var['item_link'] = $item_link; |
|
649
|
|
|
} |
|
650
|
|
|
$current_menu['entries'][] = $var; |
|
651
|
|
|
} |
|
652
|
|
|
|
|
653
|
|
|
if ($file['sendToBottom']) |
|
654
|
|
|
{ |
|
655
|
|
|
$sendToBottom[] = $current_menu; |
|
656
|
|
|
} |
|
657
|
|
|
else |
|
658
|
|
|
{ |
|
659
|
|
|
$data[] = $current_menu; |
|
660
|
|
|
} |
|
661
|
|
|
} |
|
662
|
|
|
return array_merge($data, $sendToBottom); |
|
663
|
|
|
} |
|
664
|
|
|
|
|
665
|
|
|
/** |
|
666
|
|
|
* Ajax callback which is called whenever a previously opened tab is closed or |
|
667
|
|
|
* opened. |
|
668
|
|
|
* |
|
669
|
|
|
* @param $tablist is an array which contains each tab as an associative array |
|
670
|
|
|
* with the keys 'appName' and 'active' |
|
671
|
|
|
*/ |
|
672
|
|
|
public static function ajax_tab_changed_state($tablist) |
|
673
|
|
|
{ |
|
674
|
|
|
$tabs = array(); |
|
675
|
|
|
foreach($tablist as $data) |
|
676
|
|
|
{ |
|
677
|
|
|
$tabs[] = $data['appName']; |
|
678
|
|
|
if ($data['active']) $active = $data['appName']; |
|
679
|
|
|
} |
|
680
|
|
|
// send app a notification, that it's tab got closed |
|
681
|
|
|
// used eg. in phpFreeChat to leave the chat |
|
682
|
|
|
if (($old_tabs = Api\Cache::getSession(__CLASS__, 'open_tabs'))) |
|
683
|
|
|
{ |
|
684
|
|
|
foreach(array_diff(explode(',',$old_tabs),$tabs) as $app) |
|
685
|
|
|
{ |
|
686
|
|
|
//error_log("Tab '$app' closed, old_tabs=$old_tabs"); |
|
687
|
|
|
Api\Hooks::single(array( |
|
688
|
|
|
'location' => 'tab_closed', |
|
689
|
|
|
'app' => $app, |
|
690
|
|
|
), $app); |
|
691
|
|
|
} |
|
692
|
|
|
} |
|
693
|
|
|
$open = implode(',',$tabs); |
|
694
|
|
|
|
|
695
|
|
|
if ($open != $GLOBALS['egw_info']['user']['preferences']['common']['open_tabs'] || |
|
696
|
|
|
$active != $GLOBALS['egw_info']['user']['preferences']['common']['active_tab']) |
|
697
|
|
|
{ |
|
698
|
|
|
//error_log(__METHOD__.'('.array2string($tablist).") storing common prefs: open_tabs='$tabs', active_tab='$active'"); |
|
699
|
|
|
Api\Cache::setSession(__CLASS__, 'open_tabs', $open); |
|
700
|
|
|
$GLOBALS['egw']->preferences->read_repository(); |
|
701
|
|
|
$GLOBALS['egw']->preferences->add('common', 'open_tabs', $open); |
|
702
|
|
|
$GLOBALS['egw']->preferences->add('common', 'active_tab', $active); |
|
703
|
|
|
$GLOBALS['egw']->preferences->save_repository(true); |
|
704
|
|
|
} |
|
705
|
|
|
} |
|
706
|
|
|
|
|
707
|
|
|
/** |
|
708
|
|
|
* Return sidebox data for an application |
|
709
|
|
|
* |
|
710
|
|
|
* Format see get_sidebox() |
|
711
|
|
|
* |
|
712
|
|
|
* @param $appname |
|
713
|
|
|
*/ |
|
714
|
|
|
public function ajax_sidebox($appname, $md5) |
|
715
|
|
|
{ |
|
716
|
|
|
// dont block session, while we read sidebox, they are not supposed to change something in the session |
|
717
|
|
|
$GLOBALS['egw']->session->commit_session(); |
|
718
|
|
|
|
|
719
|
|
|
$response = Api\Json\Response::get(); |
|
720
|
|
|
$sidebox = $this->get_sidebox($appname); |
|
721
|
|
|
$encoded = json_encode($sidebox); |
|
722
|
|
|
$new_md5 = md5($encoded); |
|
723
|
|
|
|
|
724
|
|
|
$response_array = array(); |
|
725
|
|
|
$response_array['md5'] = $new_md5; |
|
726
|
|
|
|
|
727
|
|
|
if ($new_md5 != $md5) |
|
728
|
|
|
{ |
|
729
|
|
|
//TODO: Add some proper solution to be able to attach the already |
|
730
|
|
|
//JSON data to the response in order to gain some performace improvements. |
|
731
|
|
|
$response_array['data'] = $sidebox; |
|
732
|
|
|
} |
|
733
|
|
|
|
|
734
|
|
|
$response->data($response_array); |
|
735
|
|
|
} |
|
736
|
|
|
|
|
737
|
|
|
/** |
|
738
|
|
|
* Stores the width of the sidebox menu depending on the sidebox menu settings |
|
739
|
|
|
* @param $appname the name of the application |
|
740
|
|
|
* @param $width the width set |
|
741
|
|
|
*/ |
|
742
|
|
|
public static function ajax_sideboxwidth($appname, $width) |
|
743
|
|
|
{ |
|
744
|
|
|
//error_log(__METHOD__."($appname, $width)"); |
|
745
|
|
|
//Check whether the supplied parameters are valid |
|
746
|
|
|
if (is_int($width) && $GLOBALS['egw_info']['user']['apps'][$appname]) |
|
747
|
|
|
{ |
|
748
|
|
|
self::set_sidebar_width($appname, $width); |
|
|
|
|
|
|
749
|
|
|
} |
|
750
|
|
|
} |
|
751
|
|
|
|
|
752
|
|
|
/** |
|
753
|
|
|
* Stores the user defined sorting of the applications inside the preferences |
|
754
|
|
|
* |
|
755
|
|
|
* @param array $apps |
|
756
|
|
|
*/ |
|
757
|
|
|
public static function ajax_appsort(array $apps) |
|
758
|
|
|
{ |
|
759
|
|
|
$order = array(); |
|
760
|
|
|
$i = 0; |
|
761
|
|
|
|
|
762
|
|
|
//Parse the "$apps" array for valid content (security) |
|
763
|
|
|
foreach($apps as $app) |
|
764
|
|
|
{ |
|
765
|
|
|
//Check whether the app really exists and add it to the $app_arr var |
|
766
|
|
|
if ($GLOBALS['egw_info']['user']['apps'][$app]) |
|
767
|
|
|
{ |
|
768
|
|
|
$order[$app] = $i; |
|
769
|
|
|
$i++; |
|
770
|
|
|
} |
|
771
|
|
|
} |
|
772
|
|
|
|
|
773
|
|
|
//Store the order array inside the common user Api\Preferences |
|
774
|
|
|
$GLOBALS['egw']->preferences->read_repository(); |
|
775
|
|
|
$GLOBALS['egw']->preferences->add('common', 'user_apporder', serialize($order)); |
|
776
|
|
|
$GLOBALS['egw']->preferences->save_repository(true); |
|
777
|
|
|
} |
|
778
|
|
|
|
|
779
|
|
|
/** |
|
780
|
|
|
* Prepare an array with apps used to render the navbar |
|
781
|
|
|
* |
|
782
|
|
|
* @return array of array( |
|
783
|
|
|
* 'name' => app / directory name |
|
784
|
|
|
* 'title' => translated application title |
|
785
|
|
|
* 'url' => url to call for index |
|
786
|
|
|
* 'icon' => icon name |
|
787
|
|
|
* 'icon_app' => application of icon |
|
788
|
|
|
* 'icon_hover' => hover-icon, if used by template |
|
789
|
|
|
* 'target'=> ' target="..."' attribute fragment to open url in target, popup or '' |
|
790
|
|
|
* ) |
|
791
|
|
|
*/ |
|
792
|
|
|
public function navbar_apps() |
|
793
|
|
|
{ |
|
794
|
|
|
$apps = parent::_get_navbar_apps(Api\Image::svg_usable()); // use svg if usable in browser |
|
|
|
|
|
|
795
|
|
|
|
|
796
|
|
|
//Add its sidebox width to each app |
|
797
|
|
|
foreach ($apps as $app => &$data) |
|
798
|
|
|
{ |
|
799
|
|
|
$data['sideboxwidth'] = self::get_sidebar_width($app); |
|
800
|
|
|
// overwrite icon with svg, if supported by browser |
|
801
|
|
|
unset($data['icon_hover']); // not used in jdots |
|
802
|
|
|
} |
|
803
|
|
|
|
|
804
|
|
|
unset($apps['logout']); // never display it |
|
805
|
|
|
if (isset($apps['about'])) $apps['about']['noNavbar'] = true; |
|
806
|
|
|
if (isset($apps['preferences'])) $apps['preferences']['noNavbar'] = true; |
|
807
|
|
|
if (isset($apps['manual'])) $apps['manual']['noNavbar'] = true; |
|
808
|
|
|
if (isset($apps['home'])) $apps['home']['noNavbar'] = true; |
|
809
|
|
|
|
|
810
|
|
|
// no need for website icon, if we have sitemgr |
|
811
|
|
|
if (isset($apps['sitemgr']) && isset($apps['sitemgr-link'])) |
|
812
|
|
|
{ |
|
813
|
|
|
unset($apps['sitemgr-link']); |
|
814
|
|
|
} |
|
815
|
|
|
|
|
816
|
|
|
return $apps; |
|
817
|
|
|
} |
|
818
|
|
|
|
|
819
|
|
|
/** |
|
820
|
|
|
* Prepare an array with apps used to render the navbar |
|
821
|
|
|
* |
|
822
|
|
|
* @param string $url contains the current url on the client side. It is used to |
|
823
|
|
|
* determine whether the default app/home should be opened on the client |
|
824
|
|
|
* or whether a specific application-url has been given. |
|
825
|
|
|
* |
|
826
|
|
|
* @return array of array( |
|
827
|
|
|
* 'name' => app / directory name |
|
828
|
|
|
* 'title' => translated application title |
|
829
|
|
|
* 'url' => url to call for index |
|
830
|
|
|
* 'icon' => icon name |
|
831
|
|
|
* 'icon_app' => application of icon |
|
832
|
|
|
* 'icon_hover' => hover-icon, if used by template |
|
833
|
|
|
* 'target'=> ' target="..."' attribute fragment to open url in target, popup or '' |
|
834
|
|
|
* 'opened' => unset or false if the tab should not be opened, otherwise the numeric position in the tab list |
|
835
|
|
|
* 'active' => true if this tab should be the active one when it is restored, otherwise unset or false |
|
836
|
|
|
* 'openOnce' => unset or the url which will be opened when the tab is restored |
|
837
|
|
|
* ) |
|
838
|
|
|
*/ |
|
839
|
|
|
protected function get_navbar_apps($url) |
|
840
|
|
|
{ |
|
841
|
|
|
$apps = $this->navbar_apps(); |
|
842
|
|
|
|
|
843
|
|
|
// open tab for default app, if no other tab is set |
|
844
|
|
View Code Duplication |
if (!($default_app = $GLOBALS['egw_info']['user']['preferences']['common']['default_app'])) |
|
845
|
|
|
{ |
|
846
|
|
|
$default_app = 'home'; |
|
847
|
|
|
} |
|
848
|
|
|
if (isset($apps[$default_app])) |
|
849
|
|
|
{ |
|
850
|
|
|
$apps[$default_app]['isDefault'] = true; |
|
851
|
|
|
} |
|
852
|
|
|
|
|
853
|
|
|
// check if user called a specific url --> open it as active tab |
|
854
|
|
|
$last_direct_url =& Api\Cache::getSession(__CLASS__, 'last_direct_url'); |
|
855
|
|
|
if ($url !== $last_direct_url) |
|
856
|
|
|
{ |
|
857
|
|
|
$active_tab = $url_tab = self::app_from_url($url); |
|
858
|
|
|
$last_direct_url = $url; |
|
859
|
|
|
} |
|
860
|
|
|
|
|
861
|
|
|
//self::app_from_url might return an application the user has no rights |
|
862
|
|
|
//for or may return an application that simply does not exist. So check first |
|
863
|
|
|
//whether the $active_tab really exists in the $apps array. |
|
864
|
|
|
if ($active_tab && array_key_exists($active_tab, $apps)) |
|
865
|
|
|
{ |
|
866
|
|
|
// Do not remove cd=yes if it's an ajax=true app |
|
867
|
|
|
if (strpos( $apps[$active_tab]['url'],'ajax=true') !== False) |
|
868
|
|
|
{ |
|
869
|
|
|
$url = preg_replace('/[&?]cd=yes/','',$url); |
|
870
|
|
|
} |
|
871
|
|
|
$apps[$active_tab]['openOnce'] = $url; |
|
872
|
|
|
$store_prefs = true; |
|
873
|
|
|
} |
|
874
|
|
View Code Duplication |
else |
|
875
|
|
|
{ |
|
876
|
|
|
$active_tab = $GLOBALS['egw_info']['user']['preferences']['common']['active_tab']; |
|
877
|
|
|
if (!$active_tab) $active_tab = $default_app; |
|
878
|
|
|
} |
|
879
|
|
|
// if we have the open tabs in the session, use it instead the maybe forced common prefs open_tabs |
|
880
|
|
|
if (!($open_tabs = Api\Cache::getSession(__CLASS__, 'open_tabs'))) |
|
881
|
|
|
{ |
|
882
|
|
|
$open_tabs = $GLOBALS['egw_info']['user']['preferences']['common']['open_tabs']; |
|
883
|
|
|
} |
|
884
|
|
|
$open_tabs = $open_tabs ? explode(',',$open_tabs) : array(); |
|
885
|
|
|
if ($active_tab && !in_array($active_tab,$open_tabs)) |
|
886
|
|
|
{ |
|
887
|
|
|
$open_tabs[] = $active_tab; |
|
888
|
|
|
$store_prefs = true; |
|
889
|
|
|
} |
|
890
|
|
|
if ($store_prefs) |
|
891
|
|
|
{ |
|
892
|
|
|
$GLOBALS['egw']->preferences->read_repository(); |
|
893
|
|
|
$GLOBALS['egw']->preferences->add('common', 'open_tabs', implode(',',$open_tabs)); |
|
894
|
|
|
$GLOBALS['egw']->preferences->add('common', 'active_tab', $active_tab); |
|
895
|
|
|
$GLOBALS['egw']->preferences->save_repository(true); |
|
896
|
|
|
} |
|
897
|
|
|
|
|
898
|
|
|
//error_log(__METHOD__."('$url') url_tab='$url_tab', active_tab=$active_tab, open_tabs=".array2string($open_tabs)); |
|
899
|
|
|
// Restore Tabs |
|
900
|
|
|
foreach($open_tabs as $n => $app) |
|
901
|
|
|
{ |
|
902
|
|
|
if (isset($apps[$app])) // user might no longer have app rights |
|
903
|
|
|
{ |
|
904
|
|
|
$apps[$app]['opened'] = $n; |
|
905
|
|
|
if ($app == $active_tab) |
|
906
|
|
|
{ |
|
907
|
|
|
$apps[$app]['active'] = true; |
|
908
|
|
|
} |
|
909
|
|
|
} |
|
910
|
|
|
} |
|
911
|
|
|
return array_values($apps); |
|
912
|
|
|
} |
|
913
|
|
|
|
|
914
|
|
|
/** |
|
915
|
|
|
* Have we output the footer |
|
916
|
|
|
* |
|
917
|
|
|
* @var boolean |
|
918
|
|
|
*/ |
|
919
|
|
|
static private $footer_done; |
|
920
|
|
|
|
|
921
|
|
|
/** |
|
922
|
|
|
* Returns the Api\Html from the closing div of the main application area to the closing html-tag |
|
923
|
|
|
* |
|
924
|
|
|
* @param boolean $no_framework = true |
|
925
|
|
|
* @return string |
|
926
|
|
|
*/ |
|
927
|
|
|
function footer($no_framework=true) |
|
928
|
|
|
{ |
|
929
|
|
|
//error_log(__METHOD__."($no_framework) footer_done=".array2string(self::$footer_done).' '.function_backtrace()); |
|
930
|
|
|
if (self::$footer_done) return; // prevent (multiple) footers |
|
931
|
|
|
self::$footer_done = true; |
|
932
|
|
|
|
|
933
|
|
|
if (!isset($GLOBALS['egw_info']['flags']['nofooter']) || !$GLOBALS['egw_info']['flags']['nofooter']) |
|
934
|
|
|
{ |
|
935
|
|
|
if ($no_framework && $GLOBALS['egw_info']['user']['preferences']['common']['show_generation_time']) |
|
936
|
|
|
{ |
|
937
|
|
|
$vars = $this->_get_footer(); |
|
938
|
|
|
$footer = "\n".$vars['page_generation_time']."\n"; |
|
939
|
|
|
} |
|
940
|
|
|
} |
|
941
|
|
|
return $footer. |
|
942
|
|
|
$GLOBALS['egw_info']['flags']['need_footer']."\n". // eg. javascript, which need to be at the end of the page |
|
943
|
|
|
"</body>\n</html>\n"; |
|
944
|
|
|
} |
|
945
|
|
|
|
|
946
|
|
|
/** |
|
947
|
|
|
* Return javascript (eg. for onClick) to open manual with given url |
|
948
|
|
|
* |
|
949
|
|
|
* @param string $url |
|
950
|
|
|
* @return string |
|
951
|
|
|
*/ |
|
952
|
|
|
function open_manual_js($url) |
|
953
|
|
|
{ |
|
954
|
|
|
return "callManual('$url')"; |
|
955
|
|
|
} |
|
956
|
|
|
|
|
957
|
|
|
/** |
|
958
|
|
|
* JSON reponse object |
|
959
|
|
|
* |
|
960
|
|
|
* If set output is requested for an ajax response --> no header, navbar or footer |
|
961
|
|
|
* |
|
962
|
|
|
* @var Api\Json\Response |
|
963
|
|
|
*/ |
|
964
|
|
|
public $response; |
|
965
|
|
|
|
|
966
|
|
|
/** |
|
967
|
|
|
* Run a link via ajax, returning content via egw_json_response->data() |
|
968
|
|
|
* |
|
969
|
|
|
* This behavies like /index.php, but returns the content via json. |
|
970
|
|
|
* |
|
971
|
|
|
* @param string $link |
|
972
|
|
|
*/ |
|
973
|
|
|
public static function ajax_exec($link) |
|
974
|
|
|
{ |
|
975
|
|
|
$parts = parse_url($link); |
|
976
|
|
|
$_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] = $parts['path']; |
|
977
|
|
|
if ($parts['query']) |
|
978
|
|
|
{ |
|
979
|
|
|
$_SERVER['REQUEST_URI'] = '?'.$parts['query']; |
|
980
|
|
|
parse_str($parts['query'],$_GET); |
|
981
|
|
|
$_REQUEST = $_GET; // some apps use $_REQUEST to check $_GET or $_POST |
|
982
|
|
|
} |
|
983
|
|
|
|
|
984
|
|
|
if (!isset($_GET['menuaction'])) |
|
985
|
|
|
{ |
|
986
|
|
|
throw new Api\Exception\WrongParameter(__METHOD__."('$link') no menuaction set!"); |
|
987
|
|
|
} |
|
988
|
|
|
// set session action |
|
989
|
|
|
$GLOBALS['egw']->session->set_action('Ajax: '.$_GET['menuaction']); |
|
990
|
|
|
|
|
991
|
|
|
list($app,$class,$method) = explode('.',$_GET['menuaction']); |
|
992
|
|
|
|
|
993
|
|
|
if (!isset($GLOBALS['egw_info']['user']['apps'][$app])) |
|
994
|
|
|
{ |
|
995
|
|
|
throw new Api\Exception\NoPermission\App($app); |
|
996
|
|
|
} |
|
997
|
|
|
$GLOBALS['egw_info']['flags']['currentapp'] = $app; |
|
998
|
|
|
|
|
999
|
|
|
$GLOBALS['egw']->framework->response = Api\Json\Response::get(); |
|
1000
|
|
|
|
|
1001
|
|
|
$GLOBALS[$class] = $obj = CreateObject($app.'.'.$class); |
|
|
|
|
|
|
1002
|
|
|
|
|
1003
|
|
|
if(!is_array($obj->public_functions) || !$obj->public_functions[$method]) |
|
1004
|
|
|
{ |
|
1005
|
|
|
throw new Api\Exception\NoPermission("Bad menuaction {$_GET['menuaction']}, not listed in public_functions!"); |
|
1006
|
|
|
} |
|
1007
|
|
|
// dont send header and footer |
|
1008
|
|
|
self::$header_done = self::$footer_done = true; |
|
1009
|
|
|
|
|
1010
|
|
|
// need to call do_sidebox, as header() with $header_done does NOT! |
|
1011
|
|
|
$GLOBALS['egw']->framework->do_sidebox(); |
|
1012
|
|
|
|
|
1013
|
|
|
// send Api\Preferences, so we dont need to request them in a second ajax request |
|
1014
|
|
|
$GLOBALS['egw']->framework->response->call('egw.set_preferences', |
|
1015
|
|
|
(array)$GLOBALS['egw_info']['user']['preferences'][$app], $app); |
|
1016
|
|
|
|
|
1017
|
|
|
// call application menuaction |
|
1018
|
|
|
ob_start(); |
|
1019
|
|
|
$obj->$method(); |
|
1020
|
|
|
$output .= ob_get_contents(); |
|
1021
|
|
|
ob_end_clean(); |
|
1022
|
|
|
|
|
1023
|
|
|
// add registered css and javascript to the response |
|
1024
|
|
|
self::include_css_js_response(); |
|
1025
|
|
|
|
|
1026
|
|
|
// add output if present |
|
1027
|
|
|
if ($output) |
|
1028
|
|
|
{ |
|
1029
|
|
|
$GLOBALS['egw']->framework->response->data($output); |
|
1030
|
|
|
} |
|
1031
|
|
|
} |
|
1032
|
|
|
|
|
1033
|
|
|
/** |
|
1034
|
|
|
* Apps available for mobile, if admin did not configured something else |
|
1035
|
|
|
* (needs to kept in sync with list in phpgwapi/js/framework/fw_mobile.js!) |
|
1036
|
|
|
* |
|
1037
|
|
|
* Constant is read by admin_hooks::config to set default for fw_mobile_app_list. |
|
1038
|
|
|
*/ |
|
1039
|
|
|
const DEFAULT_MOBILE_APPS = 'calendar,infolog,timesheet,resources,addressbook,projectmanager,tracker,mail,filemanager'; |
|
1040
|
|
|
} |
|
1041
|
|
|
|
This check looks for the bodies of
ifstatements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.These
ifbodies can be removed. If you have an empty if but statements in theelsebranch, consider inverting the condition.could be turned into
This is much more concise to read.