Completed
Push — dev ( da0648...23d1c0 )
by Nicolas
01:15
created

FieldEntry_Relationship::getRecursiveLevel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
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
		private $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 false;
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
				$parent_section = SectionManager::fetch($sectionId);
330
				$fields = $parent_section->fetchVisibleColumns();
331
				if (empty($fields)) {
332
					// no visible field, revert to all
333
					$fields = $parent_section->fetchFields();
334
				}
335
				$parent_field_id = current(array_keys($fields));
336
				// create association
337
				SectionManager::createSectionAssociation(
338
					$parent_section_id,
339
					$child_field_id,
340
					$parent_field_id,
341
					$this->get('show_association') == 'yes'
342
				);
343
			}
344
			
345
			// declare an array contains the field's settings
346
			$settings = array(
347
				'sections' => $this->get('sections'),
348
				'show_association' => $this->get('show_association'),
349
				'deepness' => $this->get('deepness'),
350
				'elements' => $this->get('elements'),
351
				'mode' => $this->get('mode'),
352
				'mode_table' => $this->get('mode_table'),
353
				'mode_header' => $this->get('mode_header'),
354
				'mode_footer' => $this->get('mode_footer'),
355
				'min_entries' => $this->get('min_entries'),
356
				'max_entries' => $this->get('max_entries'),
357
				'allow_new' => $this->get('allow_new'),
358
				'allow_edit' => $this->get('allow_edit'),
359
				'allow_link' => $this->get('allow_link'),
360
				'allow_delete' => $this->get('allow_delete'),
361
				'allow_collapse' => $this->get('allow_collapse'),
362
				'allow_search' => $this->get('allow_search'),
363
				'show_header' => $this->get('show_header'),
364
			);
365
366
			return FieldManager::saveSettings($id, $settings);
367
		}
368
369
		/**
370
		 *
371
		 * This function allows Fields to cleanup any additional things before it is removed
372
		 * from the section.
373
		 * @return boolean
374
		 */
375
		public function tearDown()
376
		{
377
			self::removeSectionAssociation($this->get('id'));
378
			return parent::tearDown();
379
		}
380
		
381
		/**
382
		 * Generates the where filter for searching by entry id
383
		 *
384
		 * @param string $value
385
		 * @param @optional string $col
386
		 * @param @optional boolean $andOperation
387
		 */
388
		public function generateWhereFilter($value, $col = 'd', $andOperation = true)
389
		{
390
			$junction = $andOperation ? 'AND' : 'OR';
391
			if (!$value) {
392
				return "{$junction} (`{$col}`.`entries` IS NULL)";
393
			}
394
			return " {$junction} (`{$col}`.`entries` = '{$value}' OR 
395
					`{$col}`.`entries` LIKE '{$value},%' OR 
396
					`{$col}`.`entries` LIKE '%,{$value}' OR 
397
					`{$col}`.`entries` LIKE '%,{$value},%')";
398
		}
399
400
		/**
401
		 * Fetch the number of associated entries for a particular entry id
402
		 *
403
		 * @param string $value
404
		 */
405
		public function fetchAssociatedEntryCount($value)
406
		{
407
			if (!$value) {
408
				return 0;
409
			}
410
			$join = sprintf(" INNER JOIN `tbl_entries_data_%s` AS `d` ON `e`.id = `d`.`entry_id`", $this->get('id'));
411
			$where = $this->generateWhereFilter($value);
412
			
413
			$entries = EntryManager::fetch(null, $this->get('parent_section'), null, 0, $where, $join, false, false, array());
414
			
415
			return count($entries);
416
		}
417
		
418
		public function fetchAssociatedEntrySearchValue($data, $field_id = null, $parent_entry_id = null)
419
		{
420
			return $parent_entry_id;
421
		}
422
		
423
		public function findRelatedEntries($entry_id, $parent_field_id)
424
		{
425
			$joins = '';
426
			$where = '';
427
			$this->buildDSRetrievalSQL(array($entry_id), $joins, $where, true);
428
			
429
			$entries = EntryManager::fetch(null, $this->get('parent_section'), null, 0, $where, $joins, false, false, array());
430
			
431
			return array_map(function ($e) {
432
				return $e['id'];
433
			}, $entries);
434
		}
435
		
436
		public function findParentRelatedEntries($parent_field_id, $entry_id)
437
		{
438
			$entry = $this->fetchEntry($entry_id, array($this->get('label')));
439
			
440
			if (!$entry) {
441
				return array();
442
			}
443
			
444
			return self::getEntries($entry->getData($this->get('id')));
445
		}
446
		
447
		public function prepareAssociationsDrawerXMLElement(Entry $e, array $parent_association, $prepolutate = '')
448
		{
449
			$currentSection = SectionManager::fetch($parent_association['child_section_id']);
450
			$visibleCols = $currentSection->fetchVisibleColumns();
451
			$outputFieldId = current(array_keys($visibleCols));
452
			$outputField = FieldManager::fetch($outputFieldId);
453
			
454
			$value = $outputField->prepareReadableValue($e->getData($outputFieldId), $e->get('id'), true, __('None'));
455
			
456
			$li = new XMLElement('li');
457
			$li->setAttribute('class', 'field-' . $this->get('type'));
458
			$a = new XMLElement('a', strip_tags($value));
459
			$a->setAttribute('href', SYMPHONY_URL . '/publish/' . $parent_association['handle'] . '/edit/' . $e->get('id') . '/');
460
			$li->appendChild($a);
461
462
			return $li;
463
		}
464
		
465
		/**
466
		 * @param string $joins
467
		 * @param string $where
468
		 */
469
		public function buildDSRetrievalSQL($data, &$joins, &$where, $andOperation = false)
