1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @package toolkit |
5
|
|
|
*/ |
6
|
|
|
/** |
7
|
|
|
* The `FieldManager` class is responsible for managing all fields types in Symphony. |
8
|
|
|
* Fields are stored on the file system either in the `/fields` folder of `TOOLKIT` or |
9
|
|
|
* in a `fields` folder in an extension directory. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
class FieldManager implements FileResource |
13
|
|
|
{ |
14
|
|
|
/** |
15
|
|
|
* An array of all the objects that the Manager is responsible for. |
16
|
|
|
* Defaults to an empty array. |
17
|
|
|
* @var array |
18
|
|
|
*/ |
19
|
|
|
protected static $_pool = array(); |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* An array of all fields whose have been created by ID |
23
|
|
|
* @var array |
24
|
|
|
*/ |
25
|
|
|
private static $_initialiased_fields = array(); |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Given the filename of a Field, return it's handle. This will remove |
29
|
|
|
* the Symphony conventions of `field.*.php` |
30
|
|
|
* |
31
|
|
|
* @param string $filename |
32
|
|
|
* The filename of the Field |
33
|
|
|
* @return string |
34
|
|
|
*/ |
35
|
|
|
public static function __getHandleFromFilename($filename) |
36
|
|
|
{ |
37
|
|
|
return preg_replace(array('/^field./i', '/.php$/i'), '', $filename); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Given a type, returns the full class name of a Field. Fields use a |
42
|
|
|
* 'field' prefix |
43
|
|
|
* |
44
|
|
|
* @param string $type |
45
|
|
|
* A field handle |
46
|
|
|
* @return string |
47
|
|
|
*/ |
48
|
|
|
public static function __getClassName($type) |
49
|
|
|
{ |
50
|
|
|
return 'field' . $type; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Finds a Field by type by searching the `TOOLKIT . /fields` folder and then |
55
|
|
|
* any fields folders in the installed extensions. The function returns |
56
|
|
|
* the path to the folder where the field class resides. |
57
|
|
|
* |
58
|
|
|
* @param string $type |
59
|
|
|
* The field handle, that is, `field.{$handle}.php` |
60
|
|
|
* @return string|boolean |
61
|
|
|
*/ |
62
|
|
|
public static function __getClassPath($type) |
63
|
|
|
{ |
64
|
|
|
if (is_file(TOOLKIT . "/fields/field.{$type}.php")) { |
|
|
|
|
65
|
|
|
return TOOLKIT . '/fields'; |
66
|
|
|
} else { |
67
|
|
|
$extensions = Symphony::ExtensionManager()->listInstalledHandles(); |
68
|
|
|
|
69
|
|
|
if (is_array($extensions) && !empty($extensions)) { |
70
|
|
|
foreach ($extensions as $e) { |
71
|
|
|
if (is_file(EXTENSIONS . "/{$e}/fields/field.{$type}.php")) { |
|
|
|
|
72
|
|
|
return EXTENSIONS . "/{$e}/fields"; |
|
|
|
|
73
|
|
|
} |
74
|
|
|
} |
75
|
|
|
} |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
return false; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Given a field type, return the path to it's class |
83
|
|
|
* |
84
|
|
|
* @see __getClassPath() |
85
|
|
|
* @param string $type |
86
|
|
|
* The handle of the field to load (it's type) |
87
|
|
|
* @return string |
88
|
|
|
*/ |
89
|
|
|
public static function __getDriverPath($type) |
90
|
|
|
{ |
91
|
|
|
return self::__getClassPath($type) . "/field.{$type}.php"; |
|
|
|
|
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* This function is not implemented by the `FieldManager` class |
96
|
|
|
* |
97
|
|
|
* @return boolean |
98
|
|
|
*/ |
99
|
|
|
public static function about($name) |
100
|
|
|
{ |
101
|
|
|
return false; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Given an associative array of fields, insert them into the database |
106
|
|
|
* returning the resulting Field ID if successful, or false if there |
107
|
|
|
* was an error. As fields are saved in order on a section, a query is |
108
|
|
|
* made to determine the sort order of this field to be current sort order |
109
|
|
|
* +1. |
110
|
|
|
* |
111
|
|
|
* @throws DatabaseException |
112
|
|
|
* @param array $fields |
113
|
|
|
* Associative array of field names => values for the Field object |
114
|
|
|
* @return integer|boolean |
115
|
|
|
* Returns a Field ID of the created Field on success, false otherwise. |
116
|
|
|
*/ |
117
|
|
|
public static function add(array $fields) |
118
|
|
|
{ |
119
|
|
|
if (!isset($fields['sortorder'])) { |
120
|
|
|
$fields['sortorder'] = self::fetchNextSortOrder(); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
if (!Symphony::Database()->insert($fields, 'tbl_fields')) { |
124
|
|
|
return false; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
return Symphony::Database()->getInsertID(); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Save the settings for a Field given it's `$field_id` and an associative |
132
|
|
|
* array of settings. |
133
|
|
|
* |
134
|
|
|
* @throws DatabaseException |
135
|
|
|
* @since Symphony 2.3 |
136
|
|
|
* @param integer $field_id |
137
|
|
|
* The ID of the field |
138
|
|
|
* @param array $settings |
139
|
|
|
* An associative array of settings, where the key is the column name |
140
|
|
|
* and the value is the value. |
141
|
|
|
* @return boolean |
142
|
|
|
* true on success, false on failure |
143
|
|
|
*/ |
144
|
|
|
public static function saveSettings($field_id, $settings) |
145
|
|
|
{ |
146
|
|
|
// Get the type of this field: |
147
|
|
|
$type = self::fetchFieldTypeFromID($field_id); |
148
|
|
|
|
149
|
|
|
// Delete the original settings: |
150
|
|
|
Symphony::Database()->delete("tbl_fields_$type", sprintf("`field_id` = %d LIMIT 1", $field_id)); |
|
|
|
|
151
|
|
|
|
152
|
|
|
// Insert the new settings into the type table: |
153
|
|
|
if (!isset($settings['field_id'])) { |
154
|
|
|
$settings['field_id'] = $field_id; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
return Symphony::Database()->insert($settings, 'tbl_fields_'.$type); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Given a Field ID and associative array of fields, update an existing Field |
162
|
|
|
* row in the `tbl_fields`table. Returns boolean for success/failure |
163
|
|
|
* |
164
|
|
|
* @throws DatabaseException |
165
|
|
|
* @param integer $id |
166
|
|
|
* The ID of the Field that should be updated |
167
|
|
|
* @param array $fields |
168
|
|
|
* Associative array of field names => values for the Field object |
169
|
|
|
* This array does need to contain every value for the field object, it |
170
|
|
|
* can just be the changed values. |
171
|
|
|
* @return boolean |
172
|
|
|
*/ |
173
|
|
|
public static function edit($id, array $fields) |
174
|
|
|
{ |
175
|
|
|
if (!Symphony::Database()->update($fields, "tbl_fields", sprintf(" `id` = %d", $id))) { |
|
|
|
|
176
|
|
|
return false; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
return true; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Given a Field ID, delete a Field from Symphony. This will remove the field from |
184
|
|
|
* the fields table, all of the data stored in this field's `tbl_entries_data_$id` any |
185
|
|
|
* existing section associations. This function additionally call the Field's `tearDown` |
186
|
|
|
* method so that it can cleanup any additional settings or entry tables it may of created. |
187
|
|
|
* |
188
|
|
|
* @since Symphony 2.7.0 it will check to see if the field requires a data table before |
189
|
|
|
* blindly trying to delete it. |
190
|
|
|
* |
191
|
|
|
* @throws DatabaseException |
192
|
|
|
* @throws Exception |
193
|
|
|
* @param integer $id |
194
|
|
|
* The ID of the Field that should be deleted |
195
|
|
|
* @return boolean |
196
|
|
|
*/ |
197
|
|
|
public static function delete($id) |
198
|
|
|
{ |
199
|
|
|
$existing = self::fetch($id); |
200
|
|
|
$existing->tearDown(); |
201
|
|
|
|
202
|
|
|
Symphony::Database()->delete('tbl_fields', sprintf(" `id` = %d", $id)); |
|
|
|
|
203
|
|
|
Symphony::Database()->delete('tbl_fields_'.$existing->handle(), sprintf(" `field_id` = %d", $id)); |
|
|
|
|
204
|
|
|
SectionManager::removeSectionAssociation($id); |
205
|
|
|
|
206
|
|
|
if ($existing->requiresTable()) { |
207
|
|
|
Symphony::Database()->query('DROP TABLE IF EXISTS `tbl_entries_data_'.$id.'`'); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
return true; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* The fetch method returns a instance of a Field from tbl_fields. The most common |
215
|
|
|
* use of this function is to retrieve a Field by ID, but it can be used to retrieve |
216
|
|
|
* Fields from a Section also. There are several parameters that can be used to fetch |
217
|
|
|
* fields by their Type, Location, by a Field Constant or with a custom WHERE query. |
218
|
|
|
* |
219
|
|
|
* @throws DatabaseException |
220
|
|
|
* @throws Exception |
221
|
|
|
* @param integer|array $id |
222
|
|
|
* The ID of the field to retrieve. Defaults to null which will return multiple field |
223
|
|
|
* objects. Since Symphony 2.3, `$id` will accept an array of Field ID's |
224
|
|
|
* @param integer $section_id |
225
|
|
|
* The ID of the section to look for the fields in. Defaults to null which will allow |
226
|
|
|
* all fields in the Symphony installation to be searched on. |
227
|
|
|
* @param string $order |
228
|
|
|
* Available values of ASC (Ascending) or DESC (Descending), which refer to the |
229
|
|
|
* sort order for the query. Defaults to ASC (Ascending) |
230
|
|
|
* @param string $sortfield |
231
|
|
|
* The field to sort the query by. Can be any from the tbl_fields schema. Defaults to |
232
|
|
|
* 'sortorder' |
233
|
|
|
* @param string $type |
234
|
|
|
* Filter fields by their type, ie. input, select. Defaults to null |
235
|
|
|
* @param string $location |
236
|
|
|
* Filter fields by their location in the entry form. There are two possible values, |
237
|
|
|
* 'main' or 'sidebar'. Defaults to null |
238
|
|
|
* @param string $where |
239
|
|
|
* Allows a custom where query to be included. Must be valid SQL. The tbl_fields alias |
240
|
|
|
* is t1 |
241
|
|
|
* @param integer|string $restrict |
242
|
|
|
* Only return fields if they match one of the Field Constants. Available values are |
243
|
|
|
* `__TOGGLEABLE_ONLY__`, `__UNTOGGLEABLE_ONLY__`, `__FILTERABLE_ONLY__`, |
244
|
|
|
* `__UNFILTERABLE_ONLY__` or `__FIELD_ALL__`. Defaults to `__FIELD_ALL__` |
245
|
|
|
* @return array |
246
|
|
|
* An array of Field objects. If no Field are found, null is returned. |
247
|
|
|
*/ |
248
|
|
|
public static function fetch($id = null, $section_id = null, $order = 'ASC', $sortfield = 'sortorder', $type = null, $location = null, $where = null, $restrict = Field::__FIELD_ALL__) |
|
|
|
|
249
|
|
|
{ |
250
|
|
|
$fields = array(); |
251
|
|
|
$returnSingle = false; |
252
|
|
|
$ids = array(); |
253
|
|
|
$field_contexts = array(); |
254
|
|
|
|
255
|
|
|
if (!is_null($id)) { |
256
|
|
|
if (is_numeric($id)) { |
257
|
|
|
$returnSingle = true; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
if (!is_array($id)) { |
261
|
|
|
$field_ids = array((int)$id); |
262
|
|
|
} else { |
263
|
|
|
$field_ids = $id; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
// Loop over the `$field_ids` and check to see we have |
267
|
|
|
// instances of the request fields |
268
|
|
|
foreach ($field_ids as $key => $field_id) { |
269
|
|
|
if ( |
|
|
|
|
270
|
|
|
isset(self::$_initialiased_fields[$field_id]) |
271
|
|
|
&& self::$_initialiased_fields[$field_id] instanceof Field |
272
|
|
|
) { |
273
|
|
|
$fields[$field_id] = self::$_initialiased_fields[$field_id]; |
274
|
|
|
unset($field_ids[$key]); |
275
|
|
|
} |
276
|
|
|
} |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
// If there is any `$field_ids` left to be resolved lets do that, otherwise |
280
|
|
|
// if `$id` wasn't provided in the first place, we'll also continue |
281
|
|
|
if (!empty($field_ids) || is_null($id)) { |
282
|
|
|
$sql = sprintf( |
283
|
|
|
"SELECT t1.* |
|
|
|
|
284
|
|
|
FROM tbl_fields AS `t1` |
285
|
|
|
WHERE 1 |
286
|
|
|
%s %s %s %s |
287
|
|
|
%s", |
288
|
|
|
(isset($type) ? " AND t1.`type` = '{$type}' " : null), |
|
|
|
|
289
|
|
|
(isset($location) ? " AND t1.`location` = '{$location}' " : null), |
|
|
|
|
290
|
|
|
(isset($section_id) ? " AND t1.`parent_section` = '{$section_id}' " : null), |
|
|
|
|
291
|
|
|
$where, |
292
|
|
|
isset($field_ids) ? " AND t1.`id` IN(" . implode(',', $field_ids) . ") " : " ORDER BY t1.`{$sortfield}` {$order}" |
|
|
|
|
293
|
|
|
); |
294
|
|
|
|
295
|
|
|
if (!$result = Symphony::Database()->fetch($sql)) { |
296
|
|
|
return ($returnSingle ? null : array()); |
|
|
|
|
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
// Loop over the resultset building an array of type, field_id |
300
|
|
|
foreach ($result as $f) { |
301
|
|
|
$ids[$f['type']][] = $f['id']; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
// Loop over the `ids` array, which is grouped by field type |
305
|
|
|
// and get the field context. |
306
|
|
|
foreach ($ids as $type => $field_id) { |
307
|
|
|
$field_contexts[$type] = Symphony::Database()->fetch(sprintf( |
308
|
|
|
"SELECT * FROM `tbl_fields_%s` WHERE `field_id` IN (%s)", |
|
|
|
|
309
|
|
|
$type, |
310
|
|
|
implode(',', $field_id) |
311
|
|
|
), 'field_id'); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
foreach ($result as $f) { |
315
|
|
|
// We already have this field in our static store |
316
|
|
|
if ( |
|
|
|
|
317
|
|
|
isset(self::$_initialiased_fields[$f['id']]) |
318
|
|
|
&& self::$_initialiased_fields[$f['id']] instanceof Field |
319
|
|
|
) { |
320
|
|
|
$field = self::$_initialiased_fields[$f['id']]; |
321
|
|
|
|
322
|
|
|
// We don't have an instance of this field, so let's set one up |
323
|
|
|
} else { |
324
|
|
|
$field = self::create($f['type']); |
325
|
|
|
$field->setArray($f); |
326
|
|
|
// If the field has said that's going to have associations, then go find the |
327
|
|
|
// association setting value. In future this check will be most robust with |
328
|
|
|
// an interface, but for now, this is what we've got. RE: #2082 |
329
|
|
|
if ($field->canShowAssociationColumn()) { |
330
|
|
|
$field->set('show_association', SectionManager::getSectionAssociationSetting($f['id'])); |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
// Get the context for this field from our previous queries. |
334
|
|
|
$context = $field_contexts[$f['type']][$f['id']]; |
335
|
|
|
|
336
|
|
|
if (is_array($context) && !empty($context)) { |
337
|
|
|
try { |
338
|
|
|
unset($context['id']); |
339
|
|
|
$field->setArray($context); |
340
|
|
|
} catch (Exception $e) { |
341
|
|
|
throw new Exception(__( |
342
|
|
|
'Settings for field %s could not be found in table tbl_fields_%s.', |
343
|
|
|
array($f['id'], $f['type']) |
344
|
|
|
)); |
345
|
|
|
} |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
self::$_initialiased_fields[$f['id']] = $field; |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
// Check to see if there was any restricts imposed on the fields |
352
|
|
|
if ( |
|
|
|
|
353
|
|
|
$restrict == Field::__FIELD_ALL__ |
354
|
|
|
|| ($restrict == Field::__TOGGLEABLE_ONLY__ && $field->canToggle()) |
355
|
|
|
|| ($restrict == Field::__UNTOGGLEABLE_ONLY__ && !$field->canToggle()) |
356
|
|
|
|| ($restrict == Field::__FILTERABLE_ONLY__ && $field->canFilter()) |
357
|
|
|
|| ($restrict == Field::__UNFILTERABLE_ONLY__ && !$field->canFilter()) |
358
|
|
|
) { |
359
|
|
|
$fields[$f['id']] = $field; |
360
|
|
|
} |
361
|
|
|
} |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
return count($fields) <= 1 && $returnSingle ? current($fields) : $fields; |
|
|
|
|
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
/** |
368
|
|
|
* Given a field ID, return the type of the field by querying `tbl_fields` |
369
|
|
|
* |
370
|
|
|
* @param integer $id |
371
|
|
|
* @return string |
372
|
|
|
*/ |
373
|
|
|
public static function fetchFieldTypeFromID($id) |
374
|
|
|
{ |
375
|
|
|
return Symphony::Database()->fetchVar('type', 0, sprintf(" |
|
|
|
|
376
|
|
|
SELECT `type` FROM `tbl_fields` WHERE `id` = %d LIMIT 1", |
377
|
|
|
$id |
378
|
|
|
)); |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
/** |
382
|
|
|
* Given a field ID, return the handle of the field by querying `tbl_fields` |
383
|
|
|
* |
384
|
|
|
* @param integer $id |
385
|
|
|
* @return string |
386
|
|
|
*/ |
387
|
|
|
public static function fetchHandleFromID($id) |
388
|
|
|
{ |
389
|
|
|
return Symphony::Database()->fetchVar('element_name', 0, sprintf(" |
|
|
|
|
390
|
|
|
SELECT `element_name` FROM `tbl_fields` WHERE `id` = %d LIMIT 1", |
391
|
|
|
$id |
392
|
|
|
)); |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
/** |
396
|
|
|
* Given an `$element_name` and a `$section_id`, return the Field ID. Symphony enforces |
397
|
|
|
* a uniqueness constraint on a section where every field must have a unique |
398
|
|
|
* label (and therefore handle) so whilst it is impossible to have two fields |
399
|
|
|
* from the same section, it would be possible to have two fields with the same |
400
|
|
|
* name from different sections. Passing the `$section_id` lets you to specify |
401
|
|
|
* which section should be searched. If `$element_name` is null, this function will |
402
|
|
|
* return all the Field ID's from the given `$section_id`. |
403
|
|
|
* |
404
|
|
|
* @throws DatabaseException |
405
|
|
|
* @since Symphony 2.3 This function can now accept $element_name as an array |
406
|
|
|
* of handles. These handles can now also include the handle's mode, eg. `title: formatted` |
407
|
|
|
* @param string|array $element_name |
408
|
|
|
* The handle of the Field label, or an array of handles. These handles may contain |
409
|
|
|
* a mode as well, eg. `title: formatted`. |
410
|
|
|
* @param integer $section_id |
411
|
|
|
* The section that this field belongs too |
412
|
|
|
* The field ID, or an array of field ID's |
413
|
|
|
* @return mixed |
414
|
|
|
*/ |
415
|
|
|
public static function fetchFieldIDFromElementName($element_name, $section_id = null) |
|
|
|
|
416
|
|
|
{ |
417
|
|
|
if (is_null($element_name)) { |
|
|
|
|
418
|
|
|
$schema_sql = sprintf(" |
|
|
|
|
419
|
|
|
SELECT `id` |
420
|
|
|
FROM `tbl_fields` |
421
|
|
|
WHERE `parent_section` = %d |
422
|
|
|
ORDER BY `sortorder` ASC", |
423
|
|
|
$section_id |
424
|
|
|
); |
425
|
|
|
} else { |
426
|
|
|
$element_names = !is_array($element_name) ? array($element_name) : $element_name; |
427
|
|
|
|
428
|
|
|
// allow for pseudo-fields containing colons (e.g. Textarea formatted/unformatted) |
429
|
|
|
foreach ($element_names as $index => $name) { |
430
|
|
|
$parts = explode(':', $name, 2); |
431
|
|
|
|
432
|
|
|
if (count($parts) == 1) { |
433
|
|
|
continue; |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
unset($element_names[$index]); |
437
|
|
|
|
438
|
|
|
// Prevent attempting to look up 'system', which will arise |
439
|
|
|
// from `system:pagination`, `system:id` etc. |
440
|
|
|
if ($parts[0] == 'system') { |
441
|
|
|
continue; |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
$element_names[] = Symphony::Database()->cleanValue(trim($parts[0])); |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
$schema_sql = empty($element_names) ? null : sprintf(" |
|
|
|
|
448
|
|
|
SELECT `id` |
449
|
|
|
FROM `tbl_fields` |
450
|
|
|
WHERE 1 |
451
|
|
|
%s |
452
|
|
|
AND `element_name` IN ('%s') |
453
|
|
|
ORDER BY `sortorder` ASC", |
454
|
|
|
(!is_null($section_id) ? sprintf("AND `parent_section` = %d", $section_id) : ""), |
|
|
|
|
455
|
|
|
implode("', '", array_map(array('MySQL', 'cleanValue'), array_unique($element_names))) |
456
|
|
|
); |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
if (is_null($schema_sql)) { |
460
|
|
|
return false; |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
$result = Symphony::Database()->fetch($schema_sql); |
464
|
|
|
|
465
|
|
|
if (count($result) == 1) { |
466
|
|
|
return (int)$result[0]['id']; |
467
|
|
|
} elseif (empty($result)) { |
468
|
|
|
return false; |
469
|
|
|
} else { |
470
|
|
|
foreach ($result as &$r) { |
471
|
|
|
$r = (int)$r['id']; |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
return $result; |
475
|
|
|
} |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
/** |
479
|
|
|
* Work out the next available sort order for a new field |
480
|
|
|
* |
481
|
|
|
* @return integer |
482
|
|
|
* Returns the next sort order |
483
|
|
|
*/ |
484
|
|
|
public static function fetchNextSortOrder() |
485
|
|
|
{ |
486
|
|
|
$next = Symphony::Database()->fetchVar( |
487
|
|
|
"next", |
|
|
|
|
488
|
|
|
0, |
489
|
|
|
"SELECT |
|
|
|
|
490
|
|
|
MAX(p.sortorder) + 1 AS `next` |
491
|
|
|
FROM |
492
|
|
|
`tbl_fields` AS p |
493
|
|
|
LIMIT 1" |
494
|
|
|
); |
495
|
|
|
return ($next ? (int)$next : 1); |
|
|
|
|
496
|
|
|
} |
497
|
|
|
|
498
|
|
|
/** |
499
|
|
|
* Given a `$section_id`, this function returns an array of the installed |
500
|
|
|
* fields schema. This includes the `id`, `element_name`, `type` |
501
|
|
|
* and `location`. |
502
|
|
|
* |
503
|
|
|
* @throws DatabaseException |
504
|
|
|
* @since Symphony 2.3 |
505
|
|
|
* @param integer $section_id |
506
|
|
|
* @return array |
507
|
|
|
* An associative array that contains four keys, `id`, `element_name`, |
508
|
|
|
* `type` and `location` |
509
|
|
|
*/ |
510
|
|
|
public static function fetchFieldsSchema($section_id) |
511
|
|
|
{ |
512
|
|
|
return Symphony::Database()->fetch(sprintf( |
513
|
|
|
"SELECT `id`, `element_name`, `type`, `location` |
|
|
|
|
514
|
|
|
FROM `tbl_fields` |
515
|
|
|
WHERE `parent_section` = %d |
516
|
|
|
ORDER BY `sortorder` ASC", |
517
|
|
|
$section_id |
518
|
|
|
)); |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
/** |
522
|
|
|
* Returns an array of all available field handles discovered in the |
523
|
|
|
* `TOOLKIT . /fields` or `EXTENSIONS . /extension_handle/fields`. |
524
|
|
|
* |
525
|
|
|
* @return array |
526
|
|
|
* A single dimensional array of field handles. |
527
|
|
|
*/ |
528
|
|
|
public static function listAll() |
529
|
|
|
{ |
530
|
|
|
$structure = General::listStructure(TOOLKIT . '/fields', '/field.[a-z0-9_-]+.php/i', false, 'asc', TOOLKIT . '/fields'); |
|
|
|
|
531
|
|
|
$extensions = Symphony::ExtensionManager()->listInstalledHandles(); |
532
|
|
|
$types = array(); |
533
|
|
|
|
534
|
|
|
if (is_array($extensions) && !empty($extensions)) { |
535
|
|
|
foreach ($extensions as $handle) { |
536
|
|
|
$path = EXTENSIONS . '/' . $handle . '/fields'; |
|
|
|
|
537
|
|
|
if (is_dir($path)) { |
538
|
|
|
$tmp = General::listStructure($path, '/field.[a-z0-9_-]+.php/i', false, 'asc', $path); |
|
|
|
|
539
|
|
|
|
540
|
|
|
if (is_array($tmp['filelist']) && !empty($tmp['filelist'])) { |
541
|
|
|
$structure['filelist'] = array_merge($structure['filelist'], $tmp['filelist']); |
542
|
|
|
} |
543
|
|
|
} |
544
|
|
|
} |
545
|
|
|
|
546
|
|
|
$structure['filelist'] = General::array_remove_duplicates($structure['filelist']); |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
foreach ($structure['filelist'] as $filename) { |
550
|
|
|
$types[] = self::__getHandleFromFilename($filename); |
551
|
|
|
} |
552
|
|
|
|
553
|
|
|
return $types; |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
/** |
557
|
|
|
* Creates an instance of a given class and returns it. Adds the instance |
558
|
|
|
* to the `$_pool` array with the key being the handle. |
559
|
|
|
* |
560
|
|
|
* @throws Exception |
561
|
|
|
* @param string $type |
562
|
|
|
* The handle of the Field to create (which is it's handle) |
563
|
|
|
* @return Field |
564
|
|
|
*/ |
565
|
|
|
public static function create($type) |
566
|
|
|
{ |
567
|
|
|
if (!isset(self::$_pool[$type])) { |
568
|
|
|
$classname = self::__getClassName($type); |
569
|
|
|
$path = self::__getDriverPath($type); |
570
|
|
|
|
571
|
|
|
if (!file_exists($path)) { |
572
|
|
|
throw new Exception( |
573
|
|
|
__('Could not find Field %1$s at %2$s.', array('<code>' . $type . '</code>', '<code>' . $path . '</code>')) |
574
|
|
|
. ' ' . __('If it was provided by an Extension, ensure that it is installed, and enabled.') |
575
|
|
|
); |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
if (!class_exists($classname)) { |
579
|
|
|
require_once($path); |
580
|
|
|
} |
581
|
|
|
|
582
|
|
|
self::$_pool[$type] = new $classname; |
583
|
|
|
|
584
|
|
|
if (self::$_pool[$type]->canShowTableColumn() && !self::$_pool[$type]->get('show_column')) { |
585
|
|
|
self::$_pool[$type]->set('show_column', 'yes'); |
586
|
|
|
} |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
return clone self::$_pool[$type]; |
590
|
|
|
} |
591
|
|
|
|
592
|
|
|
/** |
593
|
|
|
* Return boolean if the given `$field_type` is in use anywhere in the |
594
|
|
|
* current Symphony install. |
595
|
|
|
* |
596
|
|
|
* @since Symphony 2.3 |
597
|
|
|
* @param string $field_type |
598
|
|
|
* @return boolean |
599
|
|
|
*/ |
600
|
|
|
public static function isFieldUsed($field_type) |
601
|
|
|
{ |
602
|
|
|
return Symphony::Database()->fetchVar('count', 0, sprintf( |
603
|
|
|
"SELECT COUNT(*) AS `count` FROM `tbl_fields` WHERE `type` = '%s'", |
604
|
|
|
$field_type |
605
|
|
|
)) > 0; |
606
|
|
|
} |
607
|
|
|
|
608
|
|
|
/** |
609
|
|
|
* Check if a specific text formatter is used by a Field |
610
|
|
|
* |
611
|
|
|
* @since Symphony 2.3 |
612
|
|
|
* @param string $text_formatter_handle |
613
|
|
|
* The handle of the `TextFormatter` |
614
|
|
|
* @return boolean |
615
|
|
|
* true if used, false if not |
616
|
|
|
*/ |
617
|
|
|
public static function isTextFormatterUsed($text_formatter_handle) |
618
|
|
|
{ |
619
|
|
|
$fields = Symphony::Database()->fetchCol('type', "SELECT DISTINCT `type` FROM `tbl_fields` WHERE `type` NOT IN ('author', 'checkbox', 'date', 'input', 'select', 'taglist', 'upload')"); |
620
|
|
|
|
621
|
|
|
if (!empty($fields)) { |
622
|
|
|
foreach ($fields as $field) { |
623
|
|
|
try { |
624
|
|
|
$table = Symphony::Database()->fetchVar('count', 0, sprintf( |
625
|
|
|
"SELECT COUNT(*) AS `count` |
626
|
|
|
FROM `tbl_fields_%s` |
627
|
|
|
WHERE `formatter` = '%s'", |
628
|
|
|
Symphony::Database()->cleanValue($field), |
629
|
|
|
$text_formatter_handle |
630
|
|
|
)); |
631
|
|
|
} catch (DatabaseException $ex) { |
632
|
|
|
// Table probably didn't have that column |
633
|
|
|
} |
634
|
|
|
|
635
|
|
|
if ($table > 0) { |
636
|
|
|
return true; |
637
|
|
|
} |
638
|
|
|
} |
639
|
|
|
} |
640
|
|
|
|
641
|
|
|
return false; |
642
|
|
|
} |
643
|
|
|
} |
644
|
|
|
|