Completed
Push — dev ( 79fff2...5aaa21 )
by Nicolas
01:51
created

fetchAssociatedEntrySearchValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 3
1
<?php
2
	/*
3
	Copyright: Deux Huit Huit 2014
4
	LICENCE: MIT http://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(TOOLKIT . '/class.field.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 Field
20
	{
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
		 * Separator char for values
32
		 *  @var string
33
		 */
34
		const SEPARATOR = ',';
35
		
36
		
37
		/**
38
		 *
39
		 * Current recursive level of output
40
		 *  @var int
41
		 */
42
		protected $recursiveLevel = 1;
43
		
44
		/**
45
		 *
46
		 * Parent's maximum recursive level of output
47
		 *  @var int
48
		 */
49
		protected $recursiveDeepness = null;
50
		
51
		/* Cacheable Managers */
52
		private $sectionManager;
53
		private $entryManager;
54
		
55
		/**
56
		 *
57
		 * Constructor for the oEmbed Field object
58
		 */
59
		public function __construct()
60
		{
61
			// call the parent constructor
62
			parent::__construct();
63
			// set the name of the field
64
			$this->_name = __('Entry Relationship');
65
			// permits to make it required
66
			$this->_required = true;
67
			// permits the make it show in the table columns
68
			$this->_showcolumn = true;
69
			// permits association
70
			$this->_showassociation = true;
71
			// current recursive level
72
			$this->recursiveLevel = 1;
73
			// parent's maximum recursive level of output
74
			$this->recursiveDeepness = null;
75
			// set as not required by default
76
			$this->set('required', 'no');
77
			// show association by default
78
			$this->set('show_association', 'yes');
79
			// no sections
80
			$this->set('sections', null);
81
			// no max deepness
82
			$this->set('deepness', null);
83
			// no included elements
84
			$this->set('elements', null);
85
			// no modes
86
			$this->set('mode', null);
87
			$this->set('mode_table', null);
88
			// no limit
89
			$this->set('min_entries', null);
90
			$this->set('max_entries', null);
91
			// all permissions
92
			$this->set('allow_new', 'yes');
93
			$this->set('allow_edit', 'yes');
94
			$this->set('allow_link', 'yes');
95
			$this->set('allow_delete', 'no');
96
			$this->set('allow_collapse', 'yes');
97
			
98
			$this->sectionManager = new CacheableFetch('SectionManager');
99
			$this->entryManager = new CacheableFetch('EntryManager');
100
		}
101
102
		public function isSortable()
103
		{
104
			return false;
105
		}
106
107
		public function canFilter()
108
		{
109
			return true;
110
		}
111
		
112
		public function canPublishFilter()
113
		{
114
			return false;
115
		}
116
117
		public function canImport()
118
		{
119
			return false;
120
		}
121
122
		public function canPrePopulate()
123
		{
124
			return false;
125
		}
126
		
127
		public function mustBeUnique()
128
		{
129
			return false;
130
		}
131
132
		public function allowDatasourceOutputGrouping()
133
		{
134
			return false;
135
		}
136
137
		public function requiresSQLGrouping()
138
		{
139
			return false;
140
		}
141
142
		public function allowDatasourceParamOutput()
143
		{
144
			return true;
145
		}
146
147
		/**
148
		 * @param string $name
149
		 */
150
		public function getInt($name)
151
		{
152
			return General::intval($this->get($name));
153
		}
154
155
		/**
156
		 * Check if a given property is == 'yes'
157
		 * @param string $name
158
		 * @return bool
159
		 *  True if the current field's value is 'yes'
160
		 */
161
		public function is($name)
162
		{
163
			return $this->get($name) == 'yes';
164
		}
165
166
		/**
167
		 * @return bool
168
		 *  True if the current field is required
169
		 */
170
		public function isRequired()
171
		{
172
			return $this->is('required');
173
		}
174
175
		public static function getEntries(array $data)
176
		{
177
			return array_map(array('General', 'intval'), array_filter(array_map(trim, explode(self::SEPARATOR, $data['entries']))));
178
		}
179
180
		/* ********** INPUT AND FIELD *********** */
181
182
183
		/**
184
		 * 
185
		 * Validates input
186
		 * Called before <code>processRawFieldData</code>
187
		 * @param $data
188
		 * @param $message
189
		 * @param $entry_id
190
		 */
191
		public function checkPostFieldData($data, &$message, $entry_id=NULL)
192
		{
193
			$message = NULL;
194
			$required = $this->isRequired();
195
			
196
			if ($required && (!is_array($data) || count($data) == 0 || strlen($data['entries']) < 1)) {
197
				$message = __("'%s' is a required field.", array($this->get('label')));
198
				return self::__MISSING_FIELDS__;
199
			}
200
			
201
			$entries = $data['entries'];
202
			
203
			if (!is_array($entries)) {
204
				$entries = static::getEntries($data);
205
			}
206
			
207
			// enforce limits only if required or it contains data
208
			if ($required || count($entries) > 0) {
209
				if ($this->getInt('min_entries') > 0 && $this->getInt('min_entries') > count($entries)) {
210
					$message = __("'%s' requires a minimum of %s entries.", array($this->get('label'), $this->getInt('min_entries')));
211
					return self::__INVALID_FIELDS__;
212
				} else if ($this->getInt('max_entries') > 0 && $this->getInt('max_entries') < count($entries)) {
213
					$message = __("'%s' can not contains more than %s entries.", array($this->get('label'), $this->getInt('max_entries')));
214
					return self::__INVALID_FIELDS__;
215
				}
216
			}
217
			
218
			return self::__OK__;
219
		}
220
221
222
		/**
223
		 *
224
		 * Process data before saving into database.
225
		 *
226
		 * @param array $data
227
		 * @param int $status
228
		 * @param boolean $simulate
229
		 * @param int $entry_id
230
		 *
231
		 * @return Array - data to be inserted into DB
232
		 */
233
		public function processRawFieldData($data, &$status, &$message = null, $simulate = false, $entry_id = null)