470
		{
471
			$field_id = $this->get('id');
472
			
473
			// REGEX filtering is a special case, and will only work on the first item
474
			// in the array. You cannot specify multiple filters when REGEX is involved.
475
			if (self::isFilterRegex($data[0])) {
476
				return $this->buildRegexSQL($data[0], array('entries'), $joins, $where);
477
			}
478
			
479
			$this->_key++;
480
			
481
			$where .= ' AND (1=' . ($andOperation ? '1' : '0') . ' ';
482
			
483
			$joins .= "
484
				INNER JOIN
485
					`tbl_entries_data_{$field_id}` AS `t{$field_id}_{$this->_key}`
486
					ON (`e`.`id` = `t{$field_id}_{$this->_key}`.`entry_id`)
487
			";
488
			
489
			foreach ($data as $value) {
490
				$where .= $this->generateWhereFilter($this->cleanValue($value), "t{$field_id}_{$this->_key}", $andOperation);
491
			}
492
			
493
			$where .= ')';
494
			
495
			return true; // this tells the DS Manager that filters are OK!!
496
		}
497
498
		/* ******* EVENTS ******* */
499
500
		public function getExampleFormMarkup()
501
		{
502
			$label = Widget::Label($this->get('label'));
503
			$label->appendChild(Widget::Input('fields['.$this->get('element_name').'][entries]', null, 'hidden'));
504
505
			return $label;
506
		}
507
508
509
		/* ******* DATA SOURCE ******* */
510
		
511
		private function fetchEntry($eId, $elements = array())
512
		{
513
			$entry = EntryManager::fetch($eId, null, 1, 0, null, null, false, true, $elements, false);
514
			if (!is_array($entry) || count($entry) !== 1) {
515
				return null;
516
			}
517
			return $entry[0];
518
		}
519
		
520
		protected function fetchAllIncludableElements()
521
		{
522
			$sections = array_filter(array_map(trim, explode(self::SEPARATOR, trim($this->get('sections')))));
523
			return $allElements = array_reduce($this->sectionInfos->fetch($sections), function ($memo, $item) {
524
				return array_merge($memo, array_map(function ($field) use ($item) {
525
					return $item['handle'] . '.' . $field['handle'];
526
				}, $item['fields']));
527
			}, array());
528
		}
529
		
530
		public function fetchIncludableElements()
531
		{
532
			$label = $this->get('element_name');
533
			$elements = array_filter(array_map(trim, explode(self::SEPARATOR, trim($this->get('elements')))));
534
			$includedElements = array();
535
			if ($this->expandIncludableElements) {
536
				$includedElements[] = $label . ': *';
537
			}
538
			foreach ($elements as $elem) {
539
				$elem = trim($elem);
540
				if ($elem !== '*') {
541
					$includedElements[] = $label . ': ' . $elem;
542
				} else if ($this->expandIncludableElements) {
543
					$includedElements = array_unique(array_merge($includedElements, array_map(function ($item) use ($label) {
544
						return $label . ': ' . $item;
545
					}, $this->fetchAllIncludableElements())));
546
					break;
547
				}
548
			}
549
			if (empty($elements) && $this->expandIncludableElements) {
550
				$includedElements = array_unique(array_merge($includedElements, $this->fetchAllIncludableElements()));
551
			}
552
			return $includedElements;
553
		}
554
		
555
		/**
556
		 * Appends data into the XML tree of a Data Source
557
		 * @param $wrapper
558
		 * @param $data
559
		 */
560
		public function appendFormattedElement(XMLElement &$wrapper, $data, $encode = false, $mode = null, $entry_id = null)
