RedcoreModelWebservice   F
last analyzed

Complexity

Total Complexity 134

Size/Duplication

Total Lines 960
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 134
eloc 309
c 0
b 0
f 0
dl 0
loc 960
rs 2

24 Methods

Rating   Name   Duplication   Size   Complexity  
A bindElementToArray() 0 27 6
B getResourcesFromPost() 0 42 10
A bindPathToArray() 0 10 2
A addChildWithCDATA() 0 14 2
A getItem() 0 28 6
A getTransformTypes() 0 26 4
A bindReadAttributesToFormData() 0 18 5
A bindDefaultAttributesToFormData() 0 9 3
A loadFormData() 0 23 2
A save() 0 35 4
A loadFormComplexTypeXml() 0 8 2
A bindXMLToForm() 0 22 3
A loadFormOperationXml() 0 8 2
B getOperationAttributesFromPost() 0 25 7
B bindAttributesToFormData() 0 30 7
A setPropertyByXpath() 0 22 5
F saveXml() 0 138 23
A bindComplexTypesToFormData() 0 18 5
A __construct() 0 5 1
A setFieldsAndResources() 0 7 1
B appendXML() 0 29 7
A bindTaskAttributesToFormData() 0 18 5
B getFieldsFromPost() 0 42 10
C getForm() 0 53 12

How to fix   Complexity   

Complex Class

Complex classes like RedcoreModelWebservice often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use RedcoreModelWebservice, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package     Redcore.Backend
4
 * @subpackage  Models
5
 *
6
 * @copyright   Copyright (C) 2008 - 2021 redWEB.dk. All rights reserved.
7
 * @license     GNU General Public License version 2 or later, see LICENSE.
8
 */
9
10
use Joomla\Registry\Registry;
11
12
defined('_JEXEC') or die;
13
14
jimport('joomla.filesystem.folder');
15
16
use Joomla\Utilities\ArrayHelper;
17
18
/**
19
 * Webservice Model
20
 *
21
 * @package     Redcore.Backend
22
 * @subpackage  Models
23
 * @since       1.4
24
 */
