Completed
Push — dev ( 1d9d4d...56a1fc )
by Nicolas
01:09
created

FieldEntry_Relationship::update_103()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
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
	
13
	/**
14
	 *
15
	 * Field class that will represent relationships between entries
16
	 * @author Deux Huit Huit
17
	 *
18
	 */
19
	class FieldEntry_Relationship extends FieldRelationship
20
	{
21
		/**
22
		 *
23
		 * Name of the field table
24
		 *  @var string
25
		 */
26
		const FIELD_TBL_NAME = 'tbl_fields_entry_relationship';
27
		
28
		/**
29
		 *
30
		 * Current recursive level of output
31
		 *  @var int
32
		 */
33
		protected $recursiveLevel = 1;
34
		public function getRecursiveLevel()
35
		{
36
			return $this->recursiveLevel;
37
		}
38
		public function incrementRecursiveLevel($inc = 1)
39
		{
40
			return $this->recursiveLevel += $inc;
41
		}
42
		
43
		/**
44
		 *
45
		 * Parent's maximum recursive level of output
46
		 *  @var int
47
		 */
48
		protected $recursiveDeepness = null;
49
		public function getRecursiveDeepness()
50
		{
51
			return $this->recursiveDeepness;
52
		}
53
		public function setRecursiveDeepness($deepness)
54
		{
55
			return $this->recursiveDeepness = $deepness;
56
		}
57
		
58
		/* Cacheable Managers */
59
		private $sectionManager;
60
		private $entryManager;
61
		
62
		/**
63
		 *
64
		 * Constructor for the Entry_Relationship Field object
65
		 */
66
		public function __construct()
67
		{
68
			// call the parent constructor
69
			parent::__construct();
70
			// set the name of the field
71
			$this->_name = __('Entry Relationship');
72
			// permits to make it required
73
			$this->_required = true;
74
			// permits the make it show in the table columns
75
			$this->_showcolumn = true;
76
			// permits association
77
			$this->_showassociation = true;
78
			// current recursive level
79
			$this->recursiveLevel = 1;
80
			// parent's maximum recursive level of output
81
			$this->recursiveDeepness = null;
82
			// set as orderable
83
			$this->orderable = true;
84
			// set as not required by default
85
			$this->set('required', 'no');
86
			// show association by default
87
			$this->set('show_association', 'yes');
88
			// no sections
89
			$this->set('sections', null);
90
			// no max deepness
91
			$this->set('deepness', null);
92
			// no included elements
93
			$this->set('elements', null);
94
			// no modes
95
			$this->set('mode', null);
96
			$this->set('mode_table', null);
97
			$this->set('mode_header', null);
98
			$this->set('mode_footer', null);
99
			// no limit
100
			$this->set('min_entries', null);
101
			$this->set('max_entries', null);
102
			// all permissions
103
			$this->set('allow_new', 'yes');
104
			$this->set('allow_edit', 'yes');
105
			$this->set('allow_link', 'yes');
106
			$this->set('allow_delete', 'no');
107
			// display options
108
			$this->set('allow_collapse', 'yes');
109
			$this->set('show_header', 'yes');
110
			$this->sectionManager = new CacheableFetch('SectionManager');
111
			$this->entryManager = new CacheableFetch('EntryManager');
112
		}
113
114
		public function isSortable()
115
		{
116
			return false;
117
		}
118
119
		public function canFilter()
120
		{
121
			return true;
122
		}
123
		
124
		public function canPublishFilter()
125
		{
126
			return false;
127
		}
128
129
		public function canImport()
130
		{
131
			return false;
132
		}
133
134
		public function canPrePopulate()
135
		{
136
			return false;
137
		}
138
		
139
		public function mustBeUnique()
140
		{
141
			return false;
142
		}
143
144
		public function allowDatasourceOutputGrouping()
145
		{
146
			return false;
147
		}
148
149
		public function requiresSQLGrouping()
150
		{
151
			return false;
152
		}
153
154
		public function allowDatasourceParamOutput()
155
		{
156
			return true;
157
		}
158
159
		/* ********** INPUT AND FIELD *********** */
160
161
162
		/**
163
		 * 
164
		 * Validates input
165
		 * Called before <code>processRawFieldData</code>
166
		 * @param $data
167
		 * @param $message
168
		 * @param $entry_id
169
		 */
170
		public function checkPostFieldData($data, &$message, $entry_id=null)
171
		{
172
			$message = null;
173
			$required = $this->isRequired();
174
			
175
			if ($required && (!is_array($data) || count($data) == 0 || strlen($data['entries']) < 1)) {
176
				$message = __("'%s' is a required field.", array($this->get('label')));
177
				return self::__MISSING_FIELDS__;
178
			}
179
			
180
			$entries = $data['entries'];
181
			
182
			if (!is_array($entries)) {
183
				$entries = static::getEntries($data);
184
			}
185
			
186
			// enforce limits only if required or it contains data
187
			if ($required || count($entries) > 0) {
188
				if ($this->getInt('min_entries') > 0 && $this->getInt('min_entries') > count($entries)) {
189
					$message = __("'%s' requires a minimum of %s entries.", array($this->get('label'), $this->getInt('min_entries')));
190
					return self::__INVALID_FIELDS__;
191
				} else if ($this->getInt('max_entries') > 0 && $this->getInt('max_entries') < count($entries)) {
192
					$message = __("'%s' can not contains more than %s entries.", array($this->get('label'), $this->getInt('max_entries')));
193
					return self::__INVALID_FIELDS__;
194
				}
195
			}
196
			
197
			return self::__OK__;
198
		}
199
200
201
		/**
202
		 *
203
		 * Process data before saving into database.
204
		 *
205
		 * @param array $data
206
		 * @param int $status
207
		 * @param boolean $simulate
208
		 * @param int $entry_id
209
		 *
210
		 * @return Array - data to be inserted into DB
211
		 */
212
		public function processRawFieldData($data, &$status, &$message = null, $simulate = false, $entry_id = null)
213
		{
214
			$status = self::__OK__;
215
			$entries = null;
216
			
217
			if (!is_array($data) && !is_string($data)) {
218
				return null;
219
			}
220
			
221
			if (isset($data['entries'])) {
222
				$entries = $data['entries'];
223
			}
224
			else if (is_string($data)) {
225
				$entries = $data;
226
			}
227
			
228
			$row = array(
229
				'entries' => $entries
230
			);
231
			
232
			// return row
233
			return $row;
234
		}
235
236
		/**
237
		 * This function permits parsing different field settings values
238
		 *
239
		 * @param array $settings
240
		 *	the data array to initialize if necessary.
241
		 */
242
		public function setFromPOST(Array $settings = array())
243
		{
244
			// call the default behavior
245
			parent::setFromPOST($settings);
246
247
			// declare a new setting array
248
			$new_settings = array();
249
250
			// set new settings
251
			$new_settings['sections'] = is_array($settings['sections']) ? 
252
				implode(self::SEPARATOR, $settings['sections']) : 
253
				(is_string($settings['sections']) ? $settings['sections'] : null);
254
				
255
			$new_settings['show_association'] = $settings['show_association'] == 'yes' ? 'yes' : 'no';
256
			$new_settings['deepness'] = General::intval($settings['deepness']);
257
			$new_settings['deepness'] = $new_settings['deepness'] < 1 ? null : $new_settings['deepness'];
258
			$new_settings['elements'] = empty($settings['elements']) ? null : $settings['elements'];
259
			$new_settings['mode'] = empty($settings['mode']) ? null : $settings['mode'];
260
			$new_settings['mode_table'] = empty($settings['mode_table']) ? null : $settings['mode_table'];
261
			$new_settings['mode_header'] = empty($settings['mode_header']) ? null : $settings['mode_header'];
262
			$new_settings['mode_footer'] = empty($settings['mode_footer']) ? null : $settings['mode_footer'];
263
			$new_settings['allow_new'] = $settings['allow_new'] == 'yes' ? 'yes' : 'no';
264
			$new_settings['allow_edit'] = $settings['allow_edit'] == 'yes' ? 'yes' : 'no';
265
			$new_settings['allow_link'] = $settings['allow_link'] == 'yes' ? 'yes' : 'no';
266
			$new_settings['allow_delete'] = $settings['allow_delete'] == 'yes' ? 'yes' : 'no';
267
			$new_settings['allow_collapse'] = $settings['allow_collapse'] == 'yes' ? 'yes' : 'no';
268
			$new_settings['show_header'] = $settings['show_header'] == 'yes' ? 'yes' : 'no';
269
			
270
			// save it into the array
271
			$this->setArray($new_settings);
272
		}
273
274
275
		/**
276
		 *
277
		 * Validates the field settings before saving it into the field's table
278
		 */
279
		public function checkFields(Array &$errors, $checkForDuplicates = true)
280
		{
281
			$parent = parent::checkFields($errors, $checkForDuplicates);
282
			if ($parent != self::__OK__) {
283
				return $parent;
284
			}
285
			
286
			$sections = $this->get('sections');
287
			
288
			if (empty($sections)) {
289
				$errors['sections'] = __('At least one section must be chosen');
290
			}
291
292
			return (!empty($errors) ? self::__ERROR__ : self::__OK__);
293
		}
294
295
		/**
296
		 *
297
		 * Save field settings into the field's table
298
		 */
299
		public function commit()
300
		{
301
			// if the default implementation works...
302
			if(!parent::commit()) return false;
303
			
304
			$id = $this->get('id');
305
			
306
			// exit if there is no id
307
			if($id == false) return false;
308
			
309
			// we are the child, with multiple parents
310
			$child_field_id = $id;
311
			
312
			// delete associations, only where we are the child
313
			self::removeSectionAssociation($child_field_id);
314
			
315
			$sections = $this->getSelectedSectionsArray();
316
			
317
			foreach ($sections as $key => $sectionId) {
318
				if (empty($sectionId)) {
319
					continue;
320
				}
321
				$parent_section_id = General::intval($sectionId);
322
				$parent_section = SectionManager::fetch($sectionId);
323
				$fields = $parent_section->fetchVisibleColumns();
324
				if (empty($fields)) {
325
					// no visible field, revert to all
326
					$fields = $parent_section->fetchFields();
327
				}
328
				$parent_field_id = current(array_keys($fields));
329
				// create association
330
				SectionManager::createSectionAssociation(
331
					$parent_section_id,
332
					$child_field_id,
333
					$parent_field_id,
334
					$this->get('show_association') == 'yes'
335
				);
336
			}
337
			
338
			// declare an array contains the field's settings
339
			$settings = array(
340
				'sections' => $this->get('sections'),
341
				'show_association' => $this->get('show_association'),
342
				'deepness' => $this->get('deepness'),
343
				'elements' => $this->get('elements'),
344
				'mode' => $this->get('mode'),
345
				'mode_table' => $this->get('mode_table'),
346
				'mode_header' => $this->get('mode_header'),
347
				'mode_footer' => $this->get('mode_footer'),
348
				'min_entries' => $this->get('min_entries'),
349
				'max_entries' => $this->get('max_entries'),
350
				'allow_new' => $this->get('allow_new'),
351
				'allow_edit' => $this->get('allow_edit'),
352
				'allow_link' => $this->get('allow_link'),
353
				'allow_delete' => $this->get('allow_delete'),
354
				'allow_collapse' => $this->get('allow_collapse'),
355
				'show_header' => $this->get('show_header'),
356
			);
357
358
			return FieldManager::saveSettings($id, $settings);
359
		}
360
361
		/**
362
		 *
363
		 * This function allows Fields to cleanup any additional things before it is removed
364
		 * from the section.
365
		 * @return boolean
366
		 */
367
		public function tearDown()
368
		{
369
			self::removeSectionAssociation($this->get('id'));
370
			return parent::tearDown();
371
		}
372
		
373
		/**
374
		 * Generates the where filter for searching by entry id
375
		 *
376
		 * @param string $value
377
		 * @param @optional string $col
378
		 * @param @optional boolean $andOperation
379
		 */
380
		public function generateWhereFilter($value, $col = 'd', $andOperation = true)
381
		{
382
			$junction = $andOperation ? 'AND' : 'OR';
383
			if (!$value) {
384
				return "{$junction} (`{$col}`.`entries` IS NULL)";
385
			}
386
			return " {$junction} (`{$col}`.`entries` = '{$value}' OR 
387
					`{$col}`.`entries` LIKE '{$value},%' OR 
388
					`{$col}`.`entries` LIKE '%,{$value}' OR 
389
					`{$col}`.`entries` LIKE '%,{$value},%')";
390
		}