561
		{
562
			if (!is_array($data) || empty($data)) {
563
				return;
564
			}
565
566
			// try to find an existing root
567
			$root = null;
568
			$newRoot = false;
569
			foreach ($wrapper->getChildren() as $xmlField) {
570
				if ($xmlField->getName() === $this->get('element_name')) {
571
					$root = $xmlField;
572
					break;
573
				}
574
			}
575
576
			// root was not found, create one
577
			if (!$root) {
578
				$root = new XMLElement($this->get('element_name'));
579
				$newRoot = true;
580
			}
581
			
582
			// selected items
583
			$entries = static::getEntries($data);
584
			
585
			// current linked entries
586
			$root->setAttribute('entries', $data['entries']);
587
			
588
			// available sections
589
			$root->setAttribute('sections', $this->get('sections'));
590
			
591
			// included elements
592
			$elements = static::parseElements($this);
593
			
594
			// DS mode
595
			if (!$mode) {
596
				$mode = '*';
597
			}
598
			
599
			$parentDeepness = General::intval($this->recursiveDeepness);
600
			$deepness = General::intval($this->get('deepness'));
601
			
602
			// both deepnesses are defined and parent restricts more
603
			if ($parentDeepness > 0 && $deepness > 0 && $parentDeepness < $deepness) {
604
				$deepness = $parentDeepness;
605
			}
606
			// parent is defined, current is not
607
			else if ($parentDeepness > 0 && $deepness < 1) {
608
				$deepness = $parentDeepness;
609
			}
610
			
611
			// cache recursive level because recursion might
612
			// change its value later on.
613
			$recursiveLevel = $this->recursiveLevel;
614
			
615
			// build entries
616
			foreach ($entries as $eId) {
617
				// try to find and existing item
618
				$item = null;
619
				$newItem = false;
620
				foreach ($root->getChildren() as $xmlItem) {
621
					if (General::intval($xmlItem->getAttribute('id')) === General::intval($eId)) {
622
						$item = $xmlItem;
623
						break;
624
					}
625
				}
626
627
				// item was not found, create one
628
				if (!$item) {
629
					$item = new XMLElement('item');
630
					// output id
631
					$item->setAttribute('id', $eId);
632
					// output recursive level
633
					$item->setAttribute('level', $recursiveLevel);
634
					$item->setAttribute('max-level', $deepness);
635
					$newItem = true;
636
				// item was found, but it is an error, so we can skip it
637
				} else if ($item->getName() === 'error') {
638
					continue;
639
				}
640
641
				// max recursion check
642
				if ($deepness < 1 || $recursiveLevel < $deepness) {
643
					// current entry, without data
644
					$entry = $this->fetchEntry($eId);
645
					
646
					// entry not found...
647
					if (!$entry || empty($entry)) {
648
						$error = new XMLElement('error');
649
						$error->setAttribute('id', $eId);
650
						$error->setValue(__('Error: entry `%s` not found', array($eId)));
651
						$root->prependChild($error);
652
						continue;
653
					}
654
					
655
					// fetch section infos
656
					$sectionId = $entry->get('section_id');
657
					$section = $this->sectionManager->fetch($sectionId);
658
					$sectionName = $section->get('handle');
659
					// cache fields info
660
					if (!isset($section->er_field_cache)) {
661
						$section->er_field_cache = $section->fetchFields();
662
					}
663
					
664
					// set section related attributes
665
					$item->setAttribute('section-id', $sectionId);
666
					$item->setAttribute('section', $sectionName);
667
					
668
					// Get the valid elements for this section only
669
					$validElements = $elements[$sectionName];
670
					
671
					// adjust the mode for the current section
672
					$curMode = $mode;
673
					
674
					// remove section name from current mode, i.e sectionName.field
675
					if (preg_match('/^(' . $sectionName . '\.)(.*)$/sU', $curMode)) {
676
						$curMode = preg_replace('/^' . $sectionName . '\./sU', '', $curMode);
677
					}
678
					// remove section name from current mode, i.e sectionName
679
					else if (preg_match('/^(' . $sectionName . ')$/sU', $curMode)) {
680
						$curMode = '*';
681
					}
682
					// section name was not found in mode
683
					else if ($curMode != '*') {
684
						// mode forbids this section
685
						$validElements = null;
686
					}
687
					
688
					// this section is not selected, bail out
689
					if (!is_array($validElements)) {
690
						$item->setAttribute('forbidden-by', $curMode);
691
						if ($newItem) {
692
							$root->appendChild($item);
693
						}
694
						continue;
695
					} else {
696
						$item->setAttribute('forbidden-by', null);
697
					}
698
					
699
					// selected fields for fetching
700
					$sectionElements = array();
701
					
702
					// everything is allowed
703
					if (in_array('*', $validElements)) {
704
						if ($curMode !== '*') {
705
							// get only the mode
706
							$sectionElements = array($curMode);
707
						}
708
						else {
709
							// setting null = get all
710
							$sectionElements = null;
711
						}
712
					}
713
					// only use valid elements
714
					else {
715
						if ($curMode !== '*') {
716
							// is this field allowed ?
717
							if (self::isFieldIncluded($curMode, $validElements)) {
718
								// get only the mode
719
								$sectionElements = array($curMode);
720
							}
721
							else {
722
								// $curMode selects something outside of
723
								// the valid elements: select nothing
724
								$sectionElements = array();
725
							}
726
						}
727
						else {
728
							// use field's valid elements
729
							$sectionElements = $validElements;
730
						}
731
					}
732
					
733
					if (is_array($sectionElements) && empty($sectionElements)) {
734
						$item->setAttribute('selection-empty', 'yes');
735
						$item->setAttribute('forbidden-by', $curMode);
736
						if ($newItem) {
737
							$root->appendChild($item);
738
						}
739
						continue;
740
					} else {
741
						$item->setAttribute('selection-empty', null);
742
						$item->setAttribute('forbidden-by', null);
743
					}
744
					
745
					// current entry again, but with data and the allowed schema
746
					$entry = $this->fetchEntry($eId, $sectionElements);
747
					
748
					// cache the entry data
749
					$entryData = $entry->getData();
750
					
751
					// for each field returned for this entry...
752
					foreach ($entryData as $fieldId => $data) {
753
						$filteredData = array_filter($data, function ($value) {
754
							return $value != null;
755
						});
756
						
757
						if (empty($filteredData)) {
758
							continue;
759
						}
760
						
761
						$field = $section->er_field_cache[$fieldId];
762
						$fieldName = $field->get('element_name');
763
						$fieldCurMode = self::extractMode($fieldName, $curMode);
764
						
765
						$parentIncludableElement = self::getSectionElementName($fieldName, $validElements);
766
						$parentIncludableElementMode = self::extractMode($fieldName, $parentIncludableElement);
767
						
768
						// Special treatments for ERF
769
						if ($field instanceof FieldEntry_relationship) {
770
							// Increment recursive level
771
							$field->recursiveLevel = $recursiveLevel + 1;
772
							$field->recursiveDeepness = $deepness;
773
						}
774
						
775
						$submodes = null;
776
						if ($parentIncludableElementMode == null) {
777
							if ($fieldCurMode == null) {
778
								$submodes = null;
779
							}
780
							else {
781
								$submodes = array($fieldCurMode);
782
							}
783
							$item->setAttribute('selection-mode-empty', null);
784
						}
785
						else {
786
							if ($fieldCurMode == null || $fieldCurMode == $parentIncludableElementMode) {
787
								$submodes = array($parentIncludableElementMode);
788
							}
789
							else {
790
								$item->setAttribute('selection-mode-empty', 'yes');
791
								$submodes = array();
792
							}
793
							$item->setAttribute('selection-mode-empty', null);
794
						}
795
						
796
						// current selection does not specify a mode
797
						if ($submodes == null) {
798
							if ($field instanceof FieldEntry_Relationship) {
799
								$field->expandIncludableElements = false;
800
							}
801
							$submodes = array_map(function ($fieldIncludableElement) use ($fieldName) {
802
								return FieldEntry_relationship::extractMode($fieldName, $fieldIncludableElement);
803
							}, $field->fetchIncludableElements());
804
							if ($field instanceof FieldEntry_Relationship) {
805
								$field->expandIncludableElements = true;
806
							}
807
						}
808
						
809
						foreach ($submodes as $submode) {
810
							$field->appendFormattedElement($item, $data, $encode, $submode, $eId);
811
						}
812
					}
813
					// output current mode
814
					$item->setAttribute('matched-element', $curMode);
815
					// no field selected
816
					if (is_array($sectionElements) && empty($sectionElements)) {
817
						$item->setAttribute('empty-selection', 'yes');
818
					}
819
				} // end max recursion check
820
821
				if ($newItem) {
822
					// append item when done
823
					$root->appendChild($item);
824
				}
825
			} // end each entries
826
827
			if ($newRoot) {
828
				// output mode for this field
829
				$root->setAttribute('data-source-mode', $mode);
830
				$root->setAttribute('field-included-elements', $this->get('elements'));
831
				
832
				// add all our data to the wrapper;
833
				$wrapper->appendChild($root);
834
			} else {
835
				$root->setAttribute('data-source-mode', $root->getAttribute('data-source-mode') . ', ' . $mode);
836
			}
837
838
			// clean up
839
			$this->recursiveLevel = 1;
840
			$this->recursiveDeepness = null;
841
		}