25
class RedcoreModelWebservice extends RModelAdmin
26
{
27
	/**
28
	 * @var SimpleXMLElement
29
	 */
30
	public $xmlFile;
31
32
	/**
33
	 * @var SimpleXMLElement
34
	 */
35
	public $defaultXmlFile;
36
37
	/**
38
	 * @var string
39
	 */
40
	public $operationXml;
41
42
	/**
43
	 * @var string
44
	 */
45
	public $complexTypeXml;
46
47
	/**
48
	 * @var array
49
	 */
50
	public $formData = array();
51
52
	/**
53
	 * @var array
54
	 */
55
	public $fields;
56
57
	/**
58
	 * @var array
59
	 */
60
	public $resources;
61
62
	/**
63
	 * Constructor.
64
	 *
65
	 * @param   array  $config  Configuration array
66
	 *
67
	 * @throws  RuntimeException
68
	 */
69
	public function __construct($config = array())
70
	{
71
		parent::__construct($config);
72
73
		$this->defaultXmlFile = new SimpleXMLElement(file_get_contents(JPATH_COMPONENT_ADMINISTRATOR . '/models/forms/webservice_defaults.xml'));
74
	}
75
76
	/**
77
	 * Method to save the form data.
78
	 *
79
	 * @param   array  $data  The form data.
80
	 *
81
	 * @return  boolean  True on success, False on error.
82
	 *
83
	 * @throws  RuntimeException
84
	 *
85
	 * @since   1.2
86
	 */
87
	public function save($data)
88
	{
89
		try
90
		{
91
			if (!$this->saveXml($data))
92
			{
93
				return false;
94
			}
95
		}
96
		catch (Exception $e)
97
		{
98
			$this->setError(JText::sprintf('COM_REDCORE_WEBSERVICE_ERROR_SAVING_XML', $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

98
			/** @scrutinizer ignore-deprecated */ $this->setError(JText::sprintf('COM_REDCORE_WEBSERVICE_ERROR_SAVING_XML', $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...
99
100
			return false;
101
		}
102
103
		/** @var RedcoreModelWebservices $model */
104
		$model = RModelAdmin::getAdminInstance('Webservices', array('ignore_request' => true), 'com_redcore');
105
106
		if ($id = $model->installWebservice(
107
			$data['main']['client'],
108
			$data['main']['name'],
109
			$data['main']['version'],
110
			$data['main']['path'],
111
			$data['main']['id']
112
		))
113
		{
114
			$this->setState($this->getName() . '.id', $id);
115
			$this->setState($this->getName() . '.new', empty($data['main']['id']));
116
117
			// Update created, modified flags
118
			return parent::save(array('id' => $id));
119
		}
120
121
		return false;
122
	}
123
124
	/**
125
	 * Method to save the form data to XML file.
126
	 *
127
	 * @param   array  $data  The form data.
128
	 *
129
	 * @return  boolean       True on success, False on error.
130
	 *
131
	 * @throws  RuntimeException
132
	 *
133
	 * @since   1.4
134
	 */
135
	public function saveXml($data)
136
	{
137
		$dataRegistry = new Registry($data);
138
		$item         = null;
139
140
		if (empty($data['main']['name']))
141
		{
142
			$this->setError(JText::_('COM_REDCORE_WEBSERVICE_NAME_FIELD_CANNOT_BE_EMPTY'));
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

142
			/** @scrutinizer ignore-deprecated */ $this->setError(JText::_('COM_REDCORE_WEBSERVICE_NAME_FIELD_CANNOT_BE_EMPTY'));

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...
143
144
			return false;
145
		}
146
147
		if (!empty($data['main']['id']))
148
		{
149
			$item = $this->getItem($data['main']['id']);
150
		}
151
152
		$client  = $dataRegistry->get('main.client', 'site');
153
		$name    = $dataRegistry->get('main.name', '');
154
		$version = $dataRegistry->get('main.version', '1.0.0');
155
		$folder  = $dataRegistry->get('main.path', '');
156
		$folder  = !empty($folder) ? JPath::clean('/' . $folder) : '';
157
158
		if (!JFolder::exists(RApiHalHelper::getWebservicesPath() . $folder))
159
		{
160
			JFolder::create(RApiHalHelper::getWebservicesPath() . $folder);
161
		}
162
163
		$fullPath = JPath::clean(RApiHalHelper::getWebservicesPath() . $folder . '/' . $client . '.' . $name . '.' . $version . '.xml');
164
165
		$xml = new SimpleXMLElement('<?xml version="1.0"?><apiservice client="' . $client . '"></apiservice>');
166
167
		$xml->addChild('name', $dataRegistry->get('main.title', $name));
168
		$xml->addChild('author', $dataRegistry->get('main.author', ''));
169
		$xml->addChild('copyright', $dataRegistry->get('main.copyright', ''));
170
		$xml->addChild('description', $dataRegistry->get('main.description', ''));
171
172
		$configXml = $xml->addChild('config');
173
		$configXml->addChild('name', $dataRegistry->get('main.name', ''));
174
		$configXml->addChild('version', $version);
175
		$configXml->addChild('authorizationAssetName', $dataRegistry->get('main.authorizationAssetName', ''));
176
177
		$operationsXml = $xml->addChild('operations');
178
		$readXml       = null;
179
		$taskXml       = null;
180
181
		foreach ($data as $operationName => $operation)
182
		{
183
			if ($operationName == 'main'
184
				|| empty($operation['isEnabled'])
185
				|| substr($operationName, 0, strlen('type-')) == 'type-')
186
			{
187
				continue;
188
			}
189
190
			$operationNameSplit = explode('-', $operationName);
191
192
			if ($operationNameSplit[0] == 'read' && count($operationNameSplit) > 1)
193
			{
194
				if (is_null($readXml))
195
				{
196
					$readXml = $operationsXml->addChild('read');
197
				}
198
199
				$operationXml = $readXml->addChild($operationNameSplit[1]);
200
			}
201
			elseif ($operationNameSplit[0] == 'task' && count($operationNameSplit) > 1)
202
			{
203
				if (is_null($taskXml))
204
				{
205
					$taskXml = $operationsXml->addChild('task');
206
				}
207
208
				$operationXml = $taskXml->addChild($operationNameSplit[1]);
209
			}
210
			else
211
			{
212
				$operationXml = $operationsXml->addChild($operationNameSplit[0]);
213
			}
214
215
			$this->getOperationAttributesFromPost($operationXml, $data, $operationName);
216
			$this->getFieldsFromPost($operationXml, $data, $operationName);
217
			$this->getResourcesFromPost($operationXml, $data, $operationName);
218
		}
219
220
		$complexArrays = $xml->addChild('complexArrays');
221
222
		foreach ($data as $typeName => $typeData)
223
		{
224
			if (substr($typeName, 0, strlen('type-')) != 'type-')
225
			{
226
				continue;
227
			}
228
229
			$typeNameSplit = explode('-', $typeName);
230
231
			$typeXml = $complexArrays->addChild($typeNameSplit[1]);
232
			$this->getOperationAttributesFromPost($typeXml, $data, $typeName);
233
			$this->getFieldsFromPost($typeXml, $data, $typeName);
234
		}
235
236
		// Needed for formatting
237
		$dom                     = dom_import_simplexml($xml)->ownerDocument;
238
		$dom->preserveWhiteSpace = false;
239
		$dom->formatOutput       = true;
240
241
		if (!$dom->save($fullPath))
0 ignored issues
show
Bug introduced by
The method save() does not exist on null. ( Ignorable by Annotation )

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

241
		if (!$dom->/** @scrutinizer ignore-call */ save($fullPath))

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
242
		{
243
			return false;
244
		}
245
246
		if (!empty($item->id))
247
		{
248
			$folder  = !empty($item->path) ? '/' . $item->path : '';
249
			$oldPath = JPath::clean(RApiHalHelper::getWebservicesPath() . $folder . '/' . $item->xmlFile);
250
251
			if ($oldPath != $fullPath)
252
			{
253
				if (JFile::exists($oldPath))
254
				{
255
					// Xml file
256
					JFile::delete($oldPath);
257
258
					// Wsdl file
259
					$oldPathWsdl = substr($oldPath, 0, strlen($oldPath) - 4) . '.wsdl';
260
261
					if (JFile::exists($oldPathWsdl))
262
					{
263
						JFile::delete($oldPathWsdl);
264
					}
265
				}
266
			}
267
		}
268
269
		$wsdl         = RApiSoapHelper::generateWsdl($xml, null, $data['main']['path']);
270
		$fullWsdlPath = substr($fullPath, 0, -4) . '.wsdl';
271
272
		return (boolean) RApiSoapHelper::saveWsdlContentToPath($wsdl, $fullWsdlPath);
273
	}
274
275
	/**
276
	 * Method to get operation attributes from Post
277
	 *
278
	 * @param   SimpleXMLElement  &$xml  Xml element
279
	 * @param   array             $data  The form data.
280
	 * @param   string            $name  Name to fetch
281
	 *
282
	 * @return  void
283
	 *
284
	 * @since   1.4
285
	 */
286
	public function getOperationAttributesFromPost(&$xml, $data, $name)
287
	{
288
		if (empty($data[$name]))
289
		{
290
			return;
291
		}
292
293
		foreach ($data[$name] as $attributeKey => $attributeValue)
294
		{
295
			if (in_array($attributeKey, array('isEnabled'))
296
				|| is_array($attributeValue))
297
			{
298
				continue;
299
			}
300
301
			if ($attributeKey != 'description')
302
			{
303
				$xml->addAttribute($attributeKey, $attributeValue);
304
305
				continue;
306
			}
307
308
			if (!empty($attributeValue))
309
			{
310
				$this->addChildWithCDATA($xml, $attributeKey, $attributeValue);
311
			}
312
		}
313
	}
314
315
	/**
316
	 * Method to get fields from Post
317
	 *
318
	 * @param   SimpleXMLElement  &$xml  Xml element
319
	 * @param   array             $data  The form data.
320
	 * @param   string            $name  Name to fetch
321
	 *
322
	 * @return  void
323
	 *
324
	 * @since   1.4
325
	 */
326
	public function getFieldsFromPost(&$xml, $data, $name)
327
	{
328
		$mainFieldsXml = null;
329
330
		if (!empty($data[$name]['fields']['field']) || !empty($data[$name]['fields']['description']))
331
		{
332
			$mainFieldsXml = $xml->addChild('fields');
333
		}
334
335
		if (!empty($data[$name]['fields']['field']))
336
		{
337
			foreach ($data[$name]['fields']['field'] as $fieldJson)
338
			{
339
				$field = json_decode($fieldJson, true);
340
341
				if (empty($field))
342
				{
343
					continue;
344
				}
345
346
				$fieldChild = $mainFieldsXml->addChild('field');
347
348
				foreach ($field as $attributeKey => $attributeValue)
349
				{
350
					if ($attributeKey != 'description')
351
					{
352
						$fieldChild->addAttribute($attributeKey, $attributeValue);
353
354
						continue;
355
					}
356
357
					if (!empty($attributeValue))
358
					{
359
						$this->addChildWithCDATA($fieldChild, 'description', $attributeValue);
360
					}
361
				}
362
			}
363
		}
364
365
		if (!empty($data[$name]['fields']['description']))
366
		{
367
			$this->addChildWithCDATA($mainFieldsXml, 'description', $data[$name]['fields']['description']);
368
		}
369
	}
370
371
	/**
372
	 * Method to get resources from Post
373
	 *
374
	 * @param   SimpleXMLElement  &$xml  Xml element to add resources
375
	 * @param   array             $data  The form data.
376
	 * @param   string            $name  Name to fetch
377
	 *
378
	 * @return  void
379
	 *
380
	 * @since   1.4
381
	 */
382
	public function getResourcesFromPost(&$xml, $data, $name)
383
	{
384
		$mainResourcesXml = null;
385
386
		if (!empty($data[$name]['resources']['resource']) || !empty($data[$name]['resources']['description']))
387
		{
388
			$mainResourcesXml = $xml->addChild('resources');
389
		}
390
391
		if (!empty($data[$name]['resources']['resource']))
392
		{
393
			foreach ($data[$name]['resources']['resource'] as $resourceJson)
394
			{
395
				$resource = json_decode($resourceJson, true);
396
397
				if (empty($resource))
398
				{
399
					continue;
400
				}
401
402
				$resourceChild = $mainResourcesXml->addChild('resource');
403
404
				foreach ($resource as $attributeKey => $attributeValue)
405
				{
406
					if ($attributeKey != 'description')
407
					{
408
						$resourceChild->addAttribute($attributeKey, $attributeValue);
409
410
						continue;
411
					}
412
413
					if (!empty($attributeValue))
414
					{
415
						$this->addChildWithCDATA($resourceChild, 'description', $attributeValue);
416
					}
417
				}
418
			}
419
		}
420
421
		if (!empty($data[$name]['resources']['description']))
422
		{
423
			$this->addChildWithCDATA($mainResourcesXml, 'description', $data[$name]['resources']['description']);
424
		}
425
	}
426
427
	/**
428
	 * Method to add child with text inside CDATA
429
	 *
430
	 * @param   SimpleXMLElement  &$xml   Xml element
431
	 * @param   string            $name   Name of the child
432
	 * @param   string            $value  Value of the child
433
	 *
434
	 * @return  SimpleXMLElement
435
	 *
436
	 * @since   1.4
437
	 */
438
	public function addChildWithCDATA(&$xml, $name, $value = '')
439
	{
440
		$newChild = $xml->addChild($name);
441
442
		if (is_null($newChild))
443
		{
444
			return $newChild;
445
		}
446
447
		$node = dom_import_simplexml($newChild);
448
		$no   = $node->ownerDocument;
449
		$node->appendChild($no->createCDATASection($value));
0 ignored issues
show
Bug introduced by
The method createCDATASection() does not exist on null. ( Ignorable by Annotation )

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

449
		$node->appendChild($no->/** @scrutinizer ignore-call */ createCDATASection($value));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
450
451
		return $newChild;
452
	}
453
454
	/**
455
	 * Add SimpleXMLElement fragment to another SimpleXMLElement
456
	 *
457
	 * @param   SimpleXMLElement  $targetXml    the xml to append to
458
	 * @param   SimpleXMLElement  $originalXml  the xml to append to targetXml
459
	 *
460
	 * @return void
461
	 */
462
	private function appendXML(SimpleXMLElement $targetXml, SimpleXMLElement $originalXml)
463
	{
464
		if ($originalXml && strlen(trim((string) $originalXml)) == 0)
465
		{
466
			$xml = $targetXml->addChild($originalXml->getName());
467
468
			foreach ($originalXml->children() as $child)
469
			{
470
				$this->appendXML($xml, $child);
0 ignored issues
show
Bug introduced by
It seems like $child can also be of type null; however, parameter $originalXml of RedcoreModelWebservice::appendXML() does only seem to accept SimpleXMLElement, 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

470
				$this->appendXML($xml, /** @scrutinizer ignore-type */ $child);
Loading history...
471
			}
472
		}
473
		elseif ($originalXml->getName() == 'description')
474
		{
475
			$xml = $this->addChildWithCDATA($targetXml, $originalXml->getName(), (string) $originalXml);
476
		}
477
		else
478
		{
479
			$xml = $targetXml->addChild($originalXml->getName(), (string) $originalXml);
480
		}
481
482
		foreach ($originalXml->attributes() as $name => $value)
483
		{
484
			// If it is not set then we just set it.
485
			if (!isset($xml[$name]))
486
			{
487
				$xml->addAttribute($name, '');
488
			}
489
490
			$xml[$name] = trim($value);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type null; however, parameter $string of trim() does only seem to accept string, 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

490
			$xml[$name] = trim(/** @scrutinizer ignore-type */ $value);
Loading history...
491
		}
492
	}
493
494
	/**
495
	 * Load item object
496
	 *
497
	 * @param   integer  $pk  The id of the primary key.
498
	 *
499
	 * @return  mixed    Object on success, false on failure.
500
	 *
501
	 * @since   1.2
502
	 */
503
	public function getItem($pk = null)
504
	{
505
		if (!$item = parent::getItem($pk))
506
		{
507
			return $item;
508
		}
509
510
		if (!empty($item->id) && is_null($this->xmlFile))
511
		{
512
			try
513
			{
514
				$this->xmlFile = RApiHalHelper::loadWebserviceConfiguration(
515
					$item->name, $item->version, 'xml', $item->path, $item->client
516
				);
517
			}
518
			catch (Exception $e)
519
			{
520
				JFactory::getApplication()->enqueueMessage(JText::sprintf('COM_REDCORE_WEBSERVICE_ERROR_LOADING_XML', $e->getMessage()), 'error');
521
			}
522
		}
523
524
		// Add default webservice parameters since this is new webservice
525
		if (empty($this->xmlFile))
526
		{
527
			$this->xmlFile = $this->defaultXmlFile;
528
		}
529
530
		return $item;
531
	}
532
533
	/**
534
	 * Method to get a form object.
535
	 *
536
	 * @param   array    $data      Data for the form.
537
	 * @param   boolean  $loadData  True if the form is to load its own data (default case), false if not.
538
	 *
539
	 * @return  mixed  A JForm object on success, false on failure
540
	 */
541
	public function getForm($data = array(), $loadData = true)
542
	{
543
		if (!$form = parent::getForm($data, $loadData))
544
		{
545
			return false;
546
		}
547
548
		// Load dynamic form for operations
549
		$form->load(str_replace('"operation"', '"create"', $this->loadFormOperationXml()));
550
		$form->load(str_replace('"operation"', '"read-list"', $this->loadFormOperationXml()));
551
		$form->load(str_replace('"operation"', '"read-item"', $this->loadFormOperationXml()));
552
		$form->load(str_replace('"operation"', '"update"', $this->loadFormOperationXml()));
553
		$form->load(str_replace('"operation"', '"delete"', $this->loadFormOperationXml()));
554
555
		if (!empty($data))
556
		{
557
			foreach ($data as $operationName => $operation)
558
			{
559
				if (substr($operationName, 0, strlen('task-')) === 'task-')
560
				{
561
					$form->load(str_replace('"operation"', '"' . $operationName . '"', $this->loadFormOperationXml()));
562
				}
563
564
				if (substr($operationName, 0, strlen('type-')) == 'type-')
565
				{
566
					$form->load(str_replace('"operation"', '"' . $operationName . '"', $this->loadFormComplexTypeXml()));
567
				}
568
			}
569
		}
570
571
		if (!empty($this->xmlFile) && $tasks = $this->xmlFile->xpath('//operations/task'))
572
		{
573
			$tasks = $tasks[0];
574
575
			foreach ($tasks as $taskName => $task)
576
			{
577
				$form->load(str_replace('"operation"', '"task-' . $taskName . '"', $this->loadFormOperationXml()));
578
			}
579
		}
580
581
		if (!empty($this->xmlFile) && $complexTypes = $this->xmlFile->xpath('//complexArrays'))
582
		{
583
			$complexTypes = $complexTypes[0];
584
585
			foreach ($complexTypes AS $typeName => $type)
586
			{
587
				$form->load(str_replace('"operation"', '"type-' . $typeName . '"', $this->loadFormComplexTypeXml()));
588
			}
589
		}
590
591
		$form->bind($this->formData);
592
593
		return $form;
594
	}
595
596
	/**
597
	 * Method to load a operation form template.
598
	 *
599
	 * @return  string  Xml
600
	 */
601
	public function loadFormOperationXml()
602
	{
603
		if (is_null($this->operationXml))
0 ignored issues
show
introduced by
The condition is_null($this->operationXml) is always false.
Loading history...
604
		{
605
			$this->operationXml = @file_get_contents(JPATH_COMPONENT_ADMINISTRATOR . '/models/forms/webservice_operation.xml');
606
		}
607
608
		return $this->operationXml;
609
	}
610
611
	/**
612
	 * Method to load a complex type form template.
613
	 *
614
	 * @return  string  Xml
615
	 */
616
	public function loadFormComplexTypeXml()
617
	{
618
		if (is_null($this->complexTypeXml))
0 ignored issues
show
introduced by
The condition is_null($this->complexTypeXml) is always false.
Loading history...
619
		{
620
			$this->complexTypeXml = @file_get_contents(JPATH_COMPONENT_ADMINISTRATOR . '/models/forms/webservice_complex_type.xml');
621
		}
622
623
		return $this->complexTypeXml;
624
	}
625
626
	/**
627
	 * Method to get the data that should be injected in the form.
628
	 *
629
	 * @return  array  The default data is an empty array.
630
	 */
631
	protected function loadFormData()
632
	{
633
		// Check the session for previously entered form data.
634
		$data = JFactory::getApplication()->getUserState(
635
			$this->context . '.data',
636
			array()
637
		);
638
639
		if (!empty($data))
640
		{
641
			return $data;
642
		}
643
644
		$dataDb = $this->getItem();
645
		$data   = $this->bindXMLToForm();
646
647
		$dataArray = ArrayHelper::fromObject($dataDb);
0 ignored issues
show
Bug introduced by
It seems like $dataDb can also be of type boolean; however, parameter $p_obj of Joomla\Utilities\ArrayHelper::fromObject() does only seem to accept object, 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

647
		$dataArray = ArrayHelper::fromObject(/** @scrutinizer ignore-type */ $dataDb);
Loading history...
648
		$dataEmpty = array('main' => array());
649
		$data      = array_merge($dataEmpty, $data);
650
651
		$data['main'] = array_merge($dataArray, $data['main']);
652
653
		return $data;
654
	}
655
656
	/**
657
	 * Return mapped array for the form data
658
	 *
659
	 * @return  array
660
	 *
661
	 * @since   1.4
662
	 */
663
	public function bindXMLToForm()
664
	{
665
		// Read operation is a special because it is part of the two separate read types
666
		$this->formData = array('read-list' => array(), 'read-item' => array());
667
668
		if (empty($this->xmlFile))
669
		{
670
			return $this->formData;
671
		}
672
673
		$this->formData['main'] = array(
674
			'author' => (string) $this->xmlFile->author,
675
			'copyright' => (string) $this->xmlFile->copyright,
676
			'description' => (string) $this->xmlFile->description,
677
			'authorizationAssetName' => !empty($this->xmlFile->config->authorizationAssetName) ? (string) $this->xmlFile->config->authorizationAssetName : '',
678
		);
679
680
		// Get attributes and descriptions
681
		$this->bindAttributesToFormData($this->defaultXmlFile);
682
		$this->bindAttributesToFormData($this->xmlFile, true);
683
684
		return $this->formData;
685
	}
686
687
	/**
688
	 * Method to bind attributes to $this->formData
689
	 *
690
	 * @param   SimpleXMLElement  $xml        the xml file to bind
691
	 * @param   bool              $overwrite  should be overwrite existing operations in the form data
692
	 *
693
	 * @return void
694
	 */
695
	private function bindAttributesToFormData($xml, $overwrite = false)
696
	{
697
		// Bind complex types first
698
		$this->bindComplexTypesToFormData($xml, $overwrite);
699
700
		if (!$operations = $xml->xpath('//operations'))
701
		{
702
			return;
703
		}
704
705
		$operations = $operations[0];
706
707
		foreach ($operations as $name => $operation)
708
		{
709
			if (!empty($this->formData[$name]) && !$overwrite)
710
			{
711
				continue;
712
			}
713
714
			switch ($name)
715
			{
716
				case 'read':
717
					$this->bindReadAttributesToFormData($xml);
718
					break;
719
				case 'task':
720
					$this->bindTaskAttributesToFormData($xml);
721
					break;
722
				default:
723
					$this->bindDefaultAttributesToFormData($xml, $name);
724
					break;
725
			}
726
		}
727
	}
728
729
	/**
730
	 * Method to bind complex type attributes to $this->formData
731
	 *
732
	 * @param   SimpleXMLElement  $xml        the xml file to read from
733
	 * @param   bool              $overwrite  should be overwrite existing operations in the form data
734
	 *
735
	 * @return void
736
	 */
737
	private function bindComplexTypesToFormData($xml, $overwrite = false)
738
	{
739
		if (!$complexTypes = $xml->xpath('//complexArrays/*'))
740
		{
741
			return;
742
		}
743
744
		foreach ($complexTypes as $type)
745
		{
746
			$typeName = (string) $type->getName();
747
748
			if (!empty($this->formData['type-' . $typeName]) && !$overwrite)
749
			{
750
				continue;
751
			}
752
753
			$this->formData['type-' . $typeName] = $this->bindPathToArray('//complexArrays/' . $typeName, $xml);
754
			$this->setPropertyByXpath('fields', 'type-' . $typeName, '//complexArrays/' . $typeName . '/fields/field', $xml);
755
		}
756
	}
757
758
	/**
759
	 * Return mapped array for the form data
760
	 *
761
	 * @param   string            $path  Path to the XML element
762
	 * @param   SimpleXMLElement  $xml   XML file
763
	 *
764
	 * @return  array
765
	 *
766
	 * @since   1.4
767
	 */
768
	public function bindPathToArray($path, $xml)
769
	{
770
		if (!$element = $xml->xpath($path))
771
		{
772
			return array();
773
		}
774
775
		$element = $element[0];
776
777
		return $this->bindElementToArray($element);
778
	}
779
780
	/**
781
	 * Return mapped array for the form data
782
	 *
783
	 * @param   SimpleXMLElement  $element  XML element
784
	 *
785
	 * @return  array
786
	 *
787
	 * @since   1.4
788
	 */
789
	public function bindElementToArray($element)
790
	{
791
		$data = array();
792
793
		if (empty($element))
794
		{
795
			return $data;
796
		}
797
798
		foreach ($element->attributes() as $key => $val)
799
		{
800
			$data[$key] = (string) $val;
801
		}
802
803
		$data['description'] = !empty($element->description) ? (string) $element->description : '';
804
805
		if (!empty($element->fields->description))
806
		{
807
			$data['fields']['description'] = (string) $element->fields->description;
808
		}
809
810
		if (!empty($element->resources->description))
811
		{
812
			$data['resources']['description'] = (string) $element->resources->description;
813
		}
814
815
		return $data;
816
	}
817
818
	/**
819
	 * Method to set a property from a given path to $this->{$propertyName}[name]
820
	 *
821
	 * @param   string            $propertyName  The name of the model property (I.E. fields,resources, types
822
	 * @param   string            $name          Operation or task name
823
	 * @param   string            $path          Path to the operation or the task
824
	 * @param   SimpleXMLElement  $xml           XML file
825
	 *
826
	 * @return  void
827
	 *
828
	 * @since   1.7
829
	 */
830
	public function setPropertyByXpath($propertyName, $name, $path, $xml)
831
	{
832
		if (!$nodes = $xml->xpath($path))
833
		{
834
			return;
835
		}
836
837
		foreach ($nodes as $node)
838
		{
839
			$properties = $this->bindElementToArray($node);
840
841
			if ($propertyName == 'resources')
842
			{
843
				$displayName                                                   = (string) $properties['displayName'];
844
				$resourceSpecific                                              = !empty($properties['resourceSpecific']) ? (string) $properties['resourceSpecific'] : 'rcwsGlobal';
845
				$this->{$propertyName}[$name][$resourceSpecific][$displayName] = $properties;
846
847
				continue;
848
			}
849
850
			$displayName                                = (string) $properties['name'];
851
			$this->{$propertyName}[$name][$displayName] = $properties;
852
		}
853
	}
854
855
	/**
856
	 * Method to bind read operation attributes to $this->formData
857
	 *
858
	 * @param   SimpleXMLElement  $xml  the xml file to read from
859
	 *
860
	 * @return void
861
	 */
862
	private function bindReadAttributesToFormData($xml)
863
	{
864
		$this->formData['read-list'] = $this->bindPathToArray('//operations/read/list', $xml);
865
		$this->formData['read-item'] = $this->bindPathToArray('//operations/read/item', $xml);
866
867
		$this->setFieldsAndResources('read-list', '//operations/read/list', $xml);
868
		$this->setFieldsAndResources('read-item', '//operations/read/item', $xml);
869
870
		if (!empty($this->formData['read-list']) && !isset($this->formData['read-list']['isEnabled']))
871
		{
872
			// Since this operation exists in XML file we are enabling it by default
873
			$this->formData['read-list']['isEnabled'] = 1;
874
		}
875
876
		if (!empty($this->formData['read-item']) && !isset($this->formData['read-item']['isEnabled']))
877
		{
878
			// Since this operation exists in XML file we are enabling it by default
879
			$this->formData['read-item']['isEnabled'] = 1;
880
		}
881
	}
882
883
	/**
884
	 * Gets Fields and Resources from given path
885
	 *
886
	 * @param   string            $name  Operation or task name
887
	 * @param   string            $path  Path to the operation or the task
888
	 * @param   SimpleXMLElement  $xml   XML file
889
	 *
890
	 * @return  void
891
	 *
892
	 * @since   1.4
893
	 */
894
	public function setFieldsAndResources($name, $path, $xml)
895
	{
896
		// Get fields
897
		$this->setPropertyByXpath('fields', $name, $path . '/fields/field', $xml);
898
899
		// Get resources
900
		$this->setPropertyByXpath('resources', $name, $path . '/resources/resource', $xml);
901
	}
902
903
	/**
904
	 * Method to bind task operation attributes to $this->formData
905
	 *
906
	 * @param   SimpleXMLElement  $xml  the xml file to read from
907
	 *
908
	 * @return void
909
	 */
910
	private function bindTaskAttributesToFormData($xml)
911
	{
912
		if (!$tasks = $xml->xpath('//operations/task'))
913
		{
914
			return;
915
		}
916
917
		$tasks = $tasks[0];
918
919
		foreach ($tasks as $taskName => $task)
920
		{
921
			$this->formData['task-' . $taskName] = $this->bindPathToArray('//operations/task/' . $taskName, $xml);
922
			$this->setFieldsAndResources('task-' . $taskName, '//operations/task/' . $taskName, $xml);
923
924
			if (!empty($this->formData['task-' . $taskName]) && !isset($this->formData['task-' . $taskName]['isEnabled']))
925
			{
926
				// Since this operation exists in XML file we are enabling it by default
927
				$this->formData['task-' . $taskName]['isEnabled'] = 1;
928
			}
929
		}
930
	}
931
932
	/**
933
	 * Method to bind all other operation attributes to $this->formData
934
	 *
935
	 * @param   SimpleXMLElement  $xml   the xml file to read from
936
	 * @param   string            $name  of the operation
937
	 *
938
	 * @return void
939
	 */
940
	private function bindDefaultAttributesToFormData($xml, $name)
941
	{
942
		$this->formData[$name] = $this->bindPathToArray('//operations/' . $name, $xml);
943
		$this->setFieldsAndResources($name, '//operations/' . $name, $xml);
944
945
		if (!empty($this->formData[$name]) && !isset($this->formData[$name]['isEnabled']))
946
		{
947
			// Since this operation exists in XML file we are enabling it by default
948
			$this->formData[$name]['isEnabled'] = 1;
949
		}
950
	}
951
952
	/**
953
	 * Method to get the additional complex transform types from the WS xml complexArray
954
	 *
955
	 * @param   string  $operation  the name of current operation or complexArray requesting the list of transforms
956
	 *
957
	 * @return array
958
	 */
959
	public function getTransformTypes($operation = null)
960
	{
961
		$transforms = RApiHalHelper::getTransformElements();
962
963
		if (!($this->xmlFile instanceof SimpleXMLElement))
0 ignored issues
show
introduced by
$this->xmlFile is always a sub-type of SimpleXMLElement.
Loading history...
964
		{
965
			$this->loadFormData();
966
		}
967
968
		$complexArrayItems = $this->xmlFile->xpath('//complexArrays/*');
969
970
		foreach ($complexArrayItems AS $transformType)
971
		{
972
			$transformTypeName = $transformType->getName();
973
974
			if ('type-' . $transformTypeName == $operation)
975
			{
976
				// Do not allow recursive transforms at this time
977
				continue;
978
			}
979
980
			$value        = 'array[' . $transformTypeName . ']';
981
			$transforms[] = array('value' => $value, 'text' => $value);
982
		}
983
984
		return $transforms;
985
	}
986
}
987