391
392
		/**
393
		 * Fetch the number of associated entries for a particular entry id
394
		 *
395
		 * @param string $value
396
		 */
397
		public function fetchAssociatedEntryCount($value)
398
		{
399
			if (!$value) {
400
				return 0;
401
			}
402
			$join = sprintf(" INNER JOIN `tbl_entries_data_%s` AS `d` ON `e`.id = `d`.`entry_id`", $this->get('id'));
403
			$where = $this->generateWhereFilter($value);
404
			
405
			$entries = EntryManager::fetch(null, $this->get('parent_section'), null, 0, $where, $join, false, false, array());
406
			
407
			return count($entries);
408
		}
409
		
410
		public function fetchAssociatedEntrySearchValue($data, $field_id = null, $parent_entry_id = null)
411
		{
412
			return $parent_entry_id;
413
		}
414
		
415
		public function findRelatedEntries($entry_id, $parent_field_id)
416
		{
417
			$joins = '';
418
			$where = '';
419
			$this->buildDSRetrievalSQL(array($entry_id), $joins, $where, true);
420
			
421
			$entries = EntryManager::fetch(null, $this->get('parent_section'), null, 0, $where, $joins, false, false, array());
422
			
423
			return array_map(function ($e) {
424
				return $e['id'];
425
			}, $entries);
426
		}