234
		{
235
			$status = self::__OK__;
236
			$entries = null;
237
			
238
			if (!is_array($data) && !is_string($data)) {
239
				return null;
240
			}
241
			
242
			if (isset($data['entries'])) {
243
				$entries = $data['entries'];
244
			}
245
			else if (is_string($data)) {
246
				$entries = $data;
247
			}
248
			
249
			$row = array(
250
				'entries' => $entries
251
			);
252
			
253
			// return row
254
			return $row;
255
		}
256
257
		/**
258
		 * This function permits parsing different field settings values
259
		 *
260
		 * @param array $settings
261
		 *	the data array to initialize if necessary.
262
		 */
263
		public function setFromPOST(Array &$settings = array())
264
		{
265
			// call the default behavior
266
			parent::setFromPOST($settings);
267
268
			// declare a new setting array
269
			$new_settings = array();
270
271
			// set new settings
272
			$new_settings['sections'] = is_array($settings['sections']) ? 
273
				implode(self::SEPARATOR, $settings['sections']) : 
274
				(is_string($settings['sections']) ? $settings['sections'] : null);
275
				
276
			$new_settings['show_association'] = $settings['show_association'] == 'yes' ? 'yes' : 'no';
277
			$new_settings['deepness'] = General::intval($settings['deepness']);
278
			$new_settings['deepness'] = $new_settings['deepness'] < 1 ? null : $new_settings['deepness'];
279
			$new_settings['elements'] = empty($settings['elements']) ? null : $settings['elements'];
280
			$new_settings['mode'] = empty($settings['mode']) ? null : $settings['mode'];
281
			$new_settings['mode_table'] = empty($settings['mode_table']) ? null : $settings['mode_table'];
282
			$new_settings['allow_new'] = $settings['allow_new'] == 'yes' ? 'yes' : 'no';
283
			$new_settings['allow_edit'] = $settings['allow_edit'] == 'yes' ? 'yes' : 'no';
284
			$new_settings['allow_link'] = $settings['allow_link'] == 'yes' ? 'yes' : 'no';
285
			$new_settings['allow_delete'] = $settings['allow_delete'] == 'yes' ? 'yes' : 'no';
286
			$new_settings['allow_collapse'] = $settings['allow_collapse'] == 'yes' ? 'yes' : 'no';
287
			
288
			// save it into the array
289
			$this->setArray($new_settings);
290
		}
291
292
293
		/**
294
		 *
295
		 * Validates the field settings before saving it into the field's table
296
		 */
297
		public function checkFields(Array &$errors, $checkForDuplicates)
298
		{
299
			$parent = parent::checkFields($errors, $checkForDuplicates);
300
			if ($parent != self::__OK__) {
301
				return $parent;
302
			}
303
			
304
			$sections = $this->get('sections');
305
			
306
			if (empty($sections)) {
307
				$errors['sections'] = __('At least one section must be chosen');
308
			}
309
310
			return (!empty($errors) ? self::__ERROR__ : self::__OK__);
311
		}
312
313
		/**
314
		 *
315
		 * Save field settings into the field's table
316
		 */
317
		public function commit()
318
		{
319
			// if the default implementation works...
320
			if(!parent::commit()) return false;
321
			
322
			$id = $this->get('id');
323
			
324
			// exit if there is no id
325
			if($id == false) return false;
326
			
327
			// we are the child, with multiple parents
328
			$child_field_id = $id;
329
			
330
			// delete associations, only where we are the child
331
			self::removeSectionAssociation($child_field_id);
332
			
333
			$sections = $this->getSelectedSectionsArray();
334
			
335
			foreach ($sections as $key => $sectionId) {
336
				if (empty($sectionId)) {
337
					continue;
338
				}
339
				$parent_section_id = General::intval($sectionId);
340
				$parent_section = SectionManager::fetch($sectionId);
341
				$fields = $parent_section->fetchVisibleColumns();
342
				if (empty($fields)) {
343
					// no visible field, revert to all
344
					$fields = $parent_section->fetchFields();
345
				}
346
				$parent_field_id = current(array_keys($fields));
347
				// create association
348
				SectionManager::createSectionAssociation(
349
					$parent_section_id,
350
					$child_field_id,
351
					$parent_field_id,
352
					$this->get('show_association') == 'yes'
353
				);
354
			}
355
			
356
			// declare an array contains the field's settings
357
			$settings = array(
358
				'sections' => $this->get('sections'),
359
				'show_association' => $this->get('show_association'),
360
				'deepness' => $this->get('deepness'),
361
				'elements' => $this->get('elements'),
362
				'mode' => $this->get('mode'),
363
				'mode_table' => $this->get('mode_table'),
364
				'min_entries' => $this->get('min_entries'),
365
				'max_entries' => $this->get('max_entries'),
366
				'allow_new' => $this->get('allow_new'),
367
				'allow_edit' => $this->get('allow_edit'),
368
				'allow_link' => $this->get('allow_link'),
369
				'allow_delete' => $this->get('allow_delete'),
370
				'allow_collapse' => $this->get('allow_collapse'),
371
			);
372
373
			return FieldManager::saveSettings($id, $settings);
374
		}
375
376
		/**
377
		 *
378
		 * This function allows Fields to cleanup any additional things before it is removed
379
		 * from the section.
380
		 * @return boolean
381
		 */
382
		public function tearDown()
383
		{
384
			self::removeSectionAssociation($this->get('id'));
385
			return parent::tearDown();
386
		}
387
		
388
		/**
389
		 * Generates the where filter for searching by entry id
390
		 *
391
		 * @param string $value
392
		 * @param @optional string $col
393
		 * @param @optional boolean $andOperation
394
		 */
395
		public function generateWhereFilter($value, $col = 'd', $andOperation = true)
396
		{
397
			$junction = $andOperation ? 'AND' : 'OR';
398
			if (!$value) {
399
				return "{$junction} (`{$col}`.`entries` IS NULL)";
400
			}
401
			return " {$junction} (`{$col}`.`entries` = '{$value}' OR 
402
					`{$col}`.`entries` LIKE '{$value},%' OR 
403
					`{$col}`.`entries` LIKE '%,{$value}' OR 
404
					`{$col}`.`entries` LIKE '%,{$value},%')";
405
		}
406
407
		/**
408
		 * Fetch the number of associated entries for a particular entry id
409
		 *
410
		 * @param string $value
411
		 */
412
		public function fetchAssociatedEntryCount($value)
