Passed
Pull Request — develop (#890)
by Tito
03:54
created

RModelList::populateState()   F

Complexity

Conditions 33
Paths 865

Size

Total Lines 192
Code Lines 89

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 2 Features 0
Metric Value
eloc 89
c 3
b 2
f 0
dl 0
loc 192
rs 0.1875
cc 33
nc 865
nop 2

How to fix   Long Method    Complexity   

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:

1
<?php
2
/**
3
 * @package     Redcore
4
 * @subpackage  Base
5
 *
6
 * @copyright   Copyright (C) 2008 - 2020 redWEB.dk. All rights reserved.
7
 * @license     GNU General Public License version 2 or later, see LICENSE.
8
 */
9
10
defined('JPATH_REDCORE') or die;
11
12
JLoader::import('joomla.application.component.modellist');
13
14
use Joomla\Utilities\ArrayHelper;
15
16
/**
17
 * redCORE Base Model List
18
 *
19
 * @package     Redcore
20
 * @subpackage  Base
21
 * @since       1.0
22
 */
23
abstract class RModelList extends JModelList
24
{
25
	/**
26
	 * Name of the filter form to load
27
	 *
28
	 * @var  string
29
	 */
30
	protected $filterFormName = null;
31
32
	/**
33
	 * Associated HTML form
34
	 *
35
	 * @var  string
36
	 */
37
	protected $htmlFormName = 'adminForm';
38
39
	/**
40
	 * Array of form objects.
41
	 *
42
	 * @var  JForm[]
43
	 */
44
	protected $forms = array();
45
46
	/**
47
	 * A prefix for pagination request variables.
48
	 *
49
	 * @var  string
50
	 */
51
	protected $paginationPrefix = '';
52
53
	/**
54
	 * Limit field used by the pagination
55
	 *
56
	 * @var  string
57
	 */
58
	protected $limitField = 'auto';
59
60
	/**
61
	 * Limitstart field used by the pagination
62
	 *
63
	 * @var  string
64
	 */
65
	protected $limitstartField = 'auto';
66
67
	/**
68
	 * A blacklist of filter variables to not merge into the model's state
69
	 *
70
	 * @var    array
71
	 * @since  1.6.10
72
	 */
73
	protected $filterBlacklist = array();
74
75
	/**
76
	 * A blacklist of list variables to not merge into the model's state
77
	 *
78
	 * @var    array
79
	 * @since  1.6.10
80
	 */
81
	protected $listBlacklist = array('select');
82
83
	/**
84
	 * Constructor.
85
	 *
86
	 * @param   array  $config  An optional associative array of configuration settings.
87
	 *
88
	 * @see     JModelLegacy
89
	 */
90
	public function __construct($config = array())
91
	{
92
		$input = JFactory::getApplication()->input;
93
		$view = $input->getString('view', '');
94
		$option = $input->getString('option', '');
95
96
		// Different context depending on the view
97
		if (empty($this->context))
98
		{
99
			$this->context = strtolower($option . '.' . $view . '.' . $this->getName());
100
		}
101
102
		// Different pagination depending on the view
103
		if (empty($this->paginationPrefix))
104
		{
105
			$this->paginationPrefix = strtolower($option . '_' . $view . '_' . $this->getName() . '_');
106
		}
107
108
		if ($this->limitstartField == 'auto')
109
		{
110
			$this->limitstartField = $this->paginationPrefix . 'limitstart';
111
		}
112
113
		if ($this->limitField == 'auto')
114
		{
115
			$this->limitField = $this->paginationPrefix . 'limit';
116
		}
117
118
		parent::__construct($config);
119
	}
120
121
	/**
122
	 * Delete items
123
	 *
124
	 * @param   mixed  $pks  id or array of ids of items to be deleted
125
	 *
126
	 * @return  boolean
127
	 */
128
	public function delete($pks = null)
129
	{
130
		// Initialise variables.
131
		$table = $this->getTable();
132
		$table->delete($pks);
133
134
		return true;
135
	}
136
137
	/**
138
	 * Function to get the active filters
139
	 *
140
	 * @return  array  Associative array in the format: array('filter_published' => 0)
141
	 *
142
	 * @since   3.2
143
	 */
144
	public function getActiveFilters()
145
	{
146
		$activeFilters = array();
147
148
		if (!empty($this->filter_fields))
149
		{
150
			foreach ($this->filter_fields as $filter)
151
			{
152
				$filterName = 'filter.' . $filter;
153
154
				if (property_exists($this->state, $filterName) && (!empty($this->state->{$filterName}) || is_numeric($this->state->{$filterName})))
155
				{
156
					$activeFilters[$filter] = $this->state->get($filterName);
157
				}
158
			}
159
		}
160
161
		return $activeFilters;
162
	}
163
164
	/**
165
	 * Get the zone form
166
	 *
167
	 * @param   array    $data      data
168
	 * @param   boolean  $loadData  load current data
169
	 *
170
	 * @return  JForm/false  the JForm object or false
0 ignored issues
show
Documentation Bug introduced by
The doc comment JForm/false at position 0 could not be parsed: Unknown type name 'JForm/false' at position 0 in JForm/false.
Loading history...
171
	 */
172
	public function getForm($data = array(), $loadData = true)
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed. ( Ignorable by Annotation )

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

172
	public function getForm(/** @scrutinizer ignore-unused */ $data = array(), $loadData = true)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
173
	{
174
		$form = null;
175
176
		if (!empty($this->filterFormName))
177
		{
178
			// Get the form.
179
			$form = $this->loadForm(
180
				$this->context . '.filter',
181
				$this->filterFormName,
182
				array('control' => '', 'load_data' => $loadData)
183
			);
184
185
			if (!empty($form))
186
			{
187
				$form->setFieldAttribute($this->limitField, 'default', JFactory::getApplication()->get('list_limit'), 'list');
188
			}
189
		}
190
191
		return $form;
192
	}
193
194
	/**
195
	 * Method to get the associated form name
196
	 *
197
	 * @return  string  The name of the form
198
	 */
199
	public function getHtmlFormName()
200
	{
201
		return $this->htmlFormName;
202
	}
203
204
	/**
205
	 * Method to get a JPagination object for the data set.
206
	 *
207
	 * @return  JPagination  A JPagination object for the data set.
208
	 */
209
	public function getPagination()
210
	{
211
		// Get a storage key.
212
		$store = $this->getStoreId('getPagination');
213
214
		// Try to load the data from internal storage.
215
		if (isset($this->cache[$store]))
216
		{
217
			return $this->cache[$store];
218
		}
219
220
		// Create the pagination object.
221
		$limit = (int) $this->getState('list.limit') - (int) $this->getState('list.links');
222
		$page  = new RPagination($this->getTotal(), $this->getStart(), $limit, $this->paginationPrefix);
223
224
		// Set the name of the HTML form associated
225
		$page->formName = $this->htmlFormName;
226
227
		// Add the object to the internal cache.
228
		$this->cache[$store] = $page;
229
230
		return $this->cache[$store];
231
	}
232
233
	/**
234
	 * Method to auto-populate the model state.
235
	 *
236
	 * This method should only be called once per instantiation and is designed
237
	 * to be called on the first call to the getState() method unless the model
238
	 * configuration flag to ignore the request is set.
239
	 *
240
	 * Note. Calling getState in this method will result in recursion.
241
	 *
242
	 * @param   string  $ordering   An optional ordering field.
243
	 * @param   string  $direction  An optional direction (asc|desc).
244
	 *
245
	 * @return  void
246
	 *
247
	 * @since   1.0.0
248
	 */
249
	protected function populateState($ordering = null, $direction = null)
250
	{
251
		// If the context is set, assume that stateful lists are used.
252
		if (!$this->context)
253
		{
254
			$this->setState('list.start', 0);
255
			$this->setState('list.limit', 0);
256
257
			return;
258
		}
259
260
		$app         = JFactory::getApplication();
261
		$inputFilter = JFilterInput::getInstance();
262
		$params      = null;
263
264
		// Load the parameters for frontend.
265
		if (version_compare(JVERSION, '3.7', '<') && $app->isSite())
266
		{
267
			$params = $app->getParams();
0 ignored issues
show
Bug introduced by
The method getParams() does not exist on JApplicationCms. It seems like you code against a sub-type of JApplicationCms such as JApplicationSite. ( Ignorable by Annotation )

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

267
			/** @scrutinizer ignore-call */ 
268
   $params = $app->getParams();
Loading history...
268
		}
269
		elseif (method_exists($app, 'isClient') && $app->isClient('site'))
270
		{
271
			$params = $app->getParams();
272
		}
273
274
		// Receive & set filters
275
		$filters = $app->getUserStateFromRequest($this->context . '.filter', 'filter', array(), 'array');
0 ignored issues
show
Bug introduced by
array() of type array is incompatible with the type string expected by parameter $default of JApplicationCms::getUserStateFromRequest(). ( Ignorable by Annotation )

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

275
		$filters = $app->getUserStateFromRequest($this->context . '.filter', 'filter', /** @scrutinizer ignore-type */ array(), 'array');
Loading history...
276
277
		if ($filters)
278
		{
279
			foreach ($filters as $name => $value)
280
			{
281
				// Exclude if blacklisted
282
				if (!in_array($name, $this->filterBlacklist))
283
				{
284
					$this->setState('filter.' . $name, $value);
285
				}
286
			}
287
		}
288
289
		$limit = 0;
290
291
		// Receive & set list options
292
		$list = $app->getUserStateFromRequest($this->context . '.list', 'list', array(), 'array');
293
294
		if ($list)
295
		{
296
			foreach ($list as $name => $value)
297
			{
298
				// Exclude if blacklisted
299
				if (!in_array($name, $this->listBlacklist))
300
				{
301
					// Extra validations
302
					switch ($name)
303
					{
304
						case 'fullordering':
305
							$orderingParts = explode(' ', $value);
306
307
							if (count($orderingParts) >= 2)
308
							{
309
								// Latest part will be considered the direction
310
								$fullDirection = end($orderingParts);
311
312
								if (in_array(strtoupper($fullDirection), array('ASC', 'DESC', '')))
313
								{
314
									$this->setState('list.direction', $fullDirection);
315
								}
316
								else
317
								{
318
									$this->setState('list.direction', $direction);
319
320
									// Fallback to the default value
321
									$value = $ordering . ' ' . $direction;
322
								}
323
324
								unset($orderingParts[count($orderingParts) - 1]);
325
326
								// The rest will be the ordering
327
								$fullOrdering = implode(' ', $orderingParts);
328
329
								if (in_array($fullOrdering, $this->filter_fields))
330
								{
331
									$this->setState('list.ordering', $fullOrdering);
332
								}
333
								else
334
								{
335
									$this->setState('list.ordering', $ordering);
336
337
									// Fallback to the default value
338
									$value = $ordering . ' ' . $direction;
339
								}
340
							}
341
							else
342
							{
343
								$this->setState('list.ordering', $ordering);
344
								$this->setState('list.direction', $direction);
345
346
								// Fallback to the default value
347
								$value = $ordering . ' ' . $direction;
348
							}
349
							break;
350
351
						case 'ordering':
352
							if (!in_array($value, $this->filter_fields))
353
							{
354
								$value = $ordering;
355
							}
356
							break;
357
358
						case 'direction':
359
							if (!in_array(strtoupper($value), array('ASC', 'DESC', '')))
360
							{
361
								$value = $direction;
362
							}
363
							break;
364
365
						case $this->limitField:
366
							$value = $inputFilter->clean($value, 'int');
367
							$this->setState('list.limit', $value);
368
							$limit = $value;
369
							break;
370
371
						case $this->limitstartField:
372
							$value = $inputFilter->clean($value, 'int');
373
							break;
374
375
						case 'select':
376
							$explodedValue = explode(',', $value);
377
378
							foreach ($explodedValue as &$field)
379
							{
380
								$field = $inputFilter->clean($field, 'cmd');
381
							}
382
383
							$value = implode(',', $explodedValue);
384
							break;
385
					}
386
387
					$this->setState('list.' . $name, $value);
388
				}
389
			}
390
		}
391
		else
392
		// Keep B/C for components previous to jform forms for filters
393
		{
394
			// Pre-fill the limits
395
			$defaultLimit = ($params && $params->get('list_limit') >= 0) ? $params->get('list_limit', $app->get('list_limit')) : $app->get('list_limit');
396
			$limit = $app->getUserStateFromRequest('global.list.' . $this->limitField, $this->limitField, $defaultLimit, 'uint');
397
			$this->setState('list.limit', $limit);
398
399
			// Check if the ordering field is in the white list, otherwise use the incoming value.
400
			$value = $app->getUserStateFromRequest($this->context . '.ordercol', 'filter_order', $ordering);
401
402
			if (!in_array($value, $this->filter_fields))
403
			{
404
				$value = $ordering;
405
				$app->setUserState($this->context . '.ordercol', $value);
406
			}
407
408
			$this->setState('list.ordering', $value);
409
410
			// Check if the ordering direction is valid, otherwise use the incoming value.
411
			$value = $app->getUserStateFromRequest($this->context . '.orderdirn', 'filter_order_Dir', $direction);
412
413
			if (!in_array(strtoupper($value), array('ASC', 'DESC', '')))
414
			{
415
				$value = $direction;
416
				$app->setUserState($this->context . '.orderdirn', $value);
417
			}
418
419
			$this->setState('list.direction', $value);
420
		}
421
422
		// Support old ordering field
423
		$oldOrdering = $app->input->get('filter_order');
424
425
		if (!empty($oldOrdering) && in_array($oldOrdering, $this->filter_fields))
426
		{
427
			$this->setState('list.ordering', $oldOrdering);
428
		}
429
430
		// Support old direction field
431
		$oldDirection = $app->input->get('filter_order_Dir');
432
433
		if (!empty($oldDirection) && in_array(strtoupper($oldDirection), array('ASC', 'DESC', '')))
434
		{
435
			$this->setState('list.direction', $oldDirection);
436
		}
437
438
		$value = $app->getUserStateFromRequest($this->context . '.' . $this->limitstartField, $this->limitstartField, 0, 'int');
439
		$limitstart = ($limit != 0 ? (floor($value / $limit) * $limit) : 0);
440
		$this->setState('list.start', $limitstart);
441
	}
442
443
	/**
444
	 * Method to get a form object.
445
	 *
446
	 * @param   string   $name     The name of the form.
447
	 * @param   string   $source   The form source. Can be XML string if file flag is set to false.
448
	 * @param   array    $options  Optional array of options for the form creation.
449
	 * @param   boolean  $clear    Optional argument to force load a new form.
450
	 * @param   mixed    $xpath    An optional xpath to search for the fields.
451
	 *
452
	 * @return  mixed  JForm object on success, False on error.
453
	 *
454
	 * @see     JForm
455
	 */
456
	protected function loadForm($name, $source = null, $options = array(), $clear = false, $xpath = false)
457
	{
458
		// Handle the optional arguments.
459
		$options['control'] = ArrayHelper::getValue($options, 'control', false);
460
461
		// Create a signature hash.
462
		$hash = md5($source . serialize($options));
463
464
		// Check if we can use a previously loaded form.
465
		if (isset($this->forms[$hash]) && !$clear)
466
		{
467
			return $this->forms[$hash];
468
		}
469
470
		// Get the form.
471
		RForm::addFormPath(JPATH_COMPONENT . '/models/forms');
472
		RForm::addFieldPath(JPATH_COMPONENT . '/models/fields');
473
474
		try
475
		{
476
			$form = RForm::getInstance($name, $source, $options, false, $xpath);
477
478
			if (isset($options['load_data']) && $options['load_data'])
479
			{
480
				// Get the data for the form.
481
				$data = $this->loadFormData();
482
			}
483
			else
484
			{
485
				$data = array();
486
			}
487
488
			// Allow for additional modification of the form, and events to be triggered.
489
			// We pass the data because plugins may require it.
490
			$this->preprocessForm($form, $data);
491
492
			// Filter and validate the form data.
493
			$data = $form->filter($data);
494
495
			// Load the data into the form after the plugins have operated.
496
			$form->bind($data);
497
		}
498
		catch (Exception $e)
499
		{
500
			$this->setError($e->getMessage());
0 ignored issues
show
Deprecated Code introduced by
The function JObject::setError() has been deprecated: 12.3 JError has been deprecated ( Ignorable by Annotation )

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

500
			/** @scrutinizer ignore-deprecated */ $this->setError($e->getMessage());

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
501
502
			return false;
503
		}
504
505
		// Store the form for later.
506
		$this->forms[$hash] = $form;
507
508
		return $form;
509
	}
510
511
	/**
512
	 * Method to get the data that should be injected in the form.
513
	 *
514
	 * @return	mixed	The data for the form.
515
	 */
516
	protected function loadFormData()
517
	{
518
		// Check the session for previously entered form data.
519
		$data = JFactory::getApplication()->getUserState($this->context, array());
520
521
		return $data;
522
	}
523
524
	/**
525
	 * Method to allow derived classes to preprocess the form.
526
	 *
527
	 * @param   JForm   $form   A JForm object.
528
	 * @param   mixed   $data   The data expected for the form.
529
	 * @param   string  $group  The name of the plugin group to import (defaults to "content").
530
	 *
531
	 * @return  void
532
	 *
533
	 * @throws  Exception if there is an error in the form event.
534
	 */
535
	protected function preprocessForm(JForm $form, $data, $group = 'content')
536
	{
537
		// Import the appropriate plugin group.
538
		JPluginHelper::importPlugin($group);
539
540
		// Get the dispatcher.
541
		$dispatcher = RFactory::getDispatcher();
542
543
		// Trigger the form preparation event.
544
		$results = $dispatcher->trigger('onContentPrepareForm', array($form, $data));
545
546
		// Check for errors encountered while preparing the form.
547
		if (count($results) && in_array(false, $results, true))
548
		{
549
			// Get the last error.
550
			$error = $dispatcher->getError();
0 ignored issues
show
Deprecated Code introduced by
The function JObject::getError() has been deprecated: 12.3 JError has been deprecated ( Ignorable by Annotation )

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

550
			$error = /** @scrutinizer ignore-deprecated */ $dispatcher->getError();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
551
552
			if (!($error instanceof Exception))
0 ignored issues
show
introduced by
$error is never a sub-type of Exception.
Loading history...
553
			{
554
				throw new Exception($error);
555
			}
556
		}
557
	}
558
559
	/**
560
	 * Publish/Unpublish items
561
	 *
562
	 * @param   mixed    $pks    id or array of ids of items to be published/unpublished
563
	 * @param   integer  $state  New desired state
564
	 *
565
	 * @return  boolean
566
	 */
567
	public function publish($pks = null, $state = 1)
568
	{
569
		// Initialise variables.
570
		$table = $this->getTable();
571
		$table->publish($pks, $state);
572
573
		return true;
574
	}
575
576
	/**
577
	 * Method to validate the form data.
578
	 *
579
	 * @param   JForm   $form   The form to validate against.
580
	 * @param   array   $data   The data to validate.
581
	 * @param   string  $group  The name of the field group to validate.
582
	 *
583
	 * @return  mixed  Array of filtered data if valid, false otherwise.
584
	 *
585
	 * @see     JFormRule
586
	 * @see     JFilterInput
587
	 * @since   1.7
588
	 */
589
	public function validate($form, $data, $group = null)
590
	{
591
		// Filter and validate the form data.
592
		$data = $form->filter($data);
593
		$return = $form->validate($data, $group);
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type false; however, parameter $data of JForm::validate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

593
		$return = $form->validate(/** @scrutinizer ignore-type */ $data, $group);
Loading history...
594
595
		// Check for an error.
596
		if ($return instanceof Exception)
0 ignored issues
show
introduced by
$return is never a sub-type of Exception.
Loading history...
597
		{
598
			$this->setError($return->getMessage());
599
600
			return false;
601
		}
602
603
		// Check the validation results.
604
		if ($return === false)
605
		{
606
			// Get the validation messages from the form.
607
			foreach ($form->getErrors() as $message)
608
			{
609
				$this->setError($message);
0 ignored issues
show
Deprecated Code introduced by
The function JObject::setError() has been deprecated: 12.3 JError has been deprecated ( Ignorable by Annotation )

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

609
				/** @scrutinizer ignore-deprecated */ $this->setError($message);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
610
			}
611
612
			return false;
613
		}
614
615
		return $data;
616
	}
617
}
618