Completed
Push — master ( fad4fc...cca6ef )
by Nicolas
04:16 queued 03:17
created

FieldEntry_Relationship::commit()   C

Complexity

Conditions 9
Paths 10

Size

Total Lines 74
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 74
rs 5.9292
c 0
b 0
f 0
cc 9
eloc 46
nc 10
nop 0

How to fix   Long Method   

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
	 * Copyright: Deux Huit Huit 2014
4
	 * LICENCE: MIT https://deuxhuithuit.mit-license.org
5
	 */
6
7
	if (!defined('__IN_SYMPHONY__')) die('<h2>Symphony Error</h2><p>You cannot directly access this file</p>');
8
9
	require_once(EXTENSIONS . '/entry_relationship_field/lib/class.field.relationship.php');
10
	require_once(EXTENSIONS . '/entry_relationship_field/lib/class.cacheablefetch.php');
11
	require_once(EXTENSIONS . '/entry_relationship_field/lib/class.erfxsltutilities.php');
12
	require_once(EXTENSIONS . '/entry_relationship_field/lib/class.sectionsinfos.php');
13
	
14
	/**
15
	 *
16
	 * Field class that will represent relationships between entries
17
	 * @author Deux Huit Huit
18
	 *
19
	 */
20
	class FieldEntry_Relationship extends FieldRelationship