413
		{
414
			if (!$value) {
415
				return 0;
416
			}
417
			$join = sprintf(" INNER JOIN `tbl_entries_data_%s` AS `d` ON `e`.id = `d`.`entry_id`", $this->get('id'));
418
			$where = $this->generateWhereFilter($value);
419
			
420
			$entries = EntryManager::fetch(null, $this->get('parent_section'), null, 0, $where, $join, false, false, array());
421
			
422
			return count($entries);
423
		}
424
		
425
		public function fetchAssociatedEntrySearchValue($data, $field_id = null, $parent_entry_id = null)
426
		{
427
			return $parent_entry_id;
428
		}
429
		
430
		public function findRelatedEntries($entry_id)
431
		{
432
			$joins = '';
433
			$where = '';
434
			$this->buildDSRetrievalSQL(array($entry_id), $joins, $where, true);
435
			
436
			$entries = EntryManager::fetch(null, $this->get('parent_section'), null, 0, $where, $joins, false, false, array());
437
			
438
			$ids = array();
439
			foreach ($entries as $key => $e) {
440
				$ids[] = $e['id'];
441
			}
442
			return $ids;
443
		}
444
		
445
		public function prepareAssociationsDrawerXMLElement(Entry $e, array $parent_association, $prepolutate = '')
446
		{
447
			$currentSection = SectionManager::fetch($parent_association['child_section_id']);
448
			$visibleCols = $currentSection->fetchVisibleColumns();
449
			$outputFieldId = current(array_keys($visibleCols));
450
			$outputField = FieldManager::fetch($outputFieldId);
451
			
452
			$value = $outputField->prepareReadableValue($e->getData($outputFieldId), $e->get('id'), true, __('None'));
453
			
454
			$li = new XMLElement('li');
455
			$li->setAttribute('class', 'field-' . $this->get('type'));
456
			$a = new XMLElement('a', strip_tags($value));
457
			$a->setAttribute('href', SYMPHONY_URL . '/publish/' . $parent_association['handle'] . '/edit/' . $e->get('id') . '/');
458
			$li->appendChild($a);
459
460
			return $li;
461
		}
462
		
463
		/**
464
		 * @param string $joins
465
		 * @param string $where
466
		 */
467
		public function buildDSRetrievalSQL($data, &$joins, &$where, $andOperation = false)
468
		{
469
			$field_id = $this->get('id');
470
			
471
			// REGEX filtering is a special case, and will only work on the first item
472
			// in the array. You cannot specify multiple filters when REGEX is involved.
473
			if (self::isFilterRegex($data[0])) {
474
				return $this->buildRegexSQL($data[0], array('entries'), $joins, $where);
475
			}
476
			
477
			$this->_key++;
478
			
479
			$where .= ' AND (1=' . ($andOperation ? '1' : '0') . ' ';
480
			
481
			$joins .= "
482
				INNER JOIN
483
					`tbl_entries_data_{$field_id}` AS `t{$field_id}_{$this->_key}`
484
					ON (`e`.`id` = `t{$field_id}_{$this->_key}`.`entry_id`)
485
			";
486
			
487
			foreach ($data as $value) {
488
				$where .= $this->generateWhereFilter($this->cleanValue($value), "t{$field_id}_{$this->_key}", $andOperation);
489
			}
490
			
491
			$where .= ')';
492
			
493
			return true; // this tells the DS Manager that filters are OK!!
494
		}
495
496
		/* ******* EVENTS ******* */
497
498
		public function getExampleFormMarkup()
499
		{
500
			$label = Widget::Label($this->get('label'));
501
			$label->appendChild(Widget::Input('fields['.$this->get('element_name').'][entries]', null, 'hidden'));
502
503
			return $label;
504
		}
505
506
507
		/* ******* DATA SOURCE ******* */
508
		
509
		private function fetchEntry($eId, $elements = array())
510
		{
511
			$entry = EntryManager::fetch($eId, null, 1, 0, null, null, false, true, $elements, false);
512
			if (!is_array($entry) || count($entry) !== 1) {
513
				return null;
514
			}
515
			return $entry[0];
516
		}
517
		
518
		public function fetchIncludableElements()
519
		{
520
			$label = $this->get('element_name');
521
			$elements = array_filter(array_map(trim, explode(self::SEPARATOR, trim($this->get('elements')))));
522
			$includedElements = array($label . ': *');
523
			foreach ($elements as $elem) {
524
				$elem = trim($elem);
525
				if ($elem !== '*') {
526
					$includedElements[] = $label . ': ' . $elem;
527
				}
528
			}
529
			return $includedElements;
530
		}
531
		
532
		/**
533
		 * Appends data into the XML tree of a Data Source
534
		 * @param $wrapper
535
		 * @param $data
536
		 */
537
		public function appendFormattedElement(&$wrapper, $data, $encode = false, $mode = null, $entry_id = null)