427
		
428
		public function findParentRelatedEntries($parent_field_id, $entry_id)
429
		{
430
			$entry = $this->fetchEntry($entry_id, array($this->get('label')));
431
			
432
			if (!$entry) {
433
				return array();
434
			}
435
			
436
			return self::getEntries($entry->getData($this->get('id')));
437
		}
438
		
439
		public function prepareAssociationsDrawerXMLElement(Entry $e, array $parent_association, $prepolutate = '')
440
		{
441
			$currentSection = SectionManager::fetch($parent_association['child_section_id']);
442
			$visibleCols = $currentSection->fetchVisibleColumns();
443
			$outputFieldId = current(array_keys($visibleCols));
444
			$outputField = FieldManager::fetch($outputFieldId);
445
			
446
			$value = $outputField->prepareReadableValue($e->getData($outputFieldId), $e->get('id'), true, __('None'));
447
			
448
			$li = new XMLElement('li');
449
			$li->setAttribute('class', 'field-' . $this->get('type'));
450
			$a = new XMLElement('a', strip_tags($value));
451
			$a->setAttribute('href', SYMPHONY_URL . '/publish/' . $parent_association['handle'] . '/edit/' . $e->get('id') . '/');
452
			$li->appendChild($a);
453
454
			return $li;
455
		}
456
		
457
		/**
458
		 * @param string $joins
459
		 * @param string $where
460
		 */
461
		public function buildDSRetrievalSQL($data, &$joins, &$where, $andOperation = false)
462
		{
463
			$field_id = $this->get('id');
464
			
465
			// REGEX filtering is a special case, and will only work on the first item
466
			// in the array. You cannot specify multiple filters when REGEX is involved.
467
			if (self::isFilterRegex($data[0])) {
468
				return $this->buildRegexSQL($data[0], array('entries'), $joins, $where);
469
			}
470
			
471
			$this->_key++;
472
			
473
			$where .= ' AND (1=' . ($andOperation ? '1' : '0') . ' ';
474
			
475
			$joins .= "
476
				INNER JOIN
477
					`tbl_entries_data_{$field_id}` AS `t{$field_id}_{$this->_key}`
478
					ON (`e`.`id` = `t{$field_id}_{$this->_key}`.`entry_id`)
479
			";
480
			
481
			foreach ($data as $value) {
482
				$where .= $this->generateWhereFilter($this->cleanValue($value), "t{$field_id}_{$this->_key}", $andOperation);
483
			}
484
			
485
			$where .= ')';
486
			
487
			return true; // this tells the DS Manager that filters are OK!!
488
		}
489
490
		/* ******* EVENTS ******* */
491
492
		public function getExampleFormMarkup()
493
		{
494
			$label = Widget::Label($this->get('label'));
495
			$label->appendChild(Widget::Input('fields['.$this->get('element_name').'][entries]', null, 'hidden'));
496
497
			return $label;
498
		}
499
500
501
		/* ******* DATA SOURCE ******* */
502
		
503
		private function fetchEntry($eId, $elements = array())
504
		{
505
			$entry = EntryManager::fetch($eId, null, 1, 0, null, null, false, true, $elements, false);
506
			if (!is_array($entry) || count($entry) !== 1) {
507
				return null;
508
			}
509
			return $entry[0];
510
		}
511
		
512
		public function fetchIncludableElements()
513
		{
514
			$label = $this->get('element_name');
515
			$elements = array_filter(array_map(trim, explode(self::SEPARATOR, trim($this->get('elements')))));
516
			$includedElements = array($label . ': *');
517
			foreach ($elements as $elem) {
518
				$elem = trim($elem);
519
				if ($elem !== '*') {
520
					$includedElements[] = $label . ': ' . $elem;
521
				}
522
			}
523
			return $includedElements;
524
		}
525
		
526
		/**
527
		 * Appends data into the XML tree of a Data Source
528
		 * @param $wrapper
529
		 * @param $data
530
		 */
531
		public function appendFormattedElement(XMLElement &$wrapper, $data, $encode = false, $mode = null, $entry_id = null)