21
	{
22
		/**
23
		 *
24
		 * Name of the field table
25
		 *  @var string
26
		 */
27
		const FIELD_TBL_NAME = 'tbl_fields_entry_relationship';
28
		
29
		/**
30
		 *
31
		 * Current recursive level of output
32
		 *  @var int
33
		 */
34
		protected $recursiveLevel = 1;
35
		public function getRecursiveLevel()
36
		{
37
			return $this->recursiveLevel;
38
		}
39
		public function incrementRecursiveLevel($inc = 1)
40
		{
41
			return $this->recursiveLevel += $inc;
42
		}
43
		
44
		/**
45
		 *
46
		 * Parent's maximum recursive level of output
47
		 *  @var int
48
		 */
49
		protected $recursiveDeepness = null;
50
		public function getRecursiveDeepness()
51
		{
52
			return $this->recursiveDeepness;
53
		}
54
		public function setRecursiveDeepness($deepness)
55
		{
56
			return $this->recursiveDeepness = $deepness;
57
		}
58
		
59
		/* Cacheable Managers */
60
		private $sectionManager;
61
		private $entryManager;
62
		private $sectionInfos;
63
		
64
		public $expandIncludableElements = true;
65
		
66
		/**
67
		 *
68
		 * Constructor for the Entry_Relationship Field object
69
		 */
70
		public function __construct()
71
		{
72
			// call the parent constructor
73
			parent::__construct();
74
			// set the name of the field
75
			$this->_name = __('Entry Relationship');
76
			// permits to make it required
77
			$this->_required = true;
78
			// permits the make it show in the table columns
79
			$this->_showcolumn = true;
80
			// permits association
81
			$this->_showassociation = true;
82
			// current recursive level
83
			$this->recursiveLevel = 1;
84
			// parent's maximum recursive level of output
85
			$this->recursiveDeepness = null;
86
			// set as orderable
87
			$this->orderable = true;
88
			// set as not required by default
89
			$this->set('required', 'no');
90
			// show association by default
91
			$this->set('show_association', 'yes');
92
			// no sections
93
			$this->set('sections', null);
94
			// no max deepness
95
			$this->set('deepness', null);
96
			// no included elements
97
			$this->set('elements', null);
98
			// no modes
99
			$this->set('mode', null);
100
			$this->set('mode_table', null);
101
			$this->set('mode_header', null);
102
			$this->set('mode_footer', null);
103
			// no limit
104
			$this->set('min_entries', null);
105
			$this->set('max_entries', null);
106
			// all permissions
107
			$this->set('allow_new', 'yes');
108
			$this->set('allow_edit', 'yes');
109
			$this->set('allow_link', 'yes');
110
			$this->set('allow_delete', 'no');
111
			// display options
112
			$this->set('allow_collapse', 'yes');
113
			$this->set('allow_search', 'no');
114
			$this->set('show_header', 'yes');
115
			$this->sectionManager = new CacheableFetch('SectionManager');
116
			$this->entryManager = new CacheableFetch('EntryManager');
117
			$this->sectionInfos = new CacheableFetch('SectionsInfos');
118
		}
119
120
		public function isSortable()
121
		{
122
			return false;
123
		}
124
125
		public function canFilter()
126
		{
127
			return true;
128
		}
129
		
130
		public function canPublishFilter()
131
		{
132
			return true;
133
		}
134
135
		public function canImport()
136
		{
137
			return false;
138
		}
139
140
		public function canPrePopulate()
141
		{
142
			return false;
143
		}
144
		
145
		public function mustBeUnique()
146
		{
147
			return false;
148
		}
149
150
		public function allowDatasourceOutputGrouping()
151
		{
152
			return false;
153
		}
154
155
		public function requiresSQLGrouping()
156
		{
157
			return false;
158
		}
159
160
		public function allowDatasourceParamOutput()
161
		{
162
			return true;
163
		}
164
165
		/* ********** INPUT AND FIELD *********** */
166
167
168
		/**
169
		 * 
170
		 * Validates input
171
		 * Called before <code>processRawFieldData</code>
172
		 * @param $data
173
		 * @param $message
174
		 * @param $entry_id
175
		 */
176
		public function checkPostFieldData($data, &$message, $entry_id=null)
177
		{
178
			$message = null;
179
			$required = $this->isRequired();
180
			
181
			if ($required && (!is_array($data) || count($data) == 0 || strlen($data['entries']) < 1)) {
182
				$message = __("'%s' is a required field.", array($this->get('label')));
183
				return self::__MISSING_FIELDS__;
184
			}
185
			
186
			$entries = $data['entries'];
187
			
188
			if (!is_array($entries)) {
189
				$entries = static::getEntries($data);
190
			}
191
			
192
			// enforce limits only if required or it contains data
193
			if ($required || count($entries) > 0) {
194
				if ($this->getInt('min_entries') > 0 && $this->getInt('min_entries') > count($entries)) {
195
					$message = __("'%s' requires a minimum of %s entries.", array($this->get('label'), $this->getInt('min_entries')));
196
					return self::__INVALID_FIELDS__;
197
				} else if ($this->getInt('max_entries') > 0 && $this->getInt('max_entries') < count($entries)) {
198
					$message = __("'%s' can not contains more than %s entries.", array($this->get('label'), $this->getInt('max_entries')));
199
					return self::__INVALID_FIELDS__;
200
				}
201
			}
202
			
203
			return self::__OK__;
204
		}
205
206
207
		/**
208
		 *
209
		 * Process data before saving into database.
210
		 *
211
		 * @param array $data
212
		 * @param int $status
213
		 * @param boolean $simulate
214
		 * @param int $entry_id
215
		 *
216
		 * @return Array - data to be inserted into DB
217
		 */
218
		public function processRawFieldData($data, &$status, &$message = null, $simulate = false, $entry_id = null)
219
		{
220
			$status = self::__OK__;
221
			$entries = null;
222
			
223
			if (!is_array($data) && !is_string($data)) {
224
				return null;
225
			}
226
			
227
			if (isset($data['entries'])) {
228
				$entries = $data['entries'];
229
			}
230
			else if (is_string($data)) {
231
				$entries = $data;
232
			}
233
			
234
			$row = array(
235
				'entries' => $entries
236
			);
237
			
238
			// return row
239
			return $row;
240
		}
241
242
		/**
243
		 * This function permits parsing different field settings values
244
		 *
245
		 * @param array $settings
246
		 *	the data array to initialize if necessary.
247
		 */
248
		public function setFromPOST(Array $settings = array())
249
		{
250
			// call the default behavior
251
			parent::setFromPOST($settings);
252
253
			// declare a new setting array
254
			$new_settings = array();
255
256
			// set new settings
257
			$new_settings['sections'] = is_array($settings['sections']) ? 
258
				implode(self::SEPARATOR, $settings['sections']) : 
259
				(is_string($settings['sections']) ? $settings['sections'] : null);
260
				
261
			$new_settings['show_association'] = $settings['show_association'] == 'yes' ? 'yes' : 'no';
262
			$new_settings['deepness'] = General::intval($settings['deepness']);
263
			$new_settings['deepness'] = $new_settings['deepness'] < 1 ? null : $new_settings['deepness'];
264
			$new_settings['elements'] = empty($settings['elements']) ? null : $settings['elements'];
265
			$new_settings['mode'] = empty($settings['mode']) ? null : $settings['mode'];
266
			$new_settings['mode_table'] = empty($settings['mode_table']) ? null : $settings['mode_table'];
267
			$new_settings['mode_header'] = empty($settings['mode_header']) ? null : $settings['mode_header'];
268
			$new_settings['mode_footer'] = empty($settings['mode_footer']) ? null : $settings['mode_footer'];
269
			$new_settings['allow_new'] = $settings['allow_new'] == 'yes' ? 'yes' : 'no';
270
			$new_settings['allow_edit'] = $settings['allow_edit'] == 'yes' ? 'yes' : 'no';
271
			$new_settings['allow_link'] = $settings['allow_link'] == 'yes' ? 'yes' : 'no';
272
			$new_settings['allow_delete'] = $settings['allow_delete'] == 'yes' ? 'yes' : 'no';
273
			$new_settings['allow_collapse'] = $settings['allow_collapse'] == 'yes' ? 'yes' : 'no';
274
			$new_settings['allow_search'] = $settings['allow_search'] == 'yes' ? 'yes' : 'no';
275
			$new_settings['show_header'] = $settings['show_header'] == 'yes' ? 'yes' : 'no';
276
			
277
			// save it into the array
278
			$this->setArray($new_settings);
279
		}
280
281
282
		/**
283
		 *
284
		 * Validates the field settings before saving it into the field's table
285
		 */
286
		public function checkFields(Array &$errors, $checkForDuplicates = true)
287
		{
288
			$parent = parent::checkFields($errors, $checkForDuplicates);
289
			if ($parent != self::__OK__) {
290
				return $parent;
291
			}
292
			
293
			$sections = $this->get('sections');
294
			
295
			if (empty($sections)) {
296
				$errors['sections'] = __('At least one section must be chosen');
297
			}
298
299
			return (!empty($errors) ? self::__ERROR__ : self::__OK__);
300
		}
301
302
		/**
303
		 *
304
		 * Save field settings into the field's table
305
		 */
306
		public function commit()
307
		{
308
			// if the default implementation works...
309
			if(!parent::commit()) return false;
310
			
311
			$id = $this->get('id');
312
			
313
			// exit if there is no id
314
			if($id == false) return false;
315
			
316
			// we are the child, with multiple parents
317
			$child_field_id = $id;
318
			
319
			// delete associations, only where we are the child
320
			self::removeSectionAssociation($child_field_id);
321
			
322
			$sections = $this->getSelectedSectionsArray();
323
			
324
			foreach ($sections as $key => $sectionId) {
325
				if (empty($sectionId)) {
326
					continue;
327
				}
328
				$parent_section_id = General::intval($sectionId);
329
				if ($parent_section_id < 1) {
330
					// section not found, bail out
331
					continue;
332
				}
333
				$parent_section = SectionManager::fetch($parent_section_id);
334
				if (!$parent_section) {
335
					// section not found, bail out
336
					continue;
337
				}
338
				$fields = $parent_section->fetchVisibleColumns();
339
				if (empty($fields)) {
340
					// no visible field, revert to all
341
					$fields = $parent_section->fetchFields();
342
				}
343
				if (empty(fields)) {
344
					// no fields found, bail out
345
					continue;
346
				}
347
				$parent_field_id = current(array_keys($fields));
348
				// create association
349
				SectionManager::createSectionAssociation(
350
					$parent_section_id,
351
					$child_field_id,
352
					$parent_field_id,
353
					$this->get('show_association') == 'yes'
354
				);
355
			}
356
			
357
			// declare an array contains the field's settings
358
			$settings = array(
359
				'sections' => $this->get('sections'),
360
				'show_association' => $this->get('show_association'),
361
				'deepness' => $this->get('deepness'),
362
				'elements' => $this->get('elements'),
363
				'mode' => $this->get('mode'),
364
				'mode_table' => $this->get('mode_table'),
365
				'mode_header' => $this->get('mode_header'),
366
				'mode_footer' => $this->get('mode_footer'),
367
				'min_entries' => $this->get('min_entries'),
368
				'max_entries' => $this->get('max_entries'),
369
				'allow_new' => $this->get('allow_new'),
370
				'allow_edit' => $this->get('allow_edit'),
371
				'allow_link' => $this->get('allow_link'),
372
				'allow_delete' => $this->get('allow_delete'),
373
				'allow_collapse' => $this->get('allow_collapse'),
374
				'allow_search' => $this->get('allow_search'),
375
				'show_header' => $this->get('show_header'),
376
			);
377
378
			return FieldManager::saveSettings($id, $settings);
379
		}
380
381
		/**
382
		 *
383
		 * This function allows Fields to cleanup any additional things before it is removed
384
		 * from the section.
385
		 * @return boolean
386
		 */
387
		public function tearDown()
388
		{
389
			self::removeSectionAssociation($this->get('id'));
390
			return parent::tearDown();
391
		}
392
		
393
		/**
394
		 * Generates the where filter for searching by entry id
395
		 *
396
		 * @param string $value
397
		 * @param @optional string $col
398
		 * @param @optional boolean $andOperation
399
		 */
400
		public function generateWhereFilter($value, $col = 'd', $andOperation = true)
401
		{
402
			$junction = $andOperation ? 'AND' : 'OR';
403
			if (!$value) {
404
				return "{$junction} (`{$col}`.`entries` IS NULL)";
405
			}
406
			return " {$junction} (`{$col}`.`entries` = '{$value}' OR 
407
					`{$col}`.`entries` LIKE '{$value},%' OR 
408
					`{$col}`.`entries` LIKE '%,{$value}' OR 
409
					`{$col}`.`entries` LIKE '%,{$value},%')";
410
		}
411
412
		/**
413
		 * Fetch the number of associated entries for a particular entry id
414
		 *
415
		 * @param string $value
416
		 */
417
		public function fetchAssociatedEntryCount($value)
418
		{
419
			if (!$value) {
420
				return 0;
421
			}
422
			$join = sprintf(" INNER JOIN `tbl_entries_data_%s` AS `d` ON `e`.id = `d`.`entry_id`", $this->get('id'));
423
			$where = $this->generateWhereFilter($value);
424
			
425
			$entries = EntryManager::fetch(null, $this->get('parent_section'), null, 0, $where, $join, false, false, array());
426
			
427
			return count($entries);
428
		}