538
		{
539
			if(!is_array($data) || empty($data)) return;
540
541
			// root for all values
542
			$root = new XMLElement($this->get('element_name'));
543
			
544
			// selected items
545
			$entries = static::getEntries($data);
546
			
547
			// current linked entries
548
			$root->setAttribute('entries', $data['entries']);
549
			
550
			// available sections
551
			$root->setAttribute('sections', $this->get('sections'));
552
			
553
			// included elements
554
			$elements = static::parseElements($this);
555
			
556
			// DS mode
557
			if (!$mode) {
558
				$mode = '*';
559
			}
560
			
561
			$parentDeepness = General::intval($this->recursiveDeepness);
562
			$deepness = General::intval($this->get('deepness'));
563
			
564
			// both deepnesses are defined and parent restricts more
565
			if ($parentDeepness > 0 && $deepness > 0 && $parentDeepness < $deepness) {
566
				$deepness = $parentDeepness;
567
			}
568
			// parent is defined, current is not
569
			else if ($parentDeepness > 0 && $deepness < 1) {
570
				$deepness = $parentDeepness;
571
			}
572
			
573
			// cache recursive level because recursion might
574
			// change its value later on.
575
			$recursiveLevel = $this->recursiveLevel;
576
			
577
			// build entries
578
			foreach ($entries as $eId) {
579
				$item = new XMLElement('item');
580
				// output id
581
				$item->setAttribute('id', $eId);
582
				// output recursive level
583
				$item->setAttribute('level', $recursiveLevel);
584
				$item->setAttribute('max-level', $deepness);
585
				
586
				// max recursion check
587
				if ($deepness < 1 || $recursiveLevel < $deepness) {
588
					// current entry, without data
589
					$entry = $this->fetchEntry($eId);
590
					
591
					// entry not found...
592
					if (!$entry || empty($entry)) {
593
						$error = new XMLElement('error');
594
						$error->setAttribute('id', $eId);
595
						$error->setValue(__('Error: entry `%s` not found', array($eId)));
596
						$root->prependChild($error);
597
						continue;
598
					}
599
					
600
					// fetch section infos
601
					$sectionId = $entry->get('section_id');
602
					$section = $this->sectionsManager->fetch($sectionId);
603
					$sectionName = $section->get('handle');
604
					// cache fields info
605
					if (!isset($section->er_field_cache)) {
606
						$section->er_field_cache = $section->fetchFields();
607
					}
608
					
609
					// set section related attributes
610
					$item->setAttribute('section-id', $sectionId);
611
					$item->setAttribute('section', $sectionName);
612
					
613
					// Get the valid elements for this section only
614
					$validElements = $elements[$sectionName];
615
					
616
					// adjust the mode for the current section
617
					$curMode = $mode;
618
					
619
					// remove section name from current mode, i.e sectionName.field
620
					if (preg_match('/^(' . $sectionName . '\.)(.*)$/sU', $curMode)) {
621
						$curMode = preg_replace('/^' . $sectionName . '\./sU', '', $curMode);
622
					}
623
					// remove section name from current mode, i.e sectionName
624
					else if (preg_match('/^(' . $sectionName . ')$/sU', $curMode)) {
625
						$curMode = '*';
626
					}
627
					// section name was not found in mode
628
					else if ($curMode != '*') {
629
						// mode forbids this section
630
						$validElements = null;
631
					}
632
					
633
					// this section is not selected, bail out
634
					if (!is_array($validElements)) {
635
						$item->setAttribute('forbidden-by', $curMode);
636
						$root->appendChild($item);
637
						continue;
638
					}
639
					
640
					// selected fields for fetching
641
					$sectionElements = array();
0 ignored issues
show
Unused Code introduced by
$sectionElements is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
642
					
643
					// everything is allowed
644
					if (in_array('*', $validElements)) {
645
						if ($curMode !== '*') {
646
							// get only the mode
647
							$sectionElements = array($curMode);
648
						}
649
						else {
650
							// setting null = get all
651
							$sectionElements = null;
652
						}
653
					}
654
					// only use valid elements
655
					else {
656
						if ($curMode !== '*') {
657
							// is this field allowed ?
658
							if (self::isFieldIncluded($curMode, $validElements)) {
659
								// get only the mode
660
								$sectionElements = array($curMode);
661
							}
662
							else {
663
								// $curMode selects something outside of
664
								// the valid elements: select nothing
665
								$sectionElements = array();
666
							}
667
						}
668
						else {
669
							// use field's valid elements
670
							$sectionElements = $validElements;
671
						}
672
					}
673
					
674
					if (is_array($sectionElements) && empty($sectionElements)) {
675
						$item->setAttribute('selection-empty', 'yes');
676
						$item->setAttribute('forbidden-by', $curMode);
677
						$root->appendChild($item);
678
						continue;
679
					}
680
					
681
					// current entry again, but with data and the allowed schema
682
					$entry = $this->fetchEntry($eId, $sectionElements);
683
					
684
					// cache the entry data
685
					$entryData = $entry->getData();
686
					
687
					// for each field returned for this entry...
688
					foreach ($entryData as $fieldId => $data) {
689
						$filteredData = array_filter($data, function ($value) {
690
							return $value != null;
691
						});
692
						
693
						if (empty($filteredData)) {
694
							continue;
695
						}
696
						
697
						$field = $section->er_field_cache[$fieldId];
698
						$fieldName = $field->get('element_name');
699
						$fieldCurMode = self::extractMode($fieldName, $curMode);
700
						
701
						$parentIncludableElement = self::getSectionElementName($fieldName, $validElements);
702
						$parentIncludableElementMode = self::extractMode($fieldName, $parentIncludableElement);
703
						
704
						// Special treatments for ERF
705
						if ($field instanceof FieldEntry_relationship) {
706
							// Increment recursive level
707
							$field->recursiveLevel = $recursiveLevel + 1;
708
							$field->recursiveDeepness = $deepness;
709
						}
710
						
711
						$submodes = null;
0 ignored issues
show
Unused Code introduced by
$submodes is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
712
						if ($parentIncludableElementMode == null) {
713
							if ($fieldCurMode == null) {
714
								$submodes = null;
715
							}
716
							else {
717
								$submodes = array($fieldCurMode);
718
							}
719
						}
720
						else {
721
							if ($fieldCurMode == null || $fieldCurMode == $parentIncludableElementMode) {
722
								$submodes = array($parentIncludableElementMode);
723
							}
724
							else {
725
								$item->setAttribute('selection-mode-empty', 'yes');
726
								$submodes = array();
727
							}
728
						}
729
						
730
						// current selection does not specify a mode
731 View Code Duplication
						if ($submodes == null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
732
							$submodes = array_map(function ($fieldIncludableElement) use ($fieldName) {
733
								return FieldEntry_relationship::extractMode($fieldName, $fieldIncludableElement);
734
							}, $field->fetchIncludableElements());
735
						}
736
						
737
						foreach ($submodes as $submode) {
738
							$field->appendFormattedElement($item, $data, $encode, $submode, $eId);
739
						}
740
					}
741
					// output current mode
742
					$item->setAttribute('matched-element', $curMode);
743
					// no field selected
744
					if (is_array($sectionElements) && empty($sectionElements)) {
745
						$item->setAttribute('empty-selection', 'yes');
746
					}
747
				}
748
				// append item when done
749
				$root->appendChild($item);
750
			} // end each entries
751
			
752
			// output mode for this field
753
			$root->setAttribute('data-source-mode', $mode);
754
			$root->setAttribute('field-included-elements', $this->get('elements'));
755
			
756
			// add all our data to the wrapper;
757
			$wrapper->appendChild($root);
758
			
759
			// clean up
760
			$this->recursiveLevel = 1;
761
			$this->recursiveDeepness = null;
762
		}