532
		{
533
			if(!is_array($data) || empty($data)) return;
534
535
			// root for all values
536
			$root = new XMLElement($this->get('element_name'));
537
			
538
			// selected items
539
			$entries = static::getEntries($data);
540
			
541
			// current linked entries
542
			$root->setAttribute('entries', $data['entries']);
543
			
544
			// available sections
545
			$root->setAttribute('sections', $this->get('sections'));
546
			
547
			// included elements
548
			$elements = static::parseElements($this);
549
			
550
			// DS mode
551
			if (!$mode) {
552
				$mode = '*';
553
			}
554
			
555
			$parentDeepness = General::intval($this->recursiveDeepness);
556
			$deepness = General::intval($this->get('deepness'));
557
			
558
			// both deepnesses are defined and parent restricts more
559
			if ($parentDeepness > 0 && $deepness > 0 && $parentDeepness < $deepness) {
560
				$deepness = $parentDeepness;
561
			}
562
			// parent is defined, current is not
563
			else if ($parentDeepness > 0 && $deepness < 1) {
564
				$deepness = $parentDeepness;
565
			}
566
			
567
			// cache recursive level because recursion might
568
			// change its value later on.
569
			$recursiveLevel = $this->recursiveLevel;
570
			
571
			// build entries
572
			foreach ($entries as $eId) {
573
				$item = new XMLElement('item');
574
				// output id
575
				$item->setAttribute('id', $eId);
576
				// output recursive level
577
				$item->setAttribute('level', $recursiveLevel);
578
				$item->setAttribute('max-level', $deepness);
579
				
580
				// max recursion check
581
				if ($deepness < 1 || $recursiveLevel < $deepness) {
582
					// current entry, without data
583
					$entry = $this->fetchEntry($eId);
584
					
585
					// entry not found...
586
					if (!$entry || empty($entry)) {
587
						$error = new XMLElement('error');
588
						$error->setAttribute('id', $eId);
589
						$error->setValue(__('Error: entry `%s` not found', array($eId)));
590
						$root->prependChild($error);
591
						continue;
592
					}
593
					
594
					// fetch section infos
595
					$sectionId = $entry->get('section_id');
596
					$section = $this->sectionManager->fetch($sectionId);
597
					$sectionName = $section->get('handle');
598
					// cache fields info
599
					if (!isset($section->er_field_cache)) {
600
						$section->er_field_cache = $section->fetchFields();
601
					}
602
					
603
					// set section related attributes
604
					$item->setAttribute('section-id', $sectionId);
605
					$item->setAttribute('section', $sectionName);
606
					
607
					// Get the valid elements for this section only
608
					$validElements = $elements[$sectionName];
609
					
610
					// adjust the mode for the current section
611
					$curMode = $mode;
612
					
613
					// remove section name from current mode, i.e sectionName.field
614
					if (preg_match('/^(' . $sectionName . '\.)(.*)$/sU', $curMode)) {
615
						$curMode = preg_replace('/^' . $sectionName . '\./sU', '', $curMode);
616
					}
617
					// remove section name from current mode, i.e sectionName
618
					else if (preg_match('/^(' . $sectionName . ')$/sU', $curMode)) {
619
						$curMode = '*';
620
					}
621
					// section name was not found in mode
622
					else if ($curMode != '*') {
623
						// mode forbids this section
624
						$validElements = null;
625
					}
626
					
627
					// this section is not selected, bail out
628
					if (!is_array($validElements)) {
629
						$item->setAttribute('forbidden-by', $curMode);
630
						$root->appendChild($item);
631
						continue;
632
					}
633
					
634
					// selected fields for fetching
635
					$sectionElements = array();
636
					
637
					// everything is allowed
638
					if (in_array('*', $validElements)) {
639
						if ($curMode !== '*') {
640
							// get only the mode
641
							$sectionElements = array($curMode);
642
						}
643
						else {
644
							// setting null = get all
645
							$sectionElements = null;
646
						}
647
					}
648
					// only use valid elements
649
					else {
650
						if ($curMode !== '*') {
651
							// is this field allowed ?
652
							if (self::isFieldIncluded($curMode, $validElements)) {
653
								// get only the mode
654
								$sectionElements = array($curMode);
655
							}
656
							else {
657
								// $curMode selects something outside of
658
								// the valid elements: select nothing
659
								$sectionElements = array();
660
							}
661
						}
662
						else {
663
							// use field's valid elements
664
							$sectionElements = $validElements;
665
						}
666
					}
667
					
668
					if (is_array($sectionElements) && empty($sectionElements)) {
669
						$item->setAttribute('selection-empty', 'yes');
670
						$item->setAttribute('forbidden-by', $curMode);
671
						$root->appendChild($item);
672
						continue;
673
					}
674
					
675
					// current entry again, but with data and the allowed schema
676
					$entry = $this->fetchEntry($eId, $sectionElements);
677
					
678
					// cache the entry data
679
					$entryData = $entry->getData();
680
					
681
					// for each field returned for this entry...
682
					foreach ($entryData as $fieldId => $data) {
683
						$filteredData = array_filter($data, function ($value) {
684
							return $value != null;
685
						});
686
						
687
						if (empty($filteredData)) {
688
							continue;
689
						}
690
						
691
						$field = $section->er_field_cache[$fieldId];
692
						$fieldName = $field->get('element_name');
693
						$fieldCurMode = self::extractMode($fieldName, $curMode);
694
						
695
						$parentIncludableElement = self::getSectionElementName($fieldName, $validElements);
696
						$parentIncludableElementMode = self::extractMode($fieldName, $parentIncludableElement);
697
						
698
						// Special treatments for ERF
699
						if ($field instanceof FieldEntry_relationship) {
700
							// Increment recursive level
701
							$field->recursiveLevel = $recursiveLevel + 1;
702
							$field->recursiveDeepness = $deepness;
703
						}
704
						
705
						$submodes = null;
706
						if ($parentIncludableElementMode == null) {
707
							if ($fieldCurMode == null) {
708
								$submodes = null;
709
							}
710
							else {
711
								$submodes = array($fieldCurMode);
712
							}
713
						}
714
						else {
715
							if ($fieldCurMode == null || $fieldCurMode == $parentIncludableElementMode) {
716
								$submodes = array($parentIncludableElementMode);
717
							}
718
							else {
719
								$item->setAttribute('selection-mode-empty', 'yes');
720
								$submodes = array();
721
							}
722
						}
723
						
724
						// current selection does not specify a mode
725 View Code Duplication
						if ($submodes == null) {
726
							$submodes = array_map(function ($fieldIncludableElement) use ($fieldName) {
727
								return FieldEntry_relationship::extractMode($fieldName, $fieldIncludableElement);
728
							}, $field->fetchIncludableElements());
729
						}
730
						
731
						foreach ($submodes as $submode) {
732
							$field->appendFormattedElement($item, $data, $encode, $submode, $eId);
733
						}
734
					}
735
					// output current mode
736
					$item->setAttribute('matched-element', $curMode);
737
					// no field selected
738
					if (is_array($sectionElements) && empty($sectionElements)) {
739
						$item->setAttribute('empty-selection', 'yes');
740
					}
741
				}
742
				// append item when done
743
				$root->appendChild($item);
744
			} // end each entries
745
			
746
			// output mode for this field
747
			$root->setAttribute('data-source-mode', $mode);
748
			$root->setAttribute('field-included-elements', $this->get('elements'));
749
			
750
			// add all our data to the wrapper;
751
			$wrapper->appendChild($root);
752
			
753
			// clean up
754
			$this->recursiveLevel = 1;
755
			$this->recursiveDeepness = null;
756
		}