429
		
430
		public function fetchAssociatedEntrySearchValue($data, $field_id = null, $parent_entry_id = null)
431
		{
432
			return $parent_entry_id;
433
		}
434
		
435
		public function findRelatedEntries($entry_id, $parent_field_id)
436
		{
437
			$joins = '';
438
			$where = '';
439
			$this->buildDSRetrievalSQL(array($entry_id), $joins, $where, true);
440
			
441
			$entries = EntryManager::fetch(null, $this->get('parent_section'), null, 0, $where, $joins, false, false, array());
442
			
443
			return array_map(function ($e) {
444
				return $e['id'];
445
			}, $entries);
446
		}
447
		
448
		public function findParentRelatedEntries($parent_field_id, $entry_id)
449
		{
450
			$entry = $this->fetchEntry($entry_id, array($this->get('label')));
451
			
452
			if (!$entry) {
453
				return array();
454
			}
455
			
456
			return self::getEntries($entry->getData($this->get('id')));
457
		}
458
459
		public function fetchFilterableOperators()
460
		{
461
			return array(
462
				array(
463
					'title' => 'links to',
464
					'filter' => ' ',
465
					'help' => __('Find entries that links to the specified filter')
466
				),
467
			);
468
		}
469
470
		public function fetchSuggestionTypes()
471
		{
472
			return array('association');
473
		}
474
475
		public function fetchIDsfromValue($value)
476
		{
477
			$ids = array();
478
			$sections = $this->getArray('sections');
479
			//$value = Lang::createHandle($value);
480
481
			foreach ($sections as $sectionId) {
482
				$section = $this->sectionManager->fetch($sectionId);
483
				if (!$section) {
484
					continue;
485
				}
486
				$filterableFields = $section->fetchFilterableFields();
487
				if (empty($filterableFields)) {
488
					continue;
489
				}
490
				foreach ($filterableFields as $fId => $field) {
491
					$joins = '';
492
					$where = '';
493
					if ($field instanceof FieldRelationship) {
494
						continue;
495
					}
496
					$field->buildDSRetrievalSQL(array($value), $joins, $where, false);
497
					$fEntries = $this->entryManager->fetch(null, $sectionId, null, null, $where, $joins, true, false, null, false);
498
					if (!empty($fEntries)) {
499
						$ids = array_merge($ids, $fEntries);
500
						break;
501
					}
502
				}
503
			}
504
505
			return array_map(function ($e) {
506
				return $e['id'];
507
			}, $ids);
508
		}
509
510
		public function prepareAssociationsDrawerXMLElement(Entry $e, array $parent_association, $prepolutate = '')
511
		{
512
			$currentSection = SectionManager::fetch($parent_association['child_section_id']);
513
			$visibleCols = $currentSection->fetchVisibleColumns();
514
			$outputFieldId = current(array_keys($visibleCols));
515
			$outputField = FieldManager::fetch($outputFieldId);
516
			
517
			$value = $outputField->prepareReadableValue($e->getData($outputFieldId), $e->get('id'), true, __('None'));
518
			
519
			$li = new XMLElement('li');
520
			$li->setAttribute('class', 'field-' . $this->get('type'));
521
			$a = new XMLElement('a', strip_tags($value));
522
			$a->setAttribute('href', SYMPHONY_URL . '/publish/' . $parent_association['handle'] . '/edit/' . $e->get('id') . '/');
523
			$li->appendChild($a);
524
525
			return $li;
526
		}
527
		
528
		/**
529
		 * @param string $joins
530
		 * @param string $where
531
		 */
532
		public function buildDSRetrievalSQL($data, &$joins, &$where, $andOperation = false)
533
		{
534
			$field_id = $this->get('id');
535
			
536
			// REGEX filtering is a special case, and will only work on the first item
537
			// in the array. You cannot specify multiple filters when REGEX is involved.
538
			if (self::isFilterRegex($data[0])) {
539
				return $this->buildRegexSQL($data[0], array('entries'), $joins, $where);
540
			}
541
			
542
			$this->_key++;
543
			
544
			$where .= ' AND (1=' . ($andOperation ? '1' : '0') . ' ';
545
			
546
			$joins .= "
547
				INNER JOIN
548
					`tbl_entries_data_{$field_id}` AS `t{$field_id}_{$this->_key}`
549
					ON (`e`.`id` = `t{$field_id}_{$this->_key}`.`entry_id`)
550
			";
551
			
552
			$normalizedValues = array();
553
			foreach ($data as $value) {
554
				if (!is_numeric($value) && !is_null($value)) {
555
					$normalizedValues = array_merge($normalizedValues, $this->fetchIDsfromValue($value));
556
				} else {
557
					$normalizedValues[] = $value;
558
				}
559
			}
560
			
561
			foreach ($normalizedValues as $value) {
562
				$where .= $this->generateWhereFilter($this->cleanValue($value), "t{$field_id}_{$this->_key}", $andOperation);
563
			}
564
			
565
			$where .= ')';
566
			
567
			return true; // this tells the DS Manager that filters are OK!!
568
		}
569
570
		/* ******* EVENTS ******* */
571
572
		public function getExampleFormMarkup()
573
		{
574
			$label = Widget::Label($this->get('label'));
575
			$label->appendChild(Widget::Input('fields['.$this->get('element_name').'][entries]', null, 'hidden'));
576
577
			return $label;
578
		}
579
580
581
		/* ******* DATA SOURCE ******* */
582
		
583
		private function fetchEntry($eId, $elements = array())
584
		{
585
			$entry = EntryManager::fetch($eId, null, 1, 0, null, null, false, true, $elements, false);
586
			if (!is_array($entry) || count($entry) !== 1) {
587
				return null;
588
			}
589
			return $entry[0];
590
		}
591
		
592
		protected function fetchAllIncludableElements()
593
		{
594
			$sections = $this->getArray('sections');
595
			return $allElements = array_reduce($this->sectionInfos->fetch($sections), function ($memo, $item) {
596
				return array_merge($memo, array_map(function ($field) use ($item) {
597
					return $item['handle'] . '.' . $field['handle'];
598
				}, $item['fields']));
599
			}, array());
600
		}
601
		
602
		public function fetchIncludableElements()
603
		{
604
			$label = $this->get('element_name');
605
			$elements = $this->getArray('elements');
606
			if (empty($elements)) {
607
				$elements = array('*');
608
			}
609
			$includedElements = array();
610
			// Include everything
611
			if ($this->expandIncludableElements) {
612
				$includedElements[] = $label . ': *';
613
			}
614
			// Include individual elements
615
			foreach ($elements as $elem) {
616
				$elem = trim($elem);
617
				if ($elem !== '*') {
618
					$includedElements[] = $label . ': ' . $elem;
619
				} else if ($this->expandIncludableElements) {
620
					$includedElements = array_unique(array_merge($includedElements, array_map(function ($item) use ($label) {
621
						return $label . ': ' . $item;
622
					}, $this->fetchAllIncludableElements())));
623
				} else {
624
					$includedElements = array('*');
625
				}
626
			}
627
			return $includedElements;
628
		}
629
		
630
		/**
631
		 * Appends data into the XML tree of a Data Source
632
		 * @param $wrapper
633
		 * @param $data
634
		 */
635
		public function appendFormattedElement(XMLElement &$wrapper, $data, $encode = false, $mode = null, $entry_id = null)
