importexport_definitions_ui::action()   D
last analyzed

Complexity

Conditions 26
Paths 90

Size

Total Lines 118
Code Lines 80

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 26
eloc 80
c 1
b 1
f 0
nc 90
nop 8
dl 0
loc 118
rs 4.1666

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * eGroupWare - importexport
4
 *
5
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
6
 * @package importexport
7
 * @link http://www.egroupware.org
8
 * @author Cornelius Weiss <[email protected]>
9
 * @copyright Cornelius Weiss <[email protected]>
10
 * @version $Id$
11
 */
12
13
use EGroupware\Api;
14
use EGroupware\Api\Framework;
15
use EGroupware\Api\Egw;
16
use EGroupware\Api\Acl;
17
use EGroupware\Api\Etemplate;
18
19
/**
20
 * Userinterface to define {im|ex}ports
21
 *
22
 * @package importexport
23
 */
24
class importexport_definitions_ui
25
{
26
	const _debug = false;
0 ignored issues
show
Coding Style introduced by
This class constant is not uppercase (expected _DEBUG).
Loading history...
27
28
	const _appname = 'importexport';
0 ignored issues
show
Coding Style introduced by
This class constant is not uppercase (expected _APPNAME).
Loading history...
29
30
	// To skip a step, step returns this
31
	const SKIP = '-skip-';
32
33
	public $public_functions = array(
34
		'edit' => true,
35
		'index' => true,
36
		'wizard' => true,
37
		'import_definition' => true,
38
		'site_config' => true,
39
	);
40
41
	/**
42
	 * holds all available plugins
43
	 *
44
	 * @var array
45
	 */
46
	var $plugins;
47
48
	/**
49
	 * holds user chosen plugin after step20
50
	 *
51
	 * @var object
52
	 */
53
	var $plugin;
54
55
	function __construct()
56
	{
57
		// we cant deal with notice and warnings, as we are on ajax!
58
		error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
59
		Api\Translation::add_app(self::_appname);
60
		$GLOBALS['egw_info']['flags']['currentapp'] = self::_appname;
61
62
		$this->etpl = new Etemplate();
0 ignored issues
show
Bug Best Practice introduced by
The property etpl does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
63
		$this->clock = Api\Html::image(self::_appname,'clock');
0 ignored issues
show
Bug Best Practice introduced by
The property clock does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
64
		$this->steps = array(
0 ignored issues
show
Bug Best Practice introduced by
The property steps does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
65
			'wizard_step10' => lang('Choose an application'),
66
			'wizard_step20' => lang('Choose a plugin'),
67
			'wizard_step21' => lang('Choose a name for this definition'),
68
			'wizard_step90' => lang('Which users are allowed to use this definition'),
69
			'wizard_finish' => '',
70
		);
71
		//register plugins
72
		$this->plugins = importexport_helper_functions::get_plugins();
73
	}
74
75
	/**
76
	 * List defined {im|ex}ports
77
	 *
78
	 * @param array $content=null
79
	 */
80
	function index($content = null,$msg='')
81
	{
82
		$filter = array('name' => '*');
83
84
		if($GLOBALS['egw_info']['user']['apps']['admin']) {
85
			// Any public definition
86
			$filter[] = '(owner=0 OR owner IS NULL OR allowed_users IS NOT NULL OR owner = ' . $GLOBALS['egw_info']['user']['account_id'] . ')';
87
		} else {
88
			// Filter private definitions
89
			$filter['owner'] = $GLOBALS['egw_info']['user']['account_id'];
90
			$config = Api\Config::read('phpgwapi');
91
			if($config['export_limit'] == 'no' && !Api\Storage\Merge::is_export_limit_excepted()) {
92
				$filter['type'] = 'import';
93
			}
94
		}
95
96
		if (is_array($content))
97
		{
98
			// Handle legacy actions
99
			if (isset($content['nm']['rows']['delete']))
100
			{
101
				$content['nm']['action'] = 'delete';
102
				$content['nm']['selected'] = array_keys($content['nm']['rows']['delete'],'pressed');
103
			}
104
			elseif(($button = array_search('pressed',$content['nm']['rows'])) !== false)
105
			{
106
				$selected = $content['nm']['rows']['selected'];
107
				if(count($selected) < 1 || !is_array($selected)) exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
108
				switch ($button)
109
				{
110
					case 'delete_selected' :
111
						$content['nm']['selected'] = $selected;
112
						$content['nm']['action'] = 'delete';
113
						break;
114
					case 'export_selected':
115
						$content['nm']['selected'] = $selected;
116
						$content['nm']['action'] = 'export';
117
						break;
118
				}
119
			}
120
			if ($content['nm']['action'])
121
			{
122
				if (!count($content['nm']['selected']) && !$content['nm']['select_all'])
123
				{
124
					$msg = lang('You need to select some entries first!');
125
				}
126
				else
127
				{
128
					// Action has an additional parameter
129
					if(in_array($content['nm']['action'], array('owner', 'allowed')))
130
					{
131
						$action = $content['nm']['action'];
132
						if($content['nm']['action'] == 'allowed')
133
						{
134
							$content['allowed'] = $content['allowed_popup']['allowed_private'] == 'true' ? null : (
135
								$content['allowed_popup']['all_users']=='true' ? 'all' : implode(',',$content['allowed_popup']['allowed'])
136
							);
137
						}
138
						else
139
						{
140
							$content['owner'] = $content['owner_popup']['owner'];
141
						}
142
						if(is_array($content[$content['nm']['action']]))
143
						{
144
							$content[$content['nm']['action']] = implode(',',$content[$content['nm']['action']]);
145
						}
146
						$content['nm']['action'] .= '_' . $content[$action];
147
						unset($content[$action]);
148
						unset($content['allowed_popup']);
149
					}
150
					if ($this->action($content['nm']['action'],$content['nm']['selected'],$content['nm']['select_all'],
151
						$success,$failed,$action_msg,'index',$msg))
152
					{
153
						$msg .= lang('%1 definition(s) %2',$success,$action_msg);
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with $success. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

153
						$msg .= /** @scrutinizer ignore-call */ lang('%1 definition(s) %2',$success,$action_msg);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
154
					}
155
					elseif(empty($msg))
156
					{
157
						$msg .= lang('%1 definition(s) %2, %3 failed because of insufficent rights !!!',$success,$action_msg,$failed);
158
					}
159
				}
160
			}
161
		}
162
163
		$content['nm'] = array(
164
			'get_rows'	=> 'importexport.importexport_definitions_ui.get_rows',
165
			'no_cat'	=> true,
166
			'no_filter'	=> true,
167
			'no_filter2'	=> true,
168
			'csv_fields'	=> false,	// Disable CSV export, uses own export
169
			'default_cols'  => '!actions',  // switch legacy actions column and row off by default
170
			'row_id'	=> 'definition_id',
171
			'placeholder_actions' => array('add')
172
		);
173
		if($_GET['application']) $content['nm']['col_filter']['application'] = $_GET['application'];
174
175
		if(Api\Cache::getSession('importexport', 'index'))
176
		{
177
			$content['nm'] = array_merge($content['nm'], Api\Cache::getSession('importexport', 'index'));
178
		}
179
		$content['nm']['actions'] = $this->get_actions();
180
		$sel_options = array(
181
			'type'	=> array(
182
				'import'	=> lang('import'),
183
				'export'	=> lang('export'),
184
			),
185
			'allowed_users' => array(
186
				array('value' => 'private', 'label' => lang('Private')),
187
				array('value' => 'all', 'label' => lang('all'))
188
			)
189
		);
190
		foreach ($this->plugins as $appname => $options)
191
		{
192
			if($GLOBALS['egw_info']['user']['apps'][$appname] || $GLOBALS['egw_info']['user']['apps']['admin']) {
193
				$sel_options['application'][$appname] = lang($appname);
194
			}
195
		}
196
		if($msg) $content['msg'] = $msg;
197
198
		$etpl = new Etemplate(self::_appname.'.definition_index');
199
		return $etpl->exec( self::_appname.'.importexport_definitions_ui.index', $content, $sel_options, $readonlys, $preserv );
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $readonlys seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $preserv seems to be never defined.
Loading history...
200
	}
201
202
	private function get_actions() {
203
		$group = 0;
204
		$actions = array(
205
			'edit' => array(
206
				'caption' => 'Open',
207
				'default' => true,
208
				'allowOnMultiple' => false,
209
				'url' => 'menuaction=importexport.importexport_definitions_ui.edit&definition=$id',
210
				'popup' => '500x500',
211
				'group' => $group,
212
			),
213
			'add' => array(
214
				'caption' => 'Add',
215
				'url' => 'menuaction=importexport.importexport_definitions_ui.edit',
216
				'popup' => '500x500',
217
				'group' => $group,
218
			),
219
			'execute' => array(
220
				'caption' => 'Execute',
221
				'icon' => 'importexport/navbar',
222
				'allowOnMultiple' => false,
223
				'group' => $group,
224
				'onExecute' => 'javaScript:app.importexport.run_definition'
225
			),
226
			'schedule' => array(
227
				'caption' => 'Schedule',
228
				'icon' => 'timesheet/navbar',
229
				'allowOnMultiple' => false,
230
				'url' => 'menuaction=importexport.importexport_schedule_ui.edit&definition=$id',
231
				'popup' => '500x500',
232
				'group' => $group,
233
			),
234
			'change' => array(
235
				'caption' => 'Change',
236
				'icon' => 'edit',
237
				'children' => array(
238
					'owner' => array(
239
						'caption' => 'Owner',
240
						'group' => 1,
241
						'icon' => 'addressbook/accounts',
242
						'nm_action' => 'open_popup',
243
					),
244
					'allowed' => array(
245
						'caption' => 'Allowed users',
246
						'group' => 1,
247
						'icon' => 'addressbook/group',
248
						'nm_action' => 'open_popup',
249
					)
250
				),
251
				'disableClass' => 'rowNoEdit',
252
			),
253
			'select_all' => array(
254
				'caption' => 'Whole query',
255
				'checkbox' => true,
256
				'hint' => 'Apply the action on the whole query, NOT only the shown definitions!!!',
257
				'group' => ++$group,
258
			),
259
			'copy' => array(
260
				'caption' => 'Copy',
261
				'group' => ++$group,
262
			),
263
			'createexport' => array(
264
				'caption' => 'Create export',
265
				'hint' => 'Create a matching export definition based on this import definition',
266
				'icon' => 'export',
267
				'group' => $group,
268
				'allowOnMultiple' => false,
269
				'disableClass' => 'export'
270
			),
271
272
			'export' => array(
273
				'caption' => 'Export',
274
				'group' => $group,
275
				'icon' => 'filesave',
276
				'postSubmit' => true
277
			),
278
			'delete' => array(
279
				'caption' => 'Delete',
280
				'confirm' => 'Delete this entry',
281
				'confirm_multiple' => 'Delete these entries',
282
				'group' => ++$group,
283
				'disableClass' => 'rowNoDelete',
284
			),
285
		);
286
287
		// Unset admin actions
288
		if(!$GLOBALS['egw_info']['user']['apps']['admin'])
289
		{
290
			unset($actions['schedule']);
291
		}
292
		return $actions;
293
	}
294
295
	/**
296
	 * apply an action to multiple entries
297
	 *
298
	 * @param string/int $action 'delete', 'export', etc.
0 ignored issues
show
Documentation Bug introduced by
The doc comment string/int at position 0 could not be parsed: Unknown type name 'string/int' at position 0 in string/int.
Loading history...
299
	 * @param array $selected id's to use if !$use_all
300
	 * @param boolean $use_all if true use all entries of the current selection (in the session)
301
	 * @param int &$success number of succeded actions
302
	 * @param int &$failed number of failed actions (not enought permissions)
303
	 * @param string &$action_msg translated verb for the actions, to be used in a message like %1 entries 'deleted'
304
	 * @param string/array $session_name 'index' or 'email', or array with session-data depending if we are in the main list or the popup
305
	 * @return boolean true if all actions succeded, false otherwise
306
	 */
307
	function action($action,$selected,$use_all,&$success,&$failed,&$action_msg,$session_name,&$msg)
308
	{
309
		//error_log( __METHOD__."('$action', ".array2string($selected).', '.array2string($use_all).",,, '$session_name')");
310
		if ($use_all)
311
		{
312
			// get the whole selection
313
			$old_query = $query = is_array($session_name) ? $session_name : Api\Cache::getSession('importexport', $session_name);
314
315
			@set_time_limit(0);				// switch off the execution time limit, as it's for big selections to small
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for set_time_limit(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

315
			/** @scrutinizer ignore-unhandled */ @set_time_limit(0);				// switch off the execution time limit, as it's for big selections to small

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
316
			$query['num_rows'] = -1;		// all
317
			$query['csv_export'] = true;	// so get_rows method _can_ produce different content or not store state in the session
318
			$this->get_rows($query,$rows,$readonlys);
319
320
			$selected = array();
321
			foreach($rows as $row) {
322
				$selected[] = $row['definition_id'];
323
			}
324
			if(!is_array($session_name))
325
			{
326
				// Restore old query
327
				Api\Cache::setSession('importexport', $session_name, $old_query);
328
			}
329
		}
330
331
		// Dialogs to get options
332
		list($action, $settings) = explode('_', $action, 2);
333
334
		$bodefinitions = new importexport_definitions_bo(false, true);
335
336
		switch($action) {
337
			case 'allowed':
338
				// Field is allowed_users, popup doesn't like _
339
				$action = 'allowed_users';
340
				// Fall through
341
			case 'owner':
342
				$action_msg = lang('changed'. ' ' . $action);
343
				foreach($selected as $id) {
344
					$definition = $bodefinitions->read((int)$id);
345
					if($definition['definition_id']) {
346
						// Prevent private with no owner
347
						if(!$definition['owner'] && !$settings) $definition['owner'] = $GLOBALS['egw_info']['user']['account_id'];
348
349
						$definition[$action] = $settings;
350
						$bodefinitions->save($definition);
351
						$success++;
352
					}
353
				}
354
				break;
355
			case 'delete':
356
				$bodefinitions->delete($selected);
357
				$action_msg = lang('deleted');
358
				break;
359
360
			case 'export' :
361
				$action_msg = lang('exported');
362
				$mime_type = ($GLOBALS['egw']->html->user_agent == 'msie' || $GLOBALS['egw']->html->user_agent == 'opera') ?
363
					'application/octetstream' : 'application/octet-stream';
364
				$name = 'importexport_definition.xml';
365
				header('Content-Type: ' . $mime_type);
366
				header('Content-Disposition: attachment; filename="'.$name.'"');
367
				echo $bodefinitions->export($selected);
368
				exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
369
				break;
370
371
			case 'copy':
372
				$action_msg = lang('copied');
373
				// Should only be one selected
374
				foreach($selected as $id) {
375
					$definition = $bodefinitions->read((int)$id);
376
					if($definition['definition_id']) {
377
						unset($definition['definition_id']);
378
						$definition['name'] = $settings ? $settings : $definition['name'] . ' copy';
379
						try {
380
							$bodefinitions->save($definition);
381
						} catch (Exception $e) {
382
							try {
383
								$definition['name'] .= ' ' . $GLOBALS['egw_info']['user']['account_lid'];
384
								$bodefinitions->save($definition);
385
							} catch (Exception $ex) {
386
								$failed++;
387
								$msg .= lang('Duplicate name, please choose another.');
388
								continue;
389
							}
390
						}
391
						$success++;
392
					}
393
				}
394
				break;
395
			case 'createexport':
396
				$action_msg = lang('created');
397
				// Should only be one selected
398
				foreach($selected as $id) {
399
					$definition = new importexport_definition($id);
400
					try {
401
						$export = $bodefinitions->export_from_import($definition);
402
						$export->save($definition->get_title());
403
					} catch (Exception $e) {
404
						if($export)
405
						{
406
							try {
407
								$export->name = $export->name.' ' . $GLOBALS['egw_info']['user']['account_lid'];
0 ignored issues
show
Bug Best Practice introduced by
The property name does not exist on importexport_definition. Since you implemented __set, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property name does not exist on importexport_definition. Since you implemented __get, consider adding a @property annotation.
Loading history...
408
								$export->save($export->name);
409
							} catch (Exception $ex) {
410
								$failed++;
411
								$msg .= lang('Duplicate name, please choose another.');
412
								continue;
413
							}
414
						}
415
						else
416
						{
417
							$failed++;
418
						}
419
					}
420
					$success++;
421
				}
422
				break;
423
		}
424
		return !$failed;
425
	}
426
427
	public function get_rows(&$query, &$rows, &$readonlys) {
428
		$rows = array();
429
		Api\Cache::setSession('importexport', 'index', array_intersect_key($query, array_flip(array(
430
			'col_filter', 'search', 'filter', 'filter2'
431
		))));
432
433
		// Special handling for allowed users 'private'
434
		if($query['col_filter']['allowed_users'] == 'private')
435
		{
436
			unset($query['col_filter']['allowed_users']);
437
			$query['col_filter'][] = 'allowed_users = ' . $GLOBALS['egw']->db->quote(',,');
438
		}
439
		$bodefinitions = new importexport_definitions_bo($query['col_filter'], true);
440
		// We don't care about readonlys for the UI
441
		return $bodefinitions->get_rows($query, $rows, $discard);
442
	}
443
444
	/**
445
	 * Edit a definition
446
	 *
447
	 * To jump to a certain step, pass the previous step in the URL step=wizard_stepXX
448
	 * The wizard will validate that step, then display the _next_ step..
449
	 */
450
	function edit()
451
	{
452
		if(!$_definition = $_GET['definition'])
453
		{
454
			$content = array(
455
				'edit'		=> true,
456
				'application'	=> $_GET['application'],
457
				'plugin'	=> $_GET['plugin']
458
			);
459
460
			// Jump to a step
461
			if($_GET['step'])
462
			{
463
				$content['edit'] = false;
464
				// Wizard will process previous step, then advance
465
				$content['step'] = $this->get_step($_GET['step'],-1);
466
				$content['button']['next'] = 'pressed';
467
				$this->wizard($content);
468
			}
469
			else
470
			{
471
				// Initial form
472
				$this->wizard($content);
473
			}
474
			return;
475
		}
476
		if(is_numeric($_GET['definition']))
477
		{
478
			$definition = (int)$_GET['definition'];
479
		}
480
		else
481
		{
482
			$definition = array('name' => $_definition);
483
		}
484
		$bodefinitions = new importexport_definitions_bo();
485
		$definition = $bodefinitions->read($definition);
486
		$definition['edit'] = true;
487
		// Jump to a step
488
		if($_GET['step'])
489
		{
490
			$definition['edit'] = false;
491
			// Wizard will process previous step, then advance
492
			$definition['step'] = $_GET['step'];;
493
			$definition['button'] = array('next' => 'pressed');
494
		}
495
		$this->wizard($definition);
496
	}
497
498
	function wizard($content = null, $msg='')
499
	{
500
		$this->etpl->read('importexport.wizardbox');
501
502
		if(is_array($content) &&! $content['edit'])
503
		{
504
			if(self::_debug) error_log('importexport.wizard->$content '. print_r($content,true));
505
			//foreach($content as $key => $val) error_log(" $key : ".array2string($val));
506
			// fetch plugin object
507
			if($content['plugin'] && $content['application'])
508
			{
509
				$wizard_name = $content['application'] . '_wizard_' . str_replace($content['application'] . '_', '', $content['plugin']);
510
511
				// we need to deal with the wizard object if exists
512
				if (file_exists(EGW_SERVER_ROOT . '/'. $content['application']."/inc/class.$wizard_name.inc.php"))
513
				{
514
					$wizard_plugin = $wizard_name;
515
				}
516
				else
517
				{
518
					$wizard_plugin = $content['plugin'];
519
				}
520
				// App translations
521
				if($content['application']) Api\Translation::add_app($content['application']);
522
523
				$this->plugin = is_object($GLOBALS['egw']->$wizard_plugin) ? $GLOBALS['egw']->$wizard_plugin : new $wizard_plugin;
524
525
				// Global object needs to be the same, or references to plugin don't work
526
				if(!is_object($GLOBALS['egw']->importexport_definitions_ui) || $GLOBALS['egw']->importexport_definitions_ui !== $this)
527
					$GLOBALS['egw']->importexport_definitions_ui =& $this;
528
			}
529
			// deal with buttons even if we are not on ajax
530
			if(isset($content['button']) && array_search('pressed',$content['button']) === false && count($content['button']) == 1)
531
			{
532
				$button = array_keys($content['button']);
533
				$content['button'] = array($button[0] => 'pressed');
534
			}
535
536
			// post process submitted step
537
			if($content['step'])
538
			{
539
				if(!$this->can_edit($content))
540
				{
541
					// Each step changes definition, reload it
542
					$bodefinitions = new importexport_definitions_bo();
543
					$definition = $bodefinitions->read($content);
544
					$content = $definition + array('step' => $content['step'], 'button' => $content['button']);
545
				}
546
				if(!key_exists($content['step'],$this->steps))
547
				{
548
					$next_step = $this->plugin->{$content['step']}($content,$sel_options,$readonlys,$preserv);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $readonlys seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $preserv does not exist. Did you maybe mean $preserve?
Loading history...
Comprehensibility Best Practice introduced by
The variable $sel_options seems to be never defined.
Loading history...
549
				}
550
				else
551
				{
552
					$next_step = $this->{$content['step']}($content,$sel_options,$readonlys,$preserv);
553
				}
554
			}
555
			else
556
			{
557
				die('Cannot find next step');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
558
			}
559
560
			// pre precess next step
561
			$sel_options = $readonlys = $preserv = array();
562
563
			// Disable finish button if required fields are missing
564
			if(!$content['name'] || !$content['type'] || !$content['plugin'])
565
			{
566
				$readonlys['button[finish]'] = true;
567
			}
568
			do
569
			{
570
				if(!key_exists($next_step,$this->steps))
571
				{
572
					$this->wizard_content_template = $this->plugin->$next_step($content,$sel_options,$readonlys,$preserv);
0 ignored issues
show
Bug Best Practice introduced by
The property wizard_content_template does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
573
				}
574
				else
575
				{
576
					$this->wizard_content_template = $this->$next_step($content,$sel_options,$readonlys,$preserv);
577
				}
578
				if($this->wizard_content_template == self::SKIP)
579
				{
580
					if(!key_exists($content['step'],$this->steps))
581
					{
582
						$next_step = $this->plugin->{$content['step']}($content);
583
					}
584
					else
585
					{
586
						$next_step = $this->{$content['step']}($content);
587
					}
588
				}
589
			} while($this->wizard_content_template == self::SKIP);
590
591
			if(!$this->can_edit($content))
592
			{
593
				$readonlys[$this->wizard_content_template] = true;
594
				$preserve = $content;
0 ignored issues
show
Unused Code introduced by
The assignment to $preserve is dead and can be removed.
Loading history...
595
				$readonlys['button[finish]'] = true;
596
			}
597
598
			unset($content['button']);
599
			$content['wizard_content'] = $this->wizard_content_template;
600
			$this->etpl->exec(self::_appname.'.importexport_definitions_ui.wizard',$content,$sel_options,$readonlys,$preserv,2);
601
602
			// Make sure JS is loaded - Framework won't send it
603
			Api\Framework::include_css_js_response();
604
		}
605
		else
606
		{
607
			// initial content
608
			$sel_options = $readonlys = $preserv = array();
609
			$readonlys['button[previous]'] = true;
610
			if($content['edit'])
611
			{
612
				unset ($content['edit']);
613
			}
614
615
			$this->wizard_content_template = $this->wizard_step10($content, $sel_options, $readonlys, $preserv);
616
617
			if(!$this->can_edit($content))
618
			{
619
				$readonlys[$this->wizard_content_template] = true;
620
				$preserve = $content;
621
				$readonlys['button[finish]'] = true;
622
			}
623
624
			$content['wizard_content'] = $this->wizard_content_template;
625
			$this->etpl->exec(self::_appname.'.importexport_definitions_ui.wizard',$content,$sel_options,$readonlys,$preserv,2);
626
		}
627
	}
628
629
	/**
630
	 * gets name of next step
631
	 *
632
	 * @param string  $curr_step
633
	 * @param int $step_width
634
	 * @return string containing function name of next step
635
	 */
636
	function get_step ($curr_step, $step_width)
637
	{
638
		/*if($content['plugin'] && $content['application']&& !is_object($this->plugin))
639
		{
640
			$plugin_definition =  $this->plugins[$content['application']][$content['plugin']]['definition'];
641
			if($plugin_definition) $this->plugin = new $plugin_definition;
642
		}*/
643
		if(is_object($this->plugin) && is_array($this->plugin->steps))
644
		{
645
			$steps = array_merge($this->steps,$this->plugin->steps);
646
			$steps = array_flip($steps); asort($steps);	$steps = array_flip($steps);
647
		}
648
		else
649
		{
650
			$steps = $this->steps;
651
		}
652
		$step_keys = array_keys($steps);
653
		$nn = array_search($curr_step,$step_keys)+(int)$step_width;
654
		return (key_exists($nn,$step_keys)) ? $step_keys[$nn] : 'wizard_finish';
655
	}
656
657
658
	function wizard_step10(&$content, &$sel_options, &$readonlys, &$preserv)
659
	{
660
		if(self::_debug) error_log('importexport.importexport_definitions_ui::wizard_step10->$content '.print_r($content,true));
661
662
		// return from step10
663
		if ($content['step'] == 'wizard_step10')
664
		{
665
			switch (array_search('pressed', $content['button']))
666
			{
667
				case 'next':
668
					return $this->get_step($content['step'],1);
669
				case 'finish':
670
					return 'wizard_finish';
671
				default :
672
					return $this->wizard_step10($content,$sel_options,$readonlys,$preserv);
673
			}
674
675
		}
676
		// init step10
677
		else
678
		{
679
			$content['text'] = $this->steps['wizard_step10'];
680
			foreach ($this->plugins as $appname => $options)
681
			{
682
				if($GLOBALS['egw_info']['user']['apps'][$appname] || $GLOBALS['egw_info']['user']['apps']['admin']) {
683
					$sel_options['application'][$appname] = lang($appname);
684
				}
685
			}
686
			$readonlys['button[previous]'] = true;
687
			$content['step'] = 'wizard_step10';
688
			$preserv = $content;
689
			unset ($preserv['button']);
690
			return 'importexport.wizard_chooseapp';
691
		}
692
693
	}
694
695
	// get plugin
696
	function wizard_step20(&$content, &$sel_options, &$readonlys, &$preserv)
697
	{
698
		if(self::_debug) error_log('importexport.' . get_class($this) . '::wizard_step20->$content '.print_r($content,true));
699
700
		// return from step20
701
		if ($content['step'] == 'wizard_step20')
702
		{
703
			switch (array_search('pressed', $content['button']))
704
			{
705
				case 'next':
706
					// There's no real reason the plugin has to come from any of these, as long as it has a $steps variable
707
					if($this->plugin instanceof importexport_iface_import_plugin || $this->plugin instanceof importexport_wizard_basic_import_csv || strpos(get_class($this->plugin), 'import') !== false) {
708
						$content['type'] = 'import';
709
					} elseif($this->plugin instanceof importexport_iface_export_plugin || $this->plugin instanceof importexport_wizard_basic_export_csv || strpos(get_class($this->plugin),'export') !== false) {
710
						$content['type'] = 'export';
711
					} else {
712
						throw new Api\Exception('Invalid plugin');
713
					}
714
					return $this->get_step($content['step'],1);
715
				case 'previous' :
716
					$readonlys['button[previous]'] = true;
717
					return $this->get_step($content['step'],-1);
718
				case 'finish':
719
					return 'wizard_finish';
720
				default :
721
					return $this->wizard_step20($content,$sel_options,$readonlys,$preserv);
722
			}
723
		}
724
		// init step20
725
		else
726
		{
727
			$content['text'] = $this->steps['wizard_step20'];
728
			$config = Api\Config::read('phpgwapi');
729
			foreach ($this->plugins[$content['application']] as $type => $plugins) {
730
				if($config['export_limit'] == 'no' && !$GLOBALS['egw_info']['user']['apps']['admin'] && $type == 'export') continue;
731
				foreach($plugins as $plugin => $name) {
732
					$sel_options['plugin'][$plugin] = $name;
733
				}
734
			}
735
			$content['step'] = 'wizard_step20';
736
			$preserv = $content;
737
			unset ($preserv['button']);
738
			return 'importexport.wizard_chooseplugin';
739
		}
740
741
742
	}
743
744
	// name
745
	function wizard_step21(&$content, &$sel_options, &$readonlys, &$preserv)
746
	{
747
		if(self::_debug) error_log('importexport.importexport_definitions_ui::wizard_step21->$content '.print_r($content,true));
748
749
		// Check for duplicate name
750
		$duplicate = isset($content['duplicate_error']);
751
		if($content['name'] && !$duplicate)
752
		{
753
			try {
754
				$check_definition = new importexport_definition($content['name']);
755
				if($check_definition && $check_definition->definition_id && $check_definition->definition_id != $content['definition_id'])
0 ignored issues
show
Bug Best Practice introduced by
The property definition_id does not exist on importexport_definition. Since you implemented __get, consider adding a @property annotation.
Loading history...
756
				{
757
					throw new Exception('Already exists');
758
				}
759
			} catch (Exception $e) {
760
			//		throw new Exception('Already exists');
761
				$content['duplicate_error'] = lang('Duplicate name, please choose another.');
762
763
				// Try some suggestions
764
				$suggestions = array(
765
					$content['name'] .'-'. $GLOBALS['egw_info']['user']['account_lid'],
766
					$content['name'] .'-'. $GLOBALS['egw_info']['user']['account_id'],
767
					$content['name'] .'-'. Api\DateTime::to('now', true),
768
					//$content['name'] .'-'. rand(0,100),
769
				);
770
				foreach($suggestions as $key => $suggestion) {
771
					$sug_definition = new importexport_definition($suggestion);
772
					if($sug_definition->definition_id) {
773
						unset($suggestions[$key]);
774
					}
775
				}
776
				if($suggestions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $suggestions of type array<integer,string> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
introduced by
$suggestions is a non-empty array, thus is always true.
Loading history...
777
					$content['duplicate_error'] .= '  '. lang('Try')." \n" . implode("\n", $suggestions);
778
				}
779
				return $this->get_step($content['step'],0);
780
			}
781
		}
782
783
		// return from step21
784
		if ($content['step'] == 'wizard_step21' && !$duplicate)
785
		{
786
			switch (array_search('pressed', $content['button']))
787
			{
788
				case 'next':
789
					return $this->get_step($content['step'],1);
790
				case 'previous' :
791
					return $this->get_step($content['step'],-1);
792
				case 'finish':
793
					return 'wizard_finish';
794
				default :
795
					return $this->wizard_step21($content,$sel_options,$readonlys,$preserv);
796
			}
797
		}
798
		// init step21
799
		else
800
		{
801
			$content['text'] = $this->steps['wizard_step21'] . ($duplicate ? "\n".$content['duplicate_error'] : '');
802
			$content['step'] = 'wizard_step21';
803
			unset($content['duplicate_error']);
804
			$preserv = $content;
805
			unset ($preserv['button']);
806
			return 'importexport.wizard_choosename';
807
		}
808
	}
809
810
	// allowed users
811
	function wizard_step90(&$content, &$sel_options, &$readonlys, &$preserv)
812
	{
813
		if(self::_debug) error_log('importexport.importexport_definitions_ui::wizard_step90->$content '.print_r($content,true));
814
815
		// return from step90
816
		if ($content['step'] == 'wizard_step90')
817
		{
818
			if($this->can_edit($content))
819
			{
820
				$content['owner'] = $content['just_me'] || !$GLOBALS['egw']->acl->check('share_definitions', Acl::READ,'importexport') ?
821
					($content['owner'] ? $content['owner'] : $GLOBALS['egw_info']['user']['account_id']) :
822
					null;
823
				$content['allowed_users'] = $content['just_me'] ? '' : ($content['all_users'] ? 'all' : implode(',',$content['allowed_users']));
824
				unset($content['just_me']);
825
			}
826
827
			// workaround for some ugly bug related to readonlys;
828
			switch (array_search('pressed', $content['button']))
829
			{
830
				case 'previous' :
831
					return $this->get_step($content['step'],-1);
832
				case 'next':
833
				case 'finish':
834
					return 'wizard_finish';
835
				default :
836
					return $this->wizard_step90($content,$sel_options,$readonlys,$preserv);
837
			}
838
		}
839
		// init step90
840
		else
841
		{
842
			$content['text'] = $this->steps['wizard_step90'];
843
			$content['step'] = 'wizard_step90';
844
			$preserv = $content;
845
846
			// Set owner for non-admins
847
			$content['just_me'] = ((!$content['allowed_users'] || !$content['allowed_users'][0] && count($content['allowed_users']) ==1) && $content['owner']);
848
			$content['all_users'] = is_array($content['allowed_users']) && array_key_exists('0',$content['allowed_users']) && $content['allowed_users'][0] == 'all' ||
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: $content['all_users'] = ...lowed_users'] == 'all'), Probably Intended Meaning: $content['all_users'] = ...lowed_users'] == 'all')
Loading history...
849
			$content['allowed_users'] == 'all';
850
			if(!$GLOBALS['egw']->acl->check('share_definition', Acl::READ, 'importexport') && !$GLOBALS['egw_info']['user']['apps']['admin'])
851
			{
852
				$content['allowed_users'] = array();
853
				$readonlys['allowed_users'] = true;
854
				$readonlys['just_me'] = true;
855
				$readonlys['all_users'] = true;
856
				$content['just_me'] = true;
857
			}
858
859
			$sel_options = array(
860
				'allowed_users' => array(
861
					array('value' => null, 'label' => lang('Just me')),
862
					array('value' => 'all', 'label' => lang('all users'))
863
				)
864
			);
865
			// Hide 'just me' checkbox, users get confused by read-only
866
			if($readonlys['just_me'] || !$this->can_edit($content))
867
			{
868
				$content['no_just_me'] = true;
869
			}
870
			if($readonlys['all_users'] || !$this->can_edit($content))
871
			{
872
				$content['no_all_users'] = true;
873
			}
874
			unset ($preserv['button']);
875
876
			$readonlys['button[next]'] = true;
877
			return 'importexport.wizard_chooseallowedusers';
878
		}
879
	}