763
764
		public function getParameterPoolValue(array $data, $entry_id = null)
765
		{
766
			if(!is_array($data) || empty($data)) return;
767
			return static::getEntries($data);
768
		}
769
770
		/* ********* Utils *********** */
771
		
772
		/**
773
		 * Return true if $fieldName is allowed in $sectionElements
774
		 * @param string $fieldName
775
		 * @param string $sectionElements
776
		 * @return bool
777
		 */
778
		public static function isFieldIncluded($fieldName, $sectionElements)
779
		{
780
			return self::getSectionElementName($fieldName, $sectionElements) !== null;
781
		}
782
783
		public static function getSectionElementName($fieldName, $sectionElements)
784
		{
785
			if (is_array($sectionElements)) {
786
				foreach ($sectionElements as $element) {
787
					if ($element == '*') {
788
						return $fieldName;
789
					}
790
					if ($fieldName == $element || preg_match('/^' . $fieldName . '\s*:/sU', $element)) {
791
						return $element;
792
					}
793
				}
794
			}
795
			return null;
796
		}
797
		
798
		public static function parseElements($field)
799
		{
800
			$elements = array();
801
			$exElements = array_map(trim, explode(self::SEPARATOR, $field->get('elements')));
802
			
803
			if (in_array('*', $exElements)) {
804
				$sections = array_map(trim, explode(self::SEPARATOR, $field->get('sections')));
805
				$sections = SectionManager::fetch($sections);
806
				return array_reduce($sections, function ($result, $section) {
807
					$result[$section->get('handle')] = array('*');
808
					return $result;
809
				}, array());
810
			}
811
			
812
			foreach ($exElements as $value) {
813
				if (!$value) {
814
					continue;
815
				}
816
				// sectionName.fieldName or sectionName.*
817
				$parts = array_map(trim, explode('.', $value));
818
				// first time seeing this section
819
				if (!isset($elements[$parts[0]])) {
820
					$elements[$parts[0]] = array();
821
				}
822
				// we have a value after the dot
823
				if (isset($parts[1]) && !!$parts[1]) {
824
					$elements[$parts[0]][] = $parts[1];
825
				}
826
				// sectionName only
827
				else if (!isset($parts[1])) {
828
					$elements[$parts[0]][] = '*';
829
				}
830
			}
831
			
832
			return $elements;
833
		}
834
835
		public static function extractMode($fieldName, $mode)
836
		{
837
			$pattern = '/^' . $fieldName . '\s*:\s*/s';
838
			if (!preg_match($pattern, $mode)) {
839
				return null;
840
			}
841
			$mode = preg_replace($pattern, '', $mode, 1);
842
			if ($mode === '*') {
843
				return null;
844
			}
845
			return $mode;
846
		}
847
848
		/**
849
		 * @param string $prefix
850
		 * @param string $name
851
		 * @param @optional bool $multiple
852
		 */
853
		private function createFieldName($prefix, $name, $multiple = false)
854
		{
855
			$name = "fields[$prefix][$name]";
856
			if ($multiple) {
857
				$name .= '[]';
858
			}
859
			return $name;
860
		}
861
		
862
		/**
863
		 * @param string $name
864
		 */
865
		private function createSettingsFieldName($name, $multiple = false)
866
		{
867
			return $this->createFieldName($this->get('sortorder'), $name, $multiple);
868
		}
869
		
870
		/**
871
		 * @param string $name
872
		 */
873
		private function createPublishFieldName($name, $multiple = false)
874
		{
875
			return $this->createFieldName($this->get('element_name'), $name, $multiple);
876
		}
877
		
878
		private function getSelectedSectionsArray()
879
		{
880
			$selectedSections = $this->get('sections');
881
			if (!is_array($selectedSections)) {
882
				if (is_string($selectedSections) && strlen($selectedSections) > 0) {
883
					$selectedSections = explode(self::SEPARATOR, $selectedSections);
884
				}
885
				else {
886
					$selectedSections = array();
887
				}
888
			}
889
			return $selectedSections;
890
		}
891
		
892
		private function buildSectionSelect($name)
893
		{
894
			$sections = SectionManager::fetch();
895
			$options = array();
896
			$selectedSections = $this->getSelectedSectionsArray();
897
			
898
			foreach ($sections as $section) {
899
				$driver = $section->get('id');
900
				$selected = in_array($driver, $selectedSections);
901
				$options[] = array($driver, $selected, $section->get('name'));
902
			}
903
			
904
			return Widget::Select($name, $options, array('multiple' => 'multiple'));
905
		} 
906
		
907
		private function appendSelectionSelect(&$wrapper)
908
		{
909
			$name = $this->createSettingsFieldName('sections', true);
910
911
			$input = $this->buildSectionSelect($name);
912
			$input->setAttribute('class', 'entry_relationship-sections');
913
914
			$label = Widget::Label();
915
			$label->setAttribute('class', 'column');
916
917
			$label->setValue(__('Available sections %s', array($input->generate())));
918
919
			$wrapper->appendChild($label);
920
		}
921
922
		private function createEntriesList($entries)
923
		{
924
			$wrap = new XMLElement('div');
925
			$wrap->setAttribute('class', 'frame collapsible orderable' . (count($entries) > 0 ? '' : ' empty'));
926
			
927
			$list = new XMLElement('ul');
928
			$list->setAttribute('class', '');
929
			if ($this->is('allow_collapse')) {
930
				$list->setAttribute('data-collapsible', '');
931
			}
932
			
933
			$wrap->appendChild($list);
934
			
935
			return $wrap;
936
		}
937
		
938
		private function createEntriesHiddenInput($data)
939
		{
940
			$hidden = new XMLElement('input', null, array(
941
				'type' => 'hidden',
942
				'name' => $this->createPublishFieldName('entries'),
943
				'value' => $data['entries']
944
			));
945
			
946
			return $hidden;
947
		}
948
		
949
		private function createPublishMenu($sections)