636
		{
637
			if (!is_array($data) || empty($data)) {
638
				return;
639
			}
640
641
			// try to find an existing root
642
			$root = null;
643
			$newRoot = false;
644
			foreach (array_reverse($wrapper->getChildren()) as $xmlField) {
645
				if ($xmlField->getName() === $this->get('element_name')) {
646
					$root = $xmlField;
647
					break;
648
				}
649
			}
650
651
			// root was not found, create one
652
			if (!$root) {
653
				$root = new XMLElement($this->get('element_name'));
654
				$newRoot = true;
655
			}
656
			
657
			// devkit will load
658
			$devkit = isset($_GET['debug']) && (empty($_GET['debug']) || $_GET['debug'] == 'xml');
659
			
660
			// selected items
661
			$entries = static::getEntries($data);
662
			
663
			// current linked entries
664
			$root->setAttribute('entries', $data['entries']);
665
			
666
			// available sections
667
			$root->setAttribute('sections', $this->get('sections'));
668
			
669
			// included elements
670
			$elements = static::parseElements($this);
671
			
672
			// DS mode
673
			if (!$mode) {
674
				$mode = '*';
675
			}
676
			
677
			$parentDeepness = General::intval($this->recursiveDeepness);
678
			$deepness = General::intval($this->get('deepness'));
679
			
680
			// both deepnesses are defined and parent restricts more
681
			if ($parentDeepness > 0 && $deepness > 0 && $parentDeepness < $deepness) {
682
				$deepness = $parentDeepness;
683
			}
684
			// parent is defined, current is not
685
			else if ($parentDeepness > 0 && $deepness < 1) {
686
				$deepness = $parentDeepness;
687
			}
688
			
689
			// cache recursive level because recursion might
690
			// change its value later on.
691
			$recursiveLevel = $this->recursiveLevel;
692
			
693
			// build entries
694
			foreach ($entries as $eId) {
695
				// try to find and existing item
696
				// TODO: keep last index found since it should be the next
697
				$item = null;
698
				$newItem = false;
699
				foreach ($root->getChildren() as $xmlItem) {
700
					if (General::intval($xmlItem->getAttribute('id')) === General::intval($eId)) {
701
						$item = $xmlItem;
702
						break;
703
					}
704
				}
705
706
				// item was not found, create one
707
				if (!$item) {
708
					$item = new XMLElement('item');
709
					$item->setAllowEmptyAttributes(false);
710
					// output id
711
					$item->setAttribute('id', $eId);
712
					// output recursive level
713
					$item->setAttribute('level', $recursiveLevel);
714
					$item->setAttribute('max-level', $deepness);
715
					$newItem = true;
716
				// item was found, but it is an error, so we can skip it
717
				} else if ($item->getName() === 'error') {
718
					continue;
719
				}
720
721
				// max recursion check
722
				if ($deepness < 1 || $recursiveLevel < $deepness) {
723
					// current entry, without data
724
					$entry = $this->fetchEntry($eId);
725
					
726
					// entry not found...
727
					if (!$entry || empty($entry)) {
728
						$error = new XMLElement('error');
729
						$error->setAttribute('id', $eId);
730
						$error->setValue(__('Error: entry `%s` not found', array($eId)));
731
						$root->prependChild($error);
732
						continue;
733
					}
734
					
735
					// fetch section infos
736
					$sectionId = $entry->get('section_id');
737
					$section = $this->sectionManager->fetch($sectionId);
738
					$sectionName = $section->get('handle');
739
					// cache fields info
740
					if (!isset($section->er_field_cache)) {
741
						$section->er_field_cache = $section->fetchFields();
742
					}
743
					
744
					// set section related attributes
745
					$item->setAttribute('section-id', $sectionId);
746
					$item->setAttribute('section', $sectionName);
747
					
748
					// Get the valid elements for this section only
749
					$validElements = $elements[$sectionName];
750
					
751
					// adjust the mode for the current section
752
					$curMode = $mode;
753
					
754
					// remove section name from current mode so "sectionName.field" becomes simply "field"
755
					if (preg_match('/^(' . $sectionName . '\.)(.*)$/sU', $curMode)) {
756
						$curMode = preg_replace('/^' . $sectionName . '\./sU', '', $curMode);
757
					}
758
					// remove section name from current mode "sectionName" and
759
					// treat it like if it is "sectionName: *"
760
					else if (preg_match('/^(' . $sectionName . ')$/sU', $curMode)) {
761
						$curMode = '*';
762
					}
763
					// section name was not found in mode, check if the mode is "*"
764
					else if ($curMode != '*') {
765
						// mode forbids this section
766
						$validElements = null;
767
					}
768
					
769
					// this section is not selected, bail out
770 View Code Duplication
					if (!is_array($validElements)) {
771
						if ($newItem) {
772
							if ($devkit) {
773
								$item->setAttribute('x-forbidden-by-ds', $curMode);
774
							}
775
							$root->appendChild($item);
776
						}
777
						continue;
778
					} else {
779
						if ($devkit) {
780
							$item->setAttribute('x-forbidden-by-ds', null);
781
						}
782
					}
783
					
784
					// selected fields for fetching
785
					$sectionElements = array();
786
					
787
					// everything is allowed
788
					if (in_array('*', $validElements)) {
789
						if ($curMode !== '*') {
790
							// get only the mode
791
							$sectionElements = array($curMode);
792
						}
793
						else {
794
							// setting null = get all
795
							$sectionElements = null;
796
						}
797
					}
798
					// only use valid elements
799
					else {
800
						if ($curMode !== '*') {
801
							// is this field allowed ?
802
							if (self::isFieldIncluded($curMode, $validElements)) {
803
								// get only the mode
804
								$sectionElements = array($curMode);
805
							}
806
							else {
807
								// $curMode selects something outside of
808
								// the valid elements: select nothing
809
								$sectionElements = array();
810
							}
811
						}
812
						else {
813
							// use field's valid elements
814
							$sectionElements = $validElements;
815
						}
816
					}
817
					
818
					// Filtering is enabled, but nothing is selected
819 View Code Duplication
					if (is_array($sectionElements) && empty($sectionElements)) {
820
						if ($newItem) {
821
							$root->appendChild($item);
822
							if ($devkit) {
823
								$item->setAttribute('x-forbidden-by-selection', $curMode);
824
							}
825
						}
826
						continue;
827
					} else {
828
						if ($devkit) {
829
							$item->setAttribute('x-forbidden-by-selection', null);
830
						}
831
					}
832
					
833
					// fetch current entry again, but with data for the allowed schema
834
					$entry = $this->fetchEntry($eId, $sectionElements);
835
					
836
					// cache the entry data
837
					$entryData = $entry->getData();
838
					
839
					// for each field returned for this entry...
840
					foreach ($entryData as $fieldId => $data) {
841
						$filteredData = array_filter($data, function ($value) {
842
							return $value != null;
843
						});
844
						
845
						if (empty($filteredData)) {
846
							continue;
847
						}
848
						
849
						$field = $section->er_field_cache[$fieldId];
850
						$fieldName = $field->get('element_name');
851
						$fieldCurMode = self::extractMode($fieldName, $curMode);
852
						
853
						$parentIncludableElement = self::getSectionElementName($fieldName, $validElements);
854
						$parentIncludableElementMode = self::extractMode($fieldName, $parentIncludableElement);
855
						
856
						// Special treatments for ERF
857
						if ($field instanceof FieldEntry_relationship) {
858
							// Increment recursive level
859
							$field->recursiveLevel = $recursiveLevel + 1;
860
							$field->recursiveDeepness = $deepness;
861
						}
862
						
863
						$submodes = null;
864
						// Parent mode is not defined (we are selecting the whole section)
865
						if ($parentIncludableElementMode === null) {
866
							if ($fieldCurMode == null) {
867
								// Field does not defined a mode either: use the field's default
868
								$submodes = null;
869
							} else {
870
								// Use the current field's mode
871
								$submodes = array($fieldCurMode);
872
							}
873
							if ($devkit) {
874
								$item->setAttribute('x-selection-mode-empty', null);
875
							}
876
						} else {
877
							// Field mode is not defined or it is the same as the parent
878
							if ($fieldCurMode === null || $fieldCurMode == $parentIncludableElementMode) {
879
								if ($devkit) {
880
									$item->setAttribute('x-selection-mode-empty', null);
881
								}
882
								// Use parent mode
883
								$submodes = array($parentIncludableElementMode);
884
							} else {
885
								if ($devkit) {
886
									$item->setAttribute('x-selection-mode-empty', 'yes');
887
								}
888
								// Empty selection
889
								$submodes = array();
890
							}
891
						}
892
893
						// current selection does not specify a mode
894
						if ($submodes === null) {
895
							if ($field instanceof FieldEntry_Relationship) {
896
								$field->expandIncludableElements = false;
897
							}
898
							$submodes = array_map(function ($fieldIncludableElement) use ($fieldName) {
899
								return FieldEntry_relationship::extractMode($fieldName, $fieldIncludableElement);
900
							}, $field->fetchIncludableElements());
901
							if ($field instanceof FieldEntry_Relationship) {
902
								$field->expandIncludableElements = true;
903
							}
904
						}
905
						
906
						// Append the formatted element for each requested mode
907
						foreach ($submodes as $submode) {
908
							$field->appendFormattedElement($item, $data, $encode, $submode, $eId);
909
						}
910
					}
911
					// output current mode
912
					$item->setAttribute('matched-element', $curMode);
913
					// no field selected
914
					if (is_array($sectionElements) && empty($sectionElements)) {
915
						$item->setAttribute('empty-selection', 'yes');
916
					}
917
				} // end max recursion check
918
919
				if ($newItem) {
920
					// append item when done
921
					$root->appendChild($item);
922
				}
923
			} // end each entries
924
925
			if ($newRoot) {
926
				// output mode for this field
927
				if ($devkit) {
928
					$root->setAttribute('x-data-source-mode', $mode);
929
					$root->setAttribute('x-field-included-elements', $this->get('elements'));
930
				}
931
				// add all our data to the wrapper;
932
				$wrapper->appendChild($root);
933
			} else {
934
				if ($devkit) {
935
					$root->setAttribute('x-data-source-mode', $root->getAttribute('x-data-source-mode') . ', ' . $mode);
936
				}
937
			}
938
939
			// clean up
940
			$this->recursiveLevel = 1;
941
			$this->recursiveDeepness = null;
942
		}