880
881
	function wizard_finish(&$content)
882
	{
883
		if(self::_debug) error_log('importexport.importexport_definitions_ui::wizard_finish->$content '.print_r($content,true));
884
		// Take out some UI leavings
885
		unset($content['text']);
886
		unset($content['step']);
887
		unset($content['button']);
888
889
		$bodefinitions = new importexport_definitions_bo();
890
		$bodefinitions->save($content);
891
		// This message is displayed if browser cant close window
892
		$content['msg'] = lang('ImportExport wizard finished successfully!');
893
		Framework::refresh_opener('','importexport');
894
		Framework::window_close();
895
		return 'importexport.wizard_close';
896
	}
897
898
	function import_definition($content='')
899
	{
900
		$bodefinitions = new importexport_definitions_bo();
901
		if (is_array($content))
902
		{
903
			if($content['import_file']['tmp_name'])
904
			{
905
				$result = $bodefinitions->import($content['import_file']['tmp_name']);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $bodefinitions->import($...ort_file']['tmp_name']) targeting importexport_definitions_bo::import() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
906
				$msg = lang('%1 definitions %2', count($result), lang('imported')) ."\n". implode("\n", array_keys($result));
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with count($result). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

906
				$msg = /** @scrutinizer ignore-call */ lang('%1 definitions %2', count($result), lang('imported')) ."\n". implode("\n", array_keys($result));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
907
				return $this->index(null, $msg);
908
			}
909
			if($content['update'])
910
			{
911
				$applist = importexport_helper_functions::get_apps('all', true);
912
				foreach($applist as $appname) {
913
					importexport_helper_functions::load_defaults($appname);
914
				}
915
				return $this->index();
916
			}
917
		}
918
		else
919
		{
920
			$content = array();
921
			$etpl = new etemplate(self::_appname.'.import_definition');
922
			return $etpl->exec(self::_appname.'.importexport_definitions_ui.import_definition',$content,array(),$readonlys,$preserv);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $readonlys seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $preserv seems to be never defined.
Loading history...
923
		}
924
	}
925
926
	/**
927
	 * Determine if the user is allowed to edit the definition
928
	 *
929
	 */
930
	protected function can_edit(Array $definition)
931
	{
932
		if($definition['owner'] && $definition['owner'] == $GLOBALS['egw_info']['user']['account_id'])
933
		{
934
			// Definition belongs to user
935
			return true;
936
		}
937
		elseif($definition['definition_id'] && !$definition['owner'] && $GLOBALS['egw_info']['user']['apps']['admin'])
938
		{
939
			// Definition is unowned, and user is an admin
940
			return true;
941
		}
942
		elseif(!$definition['definition_id'])
943
		{
944
			// Definition is in-progress, not saved yet
945
			return true;
946
		}
947
		return false;
948
	}
949
}
950