Completed
Push — dev ( 90345e...c17917 )
by Nicolas
01:20
created

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