generateTranslationXml()   C
last analyzed

Complexity

Conditions 14
Paths 56

Size

Total Lines 67
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 38
c 0
b 0
f 0
dl 0
loc 67
rs 6.2666
cc 14
nc 56
nop 1

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  Translation
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
defined('_JEXEC') or die;
11
12
/**
13
 * A ContentElement helper.
14
 *
15
 * @package     Redcore
16
 * @subpackage  Translation
17
 * @since       1.0
18
 */
19
final class RTranslationContentElement
20
{
21
	/**
22
	 * The XML file content
23
	 *
24
	 * @var  SimpleXMLElement
25
	 */
26
	public $xml;
27
28
	/**
29
	 * The Extension Name ex. com_redcore
30
	 *
31
	 * @var  String
32
	 */
33
	public $extension;
34
35
	/**
36
	 * The Content Element XML file name
37
	 *
38
	 * @var  String
39
	 */
40
	public $contentElementXml;
41
42
	/**
43
	 * Full path to the Content Element XML file
44
	 *
45
	 * @var  String
46
	 */
47
	public $contentElementXmlPath;
48
49
	/**
50
	 * The Content Element name
51
	 *
52
	 * @var  String
53
	 */
54
	public $name;
55
56
	/**
57
	 * The Content Element version
58
	 *
59
	 * @var  String
60
	 */
61
	public $version;
62
63
	/**
64
	 * Table name
65
	 *
66
	 * @var  String
67
	 */
68
	public $table;
69
70
	/**
71
	 * Table Primary Key
72
	 *
73
	 * @var  String
74
	 */
75
	public $primaryKey;
76
77
	/**
78
	 * An array to hold tables from database
79
	 *
80
	 * @var    array
81
	 * @since  1.0
82
	 */
83
	public static $contentElements = array();
84
85
	/**
86
	 * Constructor
87
	 *
88
	 * @param   string  $extension          The Extension Name ex. com_redcore
89
	 * @param   string  $contentElementXml  The Content Element XML file name
90
	 */
91
	public function __construct($extension = 'com_redcore', $contentElementXml = 'table.xml')
92
	{
93
		$this->extension = $extension;
94
		$this->contentElementXml = $contentElementXml;
95
96
		if (!empty($contentElementXml))
97
		{
98
			$this->contentElementXmlPath = self::getContentElementXmlPath($extension, $contentElementXml);
99
100
			$content = @file_get_contents($this->contentElementXmlPath);
101
102
			if (is_string($content))
103
			{
104
				$xmlDoc = new SimpleXMLElement($content);
105
106
				$this->xml_hashed = md5($content);
0 ignored issues
show
Bug Best Practice introduced by
The property xml_hashed does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
107
				$this->xml = $xmlDoc;
108
				$this->table = $this->getTableName();
109
				$this->name = $this->getContentElementName();
110
				$this->version = $this->getContentElementVersion();
111
				$this->primaryKey = $this->getPrimaryKey();
112
			}
113
		}
114
	}
115
116
	/**
117
	 * Gets Table name
118
	 *
119
	 * @return  String  Table Name
120
	 */
121
	public function getTableName()
122
	{
123
		if (!empty($this->xml->reference->table['name']))
124
		{
125
			return strtolower((string) $this->xml->reference->table['name']);
126
		}
127
128
		return '';
129
	}
130
131
	/**
132
	 * Gets Content Element name
133
	 *
134
	 * @return  String  Name
135
	 */
136
	public function getContentElementName()
137
	{
138
		if (isset($this->xml->name))
139
		{
140
			return (string) $this->xml->name;
141
		}
142
143
		return '';
144
	}
145
146
	/**
147
	 * Gets Content Element name
148
	 *
149
	 * @return  String  Name
150
	 */
151
	public function getContentElementVersion()
152
	{
153
		if (isset($this->xml->version))
154
		{
155
			return (string) $this->xml->version;
156
		}
157
158
		return '1.0.0';
159
	}
160
161
	/**
162
	 * Name of primary id
163
	 *
164
	 * @return  String  Table Primary Key
165
	 */
166
	public function getPrimaryKey()
167
	{
168
		if (!empty($this->xml->reference->table->field))
169
		{
170
			foreach ($this->xml->reference->table->field as $field)
171
			{
172
				if ($field['type'] == 'referenceid')
173
				{
174
					return strtolower((string) $field['name']);
175
				}
176
			}
177
		}
178
179
		return 'id';
180
	}
181
182
	/**
183
	 * Get list of fields for translate
184
	 *
185
	 * @return  array  Array of translatable fields
186
	 */
187
	public function getTranslateFields()
188
	{
189
		if (!empty($this->xml->reference->table->field))
190
		{
191
			return $this->xml->reference->table->field;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->xml->reference->table->field returns the type SimpleXMLElement which is incompatible with the documented return type array.
Loading history...
192
		}
193
194
		return array();
195
	}
196
197
	/**
198
	 * Get list of filters for translation editor
199
	 *
200
	 * @return  array  Array of translatable fields
201
	 */
202
	public function getTranslateFilter()
203
	{
204
		if (!empty($this->xml->reference->table->filter))
205
		{
206
			return $this->xml->reference->table->filter;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->xml->reference->table->filter returns the type SimpleXMLElement which is incompatible with the documented return type array.
Loading history...
207
		}
208
209
		return array();
210
	}
211
212
	/**
213
	 * Get table description
214
	 *
215
	 * @return  string
216
	 */
217
	public function getTranslateDescription()
218
	{
219
		if (isset($this->xml->description))
220
		{
221
			return (string) $this->xml->description;
222
		}
223
224
		return '';
225
	}
226
227
	/**
228
	 * Get table copyright
229
	 *
230
	 * @return  string
231
	 */
232
	public function getTranslateCopyright()
233
	{
234
		if (isset($this->xml->copyright))
235
		{
236
			return (string) $this->xml->copyright;
237
		}
238
239
		return '';
240
	}
241
242
	/**
243
	 * Get table author
244
	 *
245
	 * @return  string
246
	 */
247
	public function getTranslateAuthor()
248
	{
249
		if (isset($this->xml->author))
250
		{
251
			return (string) $this->xml->author;
252
		}
253
254
		return '';
255
	}
256
257
	/**
258
	 * Get list of edit forms where we will not show translation
259
	 *
260
	 * @return  array  Array of edit form locations
261
	 */
262
	public function getEditForms()
263
	{
264
		$formLinks = array();
265
266
		if (isset($this->xml->reference->component))
267
		{
268
			// Old way
269
			if (isset($this->xml->reference->component->form))
270
			{
271
				foreach ($this->xml->reference->component->form as $form)
272
				{
273
					$formArray = explode('#', $form);
0 ignored issues
show
Bug introduced by
It seems like $form can also be of type null; however, parameter $string of explode() 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

273
					$formArray = explode('#', /** @scrutinizer ignore-type */ $form);
Loading history...
274
					$formLink = array();
275
276
					if (count($formArray) > 1)
277
					{
278
						$formLink['option'] = $formArray[0];
279
						$formLink['view'] = $formArray[1];
280
						$formLink['identifier'] = !empty($formArray[2]) ? $formArray[2] : '';
281
						$formLink['layout'] = !empty($formArray[4]) ? preg_replace("/[^a-zA-Z0-9]+/", "", $formArray[4]) : 'edit';
282
283
						// Set defaults
284
						$this->getEditFormDefaults($formLink);
285
286
						$formLinks[] = $formLink;
287
					}
288
				}
289
			}
290
			// Current structure
291
			else
292
			{
293
				foreach ($this->xml->reference->component->editForm as $editForm)
294
				{
295
					$formLink = array();
296
297
					if (!empty($editForm['option']) && !empty($editForm['view']))
298
					{
299
						$attributes = $editForm->attributes();
300
301
						foreach ($attributes as $key => $attribute)
302
						{
303
							$formLink[strtolower($key)] = (string) $attribute;
304
						}
305
306
						// Set defaults
307
						$this->getEditFormDefaults($formLink);
308
309
						$formLinks[] = $formLink;
310
					}
311
				}
312
			}
313
		}
314
315
		return $formLinks;
316
	}
317
318
	/**
319
	 * Sets default values to the form links if they are not set
320
	 *
321
	 * @param   array  &$formLink  Form link options
322
	 *
323
	 * @return  void
324
	 */
325
	public function getEditFormDefaults(&$formLink)
326
	{
327
		// Defaults
328
		$formLink['admin'] = !empty($formLink['admin']) ? $formLink['admin'] : 'false';
329
		$formLink['layout'] = !empty($formLink['layout']) ? $formLink['layout'] : 'edit';
330
		$formLink['identifier'] = isset($formLink['identifier']) ? $formLink['identifier'] : 'id';
331
		$formLink['showbutton'] = !empty($formLink['showbutton']) ? $formLink['showbutton'] : 'true';
332
		$formLink['htmlposition'] = !empty($formLink['htmlposition']) ? $formLink['htmlposition'] : '.btn-toolbar:first';
333
		$formLink['checkoriginalid'] = !empty($formLink['checkoriginalid']) ? $formLink['checkoriginalid'] : 'false';
334
	}
335
336
	/**
337
	 * Get status of the Content Element
338
	 *
339
	 * @return  String  Content Element Status
340
	 */
341
	public function getFieldDifference()
342
	{
343
		$return = '';
344
		$fieldsTable = RTranslationTable::getTranslationsTableColumns($this->table);
345
346
		// Language is automatically added to the table if table exists
347
		$fieldsTable = RTranslationTable::removeFixedColumnsFromArray($fieldsTable);
348
		$fieldsXml = $this->getTranslateFields();
349
		$fields = array();
350
351
		foreach ($fieldsXml as $field)
352
		{
353
			$fields[(string) $field['name']] = (string) $field['name'];
354
355
			if ((string) $field['translate'] == '0')
356
			{
357
				unset($fieldsTable[(string) $field['name']]);
358
				unset($fields[(string) $field['name']]);
359
				continue;
360
			}
361
362
			foreach ($fieldsTable as $columnKey => $columnKeyValue)
363
			{
364
				$fields[$columnKey] = $columnKey;
365
366
				if ((string) $field['name'] == $columnKey)
367
				{
368
					unset($fieldsTable[$columnKey]);
369
					unset($fields[$columnKey]);
370
				}
371
			}
372
		}
373
374
		if (!empty($fields))
375
		{
376
			$return .= JText::_('COM_REDCORE_TRANSLATION_TABLE_CONTENT_ELEMENT_FIELDS_MISSING') . implode(', ', $fields);
377
		}
378
379
		return $return;
380
	}
381
382
	/**
383
	 * Get Path to the Content element XML file
384
	 *
385
	 * @param   string  $option   The Extension Name ex. com_redcore
386
	 * @param   string  $xmlFile  XML file to install
387
	 *
388
	 * @return  string  Path to XML file
389
	 */
390
	public static function getContentElementXmlPath($option = '', $xmlFile = '')
391
	{
392
		jimport('joomla.filesystem.file');
393
394
		if (file_exists(self::getContentElementFolderPath($option) . '/' . $xmlFile))
395
		{
396
			return self::getContentElementFolderPath($option) . '/' . $xmlFile;
397
		}
398
399
		return self::getContentElementFolderPath($option, true) . '/' . $xmlFile;
400
	}
401
402
	/**
403
	 * Get Path to the Content element XML file
404
	 *
405
	 * @param   string  $option       The Extension Name ex. com_redcore
406
	 * @param   bool    $fromRedcore  Use redcore folder location
407
	 * @param   bool    $fromOption   Use redcore folder location
408
	 *
409
	 * @return  string  Path to XML file
410
	 */
411
	public static function getContentElementFolderPath($option = '', $fromRedcore = false, $fromOption = false)
412
	{
413
		jimport('joomla.filesystem.path');
414
		$extensionPath = JPATH_SITE . '/media/' . $option . '/translations';
415
		$redcorePath = JPATH_SITE . '/media/redcore/translations';
416
417
		if (empty($option))
418
		{
419
			return $redcorePath;
420
		}
421
		elseif ($fromOption)
422
		{
423
			return $extensionPath;
424
		}
425
		elseif (!is_dir($extensionPath) || $fromRedcore)
426
		{
427
			return $redcorePath . '/' . $option;
428
		}
429
430
		return $extensionPath;
431
	}
432
433
	/**
434
	 * Gets path without base path
435
	 *
436
	 * @param   string  $path  Path
437
	 *
438
	 * @return  string
439
	 */
440
	public static function getPathWithoutBase($path)
441
	{
442
		return str_replace(JPATH_SITE, '', $path);
443
	}
444
445
	/**
446
	 * Gets path without base path
447
	 *
448
	 * @param   bool    $notInstalled       Filter only not installed Content elements
449
	 * @param   string  $onlyFromExtension  Show only from specific extension
450
	 *
451
	 * @return  array
452
	 */
453
	public static function getContentElements($notInstalled = false, $onlyFromExtension = '')
454
	{
455
		$xmlFileOptions = self::loadAllContentElements();
456
		$tables = RTranslationTable::getInstalledTranslationTables(true);
457
		$xmlFiles = array();
458
459
		if (!empty($xmlFileOptions))
460
		{
461
			foreach ($xmlFileOptions as $option => $xmlFileTables)
462
			{
463
				if (!empty($onlyFromExtension) && $onlyFromExtension != $option)
464
				{
465
					continue;
466
				}
467
468
				foreach ($xmlFileTables as $key => $contentElement)
469
				{
470
					$xmlFiles[$key] = $contentElement;
471
472
					if ($notInstalled && !empty($tables))
473
					{
474
						foreach ($tables as $table)
475
						{
476
							if ($table->xml_path == self::getPathWithoutBase($contentElement->contentElementXmlPath))
477
							{
478
								// We remove it from the list
479
								unset($xmlFiles[$key]);
480
								break;
481
							}
482
483
							if (str_replace('#__', '', $table->name) == $contentElement->table)
484
							{
485
								$xmlFiles[$key]->mainTable = $table;
486
							}
487
						}
488
					}
489
				}
490
			}
491
		}
492
493
		return $xmlFiles;
494
	}
495
496
	/**
497
	 * Found out which extensions have content element files
498
	 *
499
	 * @param   string  $mediaPath      Media path
500
	 * @param   bool    $redcoreFolder  Is this redcore media folder
501
	 *
502
	 * @return  array  List of objects
503
	 */
504
	public static function loadAllContentElements($mediaPath = '', $redcoreFolder = false)
505
	{
506
		if (!empty(self::$contentElements) && !$redcoreFolder)
507
		{
508
			return self::$contentElements;
509
		}
510
511
		jimport('joomla.filesystem.folder');
512
513
		if ($mediaPath == '')
514
		{
515
			self::loadAllContentElements(JPATH_SITE . '/media');
516
			self::loadAllContentElements(JPATH_SITE . '/media/redcore/translations', true);
517
518
			return self::$contentElements;
519
		}
520
521
		$mediaFolders = JFolder::folders($mediaPath);
522
523
		if ($mediaFolders)
0 ignored issues
show
Bug Best Practice introduced by
The expression $mediaFolders of type array 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...
524
		{
525
			foreach ($mediaFolders as $mediaFolder)
526
			{
527
				// We have already processed redcore media folder
528
				if ($mediaFolder == 'redcore' && $mediaPath == JPATH_SITE . '/media')
529
				{
530
					continue;
531
				}
532
533
				$folder = $mediaPath . '/' . $mediaFolder . ($redcoreFolder ? '' : '/translations');
534
535
				if (is_dir($folder))
536
				{
537
					$contentElementsXml = JFolder::files($folder, '.xml', false);
538
539
					if (!empty($contentElementsXml))
540
					{
541
						if (!isset(self::$contentElements[$mediaFolder]))
542
						{
543
							self::$contentElements[$mediaFolder] = array();
544
						}
545
546
						foreach ($contentElementsXml as $contentElementXml)
547
						{
548
							$contentElement = new RTranslationContentElement($mediaFolder, $contentElementXml);
549
550
							if (!empty($contentElement->table) || $mediaFolder == 'upload')
551
							{
552
								self::$contentElements[$mediaFolder][$folder . '/' . $contentElement->table] = $contentElement;
553
							}
554
						}
555
					}
556
				}
557
			}
558
		}
559
560
		return self::$contentElements;
561
	}
562
563
	/**
564
	 * Gets XML string from the given table object
565
	 *
566
	 * @param   RedcoreTableTranslation_Table  $table  Table object
567
	 *
568
	 * @return  SimpleXMLElement
569
	 */
570
	public static function generateTranslationXml($table)
571
	{
572
		$xml = new SimpleXMLElement('<?xml version="1.0"?><contentelement type="contentelement"></contentelement>');
573
574
		$params = !empty($table->params) ? json_decode($table->params, true) : array();
575
576
		$xml->addChild('name', $table->title);
577
		$xml->addChild('author', $params['author'] ? $params['author'] : '');
578
		$xml->addChild('copyright', $params['copyright'] ? $params['copyright'] : '');
579
		$xml->addChild('version', $table->version);
580
		$xml->addChild('description', $params['description'] ? $params['description'] : '');
581
582
		$reference = $xml->addChild('reference');
583
584
		// Add main table
585
		$referenceTable = $reference->addChild('table');
586
		$referenceTable->addAttribute('name', str_replace('#__', '', $table->name));
587
		$columns = $table->getTranslationColumns();
588
589
		// Add table columns
590
		foreach ($columns as $column)
591
		{
592
			$field = $referenceTable->addChild('field', $column['title']);
593
594
			$field->addAttribute('name', $column['name']);
595
			$field->addAttribute('type', $column['value_type']);
596
			$field->addAttribute('translate', $column['column_type'] == 'translate' ? '1' : '0');
597
			$field->addAttribute('alwaysFallback', $column['fallback'] ? 'true' : 'false');
598
			$field->addAttribute('filter', $column['filter']);
599
			$field->addAttribute('description', $column['description']);
600
601
			if (!empty($column['params']) && is_string($column['params']))
602
			{
603
				$column['params'] = json_decode($column['params'], true);
604
			}
605
606
			if ($column['params'])
607
			{
608
				foreach ($column['params'] as $paramKey => $param)
609
				{
610
					$field->addAttribute($paramKey, $param);
611
				}
612
			}
613
		}
614
615
		// Add table filters for editor
616
		self::addChildWithCDATA($referenceTable, 'filter', $table->filter_query);
617
618
		// Add reference edit forms
619
		$referenceComponent = $reference->addChild('component');
620
		$formLinks = $table->form_links ? json_decode($table->form_links, true) : array();
621
622
		foreach ($formLinks as $formLink)
623
		{
624
			$editForm = $referenceComponent->addChild('editForm');
625
626
			$editForm->addAttribute('admin', $formLink['admin']);
627
			$editForm->addAttribute('option', $formLink['option']);
628
			$editForm->addAttribute('view', $formLink['view']);
629
			$editForm->addAttribute('layout', $formLink['layout']);
630
			$editForm->addAttribute('identifier', $formLink['identifier']);
631
			$editForm->addAttribute('showbutton', $formLink['showbutton']);
632
			$editForm->addAttribute('htmlposition', $formLink['htmlposition']);
633
			$editForm->addAttribute('checkoriginalid', $formLink['checkoriginalid']);
634
		}
635
636
		return $xml;
637
	}
638
639
	/**
640
	 * Method to add child with text inside CDATA
641
	 *
642
	 * @param   SimpleXMLElement  &$xml   Xml element
643
	 * @param   string            $name   Name of the child
644
	 * @param   string            $value  Value of the child
645
	 *
646
	 * @return  SimpleXMLElement
647
	 *
648
	 * @since   1.4
649
	 */
650
	public static function addChildWithCDATA(&$xml, $name, $value = '')
651
	{
652
		$newChild = $xml->addChild($name);
653
654
		if (is_null($newChild))
655
		{
656
			return $newChild;
657
		}
658
659
		$node = dom_import_simplexml($newChild);
660
		$no   = $node->ownerDocument;
661
		$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

661
		$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...
662
663
		return $newChild;
664
	}
665
666
	/**
667
	 * Loading of related XML files
668
	 *
669
	 * @param   string  $extensionName       Extension name
670
	 * @param   string  $contentElementsXml  XML File name
671
	 * @param   bool    $fullPath            Full path to the XML file
672
	 *
673
	 * @return  mixed  RTranslationContentElement if found or null
674
	 */
675
	public static function getContentElement($extensionName = '', $contentElementsXml = '', $fullPath = false)
676
	{
677
		$contentElements = self::getContentElements(false, $extensionName);
678
679
		if (!empty($contentElements))
680
		{
681
			$contentElementsXmlFullPath = self::getPathWithoutBase($contentElementsXml);
682
683
			foreach ($contentElements as $contentElement)
684
			{
685
				if ($fullPath
686
					&& self::getPathWithoutBase($contentElement->contentElementXmlPath) == $contentElementsXmlFullPath)
687
				{
688
					return $contentElement;
689
				}
690
691
				if ($contentElement->contentElementXml == $contentElementsXml)
692
				{
693
					return $contentElement;
694
				}
695
			}
696
		}
697
698
		return null;
699
	}
700
}
701