757
758
		public function getParameterPoolValue(array $data, $entry_id = null)
759
		{
760
			if(!is_array($data) || empty($data)) return;
761
			return static::getEntries($data);
762
		}
763
764
		/* ********* Utils *********** */
765
		
766
		/**
767
		 * Return true if $fieldName is allowed in $sectionElements
768
		 * @param string $fieldName
769
		 * @param string $sectionElements
770
		 * @return bool
771
		 */
772
		public static function isFieldIncluded($fieldName, $sectionElements)
773
		{
774
			return self::getSectionElementName($fieldName, $sectionElements) !== null;
775
		}
776
777
		public static function getSectionElementName($fieldName, $sectionElements)
778
		{
779
			if (is_array($sectionElements)) {
780
				foreach ($sectionElements as $element) {
781
					if ($element == '*') {
782
						return $fieldName;
783
					}
784
					if ($fieldName == $element || preg_match('/^' . $fieldName . '\s*:/sU', $element)) {
785
						return $element;
786
					}
787
				}
788
			}
789
			return null;
790
		}
791
		
792
		public static function parseElements($field)
793
		{
794
			$elements = array();
795
			$exElements = array_map(trim, explode(self::SEPARATOR, $field->get('elements')));
796
			
797
			if (in_array('*', $exElements)) {
798
				$sections = array_map(trim, explode(self::SEPARATOR, $field->get('sections')));
799
				$sections = SectionManager::fetch($sections);
800
				return array_reduce($sections, function ($result, $section) {
801
					$result[$section->get('handle')] = array('*');
802
					return $result;
803
				}, array());
804
			}
805
			
806
			foreach ($exElements as $value) {
807
				if (!$value) {
808
					continue;
809
				}
810
				// sectionName.fieldName or sectionName.*
811
				$parts = array_map(trim, explode('.', $value));
812
				// first time seeing this section
813
				if (!isset($elements[$parts[0]])) {
814
					$elements[$parts[0]] = array();
815
				}
816
				// we have a value after the dot
817
				if (isset($parts[1]) && !!$parts[1]) {
818
					$elements[$parts[0]][] = $parts[1];
819
				}
820
				// sectionName only
821
				else if (!isset($parts[1])) {
822
					$elements[$parts[0]][] = '*';
823
				}
824
			}
825
			
826
			return $elements;
827
		}
828
829
		public static function extractMode($fieldName, $mode)
830
		{
831
			$pattern = '/^' . $fieldName . '\s*:\s*/s';
832
			if (!preg_match($pattern, $mode)) {
833
				return null;
834
			}
835
			$mode = preg_replace($pattern, '', $mode, 1);
836
			if ($mode === '*') {
837
				return null;
838
			}
839
			return $mode;
840
		}
841
842
		private function buildSectionSelect($name)
843
		{
844
			$sections = SectionManager::fetch();
845
			$options = array();
846
			$selectedSections = $this->getSelectedSectionsArray();
847
			
848 View Code Duplication
			foreach ($sections as $section) {
849
				$driver = $section->get('id');
850
				$selected = in_array($driver, $selectedSections);
851
				$options[] = array($driver, $selected, General::sanitize($section->get('name')));
852
			}
853
			
854
			return Widget::Select($name, $options, array('multiple' => 'multiple'));
855
		}
856
857 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...
858
		{
859
			$name = $this->createSettingsFieldName('sections', true);
860
861
			$input = $this->buildSectionSelect($name);
862
			$input->setAttribute('class', 'entry_relationship-sections');
863
864
			$label = Widget::Label();
865
			$label->setAttribute('class', 'column');
866
867
			$label->setValue(__('Available sections %s', array($input->generate())));
868
869
			$wrapper->appendChild($label);
870
		}
871
872
		private function createEntriesHiddenInput($data)
873
		{
874
			$hidden = new XMLElement('input', null, array(
875
				'type' => 'hidden',
876
				'name' => $this->createPublishFieldName('entries'),
877
				'value' => $data['entries']
878
			));
879
			
880
			return $hidden;
881
		}
882
		
883
		private function createActionBarMenu($sections)
884
		{
885
			$wrap = new XMLElement('div');
886
			$actionBar = '';
887
			$modeFooter = $this->get('mode_footer');
888
			if ($modeFooter) {
889
				$section = $this->sectionManager->fetch($this->get('parent_section'));
890
				$actionBar = ERFXSLTUTilities::processXSLT($this, null, $section->get('handle'), null, 'mode_footer', isset($_REQUEST['debug']), 'field');
891
			}
892
			if (empty($actionBar)) {
893
				$fieldset = new XMLElement('fieldset');
894
				$fieldset->setAttribute('class', 'single');
895
				if ($this->is('allow_new') || $this->is('allow_link')) {
896
					$selectWrap = new XMLElement('div');
897
					$selectWrap->appendChild(new XMLElement('span', __('Related section: '), array('class' => 'sections-selection')));
898
					$options = array();
899
					foreach ($sections as $section) {
900
						$options[] = array($section->get('handle'), false, $section->get('name'));
901
					}
902
					$select = Widget::Select('', $options, array('class' => 'sections sections-selection'));
903
					$selectWrap->appendChild($select);
904
					$fieldset->appendChild($selectWrap);
905
				}
906 View Code Duplication
				if ($this->is('allow_new')) {
907
					$fieldset->appendChild(new XMLElement('button', __('Create new'), array(
908
						'type' => 'button',
909
						'class' => 'create',
910
						'data-create' => '',
911
					)));
912
				}
913 View Code Duplication
				if ($this->is('allow_link')) {
914
					$fieldset->appendChild(new XMLElement('button', __('Link to entry'), array(
915
						'type' => 'button',
916
						'class' => 'link',
917
						'data-link' => '',
918
					)));
919
				}
920
				$wrap->appendChild($fieldset);
921
			}
922
			else {
923
				$wrap->setValue($actionBar);
924
			}
925
			
926
			return $wrap;
927
		}
