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