950
		{
951
			$wrap = new XMLElement('fieldset');
952
			$wrap->setAttribute('class', 'single');
953
			
954
			if ($this->is('allow_new') || $this->is('allow_link')) {
955
				$selectWrap = new XMLElement('div');
956
				$selectWrap->appendChild(new XMLElement('span', __('Related section: ')));
957
				$options = array();
958
				foreach ($sections as $section) {
959
					$options[] = array($section->get('handle'), false, $section->get('name'));
960
				}
961
				$select = Widget::Select('', $options, array('class' => 'sections'));
962
				$selectWrap->appendChild($select);
963
				$wrap->appendChild($selectWrap);
964
			}
965 View Code Duplication
			if ($this->is('allow_new')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
966
				$wrap->appendChild(new XMLElement('button', __('Create new'), array(
967
					'type' => 'button',
968
					'class' => 'create',
969
					'data-create' => '',
970
				)));
971
			}
972 View Code Duplication
			if ($this->is('allow_link')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
973
				$wrap->appendChild(new XMLElement('button', __('Link to entry'), array(
974
					'type' => 'button',
975
					'class' => 'link',
976
					'data-link' => '',
977
				)));
978
			}
979
			
980
			return $wrap;
981
		}
982
983
		/* ********* UI *********** */
984
		
985
		/**
986
		 *
987
		 * Builds the UI for the field's settings when creating/editing a section
988
		 * @param XMLElement $wrapper
989
		 * @param array $errors
990
		 */
991
		public function displaySettingsPanel(&$wrapper, $errors=NULL)
992
		{
993
			/* first line, label and such */
994
			parent::displaySettingsPanel($wrapper, $errors);
995
			
996
			// sections
997
			$sections = new XMLElement('fieldset');
998
			
999
			$this->appendSelectionSelect($sections);
1000
			if (is_array($errors) && isset($errors['sections'])) {
1001
				$sections = Widget::Error($sections, $errors['sections']);
1002
			}
1003
			$wrapper->appendChild($sections);
1004
			
1005
			// elements
1006
			$elements = new XMLElement('div');
1007
			$element = Widget::Label();
1008
			$element->setValue(__('Included elements in Data Sources and Backend Templates'));
1009
			$element->setAttribute('class', 'column');
1010
			$element->appendChild(Widget::Input($this->createSettingsFieldName('elements'), $this->get('elements'), 'text', array(
1011
				'class' => 'entry_relationship-elements'
1012
			)));
1013
			$elements->appendChild($element);
1014
			$elements_choices = new XMLElement('ul', null, array('class' => 'tags singular entry_relationship-field-choices'));
1015
			
1016
			$elements->appendChild($elements_choices);
1017
			$wrapper->appendChild($elements);
1018
			
1019
			// limit entries
1020
			$limits = new XMLElement('fieldset');
1021
			$limits->appendChild(new XMLElement('legend', __('Limits')));
1022
			$limits_cols = new XMLElement('div');
1023
			$limits_cols->setAttribute('class', 'three columns');
1024
			// min
1025
			$limit_min = Widget::Label();
1026
			$limit_min->setValue(__('Minimum count of entries in this field'));
1027
			$limit_min->setAttribute('class', 'column');
1028
			$limit_min->appendChild(Widget::Input($this->createSettingsFieldName('min_entries'), $this->get('min_entries'), 'number', array(
1029
				'min' => 0,
1030
				'max' => 99999
1031
			)));
1032
			$limits_cols->appendChild($limit_min);
1033
			// max
1034
			$limit_max = Widget::Label();
1035
			$limit_max->setValue(__('Maximum count of entries in this field'));
1036
			$limit_max->setAttribute('class', 'column');
1037
			$limit_max->appendChild(Widget::Input($this->createSettingsFieldName('max_entries'), $this->get('max_entries'), 'number', array(
1038
				'min' => 0,
1039
				'max' => 99999
1040
			)));
1041
			$limits_cols->appendChild($limit_max);
1042
			
1043
			// deepness
1044
			$deepness = Widget::Label();
1045
			$deepness->setValue(__('Maximum level of recursion in Data Sources'));
1046
			$deepness->setAttribute('class', 'column');
1047
			$deepness->appendChild(Widget::Input($this->createSettingsFieldName('deepness'), $this->get('deepness'), 'number', array(
1048
				'min' => 0,
1049
				'max' => 99
1050
			)));
1051
			$limits_cols->appendChild($deepness);
1052
			$limits->appendChild($limits_cols);
1053
			$wrapper->appendChild($limits);
1054
			
1055
			// xsl
1056
			$xsl = new XMLElement('fieldset');
1057
			$xsl->appendChild(new XMLElement('legend', __('Backend XSL templates options')));
1058
			$xsl_cols = new XMLElement('div');
1059
			$xsl_cols->setAttribute('class', 'three columns');
1060
			
1061
			// xsl mode
1062
			$xslmode = Widget::Label();
1063
			$xslmode->setValue(__('XSL mode for entries content template'));
1064
			$xslmode->setAttribute('class', 'column');
1065
			$xslmode->appendChild(Widget::Input($this->createSettingsFieldName('mode'), $this->get('mode'), 'text'));
1066
			$xsl_cols->appendChild($xslmode);
1067
			// xsl table mode
1068
			$xslmodetable = Widget::Label();
1069
			$xslmodetable->setValue(__('XSL mode for publish table value'));
1070
			$xslmodetable->setAttribute('class', 'column');
1071
			$xslmodetable->appendChild(Widget::Input($this->createSettingsFieldName('mode_table'), $this->get('mode_table'), 'text'));
1072
			$xsl_cols->appendChild($xslmodetable);
1073
			
1074
			$xsl->appendChild($xsl_cols);
1075
			$wrapper->appendChild($xsl);
1076
			
1077
			// permissions
1078
			$permissions = new XMLElement('fieldset');
1079
			$permissions->appendChild(new XMLElement('legend', __('Permissions')));
1080
			$permissions_cols = new XMLElement('div');
1081
			$permissions_cols->setAttribute('class', 'three columns');
1082
			$permissions_cols->appendChild($this->createCheckbox('allow_new', 'Show new button'));
1083
			$permissions_cols->appendChild($this->createCheckbox('allow_edit', 'Show edit button'));
1084
			$permissions_cols->appendChild($this->createCheckbox('allow_link', 'Show link button'));
1085
			$permissions_cols->appendChild($this->createCheckbox('allow_delete', 'Show delete button'));
1086
			$permissions_cols->appendChild($this->createCheckbox('allow_collapse', 'Allow content collapsing'));
1087
			$permissions->appendChild($permissions_cols);
1088
			$wrapper->appendChild($permissions);
1089
			
1090
			// footer
1091
			$this->appendStatusFooter($wrapper);
1092
			$this->appendShowAssociationCheckbox($wrapper);
1093
		}