842
843
		public function getParameterPoolValue(array $data, $entry_id = null)
844
		{
845
			if(!is_array($data) || empty($data)) return;
846
			return static::getEntries($data);
847
		}
848
849
		/* ********* Utils *********** */
850
		
851
		/**
852
		 * Return true if $fieldName is allowed in $sectionElements
853
		 * @param string $fieldName
854
		 * @param string $sectionElements
855
		 * @return bool
856
		 */
857
		public static function isFieldIncluded($fieldName, $sectionElements)
858
		{
859
			return self::getSectionElementName($fieldName, $sectionElements) !== null;
860
		}
861
862
		public static function getSectionElementName($fieldName, $sectionElements)
863
		{
864
			if (is_array($sectionElements)) {
865
				foreach ($sectionElements as $element) {
866
					if ($element == '*') {
867
						return $fieldName;
868
					}
869
					if ($fieldName == $element || preg_match('/^' . $fieldName . '\s*:/sU', $element)) {
870
						return $element;
871
					}
872
				}
873
			}
874
			return null;
875
		}
876
		
877
		public static function parseElements($field)
878
		{
879
			$elements = array();
880
			$exElements = array_map(trim, explode(self::SEPARATOR, $field->get('elements')));
881
			
882
			if (in_array('*', $exElements)) {
883
				$sections = array_map(trim, explode(self::SEPARATOR, $field->get('sections')));
884
				$sections = SectionManager::fetch($sections);
885
				return array_reduce($sections, function ($result, $section) {
886
					$result[$section->get('handle')] = array('*');
887
					return $result;
888
				}, array());
889
			}
890
			
891
			foreach ($exElements as $value) {
892
				if (!$value) {
893
					continue;
894
				}
895
				// sectionName.fieldName or sectionName.*
896
				$parts = array_map(trim, explode('.', $value));
897
				// first time seeing this section
898
				if (!isset($elements[$parts[0]])) {
899
					$elements[$parts[0]] = array();
900
				}
901
				// we have a value after the dot
902
				if (isset($parts[1]) && !!$parts[1]) {
903
					$elements[$parts[0]][] = $parts[1];
904
				}
905
				// sectionName only
906
				else if (!isset($parts[1])) {
907
					$elements[$parts[0]][] = '*';
908
				}
909
			}
910
			
911
			return $elements;
912
		}
913
914
		public static function extractMode($fieldName, $mode)
915
		{
916
			$pattern = '/^' . $fieldName . '\s*:\s*/s';
917
			if (!preg_match($pattern, $mode)) {
918
				return null;
919
			}
920
			$mode = preg_replace($pattern, '', $mode, 1);
921
			if ($mode === '*') {
922
				return null;
923
			}
924
			return $mode;
925
		}
926
927
		private function buildSectionSelect($name)
928
		{
929
			$sections = SectionManager::fetch();
930
			$options = array();
931
			$selectedSections = $this->getSelectedSectionsArray();
932
			
933 View Code Duplication
			foreach ($sections as $section) {
934
				$driver = $section->get('id');
935
				$selected = in_array($driver, $selectedSections);
936
				$options[] = array($driver, $selected, General::sanitize($section->get('name')));
937
			}
938
			
939
			return Widget::Select($name, $options, array('multiple' => 'multiple'));
940
		}
941
942 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...
943
		{
944
			$name = $this->createSettingsFieldName('sections', true);
945
946
			$input = $this->buildSectionSelect($name);
947
			$input->setAttribute('class', 'entry_relationship-sections');
948
949
			$label = Widget::Label();
950
			$label->setAttribute('class', 'column');
951
952
			$label->setValue(__('Available sections %s', array($input->generate())));
953
954
			$wrapper->appendChild($label);
955
		}
956
957
		private function createEntriesHiddenInput($data)
