Completed
Pull Request — develop (#856)
by Shandak
05:21
created

RModelList::getState()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 9
rs 10
cc 2
nc 2
nop 2
1
<?php
2
/**
3
 * @package     Redcore
4
 * @subpackage  Base
5
 *
6
 * @copyright   Copyright (C) 2008 - 2016 redCOMPONENT.com. 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 get model state variables
235
	 *
236
	 * @param   string  $property  Optional parameter name
237
	 * @param   mixed   $default   Optional default value
238
	 *
239
	 * @return  mixed  The property where specified, the state object where omitted
240
	 *
241
	 * @since   3.0
242
	 * @throws Exception
243
	 */
244
	public function getState($property = null, $default = null)
245
	{
246
		if ($this->__state_set !== true)
247
		{
248
			\Joomla\CMS\Factory::getApplication()
0 ignored issues
show
Bug introduced by
The type Joomla\CMS\Factory was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
249
				->triggerEvent('onRegisterModelContext', [$this->context]);
250
		}
251
252
		return parent::getState($property, $default);
253
	}
254
255
	/**
256
	 * Method to auto-populate the model state.
257
	 *
258
	 * This method should only be called once per instantiation and is designed
259
	 * to be called on the first call to the getState() method unless the model
260
	 * configuration flag to ignore the request is set.
261
	 *
262
	 * Note. Calling getState in this method will result in recursion.
263
	 *
264
	 * @param   string  $ordering   An optional ordering field.
265
	 * @param   string  $direction  An optional direction (asc|desc).
266
	 *
267
	 * @return  void
268
	 *
269
	 * @since   1.0.0
270
	 */
271
	protected function populateState($ordering = null, $direction = null)
272
	{
273
		// If the context is set, assume that stateful lists are used.
274
		if (!$this->context)
275
		{
276
			$this->setState('list.start', 0);
277
			$this->setState('list.limit', 0);
278
279
			return;
280
		}
281
282
		$app         = JFactory::getApplication();
283
		$inputFilter = JFilterInput::getInstance();
284
		$params      = null;
285
286
		// Load the parameters for frontend.
287
		if (version_compare(JVERSION, '3.7', '<') && $app->isSite())
288
		{
289
			$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

289
			/** @scrutinizer ignore-call */ 
290
   $params = $app->getParams();
Loading history...
290
		}
291
		elseif (method_exists($app, 'isClient') && $app->isClient('site'))
292
		{
293
			$params = $app->getParams();
294
		}
295
296
		// Receive & set filters
297
		$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

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

522
			/** @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...
523
524
			return false;
525
		}
526
527
		// Store the form for later.
528
		$this->forms[$hash] = $form;
529
530
		return $form;
531
	}
532
533
	/**
534
	 * Method to get the data that should be injected in the form.
535
	 *
536
	 * @return	mixed	The data for the form.
537
	 */
538
	protected function loadFormData()
539
	{
540
		// Check the session for previously entered form data.
541
		$data = JFactory::getApplication()->getUserState($this->context, array());
542
543
		return $data;
544
	}
545
546
	/**
547
	 * Method to allow derived classes to preprocess the form.
548
	 *
549
	 * @param   JForm   $form   A JForm object.
550
	 * @param   mixed   $data   The data expected for the form.
551
	 * @param   string  $group  The name of the plugin group to import (defaults to "content").
552
	 *
553
	 * @return  void
554
	 *
555
	 * @throws  Exception if there is an error in the form event.
556
	 */
557
	protected function preprocessForm(JForm $form, $data, $group = 'content')
558
	{
559
		// Import the appropriate plugin group.
560
		JPluginHelper::importPlugin($group);
561
562
		// Get the dispatcher.
563
		$dispatcher = RFactory::getDispatcher();
564
565
		// Trigger the form preparation event.
566
		$results = $dispatcher->trigger('onContentPrepareForm', array($form, $data));
567
568
		// Check for errors encountered while preparing the form.
569
		if (count($results) && in_array(false, $results, true))
570
		{
571
			// Get the last error.
572
			$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

572
			$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...
573
574
			if (!($error instanceof Exception))
0 ignored issues
show
introduced by
$error is never a sub-type of Exception.
Loading history...
575
			{
576
				throw new Exception($error);
577
			}
578
		}
579
	}
580
581
	/**
582
	 * Publish/Unpublish items
583
	 *
584
	 * @param   mixed    $pks    id or array of ids of items to be published/unpublished
585
	 * @param   integer  $state  New desired state
586
	 *
587
	 * @return  boolean
588
	 */
589
	public function publish($pks = null, $state = 1)
590
	{
591
		// Initialise variables.
592
		$table = $this->getTable();
593
		$table->publish($pks, $state);
594
595
		return true;
596
	}
597
598
	/**
599
	 * Method to validate the form data.
600
	 *
601
	 * @param   JForm   $form   The form to validate against.
602
	 * @param   array   $data   The data to validate.
603
	 * @param   string  $group  The name of the field group to validate.
604
	 *
605
	 * @return  mixed  Array of filtered data if valid, false otherwise.
606
	 *
607
	 * @see     JFormRule
608
	 * @see     JFilterInput
609
	 * @since   1.7
610
	 */
611
	public function validate($form, $data, $group = null)
612
	{
613
		// Filter and validate the form data.
614
		$data = $form->filter($data);
615
		$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

615
		$return = $form->validate(/** @scrutinizer ignore-type */ $data, $group);
Loading history...
616
617
		// Check for an error.
618
		if ($return instanceof Exception)
0 ignored issues
show
introduced by
$return is never a sub-type of Exception.
Loading history...
619
		{
620
			$this->setError($return->getMessage());
621
622
			return false;
623
		}
624
625
		// Check the validation results.
626
		if ($return === false)
627
		{
628
			// Get the validation messages from the form.
629
			foreach ($form->getErrors() as $message)
630
			{
631
				$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

631
				/** @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...
632
			}
633
634
			return false;
635
		}
636
637
		return $data;
638
	}
639
}
640