943
944
		public function getParameterPoolValue(array $data, $entry_id = null)
945
		{
946
			if(!is_array($data) || empty($data)) return;
947
			return static::getEntries($data);
948
		}
949
950
		/* ********* Utils *********** */
951
		
952
		/**
953
		 * Return true if $fieldName is allowed in $sectionElements
954
		 * @param string $fieldName
955
		 * @param string $sectionElements
956
		 * @return bool
957
		 */
958
		public static function isFieldIncluded($fieldName, $sectionElements)
959
		{
960
			return self::getSectionElementName($fieldName, $sectionElements) !== null;
961
		}
962
963
		public static function getSectionElementName($fieldName, $sectionElements)
964
		{
965
			if (is_array($sectionElements)) {
966
				foreach ($sectionElements as $element) {
967
					// everything is allowed, use "fieldName" directly
968
					if ($element === '*') {
969
						return $fieldName;
970
					}
971
					// make "fieldName: *" the same as "fieldName"
972
					if (preg_match('/\s*:\s*\*/sU', $fieldName)) {
973
						$fieldName = trim(current(explode(':', $fieldName)));
974
					}
975
					// "fieldName" is included as-is or element starts with "fieldName:"
976
					if ($fieldName === $element || preg_match('/^' . $fieldName . '\s*:/sU', $element)) {
977
						return $element;
978
					}
979
				}
980
			}
981
			return null;
982
		}
983
		
984
		public static function parseElements($field)
985
		{
986
			$elements = array();
987
			$exElements = $field->getArray('elements');
988
			
989
			if (in_array('*', $exElements)) {
990
				$sections = $field->getArray('sections');
991
				$sections = SectionManager::fetch($sections);
992
				return array_reduce($sections, function ($result, $section) {
993
					$result[$section->get('handle')] = array('*');
994
					return $result;
995
				}, array());
996
			}
997
			
998
			foreach ($exElements as $value) {
999
				if (!$value) {
1000
					continue;
1001
				}
1002
				// sectionName.fieldName or sectionName.*
1003
				$parts = array_map(trim, explode('.', $value, 2));
1004
				// first time seeing this section
1005
				if (!isset($elements[$parts[0]])) {
1006
					$elements[$parts[0]] = array();
1007
				}
1008
				// we have a value after the dot
1009
				if (isset($parts[1]) && !!$parts[1]) {
1010
					$elements[$parts[0]][] = $parts[1];
1011
				}
1012
				// sectionName only
1013
				else if (!isset($parts[1])) {
1014
					$elements[$parts[0]][] = '*';
1015
				}
1016
			}
1017
			
1018
			return $elements;
1019
		}
1020
1021
		public static function extractMode($fieldName, $mode)
1022
		{
1023
			$pattern = '/^' . $fieldName . '\s*:\s*/s';
1024
			if (!preg_match($pattern, $mode)) {
1025
				return null;
1026
			}
1027
			$mode = preg_replace($pattern, '', $mode, 1);
1028
			if ($mode === '*') {
1029
				return null;
1030
			}
1031
			return $mode;
1032
		}
1033
1034
		private function buildSectionSelect($name)
1035
		{
1036
			$sections = SectionManager::fetch();
1037
			$options = array();
1038
			$selectedSections = $this->getSelectedSectionsArray();
1039
			
1040 View Code Duplication
			foreach ($sections as $section) {
1041
				$driver = $section->get('id');
1042
				$selected = in_array($driver, $selectedSections);
1043
				$options[] = array($driver, $selected, General::sanitize($section->get('name')));
1044
			}
1045
			
1046
			return Widget::Select($name, $options, array('multiple' => 'multiple'));
1047
		}
1048
1049 View Code Duplication
		private function appendSelectionSelect(&$wrapper)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1050
		{
1051
			$name = $this->createSettingsFieldName('sections', true);
1052
1053
			$input = $this->buildSectionSelect($name);
1054
			$input->setAttribute('class', 'entry_relationship-sections');
1055
1056
			$label = Widget::Label();
1057
			$label->setAttribute('class', 'column');
1058
1059
			$label->setValue(__('Available sections %s', array($input->generate())));
1060
1061
			$wrapper->appendChild($label);
1062
		}
1063
1064
		private function createEntriesHiddenInput($data)
1065
		{
1066
			$hidden = new XMLElement('input', null, array(
1067
				'type' => 'hidden',
1068
				'name' => $this->createPublishFieldName('entries'),
1069
				'value' => $data['entries']
1070
			));
1071
			
1072
			return $hidden;
1073
		}
1074
		