958
		{
959
			$hidden = new XMLElement('input', null, array(
960
				'type' => 'hidden',
961
				'name' => $this->createPublishFieldName('entries'),
962
				'value' => $data['entries']
963
			));
964
			
965
			return $hidden;
966
		}
967
		
968
		private function createActionBarMenu($sections)
969
		{
970
			$wrap = new XMLElement('div');
971
			$actionBar = '';
972
			$modeFooter = $this->get('mode_footer');
973
			if ($modeFooter) {
974
				$section = $this->sectionManager->fetch($this->get('parent_section'));
975
				$actionBar = ERFXSLTUTilities::processXSLT($this, null, $section->get('handle'), null, 'mode_footer', isset($_REQUEST['debug']), 'field');
976
			}
977
			if (empty($actionBar)) {
978
				$fieldset = new XMLElement('fieldset');
979
				$fieldset->setAttribute('class', 'single');
980
				if ($this->is('allow_search')) {
981
					$searchWrap = new XMLElement('div');
982
					$searchWrap->setAttribute('data-interactive', 'data-interactive');
983
					$searchWrap->setAttribute('class', 'search');
984
					$searchInput = Widget::Input('', null, 'text', array(
985
						'class' => 'search',
986
						'data-search' => '',
987
						'placeholder' => __('Search for entries')
988
					));
989
					$searchWrap->appendChild($searchInput);
990
					$searchSuggestions = new XMLElement('ul');
991
					$searchSuggestions->setAttribute('class', 'suggestions');
992
					$searchWrap->appendChild($searchSuggestions);
993
					$fieldset->appendChild($searchWrap);
994
				}
995
				
996
				if ($this->is('allow_new') || $this->is('allow_link') || $this->is('allow_search')) {
997
					$selectWrap = new XMLElement('div');
998
					$selectWrap->appendChild(new XMLElement('span', __('Related section: '), array('class' => 'sections-selection')));
999
					$options = array();
1000
					foreach ($sections as $section) {
1001
						$options[] = array($section->get('handle'), false, $section->get('name'));
1002
					}
1003
					$select = Widget::Select('', $options, array('class' => 'sections sections-selection'));
1004
					$selectWrap->appendChild($select);
1005
					$fieldset->appendChild($selectWrap);
1006
				}
1007 View Code Duplication
				if ($this->is('allow_new')) {
1008
					$fieldset->appendChild(new XMLElement('button', __('Create new'), array(
1009
						'type' => 'button',
1010
						'class' => 'create',
1011
						'data-create' => '',
1012
					)));
1013
				}
1014 View Code Duplication
				if ($this->is('allow_link')) {
1015
					$fieldset->appendChild(new XMLElement('button', __('Link to entry'), array(
1016
						'type' => 'button',
1017
						'class' => 'link',
1018
						'data-link' => '',
1019
					)));
1020
				}
1021
				$wrap->appendChild($fieldset);
1022
			}
1023
			else {
1024
				$wrap->setValue($actionBar);
1025
			}
1026
			
1027
			return $wrap;
1028
		}
1029
1030
		/* ********* UI *********** */
1031
		
1032
		/**
1033
		 *
1034
		 * Builds the UI for the field's settings when creating/editing a section
1035
		 * @param XMLElement $wrapper
1036
		 * @param array $errors
1037
		 */
1038
		public function displaySettingsPanel(XMLElement &$wrapper, $errors=null)
