Completed
Push — 16.1 ( b5bda6...d590ad )
by Nathan
30:38 queued 15:41
created

app.js ➔ group_list   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
/**
2
 * EGroupware - Admin - Javascript UI
3
 *
4
 * @link http://www.egroupware.org
5
 * @package filemanager
6
 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
7
 * @copyright (c) 2013-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
8
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
9
 * @version $Id: app.js 56051 2016-05-06 07:58:37Z ralfbecker $
10
 */
11
12
/**
13
 * UI for Admin
14
 *
15
 * @augments AppJS
16
 */
17
app.classes.admin = AppJS.extend(
18
/**
19
 * @lends app.classes.admin
20
 */
21
{
22
	appname: 'admin',
23
24
	/**
25
	 * reference to iframe
26
	 *
27
	 * {et2_iframe}
28
	 */
29
	iframe: null,
30
31
	/**
32
	 * reference to nextmatch
33
	 *
34
	 * {et2_extension_nextmatch}
35
	 */
36
	nm: null,
37
38
	/**
39
	 * Refarence to div to hold AJAX loadable pages
40
	 *
41
	 * {et2_box}
42
	 */
43
	ajax_target: null,
44
45
	/**
46
	 * Reference to ACL edit dialog (not the list)
47
	 */
48
	acl_dialog: null,
49
50
	/**
51
	 * Constructor
52
	 *
53
	 * @memberOf app.classes.admin
54
	 */
55
	init: function()
56
	{
57
		// call parent
58
		this._super.apply(this, arguments);
59
	},
60
61
	/**
62
	 * Destructor
63
	 */
64
	destroy: function()
65
	{
66
		this.iframe = null;
67
		this.nm = null;
68
		this.acl_dialog = null;
69
70
		// call parent
71
		this._super.apply(this, arguments);
72
	},
73
74
	/**
75
	 * This function is called when the etemplate2 object is loaded
76
	 * and ready.  If you must store a reference to the et2 object,
77
	 * make sure to clean it up in destroy().
78
	 *
79
	 * @param {etemplate2} _et2
80
	 * @param {string} _name name of template loaded
81
	 */
82
	et2_ready: function(_et2, _name)
83
	{
84
		// call parent
85
		this._super.apply(this, arguments);
86
87
		switch(_name)
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
88
		{
89
			case 'admin.index':
90
				var iframe = this.iframe = this.et2.getWidgetById('iframe');
91
				this.nm = this.et2.getWidgetById('nm');
92
				this.groups = this.et2.getWidgetById('groups');
93
				this.groups.set_disabled(true);
94
				this.ajax_target = this.et2.getWidgetById('ajax_target');
95
				if (iframe)
96
				{
97
					var self = this;
98
					jQuery(iframe.getDOMNode()).off('load.admin')
99
						.bind('load.admin', function(){
100
							if (this.contentDocument.location.href.match(/(\/admin\/|\/admin\/index.php|menuaction=admin.admin_ui.index)/))
101
							{
102
								this.contentDocument.location.href = 'about:blank';	// stops redirect from admin/index.php
103
								self.load();	// load own top-level index aka user-list
104
							}
105
							self._hide_navbar.call(self);
106
						}
107
					);
108
				}
109
				break;
110
111
			case 'admin.customfield_edit':
112
				// Load settings appropriate to currently set type
113
				var widget = _et2.widgetContainer.getWidgetById('cf_type');
114
				this.cf_type_change(null,widget);
115
				break;
116
117
			case 'admin.mailaccount':
118
				this.account_hide_not_applying();
119
				break;
120
		}
121
	},
122
123
	/**
124
	 * Show given url in (visible) iframe or nextmatch with accounts (!_url)
125
	 *
126
	 * @param {string} [_url=] url to show in iframe or nothing for showing
0 ignored issues
show
Documentation Bug introduced by
The parameter _url= does not exist. Did you maybe mean _url instead?
Loading history...
127
	 */
128
	load: function(_url)
129
	{
130
		if (this.iframe && this.iframe.getDOMNode().contentDocument.location.href
131
			.match(/menuaction=admin.admin_statistics.submit/) &&
132
			!_url.match(/statistics=(postpone|canceled|submitted)/))
133
		{
134
			this.egw.message(this.egw.lang('Please submit (or postpone) statistic first'), 'info');
135
			return;	// do not allow to leave statistics submit
136
		}
137
		// url outside EGroupware eg. eSyncPro linking to wikipedia
138
		if (_url && _url.indexOf(this.egw.webserverUrl) == -1)
139
		{
140
			window.open(_url, '_blank');
141
			return;
142
		}
143
		var ajax = false;
144
		if (_url)
145
		{
146
			// Try to load it without the iframe
147
			ajax = _url.match(/ajax=true/) && _url.match(/menuaction=/);
148
			if(ajax)
149
			{
150
151
				if(this.ajax_target.node.children.length)
152
				{
153
					// Node has children already?  Check for loading over an
154
					// existing etemplate, and remove it first
155
					jQuery(this.ajax_target.node.children).each(function() {
156
						var old = etemplate2.getById(this.id);
157
						if(old) old.clear();
158
					});
159
					jQuery(this.ajax_target.node).empty();
160
				}
161
				this.egw.json(
162
					framework.activeApp.getMenuaction('ajax_exec'),
163
					// It's important that the context is null, or etemplate2
164
					// won't load the template properly
165
					[_url], this._ajax_load_callback,null, true, this
166
				).sendRequest();
167
			}
168
			else
169
			{
170
				this.iframe.set_src(_url);
171
			}
172
		}
173
		else
174
		{
175
			this.egw.app_header('');
176
			// blank iframe, to not keep something running there
177
			this.iframe.getDOMNode().contentDocument.location.href = 'about:blank';
178
		}
179
		this.iframe.set_disabled(!_url || ajax);
180
		this.nm.set_disabled(!!_url || ajax);
181
		this.groups.set_disabled(true);
182
		this.ajax_target.set_disabled(!ajax);
183
	},
184
185
	/**
186
	 * Observer method receives update notifications from all applications
187
	 *
188
	 * App is responsible for only reacting to "messages" it is interested in!
189
	 *
190
	 * @param {string} _msg message (already translated) to show, eg. 'Entry deleted'
191
	 * @param {string} _app application name
192
	 * @param {(string|number)} _id id of entry to refresh or null
193
	 * @param {string} _type either 'update', 'edit', 'delete', 'add' or null
194
	 * - update: request just modified data from given rows.  Sorting is not considered,
195
	 *		so if the sort field is changed, the row will not be moved.
196
	 * - edit: rows changed, but sorting may be affected.  Requires full reload.
197
	 * - delete: just delete the given rows clientside (no server interaction neccessary)
198
	 * - add: requires full reload for proper sorting
199
	 * @param {string} _msg_type 'error', 'warning' or 'success' (default)
200
	 * @param {string} _targetapp which app's window should be refreshed, default current
201
	 * @return {false|*} false to stop regular refresh, thought all observers are run
202
	 */
203
	observer: function(_msg, _app, _id, _type, _msg_type, _targetapp)
204
	{
205
		switch(_app)
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
206
		{
207
			case 'admin':
208
				// if iframe is used --> refresh it
209
				var iframe_node = this.iframe ? this.iframe.getDOMNode() : undefined;
210
				var iframe_url = iframe_node ? iframe_node.contentDocument.location.href : undefined;
211
				if (_id && iframe_url != 'about:blank')
212
				{
213
					var refresh_done = false;
214
					// Try for intelligent et2 refresh inside iframe
215
					if(iframe_node && iframe_node.contentWindow && iframe_node.contentWindow.etemplate2)
216
					{
217
						var templates = iframe_node.contentWindow.etemplate2.getByApplication('admin');
218
						for(var i = 0; i < templates.length; i++)
219
						{
220
							templates[i].refresh(_msg,_app,_id,_type);
221
							refresh_done = true;
222
						}
223
					}
224
					if (!refresh_done)	// --> reload iframe
225
					{
226
						this.load(iframe_url);
227
					}
228
					return false;	// --> no regular refresh
229
				}
230
				// invalidate client-side account-cache
231
				this.egw.invalidate_account(_id, _type);
232
				// group deleted, added or updated
233
				if (_id < 0)
234
				{
235
					var tree = this.et2.getWidgetById('tree');
236
					var nm = this.et2.getWidgetById('nm');
237
					switch(_type)
238
					{
239
						case 'delete':
240
							tree.deleteItem('/groups/'+_id, false);
241
							if (nm) nm.getInstanceManager().submit();
242
							break;
243
244
						default:	// add, update, edit, null
245
							if (nm)
246
							{
247
								var activeFilters = nm.activeFilters;
248
								nm.getInstanceManager().submit();
249
								var nm = this.et2.getWidgetById('nm');
250
								nm.applyFilters(activeFilters);
251
							}
252
253
					}
254
					var refreshTree = this.et2.getWidgetById('tree');
255
					if (refreshTree) refreshTree.refreshItem('/groups');
256
					return false;	// --> no regular refresh
257
				}
258
				// not a user or group, eg. categories
259
				else if (!_id)
260
				{
261
					this.load();
262
					return false;	// --> no regular refresh needed
263
				}
264
		}
0 ignored issues
show
Comprehensibility introduced by
There is no default case in this switch, so nothing gets returned when all cases fail. You might want to consider adding a default or return undefined explicitly.
Loading history...
265
	},
266
267
	/**
268
	 * Hide navbar for idots template
269
	 *
270
	 * Just a hack for old idots, not neccesary for jdots
271
	 */
272
	_hide_navbar: function()
273
	{
274
		var document = this.iframe.getDOMNode().contentDocument;
275
276
		if (!document) return;	// nothing we can do ...
277
278
		// set white background, as transparent one lets account-list show through
279
		document.getElementsByTagName('body')[0].style.backgroundColor = 'white';
280
281
		// hide navbar elements
282
		var ids2hide = ['divLogo', 'topmenu', 'divAppIconBar', 'divStatusBar', 'tdSidebox', 'divAppboxHeader'];
283
		for(var i=0; i < ids2hide.length; ++i)
284
		{
285
			var elem = document.getElementById(ids2hide[i]);
286
			if (elem) elem.style.display = 'none';
287
		}
288
	},
289
290
	/**
291
	 * Set location of iframe for given _action and _sender (row)
292
	 *
293
	 * @param _action
294
	 * @param _senders
295
	 */
296
	iframe_location: function(_action, _senders)
297
	{
298
		var id = _senders[0].id.split('::');
299
		var url = _action.data.url.replace(/(%24|\$)id/, id[1]);
300
301
		this.load(url);
302
	},
303
304
	/**
305
	 * Callback to load an etemplate
306
	 *
307
	 * @param {Object[]} _data
308
	 */
309
	_ajax_load_callback: function(_data)
310
	{
311
		if(!_data || _data.type != undefined) return;
312
313
		// Insert the content, etemplate will load into it
314
		jQuery(this.ajax_target.node).append(typeof _data === 'string' ? _data : _data[0]);
315
	},
316
317
	/**
318
	 * Link hander for jDots template to just reload our iframe, instead of reloading whole admin app
319
	 *
320
	 * @param _url
321
	 * @return boolean true, if linkHandler took care of link, false otherwise
322
	 */
323
	linkHandler: function(_url)
324
	{
325
		var matches = _url.match(/menuaction=admin.admin_ui.index.*&load=([^&]+)/);
326
		if (_url !='about:blank' && (this.iframe != null && !_url.match('menuaction=admin.admin_ui.index') || matches))
327
		{
328
			if (matches)
329
			{
330
				_url = _url.replace(/menuaction=admin.admin_ui.index/, 'menuaction='+matches[1]).replace(/&(load=[^&]+)/g, '');
331
			}
332
			this.load(_url);
333
			return true;
334
		}
335
		// can not load our own index page, has to be done by framework
336
		return false;
337
	},
338
339
	/**
340
	 * Run an admin module / onclick callback for tree
341
	 *
342
	 * @param {string} _id id of clicked node
343
	 * @param {et2_tree} _widget reference to tree widget
344
	 */
345
	run: function(_id, _widget)
346
	{
347
		var link = _widget.getUserData(_id, 'link');
348
349
		this.groups.set_disabled(true);
350
		this.nm.set_disabled(false);
351
352
		if (_id == '/accounts' || _id.substr(0, 8) == '/groups/')
353
		{
354
			this.load();
355
			var parts = _id.split('/');
356
			this.et2.getWidgetById('nm').applyFilters({ filter: parts[2] ? parts[2] : '', search: ''});
357
		}
358
		else if (_id === '/groups')
359
		{
360
			this.load();
361
			this.group_list();
362
		}
363
		else if (typeof link == 'undefined')
364
		{
365
			_widget.openItem(_id, 'toggle');
366
		}
367
		else if (link[0] == '/' || link.substr(0,4) == 'http')
368
		{
369
			link += (link.indexOf('?') >= 0 ? '&' : '?')+'nonavbar=1';
370
			this.load(link);
371
		}
372
		else if (link.substr(0,11) == 'javascript:')
373
		{
374
			eval(link.substr(11));
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
375
		}
376
	},
377
378
	/**
379
	 * Show the group list in the main window
380
	 */
381
	group_list: function group_list()
382
	{
383
		this.nm.set_disabled(true);
384
		this.groups.set_disabled(false);
385
	},
386
387
388
	/**
389
	 * View, edit or delete a group callback for tree
390
	 *
391
	 * @param {object} _action egwAction
392
	 * @param {array} _senders egwActionObject _senders[0].id holds id
393
	 */
394
	group: function(_action, _senders)
395
	{
396
		// Tree IDs look like /groups/ID, nm uses admin::ID
397
		var from_nm = _senders[0].id.indexOf('::') > 0;
398
		var account_id = _senders[0].id.split(from_nm ? '::' : '/')[from_nm ? 1 : 2];
399
400
		switch(_action.id)
401
		{
402
			case 'view':
403
				this.run(from_nm ? '/groups/'+account_id : _senders[0].id, this.et2.getWidgetById('tree'));
404
				break;
405
406
			case 'delete':
407
				this.egw.json('admin_account::ajax_delete_group', [account_id]).sendRequest();
408
				break;
409
410
			default:
411
				if (!_action.data.url)
412
				{
413
					alert('Missing url in action '+_action.id+'!');
414
					break;
415
				}
416
				var url = unescape(_action.data.url).replace('$id', account_id);
417
				if (url[0] != '/' && url.substr(0, 4) != 'http')
418
				{
419
					url = this.egw.link('/index.php', url);
420
				}
421
				if (_action.data.popup || _action.data.width && _action.data.height)
422
				{
423
					this.egw.open_link(url, '_blank', _action.data.popup ? _action.data.popup : _action.data.width + 'x' + _action.data.height);
424
				}
425
				else
426
				{
427
					this.load(url);
428
				}
429
				break;
430
		}
431
	},
432
433
	/**
434
	 * Modify an ACL entry
435
	 *
436
	 * @param {object} _action egwAction
437
	 * @param {array} _senders egwActionObject _senders[0].id holds the id "admin::app:account:location"
438
	 */
439
	acl: function(_action, _senders)
440
	{
441
		var ids = [];
442
		for(var i=0; i < _senders.length; ++i)
443
		{
444
			ids.push(_senders[i].id.split('::').pop());	// remove "admin::" prefix
445
		}
446
447
		// For edit, set some data from the list since it's already there
448
		var content = _senders[0].id ? jQuery.extend({}, egw.dataGetUIDdata(_senders[0].id).data) : {};
449
450
		switch(_action.id)
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
451
		{
452
			case 'delete':
453
				var app = egw.app_name();	// can be either admin or preferences!
454
				if (app != 'admin') app = 'preferences';
455
				var className = app+'_acl';
456
				var request = egw.json(className+'::ajax_change_acl', [ids], this._acl_callback,this,false,this)
457
					.sendRequest();
458
				break;
459
460
			case 'add':
461
				// No content, even if they clicked on a row
462
				// Defaults set by _acl_content() based on nm values
463
				content = {};
464
				// Fall through
465
			case 'edit':
466
				this._acl_dialog(content);
467
				break;
468
		}
469
	},
470
471
	/**
472
	 * Create the ACL edit dialog, including defaults & fetching what can be found
473
	 *
474
	 * @param content List of content for the dialog template
475
	 * @param sel_options optional select options
476
	 */
477
	_acl_dialog: function(content, sel_options)
478
	{
479
		if(typeof content == 'undefined') content = {};
480
481
		// Determine which application we're running as
482
		var app = egw.app_name();	// can be either admin or preferences!
483
		if (app != 'admin') app = 'preferences';
484
		// Get by ID, since this.et2 isn't always the ACL list
485
		var et2 = etemplate2.getById('admin-acl').widgetContainer;
486
		var className = app+'_acl';
487
		var acl_rights = {};
488
		var readonlys = {acl: {}};
489
490
		// Select options are already here, just pull them and pass along
491
		sel_options = et2.getArrayMgr('sel_options').data||{};
492
493
		// Some defaults
494
		if(et2 && et2.getWidgetById('nm'))
495
		{
496
			// This is which checkboxes are available for each app
497
			var acl_rights = et2.getWidgetById('nm').getArrayMgr('content').getEntry('acl_rights')||{};
498
499
			if(!content.acl_appname)
500
			{
501
				// Pre-set appname to currently selected
502
				content.acl_appname = et2.getWidgetById('filter2').getValue()||"";
503
			}
504
			if(!content.acl_account)
505
			{
506
				content.acl_account = et2.getWidgetById('nm').getArrayMgr('content').getEntry('account_id');
507
			}
508
			if(!content.acl_location)
509
			{
510
				content.acl_location = et2.getWidgetById('filter').getValue() == 'run' ? 'run' : null;
511
			}
512
			// If no admin rights, change UI to not allow adding access to apps
513
			if(content.acl_location == 'run' && !egw.user('apps')['admin'])
514
			{
515
				content.acl_location = null;
516
			}
517
			if(content.acl_location == 'run')
518
			{
519
				// These are the apps the account has access to
520
				// Fetch current values from server
521
				this.egw.json(className+'::ajax_get_app_list', [content.acl_account], function(data) {content.apps = data;},this,false,this)
522
					.sendRequest();
523
			}
524
			else
525
			{
526
				// Restrict application selectbox to only apps that support ACL
527
				sel_options.acl_appname = [];
528
				for(var app in acl_rights)
529
				{
530
					sel_options.acl_appname.push({value: app, label: this.egw.lang(app)});
531
				}
532
				// Sort list
533
				sel_options.acl_appname.sort(function(a,b) {
534
					if(a.label > b.label) return 1;
535
					if(a.label < b.label) return -1;
536
					return 0;
537
				});
538
			}
539
540
		}
541
		if(content.acl_appname)
542
		{
543
			// Load checkboxes & their values
544
			content.acl_rights = content.acl_rights ? parseInt(content.acl_rights) : null;
545
			jQuery.extend(content, {acl:[],right:[],label:[]});
546
			for( var right in acl_rights[content.acl_appname])
547
			{
548
				// only user himself is allowed to grant private (16) rights
549
				if(right == '16' && content['acl_account'] != egw.user('account_id'))
550
				{
551
					readonlys.acl[content.acl.length] = true;
552
				}
553
				content.acl.push(content.acl_rights & right);
554
				content.right.push(right);
555
				content.label.push(egw.lang(acl_rights[content.acl_appname][right]));
556
			}
557
		}
558
559
		if(content.acl_account && !egw.user('apps')['admin'])
560
		{
561
			readonlys.acl_account = true;
562
		}
563
		if(content.acl_location)
564
		{
565
			sel_options.acl_location = jQuery.extend({},sel_options.acl_location);
566
			this.egw.link_title('api-accounts', content.acl_location, function(title) {sel_options.acl_location[content.acl_location] = title;});
567
		}
568
569
		// Create the dialog
570
		this.acl_dialog = et2_createWidget("dialog", {
571
			callback: jQuery.proxy(function(_button_id, _value) {
572
				this.acl_dialog = null;
573
				if(_button_id != et2_dialog.OK_BUTTON) return;
574
575
				// Restore account if it's readonly in dialog
576
				if(!_value.acl_account) _value.acl_account = content.acl_account;
577
578
				// Handle no applications selected
579
				if(typeof _value.apps == 'undefined' && content.acl_location == 'run')
580
				{
581
					_value.apps = [];
582
				}
583
584
				// Only send the request if they entered everything (or selected no apps)
585
				if(_value.acl_account && (_value.acl_appname && _value.acl_location || typeof _value.apps != 'undefined'))
586
				{
587
					var id = [];
588
					if(_value.acl_appname && _value.acl_account && _value.acl_location)
589
					{
590
						id = _value.acl_appname+':'+_value.acl_account+':'+_value.acl_location;
591
						if(content && content.id && id != content.id)
592
						{
593
							// Changed the account or location, remove previous or we
594
							// get a new line instead of an edit
595
							this.egw.json(className+'::ajax_change_acl', [content.id, 0], null,this,false,this)
596
								.sendRequest();
597
						}
598
						id = [id];
599
					}
600
					var rights = 0;
601
					for(var i in _value.acl)
602
					{
603
						rights += parseInt(_value.acl[i]);
604
					}
605
					if(typeof _value.apps != 'undefined' && !_value.acl_appname)
606
					{
607
						id = [];
608
						rights = 1;
609
						var removed = [];
610
611
						// Loop through all apps, remove the ones with no permission
612
						for(var idx in sel_options.filter2)
613
						{
614
							var app = sel_options.filter2[idx].value || false;
615
							if (!app) continue;
616
							var run_id = app+":"+_value.acl_account+":run";
617
							if(_value.apps.indexOf(app) < 0 && (content.apps.indexOf(app) >= 0 || content.apps.length == 0))
618
							{
619
								removed.push(run_id);
620
							}
621
							else if (_value.apps.indexOf(app) >= 0 && content.apps.indexOf(app) < 0)
622
							{
623
								id.push(run_id);
624
							}
625
						}
626
627
						// Remove any removed
628
						if(removed.length > 0)
629
						{
630
							this.egw.json(className+'::ajax_change_acl', [removed, 0], this._acl_callback,this,false,this)
631
								.sendRequest();
632
						}
633
					}
634
					this.egw.json(className+'::ajax_change_acl', [id, rights], this._acl_callback,this,false,this)
635
						.sendRequest();
636
				}
637
			},this),
638
			title: this.egw.lang('Access control'),
639
			buttons: et2_dialog.BUTTONS_OK_CANCEL,
640
			value: {
641
				content: content,
642
				sel_options: sel_options,
643
				readonlys: readonlys
644
			},
645
			template: egw.webserverUrl+'/admin/templates/default/acl.edit.xet'
646
		}, et2_dialog._create_parent(app));
647
	},
648
649
	/**
650
	 * Change handler for ACL edit dialog application selectbox.
651
	 * Re-creates the dialog with the current values
652
	 *
653
	 * @param input
654
	 * @param widget
655
	 */
656
	acl_reopen_dialog: function(input, widget)
657
	{
658
		var content = {};
659
		if(this.acl_dialog != null)
660
		{
661
			content = this.acl_dialog.get_value() || {};
662
663
			// Destroy the dialog
664
			this.acl_dialog.free();
665
			this.acl_dialog = null;
666
		}
667
		// Re-open the dialog
668
		this._acl_dialog(content);
669
	},
670
671
	/**
672
	 * Callback called on successfull call of serverside ACL handling
673
	 *
674
	 * @param {object} _data returned from server
675
	 */
676
	_acl_callback: function(_data)
677
	{
678
		// Avoid the window / framework / app and just refresh the etemplate
679
		// Framework will try to refresh the opener
680
		// Get by ID, since this.et2 isn't always the ACL list
681
		var et2 = etemplate2.getById('admin-acl').widgetContainer;
682
		et2.getInstanceManager().refresh(_data.msg, this.appname,_data.ids,_data.type);
683
	},
684
685
	/**
686
	 * Check to see if admin has taken away access to a category
687
	 *
688
	 * @@param {widget} button add/apply pressed button
689
	 */
690
	check_owner: function(button) {
691
		var select_owner = this.et2.getWidgetById('owner');
692
		var diff = [];
693
694
		if (typeof select_owner != 'undefined')
695
		{
696
			var owner = select_owner.get_value();
697
		}
698
699
		if(typeof owner != 'object')
0 ignored issues
show
Bug introduced by
The variable owner does not seem to be initialized in case typeof select_owner != "undefined" on line 694 is false. Are you sure this can never be the case?
Loading history...
700
		{
701
			owner = [owner];
702
		}
703
		// No owner probably means selectbox is read-only, so no need to check
704
		if(owner == null) return true;
705
706
		var all_users = owner.indexOf('0') >= 0;
707
708
		// If they checked all users, uncheck the others
709
		if(all_users) {
710
			select_owner.set_value(['0']);
711
			return true;
712
		}
713
714
		// Find out what changed
715
		var cat_original_owner = this.et2.getArrayMgr('content').getEntry('owner');
716
		if (cat_original_owner)
717
		{
718
			var selected_groups = select_owner.get_value().toString();
719
720
			for(var i =0;i < cat_original_owner.length;i++)
721
			{
722
				if (selected_groups.search(cat_original_owner[i]) < 0)
723
				{
724
					diff.push(cat_original_owner[i]);
725
				}
726
			}
727
728
			if (diff.length > 0)
729
			{
730
				var removed_cat_label = jQuery.map(select_owner.options.select_options, function (val, i)
731
				{
732
					for (var j=0; j <= diff.length;j++)
733
					{
734
						if (diff[j] == val.value)
735
						{
736
							return val.label;
737
						}
738
					}
739
				});
740
741
				// Somebody will lose permission, give warning.
742
				if(removed_cat_label)
743
				{
744
					var msg = this.egw.lang('Removing access for groups may cause problems for data in this category.  Are you sure?  Users in these groups may no longer have access:');
745
					return et2_dialog.confirm(button,msg + removed_cat_label.join(','));
746
				}
747
			}
748
		}
749
		return true;
750
	},
751
752
	/**
753
	 * Show icon based on icon-selectbox, hide placeholder (broken image), if no icon selected
754
	 *
755
	 * @param {widget} widget select box widget
756
	 */
757
	change_icon: function(widget)
758
	{
759
		var img = widget.getRoot().getWidgetById('icon_url');
760
761
		if (img)
762
		{
763
			img.set_src(widget.getValue());
764
		}
765
	},
766
767
768
	/**
769
	 * Add / edit an account
770
	 *
771
	 * @param {object} _action egwAction
772
	 * @param {array} _senders egwActionObject _senders[0].id holds account_id
773
	 */
774
	account: function(_action, _senders)
775
	{
776
		var params = jQuery.extend({}, this.egw.link_get_registry('addressbook', 'edit'));
777
		var popup = this.egw.link_get_registry('addressbook', 'edit_popup');
778
779
		if (_action.id == 'add')
780
		{
781
			params.owner = '0';
782
		}
783
		else
784
		{
785
			params.account_id = _senders[0].id.split('::').pop();	// get last :: separated part
786
		}
787
		this.egw.open_link(this.egw.link('/index.php', params), 'admin', popup);
788
	},
789
790
	/**
791
	 * Submit statistic
792
	 *
793
	 * Webkit browsers (Chrome, Safari, ...) do NOT allow to call form.submit() from within onclick of a submit button.
794
	 * Therefor we first store our own form action, replace it with egroupware.org submit url and set a timeout calling
795
	 * submit_statistic again with just the form, to do the second submit to our own webserver
796
	 *
797
	 * @param {DOM} form
798
	 * @param {string} submit_url
799
	 * @return {boolean}
800
	 */
801
	submit_statistic: function(form, submit_url)
802
	{
803
		var that = this;
804
		var submit = function(_button)
805
		{
806
			// submit to egroupware.org
807
			var method=form.method;
808
			form.method='POST';
809
			var action = form.action;
810
			form.action=submit_url;
811
			var target = form.target;
812
			form.target='_blank';
813
			form.submit();
814
815
			// submit to own webserver
816
			form.method=method;
817
			form.action=action;
818
			form.target=target;
819
			that.et2.getInstanceManager().submit('submit');
820
		};
821
822
		// Safari does NOT allow to call form.submit() outside of onclick callback
823
		// so we have to use browsers ugly synchron confirm
824
		if (navigator.userAgent.match(/Safari/) && !navigator.userAgent.match(/Chrome/))
825
		{
826
			if (confirm(this.egw.lang('Submit displayed information?')))
827
			{
828
				submit();
829
			}
830
		}
831
		else
832
		{
833
			et2_dialog.show_dialog(function(_button)
834
			{
835
				if (_button == et2_dialog.YES_BUTTON)
836
				{
837
					submit();
838
				}
839
			}, this.egw.lang('Submit displayed information?'), '', {},
840
				et2_dialog.BUTTON_YES_NO, et2_dialog.QUESTION_MESSAGE, undefined, egw);
841
		}
842
		return false;
843
	},
844
845
	/**
846
	 * Delete confirmation dialog
847
	 *
848
	 * @param {egw action} _action
849
	 */
850
	delete_category: function (_action)
851
	{
852
		var _buttons = [
853
				{"button_id": "delete[cancel]","text": this.egw.lang('Cancel'), id: 'delete[cancel]', image: 'cancel', "default":true},
854
				{"button_id": "delete[delete]","text": this.egw.lang('Delete'), id: 'delete[delete]', image: 'delete'},
855
				{"button_id": "delete[subs]","text": this.egw.lang('Delete including sub-entries'), id: 'delete[subs]', image: 'delete'}
856
		];
857
		var action = _action;
858
		var self = this;
859
		var delDialog_callBack = function (_buttons)
860
		{
861
			if (_buttons != "delete[cancel]")
862
			{
863
				if (self.et2._inst.name == "admin.categories.index")
864
				{
865
					var nm_widget = self.et2.getWidgetById('nm');
866
					if (nm_widget)
867
					{
868
						if (_buttons == "delete[delete]")
869
						{
870
							nm_action(action);
871
						}
872
						else
873
						{
874
							action.id = 'delete_sub';
875
							nm_action(action);
876
						}
877
					}
878
				}
879
				else
880
				{
881
					self.et2.getInstanceManager().submit(_buttons);
882
				}
883
			}
884
		};
885
		et2_dialog.show_dialog(delDialog_callBack,this.egw.lang("Are you sure you want to delete this category ?"),this.egw.lang("Delete"),{},_buttons,et2_dialog.WARNING_MESSAGE,null,'admin');
886
	},
887
888
	/**
889
	 * Change handler for when you change the type of a custom field.
890
	 * It toggles options / attributes as appropriate.
891
	 * @param {event object} e
892
	 * @param {widget object} widget
893
	 */
894
	cf_type_change: function(e,widget) {
895
		var root = widget.getRoot();
896
		var attributes = widget.getArrayMgr('content').getEntry('attributes['+widget.getValue()+']')||{};
897
		root.getWidgetById('cf_values').set_statustext(widget.egw().lang(widget.getArrayMgr('content').getEntry('options['+widget.getValue()+']')||''));
898
		jQuery(root.getWidgetById('cf_len').getDOMNode()).toggle(attributes.cf_len && true);
899
		jQuery(root.getWidgetById('cf_rows').getDOMNode()).toggle(attributes.cf_rows && true);
900
		jQuery(root.getWidgetById('cf_values').getParentDOMNode()).toggle(attributes.cf_values && true);
901
	},
902
903
	/**
904
	 * Activate none standard SMTP mail accounts for selected users
905
	 *
906
	 * @param {egw_action} _action
907
	 * @param {array} _selected selected users
908
	 * @todo remove under construction message
909
	 */
910
	emailadminActiveAccounts: function (_action, _selected){
911
912
		var menuaction = 'admin.admin_mail.ajax_activeAccounts';
913
		var accounts = [];
914
		var msg1 = egw.lang('%1 accounts being activated',Object.keys(_selected).length);
915
916
		for (var i=0;i< Object.keys(_selected).length;i++)
917
		{
918
			accounts[i] = {id:_selected[i]['id'].split('::')[1],qouta:"", domain:"", status:_action.id == 'active'?_action.id:''};
919
		}
920
		var callbackDialog = function (btn){
921
			if (btn === et2_dialog.YES_BUTTON)
922
			{
923
				// long task dialog for de/activation accounts
924
				et2_dialog.long_task(function(_val, _resp){
925
					if (_val && _resp.type !== 'error')
926
					{
927
						console.log(_val,_resp);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
928
					}
929
					else
930
					{
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
931
932
					}
933
				}, msg1, 'Mail Acounts Activation', menuaction, accounts, 'admin');
934
			}
935
		};
936
		// confirmation dialog
937
		et2_dialog.show_dialog(callbackDialog, egw.lang('Are you sure you want to %1 mail for selected accounts?', egw.lang(_action.id)), egw.lang('Active Mail Accounts'), {},
938
			et2_dialog.BUTTON_YES_NO, et2_dialog.WARNING_MESSAGE, undefined, egw);
939
	},
940
941
	/**
942
	 * No SSL
943
	 */
944
	SSL_NONE: 0,
945
	/**
946
	 * STARTTLS on regular tcp connection/port
947
	 */
948
	SSL_STARTTLS: 1,
949
	/**
950
	 * SSL (inferior to TLS!)
951
	 */
952
	SSL_SSL: 3,
953
	/**
954
	 * require TLS version 1+, no SSL version 2 or 3
955
	 */
956
	SSL_TLS: 2,
957
	/**
958
	 * if set, verify certifcate (currently not implemented in Horde_Imap_Client!)
959
	 */
960
	SSL_VERIFY: 8,
961
962
	/**
963
	 * Resize window methode
964
	 *
965
	 * @returns {undefined}
966
	 */
967
	wizard_popup_resize: function ()
968
	{
969
		var $main_div = jQuery('#popupMainDiv');
970
		var $et2 = jQuery('.et2_container');
971
		var w = {
972
			width: egw_getWindowInnerWidth(),
973
			height: egw_getWindowInnerHeight()
974
		};
975
		// Use et2_container for width since #popupMainDiv is full width, but we still need
976
		// to take padding/margin into account
977
		var delta_width = w.width - ($et2.outerWidth(true) + ($main_div.outerWidth(true) - $main_div.width()));
978
		var delta_height = w.height - ($et2.outerHeight(true) + ($main_div.outerHeight(true) - $main_div.height()));
979
		if(delta_width != 0 || delta_height != 0)
980
		{
981
			window.resizeTo(egw_getWindowOuterWidth() - delta_width,egw_getWindowOuterHeight() - delta_height);
982
		}
983
	},
984
985
	/**
986
	 * Switch account wizard to manual entry
987
	 */
988
	wizard_manual: function()
989
	{
990
		jQuery('.emailadmin_manual').fadeToggle();// not sure how to to this et2-isch
991
		this.wizard_popup_resize(); // popup needs to be resized after toggling
992
	},
993
994
	/**
995
	 * onclick for continue button to show progress animation
996
	 *
997
	 * @param {object} _event event-object or information about event
998
	 * @param {et2_baseWidget} _widget widget causing the event
999
	 */
1000
	wizard_detect: function(_event, _widget)
1001
	{
1002
		// we need to do a manual asynchronious submit to show progress animation
1003
		// default synchronious submit stops animation!
1004
		if (this.et2._inst.submit('button[continue]', true))	// true = async submit
1005
		{
1006
			var sieve_enabled = this.et2.getWidgetById('acc_sieve_enabled');
1007
			if (!sieve_enabled || sieve_enabled.get_value())
1008
			{
1009
				jQuery('#admin-mailwizard_output').hide();
1010
				jQuery('td.emailadmin_progress').show();
1011
			}
1012
		}
1013
		return false;
1014
	},
1015
1016
	/**
1017
	 * Set default port, if imap ssl-type changes
1018
	 *
1019
	 * @param {object} _event event-object or information about event
1020
	 * @param {et2_baseWidget} _widget widget causing the event
1021
	 */
1022
	wizard_imap_ssl_onchange: function(_event, _widget)
1023
	{
1024
		var ssl_type = _widget.get_value();
1025
		this.et2.getWidgetById('acc_imap_port').set_value(
1026
			ssl_type == this.SSL_SSL || ssl_type == this.SSL_TLS ? 993 : 143);
1027
	},
1028
1029
	/**
1030
	 * Set default port, if imap ssl-type changes
1031
	 *
1032
	 * @param {object} _event event-object or information about event
1033
	 * @param {et2_baseWidget} _widget widget causing the event
1034
	 */
1035
	wizard_smtp_ssl_onchange: function(_event, _widget)
1036
	{
1037
		var ssl_type = _widget.get_value();
1038
		this.et2.getWidgetById('acc_smtp_port').set_value(
1039
			ssl_type == 'no' ? 25 : (ssl_type == this.SSL_SSL || ssl_type == this.SSL_TLS ? 465 : 587));
1040
	},
1041
1042
	/**
1043
	 * Set default port, if imap ssl-type changes
1044
	 *
1045
	 * @param {object} _event event-object or information about event
1046
	 * @param {et2_baseWidget} _widget widget causing the event
1047
	 */
1048
	wizard_sieve_ssl_onchange: function(_event, _widget)
1049
	{
1050
		var ssl_type = _widget.get_value();
1051
		this.et2.getWidgetById('acc_sieve_port').set_value(
1052
			ssl_type == this.SSL_SSL || ssl_type == this.SSL_TLS ? 5190 : 4190);
1053
		this.wizard_sieve_onchange(_event, _widget);
1054
	},
1055
1056
	/**
1057
	 * Enable sieve, if user changes some setting
1058
	 *
1059
	 * @param {object} _event event-object or information about event
1060
	 * @param {et2_baseWidget} _widget widget causing the event
1061
	 */
1062
	wizard_sieve_onchange: function(_event, _widget)
1063
	{
1064
		this.et2.getWidgetById('acc_sieve_enabled').set_value(1);
1065
	},
1066
1067
	/**
1068
	 * Switch to select multiple accounts
1069
	 *
1070
	 * @param {object} _event event-object or information about event
1071
	 * @param {et2_baseWidget} _widget widget causing the event
1072
	 */
1073
	edit_multiple: function(_event, _widget)
1074
	{
1075
		// hide multiple button
1076
		_widget.set_disabled(true);
1077
1078
		// switch account-selection to multiple
1079
		var account_id = this.et2.getWidgetById('account_id');
1080
		account_id.set_multiple(true);
1081
	},
1082
1083
	/**
1084
	 * Hide not applying fields, used as:
1085
	 * - onchange handler on account_id
1086
	 * - called from et2_ready for emailadmin.account template
1087
	 *
1088
	 * @param {object} _event event-object or information about event
1089
	 * @param {et2_baseWidget} _widget widget causing the event
1090
	 */
1091
	account_hide_not_applying: function(_event, _widget)
1092
	{
1093
		var account_id = this.et2.getWidgetById('account_id');
1094
		var ids = account_id && account_id.get_value ? account_id.get_value() : [];
1095
		if (typeof ids == 'string') ids = ids.split(',');
1096
1097
		var multiple = ids.length >= 2 || ids[0] === '' || ids[0] < 0;
1098
		//alert('multiple='+(multiple?'true':'false')+': '+ids.join(','));
1099
1100
		// initial call
1101
		if (typeof _widget == 'undefined')
1102
		{
1103
			if (!multiple)
1104
			{
1105
				jQuery('.emailadmin_no_single').hide();
1106
			}
1107
			if (!this.egw.user('apps').admin)
1108
			{
1109
				jQuery('.emailadmin_no_user,#button\\[multiple\\]').hide();
1110
			}
1111
			if (ids.length == 1)
1112
			{
1113
				// switch back to single selectbox
1114
				account_id.set_multiple(false);
1115
				this.et2.getWidgetById('button[multiple]').set_disabled(false);
1116
			}
1117
		}
1118
		// switched to single user
1119
		else if (!multiple)
1120
		{
1121
			jQuery('.emailadmin_no_single').fadeOut();
1122
			// switch back to single selectbox
1123
			account_id.set_multiple(false);
1124
			this.et2.getWidgetById('button[multiple]').set_disabled(false);
1125
		}
1126
		// switched to multiple user
1127
		else
1128
		{
1129
			jQuery('.emailadmin_no_single').fadeIn();
1130
		}
1131
		if (_event && _event.stopPropagation) _event.stopPropagation();
1132
		return false;
1133
	},
1134
1135
	/**
1136
	 * Callback if user changed account selction
1137
	 *
1138
	 * @param {object} _event event-object or information about event
1139
	 * @param {et2_baseWidget} _widget widget causing the event
1140
	 */
1141
	change_account: function(_event, _widget)
1142
	{
1143
		// todo check dirty and query user to a) save changes, b) discard changes, c) cancel selection
1144
		_widget.getInstanceManager().submit();
1145
	},
1146
1147
	/**
1148
	 * Callback if user changes notification folders: unset use-default checkbox
1149
	 *
1150
	 * @param {object} _event
1151
	 * @param {et2_widget} _widget
1152
	 */
1153
	change_folders: function(_event, _widget)
1154
	{
1155
		var use_default = this.et2.getWidgetById('notify_use_default');
1156
		if (use_default) use_default.set_value(false);
1157
	},
1158
1159
	/**
1160
	 * default onExecute for admin actions
1161
	 *
1162
	 * @param {object} _action
1163
	 * @param {object} _senders
1164
	 */
1165
	account_edit_action: function(_action, _senders)
1166
	{
1167
		if (_action.data.url)
1168
		{
1169
			this.egw.open_link(_action.data.url, _action.data.target || '_blank', _action.data.popup);
1170
		}
1171
	},
1172
1173
	/**
1174
	 * Clear instance cache
1175
	 *
1176
	 * If there is an error on server-side, resend request with an parameter allowing
1177
	 * cache to use different method not requiring eg. so much memory
1178
	 */
1179
	clear_cache: function()
1180
	{
1181
		this.egw.message(this.egw.lang('Clear cache and register hooks')+"\n"+this.egw.lang('Please wait...'),'info');
1182
1183
		this.egw.json('admin.admin_hooks.ajax_clear_cache').sendRequest(true, undefined, jQuery.proxy(function(_xmlhttp, _err)
1184
		{
1185
			this.egw.json('admin.admin_hooks.ajax_clear_cache&errored=1').sendRequest(true);
1186
		}, this));
1187
	}
1188
});
1189