1094
		
1095
		/**
1096
		 * @param string $fieldName
1097
		 * @param string $text
1098
		 */
1099
		private function createCheckbox($fieldName, $text) {
1100
			$chk = Widget::Label();
1101
			$chk->setAttribute('class', 'column');
1102
			$attrs = null;
1103
			if ($this->get($fieldName) == 'yes') {
1104
				$attrs = array('checked' => 'checked');
1105
			}
1106
			$chk->appendChild(Widget::Input($this->createSettingsFieldName($fieldName), 'yes', 'checkbox', $attrs));
1107
			$chk->setValue(__($text));
1108
			return $chk;
1109
		}
1110
1111
		/**
1112
		 *
1113
		 * Builds the UI for the publish page
1114
		 * @param XMLElement $wrapper
1115
		 * @param mixed $data
1116
		 * @param mixed $flagWithError
1117
		 * @param string $fieldnamePrefix
1118
		 * @param string $fieldnamePostfix
1119
		 */
1120
		public function displayPublishPanel(&$wrapper, $data=NULL, $flagWithError=NULL, $fieldnamePrefix=NULL, $fieldnamePostfix=NULL, $entry_id = null)
1121
		{
1122
			$entriesId = array();
1123
			$sectionsId = $this->getSelectedSectionsArray();
1124
			
1125
			if ($data['entries'] != null) {
1126
				$entriesId = static::getEntries($data);
1127
			}
1128
			
1129
			$sectionsId = array_map(array('General', 'intval'), $sectionsId);
1130
			$sections = SectionManager::fetch($sectionsId);
1131
			
1132
			$label = Widget::Label($this->get('label'));
1133
			$notes = '';
1134
			
1135
			// min note
1136
			if ($this->getInt('min_entries') > 0) {
1137
				$notes .= __('Minimum number of entries: <b>%s</b>. ', array($this->get('min_entries')));
1138
			}
1139
			// max note
1140
			if ($this->getInt('max_entries') > 0) {
1141
				$notes .= __('Maximum number of entries: <b>%s</b>. ', array($this->get('max_entries')));
1142
			}
1143
			// not required note
1144
			if (!$this->isRequired()) {
1145
				$notes .= __('Optional');
1146
			}
1147
			// append notes
1148
			if ($notes) {
1149
				$label->appendChild(new XMLElement('i', $notes));
1150
			}
1151
			
1152
			// label error management
1153
			if ($flagWithError != NULL) {
1154
				$wrapper->appendChild(Widget::Error($label, $flagWithError));
1155
			} else {
1156
				$wrapper->appendChild($label);
1157
			}
1158
			
1159
			$wrapper->appendChild($this->createEntriesList($entriesId));
1160
			$wrapper->appendChild($this->createPublishMenu($sections));
1161
			$wrapper->appendChild($this->createEntriesHiddenInput($data));
1162
			$wrapper->setAttribute('data-value', $data['entries']);
1163
			$wrapper->setAttribute('data-field-id', $this->get('id'));
1164
			$wrapper->setAttribute('data-field-label', $this->get('label'));
1165
			$wrapper->setAttribute('data-min', $this->get('min_entries'));
1166
			$wrapper->setAttribute('data-max', $this->get('max_entries'));
1167
			if (isset($_REQUEST['debug'])) {
1168
				$wrapper->setAttribute('data-debug', true);
1169
			}
1170
		}
1171
1172
		/**
1173
		 * @param integer $count
1174
		 */
1175
		private static function formatCount($count)
1176
		{
1177
			if ($count == 0) {
1178
				return __('No item');
1179
			} else if ($count == 1) {
1180
				return __('1 item');
1181
			}
1182
			return __('%s items', array($count));
1183
		}
1184
1185
		/**
1186
		 *
1187
		 * Return a plain text representation of the field's data
1188
		 * @param array $data
1189
		 * @param int $entry_id
1190
		 */
1191
		public function prepareTextValue($data, $entry_id = null)
1192
		{
1193
			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...
1194
				return '';
1195
			}
1196
			return $data['entries'];
1197
		}
1198
1199
		/**
1200
		 * Format this field value for display as readable text value.
1201
		 *
1202
		 * @param array $data
1203
		 *  an associative array of data for this string. At minimum this requires a
1204
		 *  key of 'value'.
1205
		 * @param integer $entry_id (optional)
1206
		 *  An option entry ID for more intelligent processing. Defaults to null.
1207
		 * @param string $defaultValue (optional)
1208
		 *  The value to use when no plain text representation of the field's data
1209
		 *  can be made. Defaults to null.
1210
		 * @return string
1211
		 *  the readable text summary of the values of this field instance.
1212
		 */
1213
		public function prepareReadableValue($data, $entry_id = null, $truncate = false, $defaultValue = 'None')
1214
		{
1215
			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...
1216
				return __($defaultValue);
1217
			}
1218
			$entries = static::getEntries($data);
1219
			$realEntries = array();
1220
			foreach ($entries as $entryId) {
1221
				$e = EntryManager::fetch($entryId);
1222
				if (is_array($e) && !empty($e)) {
1223
					$realEntries = array_merge($realEntries, $e);
1224
				}
1225
			}
1226
			$count = count($entries);
1227
			$realCount = count($realEntries);
1228
			if ($count === $realCount) {
1229
				return self::formatCount($count);
1230
			}
1231
			return self::formatCount($realCount) . ' (' . self::formatCount($count - $realCount) . ' not found)';
1232
		}