1039
		{
1040
			/* first line, label and such */
1041
			parent::displaySettingsPanel($wrapper, $errors);
1042
			
1043
			// sections
1044
			$sections = new XMLElement('fieldset');
1045
			
1046
			$this->appendSelectionSelect($sections);
1047 View Code Duplication
			if (is_array($errors) && isset($errors['sections'])) {
1048
				$sections = Widget::Error($sections, $errors['sections']);
1049
			}
1050
			$wrapper->appendChild($sections);
1051
			
1052
			// elements
1053
			$elements = new XMLElement('div');
1054
			$element = Widget::Label();
1055
			$element->setValue(__('Included elements in Data Sources and Backend Templates'));
1056
			$element->setAttribute('class', 'column');
1057
			$element->appendChild(Widget::Input($this->createSettingsFieldName('elements'), $this->get('elements'), 'text', array(
1058
				'class' => 'entry_relationship-elements'
1059
			)));
1060
			$elements->appendChild($element);
1061
			$elements_choices = new XMLElement('ul', null, array('class' => 'tags singular entry_relationship-field-choices'));
1062
			$elements->appendChild($elements_choices);
1063
			$wrapper->appendChild($elements);
1064
			
1065
			// limit entries
1066
			$limits = new XMLElement('fieldset');
1067
			$limits->appendChild(new XMLElement('legend', __('Limits')));
1068
			$limits_cols = new XMLElement('div');
1069
			$limits_cols->setAttribute('class', 'three columns');
1070
			// min
1071
			$limit_min = Widget::Label();
1072
			$limit_min->setValue(__('Minimum count of entries in this field'));
1073
			$limit_min->setAttribute('class', 'column');
1074
			$limit_min->appendChild(Widget::Input($this->createSettingsFieldName('min_entries'), $this->get('min_entries'), 'number', array(
1075
				'min' => 0,
1076
				'max' => 99999
1077
			)));
1078
			$limits_cols->appendChild($limit_min);
1079
			// max
1080
			$limit_max = Widget::Label();
1081
			$limit_max->setValue(__('Maximum count of entries in this field'));
1082
			$limit_max->setAttribute('class', 'column');
1083
			$limit_max->appendChild(Widget::Input($this->createSettingsFieldName('max_entries'), $this->get('max_entries'), 'number', array(
1084
				'min' => 0,
1085
				'max' => 99999
1086
			)));
1087
			$limits_cols->appendChild($limit_max);
1088
			
1089
			// deepness
1090
			$deepness = Widget::Label();
1091
			$deepness->setValue(__('Maximum level of recursion in Data Sources'));
1092
			$deepness->setAttribute('class', 'column');
1093
			$deepness->appendChild(Widget::Input($this->createSettingsFieldName('deepness'), $this->get('deepness'), 'number', array(
1094
				'min' => 0,
1095
				'max' => 99
1096
			)));
1097
			$limits_cols->appendChild($deepness);
1098
			$limits->appendChild($limits_cols);
1099
			$wrapper->appendChild($limits);
1100
			
1101
			// xsl
1102
			$xsl = new XMLElement('fieldset');
1103
			$xsl->appendChild(new XMLElement('legend', __('Backend XSL templates options')));
1104
			$xsl_cols = new XMLElement('div');
1105
			$xsl_cols->setAttribute('class', 'four columns');
1106
			
1107
			// xsl mode
1108
			$xslmode = Widget::Label();
1109
			$xslmode->setValue(__('XSL mode for entries content template'));
1110
			$xslmode->setAttribute('class', 'column');
1111
			$xslmode->appendChild(Widget::Input($this->createSettingsFieldName('mode'), $this->get('mode'), 'text'));
1112
			$xsl_cols->appendChild($xslmode);
1113
			// xsl header mode
1114
			$xslmodetable = Widget::Label();
1115
			$xslmodetable->setValue(__('XSL mode for entries header template'));
1116
			$xslmodetable->setAttribute('class', 'column');
1117
			$xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_header'), $this->get('mode_header'), 'text'));
1118
			$xsl_cols->appendChild($xslmodetable);
1119
			// xsl table mode
1120
			$xslmodetable = Widget::Label();
1121
			$xslmodetable->setValue(__('XSL mode for publish table value'));
1122
			$xslmodetable->setAttribute('class', 'column');
1123
			$xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_table'), $this->get('mode_table'), 'text'));
1124
			$xsl_cols->appendChild($xslmodetable);
1125
			// xsl action bar mode
1126
			$xslmodetable = Widget::Label();
1127
			$xslmodetable->setValue(__('XSL mode for publish action bar'));
1128
			$xslmodetable->setAttribute('class', 'column');
1129
			$xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_footer'), $this->get('mode_footer'), 'text'));
1130
			$xsl_cols->appendChild($xslmodetable);
1131
			
1132
			$xsl->appendChild($xsl_cols);
1133
			$wrapper->appendChild($xsl);
1134
			
1135
			// permissions
1136
			$permissions = new XMLElement('fieldset');
1137
			$permissions->appendChild(new XMLElement('legend', __('Permissions')));
1138
			$permissions_cols = new XMLElement('div');
1139
			$permissions_cols->setAttribute('class', 'four columns');
1140
			$permissions_cols->appendChild($this->createCheckbox('allow_new', 'Show new button'));
1141
			$permissions_cols->appendChild($this->createCheckbox('allow_edit', 'Show edit button'));
1142
			$permissions_cols->appendChild($this->createCheckbox('allow_link', 'Show link button'));
1143
			$permissions_cols->appendChild($this->createCheckbox('allow_delete', 'Show delete button'));
1144
			$permissions->appendChild($permissions_cols);
1145
			$wrapper->appendChild($permissions);
1146
			
1147
			// display options
1148
			$display = new XMLElement('fieldset');
1149
			$display->appendChild(new XMLElement('legend', __('Display options')));
1150
			$display_cols = new XMLElement('div');
1151
			$display_cols->setAttribute('class', 'four columns');
1152
			$display_cols->appendChild($this->createCheckbox('allow_collapse', 'Allow content collapsing'));
1153
			$display_cols->appendChild($this->createCheckbox('allow_search', 'Allow search linking'));
1154
			$display_cols->appendChild($this->createCheckbox('show_header', 'Show the header box before entries templates'));
1155
			$display->appendChild($display_cols);
1156
			$wrapper->appendChild($display);
1157
			
1158
			// assoc
1159
			$assoc = new XMLElement('fieldset');
1160
			$assoc->appendChild(new XMLElement('legend', __('Associations')));
1161
			$assoc_cols = new XMLElement('div');
1162
			$assoc_cols->setAttribute('class', 'three columns');
1163
			$this->appendShowAssociationCheckbox($assoc_cols);
1164
			$assoc->appendChild($assoc_cols);
1165
			$wrapper->appendChild($assoc);
1166
			
1167
			// footer
1168
			$this->appendStatusFooter($wrapper);
1169
		}
1170
1171
		/**
1172
		 *
1173
		 * Builds the UI for the publish page
1174
		 * @param XMLElement $wrapper
1175
		 * @param mixed $data
1176
		 * @param mixed $flagWithError
1177
		 * @param string $fieldnamePrefix
1178
		 * @param string $fieldnamePostfix
1179
		 */
1180
		public function displayPublishPanel(XMLElement &$wrapper, $data = null, $flagWithError = null, $fieldnamePrefix = null, $fieldnamePostfix = null, $entry_id = null)