928
929
		/* ********* UI *********** */
930
		
931
		/**
932
		 *
933
		 * Builds the UI for the field's settings when creating/editing a section
934
		 * @param XMLElement $wrapper
935
		 * @param array $errors
936
		 */
937
		public function displaySettingsPanel(XMLElement &$wrapper, $errors=null)
938
		{
939
			/* first line, label and such */
940
			parent::displaySettingsPanel($wrapper, $errors);
941
			
942
			// sections
943
			$sections = new XMLElement('fieldset');
944
			
945
			$this->appendSelectionSelect($sections);
946 View Code Duplication
			if (is_array($errors) && isset($errors['sections'])) {
947
				$sections = Widget::Error($sections, $errors['sections']);
948
			}
949
			$wrapper->appendChild($sections);
950
			
951
			// elements
952
			$elements = new XMLElement('div');
953
			$element = Widget::Label();
954
			$element->setValue(__('Included elements in Data Sources and Backend Templates'));
955
			$element->setAttribute('class', 'column');
956
			$element->appendChild(Widget::Input($this->createSettingsFieldName('elements'), $this->get('elements'), 'text', array(
957
				'class' => 'entry_relationship-elements'
958
			)));
959
			$elements->appendChild($element);
960
			$elements_choices = new XMLElement('ul', null, array('class' => 'tags singular entry_relationship-field-choices'));
961
			
962
			$elements->appendChild($elements_choices);
963
			$wrapper->appendChild($elements);
964
			
965
			// limit entries
966
			$limits = new XMLElement('fieldset');
967
			$limits->appendChild(new XMLElement('legend', __('Limits')));
968
			$limits_cols = new XMLElement('div');
969
			$limits_cols->setAttribute('class', 'three columns');
970
			// min
971
			$limit_min = Widget::Label();
972
			$limit_min->setValue(__('Minimum count of entries in this field'));
973
			$limit_min->setAttribute('class', 'column');
974
			$limit_min->appendChild(Widget::Input($this->createSettingsFieldName('min_entries'), $this->get('min_entries'), 'number', array(
975
				'min' => 0,
976
				'max' => 99999
977
			)));
978
			$limits_cols->appendChild($limit_min);
979
			// max
980
			$limit_max = Widget::Label();
981
			$limit_max->setValue(__('Maximum count of entries in this field'));
982
			$limit_max->setAttribute('class', 'column');
983
			$limit_max->appendChild(Widget::Input($this->createSettingsFieldName('max_entries'), $this->get('max_entries'), 'number', array(
984
				'min' => 0,
985
				'max' => 99999
986
			)));
987
			$limits_cols->appendChild($limit_max);
988
			
989
			// deepness
990
			$deepness = Widget::Label();
991
			$deepness->setValue(__('Maximum level of recursion in Data Sources'));
992
			$deepness->setAttribute('class', 'column');
993
			$deepness->appendChild(Widget::Input($this->createSettingsFieldName('deepness'), $this->get('deepness'), 'number', array(
994
				'min' => 0,
995
				'max' => 99
996
			)));
997
			$limits_cols->appendChild($deepness);
998
			$limits->appendChild($limits_cols);
999
			$wrapper->appendChild($limits);
1000
			
1001
			// xsl
1002
			$xsl = new XMLElement('fieldset');
1003
			$xsl->appendChild(new XMLElement('legend', __('Backend XSL templates options')));
1004
			$xsl_cols = new XMLElement('div');
1005
			$xsl_cols->setAttribute('class', 'four columns');
1006
			
1007
			// xsl mode
1008
			$xslmode = Widget::Label();
1009
			$xslmode->setValue(__('XSL mode for entries content template'));
1010
			$xslmode->setAttribute('class', 'column');
1011
			$xslmode->appendChild(Widget::Input($this->createSettingsFieldName('mode'), $this->get('mode'), 'text'));
1012
			$xsl_cols->appendChild($xslmode);
1013
			// xsl header mode
1014
			$xslmodetable = Widget::Label();
1015
			$xslmodetable->setValue(__('XSL mode for entries header template'));
1016
			$xslmodetable->setAttribute('class', 'column');
1017
			$xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_header'), $this->get('mode_header'), 'text'));
1018
			$xsl_cols->appendChild($xslmodetable);
1019
			// xsl table mode
1020
			$xslmodetable = Widget::Label();
1021
			$xslmodetable->setValue(__('XSL mode for publish table value'));
1022
			$xslmodetable->setAttribute('class', 'column');
1023
			$xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_table'), $this->get('mode_table'), 'text'));
1024
			$xsl_cols->appendChild($xslmodetable);
1025
			// xsl action bar mode
1026
			$xslmodetable = Widget::Label();
1027
			$xslmodetable->setValue(__('XSL mode for publish action bar'));
1028
			$xslmodetable->setAttribute('class', 'column');
1029
			$xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_footer'), $this->get('mode_footer'), 'text'));
1030
			$xsl_cols->appendChild($xslmodetable);
1031
			
1032
			$xsl->appendChild($xsl_cols);
1033
			$wrapper->appendChild($xsl);
1034
			
1035
			// permissions
1036
			$permissions = new XMLElement('fieldset');
1037
			$permissions->appendChild(new XMLElement('legend', __('Permissions')));
1038
			$permissions_cols = new XMLElement('div');
1039
			$permissions_cols->setAttribute('class', 'four columns');
1040
			$permissions_cols->appendChild($this->createCheckbox('allow_new', 'Show new button'));
1041
			$permissions_cols->appendChild($this->createCheckbox('allow_edit', 'Show edit button'));
1042
			$permissions_cols->appendChild($this->createCheckbox('allow_link', 'Show link button'));
1043
			$permissions_cols->appendChild($this->createCheckbox('allow_delete', 'Show delete button'));
1044
			$permissions->appendChild($permissions_cols);
1045
			$wrapper->appendChild($permissions);
1046
			
1047
			// display options
1048
			$display = new XMLElement('fieldset');
1049
			$display->appendChild(new XMLElement('legend', __('Display options')));
1050
			$display_cols = new XMLElement('div');
1051
			$display_cols->setAttribute('class', 'four columns');
1052
			$display_cols->appendChild($this->createCheckbox('allow_collapse', 'Allow content collapsing'));
1053
			$display_cols->appendChild($this->createCheckbox('show_header', 'Show the header box before entries templates'));
1054
			$display->appendChild($display_cols);
1055
			$wrapper->appendChild($display);
1056
			
1057
			// assoc
1058
			$assoc = new XMLElement('fieldset');
1059
			$assoc->appendChild(new XMLElement('legend', __('Associations')));
1060
			$assoc_cols = new XMLElement('div');
1061
			$assoc_cols->setAttribute('class', 'three columns');
1062
			$this->appendShowAssociationCheckbox($assoc_cols);
1063
			$assoc->appendChild($assoc_cols);
1064
			$wrapper->appendChild($assoc);
1065
			
1066
			// footer
1067
			$this->appendStatusFooter($wrapper);
1068
		}
