importexport_definitions_ui::wizard_step10()   B
last analyzed

Complexity

Conditions 8
Paths 12

Size

Total Lines 33
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 19
nc 12
nop 4
dl 0
loc 33
rs 8.4444
c 0
b 0
f 0
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