1075
		private function createActionBarMenu($sections)
1076
		{
1077
			$wrap = new XMLElement('div');
1078
			$actionBar = '';
1079
			$modeFooter = $this->get('mode_footer');
1080
			if ($modeFooter) {
1081
				$section = $this->sectionManager->fetch($this->get('parent_section'));
1082
				$actionBar = ERFXSLTUTilities::processXSLT($this, null, $section->get('handle'), null, 'mode_footer', isset($_REQUEST['debug']), 'field');
1083
			}
1084
			if (empty($actionBar)) {
1085
				$fieldset = new XMLElement('fieldset');
1086
				$fieldset->setAttribute('class', 'single');
1087
				if ($this->is('allow_search')) {
1088
					$searchWrap = new XMLElement('div');
1089
					$searchWrap->setAttribute('data-interactive', 'data-interactive');
1090
					$searchWrap->setAttribute('class', 'search');
1091
					$searchInput = Widget::Input('', null, 'text', array(
1092
						'class' => 'search',
1093
						'data-search' => '',
1094
						'placeholder' => __('Search for entries')
1095
					));
1096
					$searchWrap->appendChild($searchInput);
1097
					$searchSuggestions = new XMLElement('ul');
1098
					$searchSuggestions->setAttribute('class', 'suggestions');
1099
					$searchWrap->appendChild($searchSuggestions);
1100
					$fieldset->appendChild($searchWrap);
1101
				}
1102
				
1103
				if ($this->is('allow_new') || $this->is('allow_link') || $this->is('allow_search')) {
1104
					$selectWrap = new XMLElement('div');
1105
					$selectWrap->appendChild(new XMLElement('span', __('Related section: '), array('class' => 'sections-selection')));
1106
					$options = array();
1107
					foreach ($sections as $section) {
1108
						$options[] = array($section->get('handle'), false, $section->get('name'));
1109
					}
1110
					$select = Widget::Select('', $options, array('class' => 'sections sections-selection'));
1111
					$selectWrap->appendChild($select);
1112
					$fieldset->appendChild($selectWrap);
1113
				}
1114 View Code Duplication
				if ($this->is('allow_new')) {
1115
					$fieldset->appendChild(new XMLElement('button', __('Create new'), array(
1116
						'type' => 'button',
1117
						'class' => 'create',
1118
						'data-create' => '',
1119
					)));
1120
				}
1121 View Code Duplication
				if ($this->is('allow_link')) {
1122
					$fieldset->appendChild(new XMLElement('button', __('Link to entry'), array(
1123
						'type' => 'button',
1124
						'class' => 'link',
1125
						'data-link' => '',
1126
					)));
1127
				}
1128
				$wrap->appendChild($fieldset);
1129
			}
1130
			else {
1131
				$wrap->setValue($actionBar);
1132
			}
1133
			
1134
			return $wrap;
1135
		}
1136
1137
		/* ********* UI *********** */
1138
		
1139
		/**
1140
		 *
1141
		 * Builds the UI for the field's settings when creating/editing a section
1142
		 * @param XMLElement $wrapper
1143
		 * @param array $errors
1144
		 */
1145
		public function displaySettingsPanel(XMLElement &$wrapper, $errors=null)
1146
		{
1147
			/* first line, label and such */
1148
			parent::displaySettingsPanel($wrapper, $errors);
1149
			
1150
			// sections
1151
			$sections = new XMLElement('fieldset');
1152
			
1153
			$this->appendSelectionSelect($sections);
1154 View Code Duplication
			if (is_array($errors) && isset($errors['sections'])) {
1155
				$sections = Widget::Error($sections, $errors['sections']);
1156
			}
1157
			$wrapper->appendChild($sections);
1158
			
1159
			// elements
1160
			$elements = new XMLElement('div');
1161
			$element = Widget::Label();
1162
			$element->setValue(__('Included elements in Data Sources and Backend Templates'));
1163
			$element->setAttribute('class', 'column');
1164
			$element->appendChild(Widget::Input($this->createSettingsFieldName('elements'), $this->get('elements'), 'text', array(
1165
				'class' => 'entry_relationship-elements'
1166
			)));
1167
			$elements->appendChild($element);
1168
			$elements_choices = new XMLElement('ul', null, array('class' => 'tags singular entry_relationship-field-choices'));
1169
			$elements->appendChild($elements_choices);
1170
			$wrapper->appendChild($elements);
1171
			
1172
			// limit entries
1173
			$limits = new XMLElement('fieldset');
1174
			$limits->appendChild(new XMLElement('legend', __('Limits')));
1175
			$limits_cols = new XMLElement('div');
1176
			$limits_cols->setAttribute('class', 'three columns');
1177
			// min
1178
			$limit_min = Widget::Label();
1179
			$limit_min->setValue(__('Minimum count of entries in this field'));
1180
			$limit_min->setAttribute('class', 'column');
1181
			$limit_min->appendChild(Widget::Input($this->createSettingsFieldName('min_entries'), $this->get('min_entries'), 'number', array(
1182
				'min' => 0,
1183
				'max' => 99999
1184
			)));
1185
			$limits_cols->appendChild($limit_min);
1186
			// max
1187
			$limit_max = Widget::Label();
1188
			$limit_max->setValue(__('Maximum count of entries in this field'));
1189
			$limit_max->setAttribute('class', 'column');
1190
			$limit_max->appendChild(Widget::Input($this->createSettingsFieldName('max_entries'), $this->get('max_entries'), 'number', array(
1191
				'min' => 0,
1192
				'max' => 99999
1193
			)));
1194
			$limits_cols->appendChild($limit_max);
1195
			
1196
			// deepness
1197
			$deepness = Widget::Label();
1198
			$deepness->setValue(__('Maximum level of recursion in Data Sources'));
1199
			$deepness->setAttribute('class', 'column');
1200
			$deepness->appendChild(Widget::Input($this->createSettingsFieldName('deepness'), $this->get('deepness'), 'number', array(
1201
				'min' => 0,
1202
				'max' => 99
1203
			)));
1204
			$limits_cols->appendChild($deepness);
1205
			$limits->appendChild($limits_cols);
1206
			$wrapper->appendChild($limits);
1207
			
1208
			// xsl
1209
			$xsl = new XMLElement('fieldset');
1210
			$xsl->appendChild(new XMLElement('legend', __('Backend XSL templates options')));
1211
			$xsl_cols = new XMLElement('div');
1212
			$xsl_cols->setAttribute('class', 'four columns');
1213
			
1214
			// xsl mode
1215
			$xslmode = Widget::Label();
1216
			$xslmode->setValue(__('XSL mode for entries content template'));
1217
			$xslmode->setAttribute('class', 'column');
1218
			$xslmode->appendChild(Widget::Input($this->createSettingsFieldName('mode'), $this->get('mode'), 'text'));
1219
			$xsl_cols->appendChild($xslmode);
1220
			// xsl header mode
1221
			$xslmodetable = Widget::Label();
1222
			$xslmodetable->setValue(__('XSL mode for entries header template'));
1223
			$xslmodetable->setAttribute('class', 'column');
1224
			$xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_header'), $this->get('mode_header'), 'text'));
1225
			$xsl_cols->appendChild($xslmodetable);
1226
			// xsl table mode
1227
			$xslmodetable = Widget::Label();
1228
			$xslmodetable->setValue(__('XSL mode for publish table value'));
1229
			$xslmodetable->setAttribute('class', 'column');
1230
			$xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_table'), $this->get('mode_table'), 'text'));
1231
			$xsl_cols->appendChild($xslmodetable);
1232
			// xsl action bar mode
1233
			$xslmodetable = Widget::Label();
1234
			$xslmodetable->setValue(__('XSL mode for publish action bar'));
1235
			$xslmodetable->setAttribute('class', 'column');
1236
			$xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_footer'), $this->get('mode_footer'), 'text'));
1237
			$xsl_cols->appendChild($xslmodetable);
1238
			
1239
			$xsl->appendChild($xsl_cols);
1240
			$wrapper->appendChild($xsl);
1241
			
1242
			// permissions
1243
			$permissions = new XMLElement('fieldset');
1244
			$permissions->appendChild(new XMLElement('legend', __('Permissions')));
1245
			$permissions_cols = new XMLElement('div');
1246
			$permissions_cols->setAttribute('class', 'four columns');
1247
			$permissions_cols->appendChild($this->createCheckbox('allow_new', 'Show new button'));
1248
			$permissions_cols->appendChild($this->createCheckbox('allow_edit', 'Show edit button'));
1249
			$permissions_cols->appendChild($this->createCheckbox('allow_link', 'Show link button'));
1250
			$permissions_cols->appendChild($this->createCheckbox('allow_delete', 'Show delete button'));
1251
			$permissions->appendChild($permissions_cols);
1252
			$wrapper->appendChild($permissions);
1253
			
1254
			// display options
1255
			$display = new XMLElement('fieldset');
1256
			$display->appendChild(new XMLElement('legend', __('Display options')));
1257
			$display_cols = new XMLElement('div');
1258
			$display_cols->setAttribute('class', 'four columns');
1259
			$display_cols->appendChild($this->createCheckbox('allow_collapse', 'Allow content collapsing'));
1260
			$display_cols->appendChild($this->createCheckbox('allow_search', 'Allow search linking'));
1261
			$display_cols->appendChild($this->createCheckbox('show_header', 'Show the header box before entries templates'));
1262
			$display->appendChild($display_cols);
1263
			$wrapper->appendChild($display);
1264
			
1265
			// assoc
1266
			$assoc = new XMLElement('fieldset');
1267
			$assoc->appendChild(new XMLElement('legend', __('Associations')));
1268
			$assoc_cols = new XMLElement('div');
1269
			$assoc_cols->setAttribute('class', 'three columns');
1270
			$this->appendShowAssociationCheckbox($assoc_cols);
1271
			$assoc->appendChild($assoc_cols);
1272
			$wrapper->appendChild($assoc);
1273
			
1274
			// footer
1275
			$this->appendStatusFooter($wrapper);
1276
		}