1069
1070
		/**
1071
		 *
1072
		 * Builds the UI for the publish page
1073
		 * @param XMLElement $wrapper
1074
		 * @param mixed $data
1075
		 * @param mixed $flagWithError
1076
		 * @param string $fieldnamePrefix
1077
		 * @param string $fieldnamePostfix
1078
		 */
1079
		public function displayPublishPanel(XMLElement &$wrapper, $data = null, $flagWithError = null, $fieldnamePrefix = null, $fieldnamePostfix = null, $entry_id = null)
1080
		{
1081
			$entriesId = array();
1082
			$sectionsId = $this->getSelectedSectionsArray();
1083
			
1084
			if ($data['entries'] != null) {
1085
				$entriesId = static::getEntries($data);
1086
			}
1087
			
1088
			$sectionsId = array_map(array('General', 'intval'), $sectionsId);
1089
			$sections = SectionManager::fetch($sectionsId);
1090
			
1091
			$label = Widget::Label($this->get('label'));
1092
			$notes = '';
1093
			
1094
			// min note
1095
			if ($this->getInt('min_entries') > 0) {
1096
				$notes .= __('Minimum number of entries: <b>%s</b>. ', array($this->get('min_entries')));
1097
			}
1098
			// max note
1099
			if ($this->getInt('max_entries') > 0) {
1100
				$notes .= __('Maximum number of entries: <b>%s</b>. ', array($this->get('max_entries')));
1101
			}
1102
			// not required note
1103
			if (!$this->isRequired()) {
1104
				$notes .= __('Optional');
1105
			}
1106
			// append notes
1107
			if ($notes) {
1108
				$label->appendChild(new XMLElement('i', $notes));
1109
			}
1110
			
1111
			// label error management
1112
			if ($flagWithError != null) {
1113
				$wrapper->appendChild(Widget::Error($label, $flagWithError));
1114
			} else {
1115
				$wrapper->appendChild($label);
1116
			}
1117
			
1118
			$wrapper->appendChild($this->createEntriesList($entriesId));
1119
			$wrapper->appendChild($this->createActionBarMenu($sections));
1120
			$wrapper->appendChild($this->createEntriesHiddenInput($data));
1121
			$wrapper->setAttribute('data-value', $data['entries']);
1122
			$wrapper->setAttribute('data-field-id', $this->get('id'));
1123
			$wrapper->setAttribute('data-field-label', $this->get('label'));
1124
			$wrapper->setAttribute('data-min', $this->get('min_entries'));
1125
			$wrapper->setAttribute('data-max', $this->get('max_entries'));
1126
			$wrapper->setAttribute('data-required', $this->get('required'));
1127
			if (isset($_REQUEST['debug'])) {
1128
				$wrapper->setAttribute('data-debug', true);
1129
			}
1130
		}
1131
1132
		/**
1133
		 * @param integer $count
1134
		 */
1135
		private static function formatCount($count)
1136
		{
1137
			if ($count == 0) {
1138
				return __('No item');
1139
			} else if ($count == 1) {
1140
				return __('1 item');
1141
			}
1142
			return __('%s items', array($count));
1143
		}
1144
1145
		/**
1146
		 *
1147
		 * Return a plain text representation of the field's data
1148
		 * @param array $data
1149
		 * @param int $entry_id
1150
		 */
1151
		public function prepareTextValue($data, $entry_id = null)
1152
		{
1153
			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...
1154
				return '';
1155
			}
1156
			return $data['entries'];
1157
		}
1158
1159
		/**
1160
		 * Format this field value for display as readable text value.
1161
		 *
1162
		 * @param array $data
1163
		 *  an associative array of data for this string. At minimum this requires a
1164
		 *  key of 'value'.
1165
		 * @param integer $entry_id (optional)
1166
		 *  An option entry ID for more intelligent processing. Defaults to null.
1167
		 * @param string $defaultValue (optional)
1168
		 *  The value to use when no plain text representation of the field's data
1169
		 *  can be made. Defaults to null.
1170
		 * @return string
1171
		 *  the readable text summary of the values of this field instance.
1172
		 */
1173
		public function prepareReadableValue($data, $entry_id = null, $truncate = false, $defaultValue = 'None')
1174
		{
1175
			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...
1176
				return __($defaultValue);
1177
			}
1178
			$entries = static::getEntries($data);
1179
			$realEntries = array();
1180
			foreach ($entries as $entryId) {
1181
				$e = EntryManager::fetch($entryId);
1182
				if (is_array($e) && !empty($e)) {
1183
					$realEntries = array_merge($realEntries, $e);
1184
				}
1185
			}
1186
			$count = count($entries);
1187
			$realCount = count($realEntries);
1188
			if ($count === $realCount) {
1189
				return self::formatCount($count);
1190
			}
1191
			return self::formatCount($realCount) . ' (' . self::formatCount($count - $realCount) . ' not found)';
1192
		}
1193
1194
		/**
1195
		 * Format this field value for display in the publish index tables.
1196
		 *
1197
		 * @param array $data
1198
		 *  an associative array of data for this string. At minimum this requires a
1199
		 *  key of 'value'.
1200
		 * @param XMLElement $link (optional)
1201
		 *  an XML link structure to append the content of this to provided it is not
1202
		 *  null. it defaults to null.
1203
		 * @param integer $entry_id (optional)
1204
		 *  An option entry ID for more intelligent processing. defaults to null
1205
		 * @return string
1206
		 *  the formatted string summary of the values of this field instance.
1207
		 */
1208
		public function prepareTableValue($data, XMLElement $link = null, $entry_id = null)
