Completed
Push — dev ( bcd858...35c279 )
by Nicolas
02:02
created

FieldEntry_relationship   D

Complexity

Total Complexity 193

Size/Duplication

Total Lines 1355
Duplicated Lines 1.4 %

Coupling/Cohesion

Components 2
Dependencies 1
Metric Value
wmc 193
lcom 2
cbo 1
dl 19
loc 1355
rs 4.4102

58 Methods

Rating   Name   Duplication   Size   Complexity  
A fetchEntry() 0 8 3
A fetchIncludableElements() 0 13 3
B __construct() 0 39 1
A isSortable() 0 4 1
A canFilter() 0 4 1
A canPublishFilter() 0 4 1
A canImport() 0 4 1
A canPrePopulate() 0 4 1
A mustBeUnique() 0 4 1
A allowDatasourceOutputGrouping() 0 4 1
A requiresSQLGrouping() 0 4 1
A allowDatasourceParamOutput() 0 4 1
A getInt() 0 4 1
A is() 0 4 1
A isRequired() 0 4 1
A getEntries() 0 4 1
C checkPostFieldData() 0 29 12
B processRawFieldData() 0 23 5
F setFromPOST() 0 28 13
A checkFields() 0 15 4
B commit() 0 58 6
A tearDown() 0 5 1
A generateWhereFilter() 0 11 3
A fetchAssociatedEntryCount() 0 12 2
A fetchAssociatedEntrySearchValue() 0 4 1
A findRelatedEntries() 0 14 2
A prepareAssociationsDrawerXMLElement() 0 17 1
B buildDSRetrievalSQL() 0 28 4
A getExampleFormMarkup() 0 7 1
F appendFormattedElement() 5 229 36
A getParameterPoolValue() 0 5 3
A isFieldIncluded() 0 4 1
B getSectionElementName() 0 14 6
C parseElements() 0 36 8
A extractMode() 0 12 3
A createFieldName() 0 8 2
A createSettingsFieldName() 0 4 1
A createPublishFieldName() 0 4 1
A getSelectedSectionsArray() 0 13 4
A buildSectionSelect() 0 14 2
A appendSelectionSelect() 0 14 1
A createEntriesList() 0 15 3
A createEntriesHiddenInput() 0 10 1
B createPublishMenu() 14 33 6
B displaySettingsPanel() 0 103 3
A createCheckbox() 0 11 2
C displayPublishPanel() 0 51 8
A formatCount() 0 9 3
A prepareTextValue() 0 7 3
B prepareReadableValue() 0 20 7
A prepareTableValue() 0 11 2
A createTable() 0 14 1
B createFieldTable() 0 24 1
B update_102() 0 26 6
A update_103() 0 10 1
A update_200() 0 12 1
A deleteFieldTable() 0 8 1
A removeSectionAssociation() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like FieldEntry_relationship often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FieldEntry_relationship, and based on these observations, apply Extract Interface, too.

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