1277
1278
		/**
1279
		 *
1280
		 * Builds the UI for the publish page
1281
		 * @param XMLElement $wrapper
1282
		 * @param mixed $data
1283
		 * @param mixed $flagWithError
1284
		 * @param string $fieldnamePrefix
1285
		 * @param string $fieldnamePostfix
1286
		 */
1287
		public function displayPublishPanel(XMLElement &$wrapper, $data = null, $flagWithError = null, $fieldnamePrefix = null, $fieldnamePostfix = null, $entry_id = null)
1288
		{
1289
			$entriesId = array();
1290
			$sectionsId = $this->getSelectedSectionsArray();
1291
			
1292
			if ($data['entries'] != null) {
1293
				$entriesId = static::getEntries($data);
1294
			}
1295
			
1296
			$sectionsId = array_map(array('General', 'intval'), $sectionsId);
1297
			$sections = SectionManager::fetch($sectionsId);
1298
			
1299
			$label = Widget::Label($this->get('label'));
1300
			$notes = '';
1301
			
1302
			// min note
1303
			if ($this->getInt('min_entries') > 0) {
1304
				$notes .= __('Minimum number of entries: <b>%s</b>. ', array($this->get('min_entries')));
1305
			}
1306
			// max note
1307
			if ($this->getInt('max_entries') > 0) {
1308
				$notes .= __('Maximum number of entries: <b>%s</b>. ', array($this->get('max_entries')));
1309
			}
1310
			// not required note
1311
			if (!$this->isRequired()) {
1312
				$notes .= __('Optional');
1313
			}
1314
			// append notes
1315
			if ($notes) {
1316
				$label->appendChild(new XMLElement('i', $notes));
1317
			}
1318
			
1319
			// label error management
1320
			if ($flagWithError != null) {
1321
				$wrapper->appendChild(Widget::Error($label, $flagWithError));
1322
			} else {
1323
				$wrapper->appendChild($label);
1324
			}
1325
			
1326
			$wrapper->appendChild($this->createEntriesList($entriesId));
1327
			$wrapper->appendChild($this->createActionBarMenu($sections));
1328
			$wrapper->appendChild($this->createEntriesHiddenInput($data));
1329
			$wrapper->setAttribute('data-value', $data['entries']);
1330
			$wrapper->setAttribute('data-field-id', $this->get('id'));
1331
			$wrapper->setAttribute('data-field-label', $this->get('label'));
1332
			$wrapper->setAttribute('data-min', $this->get('min_entries'));
1333
			$wrapper->setAttribute('data-max', $this->get('max_entries'));
1334
			$wrapper->setAttribute('data-required', $this->get('required'));
1335
			if (isset($_REQUEST['debug'])) {
1336
				$wrapper->setAttribute('data-debug', true);
1337
			}
1338
		}
1339
1340
		/**
1341
		 *
1342
		 * Return a plain text representation of the field's data
1343
		 * @param array $data
1344
		 * @param int $entry_id
1345
		 */
1346
		public function prepareTextValue($data, $entry_id = null)
1347
		{
1348
			if ($entry_id == null || !is_array($data) || empty($data)) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $entry_id of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
1349
				return '';
1350
			}
1351
			return $data['entries'];
1352
		}
1353
1354
		/**
1355
		 * Format this field value for display as readable text value.
1356
		 *
1357
		 * @param array $data
1358
		 *  an associative array of data for this string. At minimum this requires a
1359
		 *  key of 'value'.
1360
		 * @param integer $entry_id (optional)
1361
		 *  An option entry ID for more intelligent processing. Defaults to null.
1362
		 * @param string $defaultValue (optional)
1363
		 *  The value to use when no plain text representation of the field's data
1364
		 *  can be made. Defaults to null.
1365
		 * @return string
1366
		 *  the readable text summary of the values of this field instance.
1367
		 */
1368
		public function prepareReadableValue($data, $entry_id = null, $truncate = false, $defaultValue = 'None')
1369
		{
1370
			if ($entry_id == null || !is_array($data) || empty($data)) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $entry_id of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
1371
				return __($defaultValue);
1372
			}
1373
			$entries = static::getEntries($data);
1374
			$realEntries = array();
1375
			foreach ($entries as $entryId) {
1376
				$e = EntryManager::fetch($entryId);
1377
				if (is_array($e) && !empty($e)) {
1378
					$realEntries = array_merge($realEntries, $e);
1379
				}
1380
			}
1381
			$count = count($entries);
1382
			$realCount = count($realEntries);
1383
			if ($count === $realCount) {
1384
				return self::formatCount($count);
1385
			}
1386
			return self::formatCount($realCount) . ' (' . self::formatCount($count - $realCount) . ' not found)';
1387
		}