1181
		{
1182
			$entriesId = array();
1183
			$sectionsId = $this->getSelectedSectionsArray();
1184
			
1185
			if ($data['entries'] != null) {
1186
				$entriesId = static::getEntries($data);
1187
			}
1188
			
1189
			$sectionsId = array_map(array('General', 'intval'), $sectionsId);
1190
			$sections = SectionManager::fetch($sectionsId);
1191
			
1192
			$label = Widget::Label($this->get('label'));
1193
			$notes = '';
1194
			
1195
			// min note
1196
			if ($this->getInt('min_entries') > 0) {
1197
				$notes .= __('Minimum number of entries: <b>%s</b>. ', array($this->get('min_entries')));
1198
			}
1199
			// max note
1200
			if ($this->getInt('max_entries') > 0) {
1201
				$notes .= __('Maximum number of entries: <b>%s</b>. ', array($this->get('max_entries')));
1202
			}
1203
			// not required note
1204
			if (!$this->isRequired()) {
1205
				$notes .= __('Optional');
1206
			}
1207
			// append notes
1208
			if ($notes) {
1209
				$label->appendChild(new XMLElement('i', $notes));
1210
			}
1211
			
1212
			// label error management
1213
			if ($flagWithError != null) {
1214
				$wrapper->appendChild(Widget::Error($label, $flagWithError));
1215
			} else {
1216
				$wrapper->appendChild($label);
1217
			}
1218
			
1219
			$wrapper->appendChild($this->createEntriesList($entriesId));
1220
			$wrapper->appendChild($this->createActionBarMenu($sections));
1221
			$wrapper->appendChild($this->createEntriesHiddenInput($data));
1222
			$wrapper->setAttribute('data-value', $data['entries']);
1223
			$wrapper->setAttribute('data-field-id', $this->get('id'));
1224
			$wrapper->setAttribute('data-field-label', $this->get('label'));
1225
			$wrapper->setAttribute('data-min', $this->get('min_entries'));
1226
			$wrapper->setAttribute('data-max', $this->get('max_entries'));
1227
			$wrapper->setAttribute('data-required', $this->get('required'));
1228
			if (isset($_REQUEST['debug'])) {
1229
				$wrapper->setAttribute('data-debug', true);
1230
			}
1231
		}
1232
1233
		/**
1234
		 * @param integer $count
1235
		 */
1236
		private static function formatCount($count)
1237
		{
1238
			if ($count == 0) {
1239
				return __('No item');
1240
			} else if ($count == 1) {
1241
				return __('1 item');
1242
			}
1243
			return __('%s items', array($count));
1244
		}
1245
1246
		/**
1247
		 *
1248
		 * Return a plain text representation of the field's data
1249
		 * @param array $data
1250
		 * @param int $entry_id
1251
		 */
1252
		public function prepareTextValue($data, $entry_id = null)
1253
		{
1254
			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...
1255
				return '';
1256
			}
1257
			return $data['entries'];
1258
		}
1259
1260
		/**
1261
		 * Format this field value for display as readable text value.
1262
		 *
1263
		 * @param array $data
1264
		 *  an associative array of data for this string. At minimum this requires a
1265
		 *  key of 'value'.
1266
		 * @param integer $entry_id (optional)
1267
		 *  An option entry ID for more intelligent processing. Defaults to null.
1268
		 * @param string $defaultValue (optional)
1269
		 *  The value to use when no plain text representation of the field's data
1270
		 *  can be made. Defaults to null.
1271
		 * @return string
1272
		 *  the readable text summary of the values of this field instance.
1273
		 */
1274
		public function prepareReadableValue($data, $entry_id = null, $truncate = false, $defaultValue = 'None')
1275
		{
1276
			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...
1277
				return __($defaultValue);
1278
			}
1279
			$entries = static::getEntries($data);
1280
			$realEntries = array();
1281
			foreach ($entries as $entryId) {
1282
				$e = EntryManager::fetch($entryId);
1283
				if (is_array($e) && !empty($e)) {
1284
					$realEntries = array_merge($realEntries, $e);
1285
				}
1286
			}
1287
			$count = count($entries);
1288
			$realCount = count($realEntries);
1289
			if ($count === $realCount) {
1290
				return self::formatCount($count);
1291
			}
1292
			return self::formatCount($realCount) . ' (' . self::formatCount($count - $realCount) . ' not found)';
1293
		}
1294
1295
		/**
1296
		 * Format this field value for display in the publish index tables.
1297
		 *
1298
		 * @param array $data
1299
		 *  an associative array of data for this string. At minimum this requires a
1300
		 *  key of 'value'.
1301
		 * @param XMLElement $link (optional)
1302
		 *  an XML link structure to append the content of this to provided it is not
1303
		 *  null. it defaults to null.
1304
		 * @param integer $entry_id (optional)
1305
		 *  An option entry ID for more intelligent processing. defaults to null
1306
		 * @return string
1307
		 *  the formatted string summary of the values of this field instance.
1308
		 */
1309
		public function prepareTableValue($data, XMLElement $link = null, $entry_id = null)
1310
		{
1311
			$value = $this->prepareReadableValue($data, $entry_id, false, __('None'));
1312
1313
			if ($link) {
1314
				$link->setValue($value);
1315
				return $link->generate();
1316
			}
1317
			else if ($entry_id != null && $this->get('mode_table')) {
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...
1318
				$entries = static::getEntries($data);
1319
				$cellcontent = '';
1320
				foreach ($entries as $child_entry_id) {
1321
					$entry = $this->entryManager->fetch($child_entry_id);
1322
					if (!$entry || !is_array($entry) || empty($entry)) {
1323
						continue;
1324
					}
1325
					reset($entry);
1326
					$entry = current($entry);
1327
					$section = $this->sectionManager->fetch($entry->get('section_id'));
1328
					$content = ERFXSLTUTilities::processXSLT($this, $entry, $section->get('handle'), $section->fetchFields(), 'mode_table', isset($_REQUEST['debug']));
1329
					if ($content) {
1330
						$cellcontent .= $content;
1331
					}
1332
				}
1333
				
1334
				if (General::strlen(trim($cellcontent))) {
1335
					return $cellcontent;
1336
				}
1337
			}
1338
1339
			return $value;
1340
		}