1233
1234
		/**
1235
		 * Format this field value for display in the publish index tables.
1236
		 *
1237
		 * @param array $data
1238
		 *  an associative array of data for this string. At minimum this requires a
1239
		 *  key of 'value'.
1240
		 * @param XMLElement $link (optional)
1241
		 *  an XML link structure to append the content of this to provided it is not
1242
		 *  null. it defaults to null.
1243
		 * @param integer $entry_id (optional)
1244
		 *  An option entry ID for more intelligent processing. defaults to null
1245
		 * @return string
1246
		 *  the formatted string summary of the values of this field instance.
1247
		 */
1248
		public function prepareTableValue($data, XMLElement $link = null, $entry_id = null)
1249
		{
1250
			$value = $this->prepareReadableValue($data, $entry_id, false, __('None'));
1251
1252
			if ($link) {
1253
				$link->setValue($value);
1254
				return $link->generate();
1255
			}
1256
			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...
1257
				$entries = static::getEntries($data);
1258
				$cellcontent = '';
1259
				foreach ($entries as $child_entry_id) {
1260
					$entry = current($this->entryManager->fetch($child_entry_id));
1261
					$section = $this->sectionManager->fetch($entry->get('section_id'));
1262
					$content = ERFXSLTUTilities::entryToXml($this, $entry, $section->get('handle'), $section->fetchFields(), 'mode_table');
1263
					if ($content) {
1264
						$cellcontent .= $content;
1265
					}
1266
				}
1267
				
1268
				if (General::strlen(trim($cellcontent))) {
1269
					return $cellcontent;
1270
				}
1271
			}
1272
1273
			return $value;
1274
		}
1275
1276
		/* ********* SQL Data Definition ************* */
1277
1278
		/**
1279
		 *
1280
		 * Creates table needed for entries of individual fields
1281
		 */
1282
		public function createTable()
1283
		{
1284
			$id = $this->get('id');
1285
1286
			return Symphony::Database()->query("
1287
				CREATE TABLE `tbl_entries_data_$id` (
1288
					`id` int(11) 		unsigned NOT NULL AUTO_INCREMENT,
1289
					`entry_id` 			int(11) unsigned NOT NULL,
1290
					`entries` 			text COLLATE utf8_unicode_ci NULL,
1291
					PRIMARY KEY  (`id`),
1292
					UNIQUE KEY `entry_id` (`entry_id`)
1293
				) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1294
			");
1295
		}
1296
1297
		/**
1298
		 * Creates the table needed for the settings of the field
1299
		 */
1300
		public static function createFieldTable()
1301
		{
1302
			$tbl = self::FIELD_TBL_NAME;
1303
1304
			return Symphony::Database()->query("
1305
				CREATE TABLE IF NOT EXISTS `$tbl` (
1306
					`id` 			int(11) unsigned NOT NULL AUTO_INCREMENT,
1307
					`field_id` 		int(11) unsigned NOT NULL,
1308
					`sections`		varchar(255) NULL COLLATE utf8_unicode_ci,
1309
					`show_association` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1310
					`deepness` 		int(2) unsigned NULL,
1311
					`elements` 		varchar(1024) NULL COLLATE utf8_unicode_ci,
1312
					`mode`			varchar(50) NULL COLLATE utf8_unicode_ci,
1313
					`min_entries`	int(5) unsigned NULL,
1314
					`max_entries`	int(5) unsigned NULL,
1315
					`allow_edit` 	enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1316
					`allow_new` 	enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1317
					`allow_link` 	enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1318
					`allow_delete` 	enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'no',
1319
					PRIMARY KEY (`id`),
1320
					UNIQUE KEY `field_id` (`field_id`)
1321
				) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
1322
			");
1323
		}
1324
		
1325
		public static function update_102()
1326
		{
1327
			$tbl = self::FIELD_TBL_NAME;
1328
			$sql = "
1329
				ALTER TABLE `$tbl`
1330
					ADD COLUMN `allow_edit` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1331
					ADD COLUMN `allow_new` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes',
1332
					ADD COLUMN `allow_link` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes'
1333
					AFTER `max_entries`
1334
			";
1335
			$addColumns = Symphony::Database()->query($sql);
1336
			if (!$addColumns) {
1337
				return false;
1338
			}
1339
1340
			$fields = FieldManager::fetch(null, null, null, 'id', 'entry_relationship');
1341
			if (!empty($fields) && is_array($fields)) {
1342
				foreach ($fields as $fieldId => $field) {
1343
					$sql = "ALTER TABLE `tbl_entries_data_$fieldId` MODIFY `entries` TEXT";
1344
					if (!Symphony::Database()->query($sql)) {
1345
						throw new Exception(__('Could not update table `tbl_entries_data_%s`.', array($fieldId)));
1346
					}
1347
				}
1348
			}
1349
			return true;
1350
		}
1351
		
1352
		public static function update_103()
1353
		{
1354
			$tbl = self::FIELD_TBL_NAME;
1355
			$sql = "
1356
				ALTER TABLE `$tbl`
1357
					ADD COLUMN `allow_delete` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'no'
1358
						AFTER `allow_link`
1359
			";
1360
			return Symphony::Database()->query($sql);
1361
		}
1362
		
1363
		public static function update_200()
1364
		{
1365
			$tbl = self::FIELD_TBL_NAME;
1366
			$sql = "
1367
				ALTER TABLE `$tbl`
1368
					ADD COLUMN `allow_collapse` enum('yes','no') NOT NULL COLLATE utf8_unicode_ci DEFAULT 'yes'
1369
						AFTER `allow_delete`,
1370
					ADD COLUMN `mode_table` varchar(50) NULL COLLATE utf8_unicode_ci DEFAULT NULL
1371
						AFTER `mode`
1372
			";
1373
			return Symphony::Database()->query($sql);
1374
		}
1375
		
1376
		/**
1377
		 *
1378
		 * Drops the table needed for the settings of the field
1379
		 */
1380
		public static function deleteFieldTable()
1381
		{
1382
			$tbl = self::FIELD_TBL_NAME;
1383
			
1384
			return Symphony::Database()->query("
1385
				DROP TABLE IF EXISTS `$tbl`
1386
			");
1387
		}
1388
		
1389
		private static function removeSectionAssociation($child_field_id)
1390
		{
1391
			return Symphony::Database()->delete('tbl_sections_association', "`child_section_field_id` = {$child_field_id}");
1392
		}
1393
	}