1388
1389
		/**
1390
		 * Format this field value for display in the publish index tables.
1391
		 *
1392
		 * @param array $data
1393
		 *  an associative array of data for this string. At minimum this requires a
1394
		 *  key of 'value'.
1395
		 * @param XMLElement $link (optional)
1396
		 *  an XML link structure to append the content of this to provided it is not
1397
		 *  null. it defaults to null.
1398
		 * @param integer $entry_id (optional)
1399
		 *  An option entry ID for more intelligent processing. defaults to null
1400
		 * @return string
1401
		 *  the formatted string summary of the values of this field instance.
1402
		 */
1403
		public function prepareTableValue($data, XMLElement $link = null, $entry_id = null)
1404
		{
1405
			$value = $this->prepareReadableValue($data, $entry_id, false, __('None'));
1406
1407
			if ($entry_id && $this->get('mode_table')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $entry_id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1408
				$entries = static::getEntries($data);
1409
				$cellcontent = '';
1410
				foreach ($entries as $position => $child_entry_id) {
1411
					$entry = $this->entryManager->fetch($child_entry_id);
1412
					if (!$entry || !is_array($entry) || empty($entry)) {
1413
						continue;
1414
					}
1415
					reset($entry);
1416
					$entry = current($entry);
1417
					$section = $this->sectionManager->fetch($entry->get('section_id'));
1418
					$content = ERFXSLTUTilities::processXSLT($this, $entry, $section->get('handle'), $section->fetchFields(), 'mode_table', isset($_REQUEST['debug']), 'entry', $position + 1);
1419
					if ($content) {
1420
						$cellcontent .= $content;
1421
					}
1422
				}
1423
				
1424
				$cellcontent = trim($cellcontent);
1425
				
1426
				if (General::strlen($cellcontent)) {
1427
					if ($link) {
1428
						$link->setValue($cellcontent);
1429
						return $link->generate();
1430
					}
1431
					return $cellcontent;
1432
				}
1433
			} else if ($link) {
1434
				$link->setValue($value);
1435
				return $link->generate();
1436
			}
1437
1438
			return $value;
1439
		}
1440
1441
		/* ********* SQL Data Definition ************* */
1442
1443
		/**
1444
		 *
1445
		 * Creates table needed for entries of individual fields
1446
		 */
1447
		public function createTable()
1448
		{
1449
			$id = $this->get('id');
1450
1451
			return Symphony::Database()->query("
1452
				CREATE TABLE `tbl_entries_data_$id` (
1453
					`id` int(11) 		unsigned NOT NULL AUTO_INCREMENT,
1454
					`entry_id` 			int(11) unsigned NOT NULL,
1455
					`entries` 			text COLLATE utf8_unicode_ci NULL,
1456
					PRIMARY KEY  (`id`),
1457
					UNIQUE KEY `entry_id` (`entry_id`)
1458
				) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1459
			");
1460
		}
1461
1462
		/**
1463
		 * Creates the table needed for the settings of the field
1464
		 */
1465
		public static function createFieldTable()
1466
		{
1467
			$tbl = self::FIELD_TBL_NAME;
1468
1469
			return Symphony::Database()->query("
1470
				CREATE TABLE IF NOT EXISTS `$tbl` (
1471
					`id` 				int(11) unsigned NOT NULL AUTO_INCREMENT,
1472
					`field_id` 			int(11) unsigned NOT NULL,
1473
					`sections`			varchar(2048) NULL COLLATE utf8_unicode_ci,
1474
					`show_association` 	enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1475
					`deepness` 			int(2) unsigned NULL,
1476
					`elements` 			text COLLATE utf8_unicode_ci NULL,
1477
					`mode`				varchar(50) NULL COLLATE utf8_unicode_ci,
1478
					`mode_table`		varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL,
1479
					`mode_header`		varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL,
1480
					`mode_footer`		varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL,
1481
					`min_entries`		int(5) unsigned NULL,
1482
					`max_entries`		int(5) unsigned NULL,
1483
					`allow_edit` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1484
					`allow_new` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1485
					`allow_link` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1486
					`allow_delete` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'no',
1487
					`allow_collapse` 	enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1488
					`allow_search` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'no',
1489
					`show_header` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1490
					PRIMARY KEY (`id`),
1491
					UNIQUE KEY `field_id` (`field_id`)
1492
				) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1493
			");
1494
		}
1495
		
1496
		public static function update_102()
1497
		{
1498
			$tbl = self::FIELD_TBL_NAME;
1499
			$sql = "
1500
				ALTER TABLE `$tbl`
1501
					ADD COLUMN `allow_edit` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1502
					ADD COLUMN `allow_new` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1503
					ADD COLUMN `allow_link` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes'
1504
					AFTER `max_entries`
1505
			";
1506
			$addColumns = Symphony::Database()->query($sql);
1507
			if (!$addColumns) {
1508
				return false;
1509
			}
1510
1511
			$fields = FieldManager::fetch(null, null, null, 'id', 'entry_relationship');
1512
			if (!empty($fields) && is_array($fields)) {
1513
				foreach ($fields as $fieldId => $field) {
1514
					$sql = "ALTER TABLE `tbl_entries_data_$fieldId` MODIFY `entries` TEXT";
1515
					if (!Symphony::Database()->query($sql)) {
1516
						throw new Exception(__('Could not update table `tbl_entries_data_%s`.', array($fieldId)));
1517
					}
1518
				}
1519
			}
1520
			return true;
1521
		}
1522
		
1523
		public static function update_103()
1524
		{
1525
			$tbl = self::FIELD_TBL_NAME;
1526
			$sql = "
1527
				ALTER TABLE `$tbl`
1528
					ADD COLUMN `allow_delete` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'no'
1529
						AFTER `allow_link`
1530
			";
1531
			return Symphony::Database()->query($sql);
1532
		}
1533
		
1534
		public static function update_200()
1535
		{
1536
			$tbl = self::FIELD_TBL_NAME;
1537
			$sql = "
1538
				ALTER TABLE `$tbl`
1539
					ADD COLUMN `allow_collapse` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes'
1540
						AFTER `allow_delete`,
1541
					ADD COLUMN `mode_table` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
1542
						AFTER `mode`,
1543
					ADD COLUMN `mode_header` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
1544
						AFTER `mode_table`,
1545
					ADD COLUMN `show_header` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes'
1546
						AFTER `allow_collapse`,
1547
					ADD COLUMN `mode_footer` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
1548
						AFTER `mode_header`,
1549
					CHANGE `sections` `sections` varchar(2048) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
1550
					CHANGE `elements` `elements` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL
1551
			";
1552
			return Symphony::Database()->query($sql);
1553
		}
1554
		
1555
		public static function update_2008()
1556
		{
1557
			$tbl = self::FIELD_TBL_NAME;
1558
			$sql = "
1559
				ALTER TABLE `$tbl`
1560
					ADD COLUMN `allow_search` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'no'
1561
						AFTER `allow_collapse`
1562
			";
1563
			return Symphony::Database()->query($sql);
1564
		}
1565
		
1566
		/**
1567
		 *
1568
		 * Drops the table needed for the settings of the field
1569
		 */
1570
		public static function deleteFieldTable()
1571
		{
1572
			$tbl = self::FIELD_TBL_NAME;
1573
			
1574
			return Symphony::Database()->query("
1575
				DROP TABLE IF EXISTS `$tbl`
1576
			");
1577
		}
1578
		
1579
		private static function removeSectionAssociation($child_field_id)
1580
		{
1581
			return Symphony::Database()->delete('tbl_sections_association', "`child_section_field_id` = {$child_field_id}");
1582
		}
1583
	}
1584