1209
		{
1210
			$value = $this->prepareReadableValue($data, $entry_id, false, __('None'));
1211
1212
			if ($link) {
1213
				$link->setValue($value);
1214
				return $link->generate();
1215
			}
1216
			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...
1217
				$entries = static::getEntries($data);
1218
				$cellcontent = '';
1219
				foreach ($entries as $child_entry_id) {
1220
					$entry = $this->entryManager->fetch($child_entry_id);
1221
					if (!$entry || !is_array($entry) || empty($entry)) {
1222
						continue;
1223
					}
1224
					reset($entry);
1225
					$entry = current($entry);
1226
					$section = $this->sectionManager->fetch($entry->get('section_id'));
1227
					$content = ERFXSLTUTilities::processXSLT($this, $entry, $section->get('handle'), $section->fetchFields(), 'mode_table', isset($_REQUEST['debug']));
1228
					if ($content) {
1229
						$cellcontent .= $content;
1230
					}
1231
				}
1232
				
1233
				if (General::strlen(trim($cellcontent))) {
1234
					return $cellcontent;
1235
				}
1236
			}
1237
1238
			return $value;
1239
		}
1240
1241
		/* ********* SQL Data Definition ************* */
1242
1243
		/**
1244
		 *
1245
		 * Creates table needed for entries of individual fields
1246
		 */
1247
		public function createTable()
1248
		{
1249
			$id = $this->get('id');
1250
1251
			return Symphony::Database()->query("
1252
				CREATE TABLE `tbl_entries_data_$id` (
1253
					`id` int(11) 		unsigned NOT NULL AUTO_INCREMENT,
1254
					`entry_id` 			int(11) unsigned NOT NULL,
1255
					`entries` 			text COLLATE utf8_unicode_ci NULL,
1256
					PRIMARY KEY  (`id`),
1257
					UNIQUE KEY `entry_id` (`entry_id`)
1258
				) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1259
			");
1260
		}
1261
1262
		/**
1263
		 * Creates the table needed for the settings of the field
1264
		 */
1265
		public static function createFieldTable()
1266
		{
1267
			$tbl = self::FIELD_TBL_NAME;
1268
1269
			return Symphony::Database()->query("
1270
				CREATE TABLE IF NOT EXISTS `$tbl` (
1271
					`id` 				int(11) unsigned NOT NULL AUTO_INCREMENT,
1272
					`field_id` 			int(11) unsigned NOT NULL,
1273
					`sections`			varchar(2048) NULL COLLATE utf8_unicode_ci,
1274
					`show_association` 	enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1275
					`deepness` 			int(2) unsigned NULL,
1276
					`elements` 			text COLLATE utf8_unicode_ci NULL,
1277
					`mode`				varchar(50) NULL COLLATE utf8_unicode_ci,
1278
					`mode_table`		varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL,
1279
					`mode_header`		varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL,
1280
					`mode_footer`		varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL,
1281
					`min_entries`		int(5) unsigned NULL,
1282
					`max_entries`		int(5) unsigned NULL,
1283
					`allow_edit` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1284
					`allow_new` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1285
					`allow_link` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1286
					`allow_delete` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'no',
1287
					`allow_collapse` 	enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1288
					`show_header` 		enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1289
					PRIMARY KEY (`id`),
1290
					UNIQUE KEY `field_id` (`field_id`)
1291
				) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1292
			");
1293
		}
1294
		
1295
		public static function update_102()
1296
		{
1297
			$tbl = self::FIELD_TBL_NAME;
1298
			$sql = "
1299
				ALTER TABLE `$tbl`
1300
					ADD COLUMN `allow_edit` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1301
					ADD COLUMN `allow_new` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1302
					ADD COLUMN `allow_link` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes'
1303
					AFTER `max_entries`
1304
			";
1305
			$addColumns = Symphony::Database()->query($sql);
1306
			if (!$addColumns) {
1307
				return false;
1308
			}
1309
1310
			$fields = FieldManager::fetch(null, null, null, 'id', 'entry_relationship');
1311
			if (!empty($fields) && is_array($fields)) {
1312
				foreach ($fields as $fieldId => $field) {
1313
					$sql = "ALTER TABLE `tbl_entries_data_$fieldId` MODIFY `entries` TEXT";
1314
					if (!Symphony::Database()->query($sql)) {
1315
						throw new Exception(__('Could not update table `tbl_entries_data_%s`.', array($fieldId)));
1316
					}
1317
				}
1318
			}
1319
			return true;
1320
		}
1321
		
1322
		public static function update_103()
1323
		{
1324
			$tbl = self::FIELD_TBL_NAME;
1325
			$sql = "
1326
				ALTER TABLE `$tbl`
1327
					ADD COLUMN `allow_delete` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'no'
1328
						AFTER `allow_link`
1329
			";
1330
			return Symphony::Database()->query($sql);
1331
		}
1332
		
1333
		public static function update_200()
1334
		{
1335
			$tbl = self::FIELD_TBL_NAME;
1336
			$sql = "
1337
				ALTER TABLE `$tbl`
1338
					ADD COLUMN `allow_collapse` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes'
1339
						AFTER `allow_delete`,
1340
					ADD COLUMN `mode_table` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
1341
						AFTER `mode`,
1342
					ADD COLUMN `mode_header` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
1343
						AFTER `mode_table`,
1344
					ADD COLUMN `show_header` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes'
1345
						AFTER `allow_collapse`,
1346
					ADD COLUMN `mode_footer` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
1347
						AFTER `mode_header`,
1348
					CHANGE `sections` `sections` varchar(2048) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
1349
					CHANGE `elements` `elements` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL
1350
			";
1351
			return Symphony::Database()->query($sql);
1352
		}
1353
		
1354
		/**
1355
		 *
1356
		 * Drops the table needed for the settings of the field
1357
		 */
1358
		public static function deleteFieldTable()
1359
		{
1360
			$tbl = self::FIELD_TBL_NAME;
1361
			
1362
			return Symphony::Database()->query("
1363
				DROP TABLE IF EXISTS `$tbl`
1364
			");
1365
		}
1366
		
1367
		private static function removeSectionAssociation($child_field_id)
1368
		{
1369
			return Symphony::Database()->delete('tbl_sections_association', "`child_section_field_id` = {$child_field_id}");
1370
		}
1371
	}
1372