1341
1342
		/* ********* SQL Data Definition ************* */
1343
1344
		/**
1345
		 *
1346
		 * Creates table needed for entries of individual fields
1347
		 */
1348
		public function createTable()
1349
		{
1350
			$id = $this->get('id');
1351
1352
			return Symphony::Database()->query("
1353
				CREATE TABLE `tbl_entries_data_$id` (
1354
					`id` int(11) 		unsigned NOT NULL AUTO_INCREMENT,
1355
					`entry_id` 			int(11) unsigned NOT NULL,
1356
					`entries` 			text COLLATE utf8_unicode_ci NULL,
1357
					PRIMARY KEY  (`id`),
1358
					UNIQUE KEY `entry_id` (`entry_id`)
1359
				) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1360
			");
1361
		}
1362
1363
		/**
1364
		 * Creates the table needed for the settings of the field
1365
		 */
1366
		public static function createFieldTable()
1367
		{
1368
			$tbl = self::FIELD_TBL_NAME;
1369
1370
			return Symphony::Database()->query("
1371
				CREATE TABLE IF NOT EXISTS `$tbl` (
1372
					`id` 				int(11) unsigned NOT NULL AUTO_INCREMENT,
1373
					`field_id` 			int(11) unsigned NOT NULL,
1374
					`sections`			varchar(2048) NULL COLLATE utf8_unicode_ci,
1375
					`show_association` 	enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1376
					`deepness` 			int(2) unsigned NULL,
1377
					`elements` 			text COLLATE utf8_unicode_ci NULL,
1378
					`mode`				varchar(50) NULL COLLATE utf8_unicode_ci,
1379
					`mode_table`		varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL,
1380
					`mode_header`		varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL,
1381
					`mode_footer`		varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL,
1382
					`min_entries`		int(5) unsigned NULL,
1383
					`max_entries`		int(5) unsigned NULL,
1384
					`allow_edit` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1385
					`allow_new` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1386
					`allow_link` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1387
					`allow_delete` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'no',
1388
					`allow_collapse` 	enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1389
					`allow_search` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'no',
1390
					`show_header` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1391
					PRIMARY KEY (`id`),
1392
					UNIQUE KEY `field_id` (`field_id`)
1393
				) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1394
			");
1395
		}
1396
		
1397
		public static function update_102()
1398
		{
1399
			$tbl = self::FIELD_TBL_NAME;
1400
			$sql = "
1401
				ALTER TABLE `$tbl`
1402
					ADD COLUMN `allow_edit` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1403
					ADD COLUMN `allow_new` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1404
					ADD COLUMN `allow_link` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes'
1405
					AFTER `max_entries`
1406
			";
1407
			$addColumns = Symphony::Database()->query($sql);
1408
			if (!$addColumns) {
1409
				return false;
1410
			}
1411
1412
			$fields = FieldManager::fetch(null, null, null, 'id', 'entry_relationship');
1413
			if (!empty($fields) && is_array($fields)) {
1414
				foreach ($fields as $fieldId => $field) {
1415
					$sql = "ALTER TABLE `tbl_entries_data_$fieldId` MODIFY `entries` TEXT";
1416
					if (!Symphony::Database()->query($sql)) {
1417
						throw new Exception(__('Could not update table `tbl_entries_data_%s`.', array($fieldId)));
1418
					}
1419
				}
1420
			}
1421
			return true;
1422
		}
1423
		
1424
		public static function update_103()
1425
		{
1426
			$tbl = self::FIELD_TBL_NAME;
1427
			$sql = "
1428
				ALTER TABLE `$tbl`
1429
					ADD COLUMN `allow_delete` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'no'
1430
						AFTER `allow_link`
1431
			";
1432
			return Symphony::Database()->query($sql);
1433
		}
1434
		
1435
		public static function update_200()
1436
		{
1437
			$tbl = self::FIELD_TBL_NAME;
1438
			$sql = "
1439
				ALTER TABLE `$tbl`
1440
					ADD COLUMN `allow_collapse` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes'
1441
						AFTER `allow_delete`,
1442
					ADD COLUMN `mode_table` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
1443
						AFTER `mode`,
1444
					ADD COLUMN `mode_header` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
1445
						AFTER `mode_table`,
1446
					ADD COLUMN `show_header` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes'
1447
						AFTER `allow_collapse`,
1448
					ADD COLUMN `mode_footer` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
1449
						AFTER `mode_header`,
1450
					CHANGE `sections` `sections` varchar(2048) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
1451
					CHANGE `elements` `elements` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL
1452
			";
1453
			return Symphony::Database()->query($sql);
1454
		}
1455
		
1456
		public static function update_2008()
1457
		{
1458
			$tbl = self::FIELD_TBL_NAME;
1459
			$sql = "
1460
				ALTER TABLE `$tbl`
1461
					ADD COLUMN `allow_search` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'no'
1462
						AFTER `allow_collapse`
1463
			";
1464
			return Symphony::Database()->query($sql);
1465
		}
1466
		
1467
		/**
1468
		 *
1469
		 * Drops the table needed for the settings of the field
1470
		 */
1471
		public static function deleteFieldTable()
1472
		{
1473
			$tbl = self::FIELD_TBL_NAME;
1474
			
1475
			return Symphony::Database()->query("
1476
				DROP TABLE IF EXISTS `$tbl`
1477
			");
1478
		}
1479
		
1480
		private static function removeSectionAssociation($child_field_id)
1481
		{
1482
			return Symphony::Database()->delete('tbl_sections_association', "`child_section_field_id` = {$child_field_id}");
1483
		}
1484
	}
1485