1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @package content |
5
|
|
|
*/ |
6
|
|
|
/** |
7
|
|
|
* The Publish page is where the majority of an Authors time will |
8
|
|
|
* be spent in Symphony with adding, editing and removing entries |
9
|
|
|
* from Sections. This Page controls the entries table as well as |
10
|
|
|
* the Entry creation screens. |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
class contentPublish extends AdministrationPage |
|
|
|
|
14
|
|
|
{ |
15
|
|
|
public $_errors = array(); |
16
|
|
|
|
17
|
|
|
public function sort(&$sort, &$order, $params) |
18
|
|
|
{ |
19
|
|
|
$section = $params['current-section']; |
20
|
|
|
$filters = ''; |
21
|
|
|
// Format the filter query string |
22
|
|
|
if (isset($params['filters']) && !empty($params['filters'])) { |
23
|
|
|
$filters = preg_replace('/^&/i', '', $params['filters'], 1); |
24
|
|
|
$filters = '?' . trim($filters); |
25
|
|
|
} |
26
|
|
|
|
27
|
|
|
// If `?unsort` is appended to the URL, then sorting is reverted |
28
|
|
|
// to 'none', aka. by 'entry-id'. |
29
|
|
|
if ($params['unsort']) { |
30
|
|
|
$section->setSortingField('id', false); |
31
|
|
|
$section->setSortingOrder('desc'); |
32
|
|
|
|
33
|
|
|
redirect(Administration::instance()->getCurrentPageURL() . $filters); |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
// By default, sorting information are retrieved from |
37
|
|
|
// the file system and stored inside the `Configuration` object |
38
|
|
|
if (is_null($sort) && is_null($order)) { |
39
|
|
|
$sort = $section->getSortingField(); |
40
|
|
|
$order = $section->getSortingOrder(); |
41
|
|
|
|
42
|
|
|
// Set the sorting in the `EntryManager` for subsequent use |
43
|
|
|
EntryManager::setFetchSorting($sort, $order); |
44
|
|
|
} else { |
45
|
|
|
$sort = General::sanitize($sort); |
46
|
|
|
|
47
|
|
|
// Ensure that this field is infact sortable, otherwise |
48
|
|
|
// fallback to IDs |
49
|
|
|
if (($field = FieldManager::fetch($sort)) instanceof Field && !$field->isSortable()) { |
|
|
|
|
50
|
|
|
$sort = $section->getDefaultSortingField(); |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
// If the sort order or direction differs from what is saved, |
54
|
|
|
// update the config file and reload the page |
55
|
|
|
if ($sort != $section->getSortingField() || $order != $section->getSortingOrder()) { |
56
|
|
|
$section->setSortingField($sort, false); |
57
|
|
|
$section->setSortingOrder($order); |
58
|
|
|
redirect(Administration::instance()->getCurrentPageURL() . $filters); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
// If the sort order and direction remains the same, reload the page |
62
|
|
|
if ($sort == $section->getSortingField() && $order == $section->getSortingOrder()) { |
63
|
|
|
redirect(Administration::instance()->getCurrentPageURL() . $filters); |
64
|
|
|
} |
65
|
|
|
} |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* Append filtering interface |
70
|
|
|
*/ |
71
|
|
|
public function createFilteringInterface() |
72
|
|
|
{ |
73
|
|
|
//Check if section has filtering enabled |
74
|
|
|
$context = $this->getContext(); |
75
|
|
|
$handle = $context['section_handle']; |
76
|
|
|
$section_id = SectionManager::fetchIDFromHandle($handle); |
77
|
|
|
$section = SectionManager::fetch($section_id); |
78
|
|
|
$filter = $section->get('filter'); |
79
|
|
|
$count = EntryManager::fetchCount($section_id); |
80
|
|
|
|
81
|
|
|
if ($filter !== 'no' && $count > 1) { |
82
|
|
|
$drawer = Widget::Drawer('filtering-' . $section_id, __('Filter Entries'), $this->createFilteringDrawer($section)); |
83
|
|
|
$drawer->addClass('drawer-filtering'); |
84
|
|
|
$this->insertDrawer($drawer); |
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Create filtering drawer |
90
|
|
|
*/ |
91
|
|
|
public function createFilteringDrawer($section) |
92
|
|
|
{ |
93
|
|
|
$this->filteringForm = Widget::Form(null, 'get', 'filtering'); |
|
|
|
|
94
|
|
|
$this->createFilteringDuplicator($section); |
95
|
|
|
|
96
|
|
|
return $this->filteringForm; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
public function createFilteringDuplicator($section) |
100
|
|
|
{ |
101
|
|
|
$div = new XMLElement('div'); |
102
|
|
|
$div->setAttribute('class', 'frame filters-duplicator'); |
103
|
|
|
$div->setAttribute('data-interactive', 'data-interactive'); |
104
|
|
|
|
105
|
|
|
$ol = new XMLElement('ol'); |
106
|
|
|
$ol->setAttribute('data-add', __('Add filter')); |
107
|
|
|
$ol->setAttribute('data-remove', __('Clear filter')); |
108
|
|
|
$ol->setAttribute('data-empty', __('No filters applied yet.')); |
109
|
|
|
|
110
|
|
|
$this->createFieldFilters($ol, $section); |
111
|
|
|
$this->createSystemDateFilters($ol); |
112
|
|
|
|
113
|
|
|
$div->appendChild($ol); |
114
|
|
|
$this->filteringForm->appendChild($div); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
private function createFieldFilters(&$wrapper, $section) |
118
|
|
|
{ |
119
|
|
|
$filters = $_GET['filter']; |
120
|
|
|
|
121
|
|
|
foreach ($section->fetchFilterableFields() as $field) { |
122
|
|
|
if (!$field->canPublishFilter()) { |
123
|
|
|
continue; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
$filter = $filters[$field->get('element_name')]; |
127
|
|
|
|
128
|
|
|
// Filter data |
129
|
|
|
$data = array(); |
130
|
|
|
$data['type'] = $field->get('element_name'); |
131
|
|
|
$data['name'] = $field->get('label'); |
132
|
|
|
$data['filter'] = $filter; |
133
|
|
|
$data['instance'] = 'unique'; |
134
|
|
|
$data['search'] = $field->fetchSuggestionTypes(); |
135
|
|
|
$data['operators'] = $field->fetchFilterableOperators(); |
136
|
|
|
$data['comparisons'] = $this->createFilterComparisons($data); |
137
|
|
|
$data['query'] = $this->getFilterQuery($data); |
138
|
|
|
$data['field-id'] = $field->get('id'); |
139
|
|
|
|
140
|
|
|
// Add existing filter |
141
|
|
|
if (isset($filter)) { |
142
|
|
|
$this->createFilter($wrapper, $data); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
// Add filter template |
146
|
|
|
$data['instance'] = 'unique template'; |
147
|
|
|
$data['query'] = ''; |
148
|
|
|
$this->createFilter($wrapper, $data); |
149
|
|
|
} |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
private function createSystemDateFilters(&$wrapper) |
153
|
|
|
{ |
154
|
|
|
$filters = $_GET['filter']; |
155
|
|
|
$dateField = new FieldDate; |
156
|
|
|
|
157
|
|
|
$fields = array( |
158
|
|
|
array( |
159
|
|
|
'type' => 'system:creation-date', |
160
|
|
|
'label' => __('System Creation Date') |
161
|
|
|
), |
162
|
|
|
array( |
163
|
|
|
'type' => 'system:modification-date', |
164
|
|
|
'label' => __('System Modification Date') |
165
|
|
|
) |
166
|
|
|
); |
167
|
|
|
|
168
|
|
|
foreach ($fields as $field) { |
169
|
|
|
$filter = $filters[$field['type']]; |
170
|
|
|
|
171
|
|
|
// Filter data |
172
|
|
|
$data = array(); |
173
|
|
|
$data['type'] = $field['type']; |
174
|
|
|
$data['name'] = $field['label']; |
175
|
|
|
$data['filter'] = $filter; |
176
|
|
|
$data['instance'] = 'unique'; |
177
|
|
|
$data['search'] = $dateField->fetchSuggestionTypes(); |
178
|
|
|
$data['operators'] = $dateField->fetchFilterableOperators(); |
179
|
|
|
$data['comparisons'] = $this->createFilterComparisons($data); |
180
|
|
|
$data['query'] = $this->getFilterQuery($data); |
181
|
|
|
|
182
|
|
|
// Add existing filter |
183
|
|
|
if (isset($filter)) { |
184
|
|
|
$this->createFilter($wrapper, $data); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
// Add filter template |
188
|
|
|
$data['instance'] = 'unique template'; |
189
|
|
|
$data['query'] = ''; |
190
|
|
|
$this->createFilter($wrapper, $data); |
191
|
|
|
} |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
private function createFilter(&$wrapper, $data) |
195
|
|
|
{ |
196
|
|
|
$li = new XMLElement('li'); |
197
|
|
|
$li->setAttribute('class', $data['instance']); |
198
|
|
|
$li->setAttribute('data-type', $data['type']); |
199
|
|
|
|
200
|
|
|
// Header |
201
|
|
|
$li->appendChild(new XMLElement('header', $data['name'], array( |
202
|
|
|
'data-name' => $data['name'] |
203
|
|
|
))); |
204
|
|
|
|
205
|
|
|
// Settings |
206
|
|
|
$div = new XMLElement('div', null, array('class' => 'two columns')); |
207
|
|
|
|
208
|
|
|
// Comparisons |
209
|
|
|
$label = Widget::Label(); |
210
|
|
|
$label->setAttribute('class', 'column secondary'); |
211
|
|
|
|
212
|
|
|
$select = Widget::Select($data['type'] . '-comparison', $data['comparisons'], array( |
213
|
|
|
'class' => 'comparison' |
214
|
|
|
)); |
215
|
|
|
|
216
|
|
|
$label->appendChild($select); |
217
|
|
|
$div->appendChild($label); |
218
|
|
|
|
219
|
|
|
// Query |
220
|
|
|
$label = Widget::Label(); |
221
|
|
|
$label->setAttribute('class', 'column primary'); |
222
|
|
|
|
223
|
|
|
$input = Widget::Input($data['type'], General::sanitize($data['query']), 'text', array( |
224
|
|
|
'placeholder' => __('Type and hit enter to apply filter…'), |
225
|
|
|
'autocomplete' => 'off' |
226
|
|
|
)); |
227
|
|
|
$input->setAttribute('class', 'filter'); |
228
|
|
|
$label->appendChild($input); |
229
|
|
|
|
230
|
|
|
$this->createFilterSuggestions($label, $data); |
231
|
|
|
|
232
|
|
|
$div->appendChild($label); |
233
|
|
|
$li->appendChild($div); |
234
|
|
|
$wrapper->appendChild($li); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
private function createFilterComparisons($data) |
238
|
|
|
{ |
239
|
|
|
// Default comparison |
240
|
|
|
$comparisons = array(); |
241
|
|
|
|
242
|
|
|
// Custom field comparisons |
243
|
|
|
foreach ($data['operators'] as $operator) { |
|
|
|
|
244
|
|
|
|
245
|
|
|
$filter = trim($operator['filter']); |
246
|
|
|
|
247
|
|
|
// Check selected state |
248
|
|
|
$selected = false; |
249
|
|
|
|
250
|
|
|
// Selected state : Comparison mode "between" (x to y) |
251
|
|
|
if ($operator['title'] === 'between' && preg_match('/^(-?(?:\d+(?:\.\d+)?|\.\d+)) to (-?(?:\d+(?:\.\d+)?|\.\d+))$/i', $data['filter'] )) { |
252
|
|
|
$selected = true; |
253
|
|
|
// Selected state : Other comparison modes (except "is") |
254
|
|
|
} elseif ((!empty($filter) && strpos($data['filter'], $filter) === 0)) { |
255
|
|
|
$selected = true; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
$comparisons[] = array( |
259
|
|
|
$operator['filter'], |
260
|
|
|
$selected, |
261
|
|
|
__($operator['title']), |
262
|
|
|
null, |
263
|
|
|
null, |
264
|
|
|
array('data-comparison' => $operator['title']) |
265
|
|
|
); |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
return $comparisons; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
private function createFilterSuggestions(&$wrapper, $data) |
272
|
|
|
{ |
273
|
|
|
$ul = new XMLElement('ul'); |
274
|
|
|
$ul->setAttribute('class', 'suggestions'); |
275
|
|
|
$ul->setAttribute('data-field-id', $data['field-id']); |
276
|
|
|
$ul->setAttribute('data-associated-ids', '0'); |
277
|
|
|
$ul->setAttribute('data-search-types', implode($data['search'], ',')); |
|
|
|
|
278
|
|
|
|
279
|
|
|
// Add help text for each filter operator |
280
|
|
|
foreach ($data['operators'] as $operator) { |
281
|
|
|
$this->createFilterHelp($ul, $operator); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
$wrapper->appendChild($ul); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
private function createFilterHelp(&$wrapper, $operator) |
288
|
|
|
{ |
289
|
|
|
if (empty($operator['help'])) { |
290
|
|
|
return; |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
$li = new XMLElement('li', __('Comparison mode') . ': ' . $operator['help'], array( |
294
|
|
|
'class' => 'help', |
295
|
|
|
'data-comparison' => $operator['title'] |
296
|
|
|
)); |
297
|
|
|
|
298
|
|
|
$wrapper->appendChild($li); |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
private function getFilterQuery($data) |
302
|
|
|
{ |
303
|
|
|
$query = $data['filter']; |
304
|
|
|
|
305
|
|
|
foreach ($data['operators'] as $operator) { |
306
|
|
|
$filter = trim($operator['filter']); |
307
|
|
|
|
308
|
|
|
if (!empty($filter) && strpos($data['filter'], $filter) === 0) { |
309
|
|
|
$query = substr($data['filter'], strlen($operator['filter'])); |
310
|
|
|
} |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
return (string)$query; |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
public function build(array $context = array()) |
|
|
|
|
317
|
|
|
{ |
318
|
|
|
$section_id = SectionManager::fetchIDFromHandle($context['section_handle']); |
319
|
|
|
|
320
|
|
|
if ($section_id) { |
321
|
|
|
$context['associations'] = array( |
322
|
|
|
'parent' => SectionManager::fetchParentAssociations($section_id), |
323
|
|
|
'child' => SectionManager::fetchChildAssociations($section_id) |
324
|
|
|
); |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
return parent::build($context); |
|
|
|
|
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
public function action() |
331
|
|
|
{ |
332
|
|
|
$this->__switchboard('action'); |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
public function __switchboard($type = 'view') |
|
|
|
|
336
|
|
|
{ |
337
|
|
|
$function = ($type == 'action' ? '__action' : '__view') . ucfirst($this->_context['page']); |
|
|
|
|
338
|
|
|
|
339
|
|
|
if (!method_exists($this, $function)) { |
340
|
|
|
// If there is no action function, just return without doing anything |
341
|
|
|
if ($type == 'action') { |
342
|
|
|
return; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
Administration::instance()->errorPageNotFound(); |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
// Is this request allowed by server? |
349
|
|
|
if ($this->isRequestValid() === false) { |
350
|
|
|
$this->pageAlert(__('This request exceeds the maximum allowed request size of %s specified by your host.', array( |
351
|
|
|
ini_get('post_max_size') |
352
|
|
|
)), |
353
|
|
|
Alert::ERROR |
354
|
|
|
); |
355
|
|
|
} |
|
|
|
|
356
|
|
|
$this->$function(); |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
public function view() |
360
|
|
|
{ |
361
|
|
|
$this->__switchboard(); |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
public function __viewIndex() |
365
|
|
|
{ |
366
|
|
|
if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) { |
367
|
|
|
Administration::instance()->throwCustomError( |
368
|
|
|
__('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')), |
369
|
|
|
__('Unknown Section'), |
370
|
|
|
Page::HTTP_STATUS_NOT_FOUND |
371
|
|
|
); |
372
|
|
|
} elseif (!is_writable(CONFIG)) { |
|
|
|
|
373
|
|
|
$this->pageAlert(__('The Symphony configuration file, %s, is not writable. The sort order cannot be modified.', array('<code>/manifest/config.php</code>')), Alert::NOTICE); |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
$section = SectionManager::fetch($section_id); |
377
|
|
|
|
378
|
|
|
$this->setPageType('table'); |
379
|
|
|
$this->setTitle(__('%1$s – %2$s', array(General::sanitize($section->get('name')), __('Symphony')))); |
|
|
|
|
380
|
|
|
|
381
|
|
|
$filters = array(); |
382
|
|
|
$filter_querystring = $prepopulate_querystring = $where = $joins = null; |
383
|
|
|
$current_page = (isset($_REQUEST['pg']) && is_numeric($_REQUEST['pg']) ? max(1, intval($_REQUEST['pg'])) : 1); |
384
|
|
|
|
385
|
|
|
if (isset($_REQUEST['filter'])) { |
386
|
|
|
// legacy implementation, convert single filter to an array |
387
|
|
|
// split string in the form ?filter=handle:value |
388
|
|
|
// @deprecated |
389
|
|
|
// This should be removed in Symphony 4.0.0 |
390
|
|
|
if (!is_array($_REQUEST['filter'])) { |
391
|
|
|
list($field_handle, $filter_value) = explode(':', $_REQUEST['filter'], 2); |
392
|
|
|
$filters[$field_handle] = rawurldecode($filter_value); |
393
|
|
|
} else { |
394
|
|
|
$filters = $_REQUEST['filter']; |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
foreach ($filters as $handle => $value) { |
398
|
|
|
// Handle multiple values through filtering. RE: #2290 |
399
|
|
|
if ((is_array($value) && empty($value)) || trim($value) == '') { |
400
|
|
|
continue; |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
if (!is_array($value)) { |
404
|
|
|
$filter_type = Datasource::determineFilterType($value); |
405
|
|
|
$value = Datasource::splitFilter($filter_type, $value); |
406
|
|
|
} else { |
407
|
|
|
$filter_type = Datasource::FILTER_OR; |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
// Handle date meta data #2003 |
411
|
|
|
$handle = Symphony::Database()->cleanValue($handle); |
412
|
|
|
if (in_array($handle, array('system:creation-date', 'system:modification-date'))) { |
413
|
|
|
$date_joins = ''; |
414
|
|
|
$date_where = ''; |
415
|
|
|
$date = new FieldDate(); |
416
|
|
|
$date->buildDSRetrievalSQL($value, $date_joins, $date_where, ($filter_type == Datasource::FILTER_AND ? true : false)); |
|
|
|
|
417
|
|
|
|
418
|
|
|
// Replace the date field where with the `creation_date` or `modification_date`. |
419
|
|
|
$date_where = preg_replace('/`t\d+`.date/', ($field_id !== 'system:modification-date') ? '`e`.creation_date_gmt' : '`e`.modification_date_gmt', $date_where); |
|
|
|
|
420
|
|
|
$where .= $date_where; |
421
|
|
|
} else { |
422
|
|
|
// Handle normal fields |
423
|
|
|
$field_id = FieldManager::fetchFieldIDFromElementName( |
424
|
|
|
$handle, |
425
|
|
|
$section->get('id') |
|
|
|
|
426
|
|
|
); |
427
|
|
|
|
428
|
|
|
$field = FieldManager::fetch($field_id); |
|
|
|
|
429
|
|
|
if ($field instanceof Field) { |
430
|
|
|
$field->buildDSRetrievalSQL($value, $joins, $where, ($filter_type == Datasource::FILTER_AND ? true : false)); |
|
|
|
|
431
|
|
|
|
432
|
|
|
$value = implode(',', $value); |
433
|
|
|
$encoded_value = rawurlencode($value); |
434
|
|
|
$filter_querystring .= sprintf("filter[%s]=%s&", $handle, $encoded_value); |
|
|
|
|
435
|
|
|
|
436
|
|
|
// Some fields require that prepopulation be done via ID. RE: #2331 |
437
|
|
|
if (!is_numeric($value) && method_exists($field, 'fetchIDfromValue')) { |
438
|
|
|
$encoded_value = $field->fetchIDfromValue($value); |
439
|
|
|
} |
|
|
|
|
440
|
|
|
$prepopulate_querystring .= sprintf("prepopulate[%d]=%s&", $field_id, $encoded_value); |
|
|
|
|
441
|
|
|
} else { |
442
|
|
|
unset($filters[$handle]); |
443
|
|
|
} |
|
|
|
|
444
|
|
|
unset($field); |
445
|
|
|
} |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
$filter_querystring = preg_replace("/&$/", '', $filter_querystring); |
|
|
|
|
449
|
|
|
$prepopulate_querystring = preg_replace("/&$/", '', $prepopulate_querystring); |
|
|
|
|
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
Sortable::initialize($this, $entries, $sort, $order, array( |
453
|
|
|
'current-section' => $section, |
454
|
|
|
'filters' => ($filter_querystring ? "&" . $filter_querystring : ''), |
|
|
|
|
455
|
|
|
'unsort' => isset($_REQUEST['unsort']) |
456
|
|
|
)); |
457
|
|
|
|
458
|
|
|
$this->Form->setAttribute('action', Administration::instance()->getCurrentPageURL(). '?pg=' . $current_page.($filter_querystring ? "&" . $filter_querystring : '')); |
|
|
|
|
459
|
|
|
|
460
|
|
|
// Build filtering interface |
461
|
|
|
$this->createFilteringInterface(); |
462
|
|
|
|
463
|
|
|
$subheading_buttons = array( |
464
|
|
|
Widget::Anchor(__('Create New'), Administration::instance()->getCurrentPageURL().'new/'.($prepopulate_querystring ? '?' . $prepopulate_querystring : ''), __('Create a new entry'), 'create button', null, array('accesskey' => 'c')) |
|
|
|
|
465
|
|
|
); |
466
|
|
|
|
467
|
|
|
// Only show the Edit Section button if the Author is a developer. #938 ^BA |
468
|
|
|
if (Symphony::Author()->isDeveloper()) { |
469
|
|
|
array_unshift($subheading_buttons, Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/', __('Edit Section Configuration'), 'button')); |
|
|
|
|
470
|
|
|
} |
471
|
|
|
|
472
|
|
|
$this->appendSubheading(General::sanitize($section->get('name')), $subheading_buttons); |
473
|
|
|
|
474
|
|
|
/** |
475
|
|
|
* Allows adjustments to be made to the SQL where and joins statements |
476
|
|
|
* before they are used to fetch the entries for the page |
477
|
|
|
* |
478
|
|
|
* @delegate AdjustPublishFiltering |
479
|
|
|
* @since Symphony 2.3.3 |
480
|
|
|
* @param string $context |
481
|
|
|
* '/publish/' |
482
|
|
|
* @param integer $section_id |
483
|
|
|
* An array of the current columns, passed by reference |
484
|
|
|
* @param string $where |
485
|
|
|
* The current where statement, or null if not set |
486
|
|
|
* @param string $joins |
487
|
|
|
*/ |
488
|
|
|
Symphony::ExtensionManager()->notifyMembers('AdjustPublishFiltering', '/publish/', array('section-id' => $section_id, 'where' => &$where, 'joins' => &$joins)); |
489
|
|
|
|
490
|
|
|
// get visible columns |
491
|
|
|
$visible_columns = $section->fetchVisibleColumns(); |
492
|
|
|
// extract the needed schema |
493
|
|
|
$element_names = array_values(array_map(function ($field) { |
494
|
|
|
return $field->get('element_name'); |
495
|
|
|
}, $visible_columns)); |
496
|
|
|
|
497
|
|
|
// Check that the filtered query fails that the filter is dropped and an |
498
|
|
|
// error is logged. #841 ^BA |
499
|
|
|
try { |
500
|
|
|
$entries = EntryManager::fetchByPage($current_page, $section_id, Symphony::Configuration()->get('pagination_maximum_rows', 'symphony'), $where, $joins, true, false, true, $element_names); |
501
|
|
|
} catch (DatabaseException $ex) { |
502
|
|
|
$this->pageAlert(__('An error occurred while retrieving filtered entries. Showing all entries instead.'), Alert::ERROR); |
503
|
|
|
$filter_querystring = null; |
504
|
|
|
Symphony::Log()->pushToLog(sprintf( |
505
|
|
|
'%s - %s%s%s', |
506
|
|
|
$section->get('name') . ' Publish Index', |
|
|
|
|
507
|
|
|
$ex->getMessage(), |
508
|
|
|
($ex->getFile() ? " in file " . $ex->getFile() : null), |
|
|
|
|
509
|
|
|
($ex->getLine() ? " on line " . $ex->getLine() : null) |
|
|
|
|
510
|
|
|
), |
511
|
|
|
E_NOTICE, |
512
|
|
|
true |
513
|
|
|
); |
514
|
|
|
$entries = EntryManager::fetchByPage($current_page, $section_id, Symphony::Configuration()->get('pagination_maximum_rows', 'symphony'), null, null, true, false, true, $element_names); |
515
|
|
|
} |
516
|
|
|
|
517
|
|
|
// Flag filtering |
518
|
|
|
if (isset($_REQUEST['filter'])) { |
519
|
|
|
$filter_stats = new XMLElement('p', '<span>– ' . __('%d of %d entries (filtered)', array($entries['total-entries'], EntryManager::fetchCount($section_id))) . '</span>', array('class' => 'inactive')); |
520
|
|
|
} else { |
521
|
|
|
$filter_stats = new XMLElement('p', '<span>– ' . __('%d entries', array($entries['total-entries'])) . '</span>', array('class' => 'inactive')); |
522
|
|
|
} |
|
|
|
|
523
|
|
|
$this->Breadcrumbs->appendChild($filter_stats); |
524
|
|
|
|
525
|
|
|
// Build table |
526
|
|
|
$columns = array(); |
527
|
|
|
|
528
|
|
|
if (is_array($visible_columns) && !empty($visible_columns)) { |
529
|
|
|
foreach ($visible_columns as $column) { |
530
|
|
|
$columns[] = array( |
531
|
|
|
'label' => $column->get('label'), |
532
|
|
|
'sortable' => $column->isSortable(), |
533
|
|
|
'handle' => $column->get('id'), |
534
|
|
|
'attrs' => array( |
535
|
|
|
'id' => 'field-' . $column->get('id'), |
536
|
|
|
'class' => 'field-' . $column->get('type') |
537
|
|
|
) |
538
|
|
|
); |
539
|
|
|
} |
540
|
|
|
} else { |
541
|
|
|
$columns[] = array( |
542
|
|
|
'label' => __('ID'), |
543
|
|
|
'sortable' => true, |
544
|
|
|
'handle' => 'id' |
545
|
|
|
); |
546
|
|
|
} |
547
|
|
|
|
548
|
|
|
$aTableHead = Sortable::buildTableHeaders($columns, $sort, $order, ($filter_querystring) ? "&" . $filter_querystring : ''); |
|
|
|
|
549
|
|
|
|
550
|
|
|
$child_sections = array(); |
551
|
|
|
$associated_sections = $section->fetchChildAssociations(true); |
552
|
|
|
|
553
|
|
|
if (is_array($associated_sections) && !empty($associated_sections)) { |
554
|
|
|
foreach ($associated_sections as $key => $as) { |
555
|
|
|
$child_sections[$key] = SectionManager::fetch($as['child_section_id']); |
556
|
|
|
$aTableHead[] = array($child_sections[$key]->get('name'), 'col'); |
557
|
|
|
} |
558
|
|
|
} |
559
|
|
|
|
560
|
|
|
/** |
561
|
|
|
* Allows the creation of custom table columns for each entry. Called |
562
|
|
|
* after all the Section Visible columns have been added as well |
563
|
|
|
* as the Section Associations |
564
|
|
|
* |
565
|
|
|
* @delegate AddCustomPublishColumn |
566
|
|
|
* @since Symphony 2.2 |
567
|
|
|
* @param string $context |
568
|
|
|
* '/publish/' |
569
|
|
|
* @param array $tableHead |
570
|
|
|
* An array of the current columns, passed by reference |
571
|
|
|
* @param integer $section_id |
572
|
|
|
* The current Section ID |
573
|
|
|
*/ |
574
|
|
|
Symphony::ExtensionManager()->notifyMembers('AddCustomPublishColumn', '/publish/', array('tableHead' => &$aTableHead, 'section_id' => $section->get('id'))); |
575
|
|
|
|
576
|
|
|
// Table Body |
577
|
|
|
$aTableBody = array(); |
578
|
|
|
|
579
|
|
|
if (!is_array($entries['records']) || empty($entries['records'])) { |
580
|
|
|
$aTableBody = array( |
581
|
|
|
Widget::TableRow(array(Widget::TableData(__('None found.'), 'inactive', null, count($aTableHead))), 'odd') |
582
|
|
|
); |
583
|
|
|
} else { |
584
|
|
|
$field_pool = array(); |
585
|
|
|
|
586
|
|
|
if (is_array($visible_columns) && !empty($visible_columns)) { |
587
|
|
|
foreach ($visible_columns as $column) { |
588
|
|
|
$field_pool[$column->get('id')] = $column; |
589
|
|
|
} |
590
|
|
|
} |
591
|
|
|
|
592
|
|
|
$link_column = array_reverse($visible_columns); |
593
|
|
|
$link_column = end($link_column); |
594
|
|
|
reset($visible_columns); |
595
|
|
|
|
596
|
|
|
foreach ($entries['records'] as $entry) { |
597
|
|
|
$tableData = array(); |
598
|
|
|
|
599
|
|
|
// Setup each cell |
600
|
|
|
if (!is_array($visible_columns) || empty($visible_columns)) { |
601
|
|
|
$tableData[] = Widget::TableData(Widget::Anchor($entry->get('id'), Administration::instance()->getCurrentPageURL() . 'edit/' . $entry->get('id') . '/')); |
602
|
|
|
} else { |
603
|
|
|
$link = Widget::Anchor( |
604
|
|
|
'', |
605
|
|
|
Administration::instance()->getCurrentPageURL() . 'edit/' . $entry->get('id') . '/'.($filter_querystring ? '?' . $prepopulate_querystring : ''), |
|
|
|
|
606
|
|
|
$entry->get('id'), |
607
|
|
|
'content' |
608
|
|
|
); |
609
|
|
|
|
610
|
|
|
foreach ($visible_columns as $position => $column) { |
611
|
|
|
$data = $entry->getData($column->get('id')); |
612
|
|
|
$field = $field_pool[$column->get('id')]; |
613
|
|
|
|
614
|
|
|
$value = $field->prepareTableValue($data, ($column == $link_column) ? $link : null, $entry->get('id')); |
615
|
|
|
|
616
|
|
|
if (!is_object($value) && (strlen(trim($value)) == 0 || $value == __('None'))) { |
617
|
|
|
$value = ($position == 0 ? $link->generate() : __('None')); |
|
|
|
|
618
|
|
|
} |
619
|
|
|
|
620
|
|
|
if ($value == __('None')) { |
621
|
|
|
$tableData[] = Widget::TableData($value, 'inactive field-' . $column->get('type') . ' field-' . $column->get('id')); |
622
|
|
|
} else { |
623
|
|
|
$tableData[] = Widget::TableData($value, 'field-' . $column->get('type') . ' field-' . $column->get('id')); |
624
|
|
|
} |
625
|
|
|
|
626
|
|
|
unset($field); |
627
|
|
|
} |
628
|
|
|
} |
629
|
|
|
|
630
|
|
|
if (is_array($child_sections) && !empty($child_sections)) { |
631
|
|
|
foreach ($child_sections as $key => $as) { |
632
|
|
|
$field = FieldManager::fetch((int)$associated_sections[$key]['child_section_field_id']); |
633
|
|
|
$parent_section_field_id = (int)$associated_sections[$key]['parent_section_field_id']; |
634
|
|
|
|
635
|
|
|
if (!is_null($parent_section_field_id)) { |
636
|
|
|
$search_value = $field->fetchAssociatedEntrySearchValue( |
637
|
|
|
$entry->getData($parent_section_field_id), |
638
|
|
|
$parent_section_field_id, |
639
|
|
|
$entry->get('id') |
640
|
|
|
); |
641
|
|
|
} else { |
642
|
|
|
$search_value = $entry->get('id'); |
643
|
|
|
} |
644
|
|
|
|
645
|
|
|
if (!is_array($search_value)) { |
646
|
|
|
$associated_entry_count = $field->fetchAssociatedEntryCount($search_value); |
647
|
|
|
|
648
|
|
|
$tableData[] = Widget::TableData( |
649
|
|
|
Widget::Anchor( |
650
|
|
|
sprintf('%d →', max(0, intval($associated_entry_count))), |
651
|
|
|
sprintf( |
652
|
|
|
'%s/publish/%s/?filter[%s]=%s', |
653
|
|
|
SYMPHONY_URL, |
654
|
|
|
$as->get('handle'), |
655
|
|
|
$field->get('element_name'), |
656
|
|
|
rawurlencode($search_value) |
657
|
|
|
), |
658
|
|
|
$entry->get('id'), |
659
|
|
|
'content' |
660
|
|
|
) |
661
|
|
|
); |
662
|
|
|
} |
663
|
|
|
|
664
|
|
|
unset($field); |
665
|
|
|
} |
666
|
|
|
} |
667
|
|
|
|
668
|
|
|
/** |
669
|
|
|
* Allows Extensions to inject custom table data for each Entry |
670
|
|
|
* into the Publish Index |
671
|
|
|
* |
672
|
|
|
* @delegate AddCustomPublishColumnData |
673
|
|
|
* @since Symphony 2.2 |
674
|
|
|
* @param string $context |
675
|
|
|
* '/publish/' |
676
|
|
|
* @param array $tableData |
677
|
|
|
* An array of `Widget::TableData`, passed by reference |
678
|
|
|
* @param integer $section_id |
679
|
|
|
* The current Section ID |
680
|
|
|
* @param Entry $entry_id |
681
|
|
|
* The entry object, please note that this is by error and this will |
682
|
|
|
* be removed in Symphony 2.4. The entry object is available in |
683
|
|
|
* the 'entry' key as of Symphony 2.3.1. |
684
|
|
|
* @param Entry $entry |
685
|
|
|
* The entry object for this row |
686
|
|
|
*/ |
687
|
|
|
Symphony::ExtensionManager()->notifyMembers('AddCustomPublishColumnData', '/publish/', array( |
688
|
|
|
'tableData' => &$tableData, |
689
|
|
|
'section_id' => $section->get('id'), |
690
|
|
|
'entry_id' => $entry, |
691
|
|
|
'entry' => $entry |
692
|
|
|
)); |
693
|
|
|
|
694
|
|
|
$lastCol = $tableData[count($tableData) - 1]; |
695
|
|
|
$lastCol->appendChild(Widget::Label(__('Select Entry %d', array($entry->get('id'))), null, 'accessible', null, array( |
696
|
|
|
'for' => 'entry-' . $entry->get('id') |
697
|
|
|
))); |
698
|
|
|
$lastCol->appendChild(Widget::Input('items['.$entry->get('id').']', $entry->get('modification_date'), 'checkbox', array( |
699
|
|
|
'id' => 'entry-' . $entry->get('id') |
700
|
|
|
))); |
701
|
|
|
|
702
|
|
|
// Add a row to the body array, assigning each cell to the row |
703
|
|
|
$aTableBody[] = Widget::TableRow($tableData, null, 'id-' . $entry->get('id')); |
704
|
|
|
} |
705
|
|
|
} |
706
|
|
|
|
707
|
|
|
$table = Widget::Table( |
708
|
|
|
Widget::TableHead($aTableHead), |
709
|
|
|
null, |
710
|
|
|
Widget::TableBody($aTableBody), |
711
|
|
|
'selectable', |
712
|
|
|
null, |
713
|
|
|
array('role' => 'directory', 'aria-labelledby' => 'symphony-subheading', 'data-interactive' => 'data-interactive') |
714
|
|
|
); |
715
|
|
|
|
716
|
|
|
$this->Form->appendChild($table); |
717
|
|
|
|
718
|
|
|
$tableActions = new XMLElement('div'); |
719
|
|
|
$tableActions->setAttribute('class', 'actions'); |
720
|
|
|
|
721
|
|
|
$options = array( |
722
|
|
|
array(null, false, __('With Selected...')), |
723
|
|
|
array('delete', false, __('Delete'), 'confirm', null, array( |
724
|
|
|
'data-message' => __('Are you sure you want to delete the selected entries?') |
725
|
|
|
)) |
726
|
|
|
); |
727
|
|
|
|
728
|
|
|
$toggable_fields = $section->fetchToggleableFields(); |
729
|
|
|
|
730
|
|
|
if (is_array($toggable_fields) && !empty($toggable_fields)) { |
731
|
|
|
$index = 2; |
732
|
|
|
|
733
|
|
|
foreach ($toggable_fields as $field) { |
734
|
|
|
$toggle_states = $field->getToggleStates(); |
735
|
|
|
|
736
|
|
|
if (is_array($toggle_states)) { |
737
|
|
|
$options[$index] = array('label' => __('Set %s', array($field->get('label'))), 'options' => array()); |
738
|
|
|
|
739
|
|
|
foreach ($toggle_states as $value => $state) { |
740
|
|
|
$options[$index]['options'][] = array('toggle-' . $field->get('id') . '-' . $value, false, $state); |
741
|
|
|
} |
742
|
|
|
} |
743
|
|
|
|
744
|
|
|
$index++; |
745
|
|
|
} |
746
|
|
|
} |
747
|
|
|
|
748
|
|
|
/** |
749
|
|
|
* Allows an extension to modify the existing options for this page's |
750
|
|
|
* With Selected menu. If the `$options` parameter is an empty array, |
751
|
|
|
* the 'With Selected' menu will not be rendered. |
752
|
|
|
* |
753
|
|
|
* @delegate AddCustomActions |
754
|
|
|
* @since Symphony 2.3.2 |
755
|
|
|
* @param string $context |
756
|
|
|
* '/publish/' |
757
|
|
|
* @param array $options |
758
|
|
|
* An array of arrays, where each child array represents an option |
759
|
|
|
* in the With Selected menu. Options should follow the same format |
760
|
|
|
* expected by `Widget::__SelectBuildOption`. Passed by reference. |
761
|
|
|
*/ |
762
|
|
|
Symphony::ExtensionManager()->notifyMembers('AddCustomActions', '/publish/', array( |
763
|
|
|
'options' => &$options |
764
|
|
|
)); |
765
|
|
|
|
766
|
|
|
if (!empty($options)) { |
767
|
|
|
$tableActions->appendChild(Widget::Apply($options)); |
768
|
|
|
$this->Form->appendChild($tableActions); |
769
|
|
|
} |
770
|
|
|
|
771
|
|
|
if ($entries['total-pages'] > 1) { |
772
|
|
|
$ul = new XMLElement('ul'); |
773
|
|
|
$ul->setAttribute('class', 'page'); |
774
|
|
|
|
775
|
|
|
// First |
776
|
|
|
$li = new XMLElement('li'); |
777
|
|
|
|
778
|
|
|
if ($current_page > 1) { |
779
|
|
|
$li->appendChild(Widget::Anchor(__('First'), Administration::instance()->getCurrentPageURL(). '?pg=1'.($filter_querystring ? "&" . $filter_querystring : ''))); |
|
|
|
|
780
|
|
|
} else { |
781
|
|
|
$li->setValue(__('First')); |
782
|
|
|
} |
783
|
|
|
|
784
|
|
|
$ul->appendChild($li); |
785
|
|
|
|
786
|
|
|
// Previous |
787
|
|
|
$li = new XMLElement('li'); |
788
|
|
|
|
789
|
|
|
if ($current_page > 1) { |
790
|
|
|
$li->appendChild(Widget::Anchor(__('← Previous'), Administration::instance()->getCurrentPageURL(). '?pg=' . ($current_page - 1).($filter_querystring ? "&" . $filter_querystring : ''))); |
|
|
|
|
791
|
|
|
} else { |
792
|
|
|
$li->setValue(__('← Previous')); |
793
|
|
|
} |
794
|
|
|
|
795
|
|
|
$ul->appendChild($li); |
796
|
|
|
|
797
|
|
|
// Summary |
798
|
|
|
$li = new XMLElement('li'); |
799
|
|
|
|
800
|
|
|
$li->setAttribute('title', __('Viewing %1$s - %2$s of %3$s entries', array( |
801
|
|
|
$entries['start'], |
802
|
|
|
($current_page != $entries['total-pages']) ? $current_page * Symphony::Configuration()->get('pagination_maximum_rows', 'symphony') : $entries['total-entries'], |
803
|
|
|
$entries['total-entries'] |
804
|
|
|
))); |
805
|
|
|
|
806
|
|
|
$pgform = Widget::Form(Administration::instance()->getCurrentPageURL(), 'get', 'paginationform'); |
807
|
|
|
|
808
|
|
|
$pgmax = max($current_page, $entries['total-pages']); |
809
|
|
|
$pgform->appendChild(Widget::Input('pg', null, 'text', array( |
810
|
|
|
'data-active' => __('Go to page …'), |
811
|
|
|
'data-inactive' => __('Page %1$s of %2$s', array((string)$current_page, $pgmax)), |
812
|
|
|
'data-max' => $pgmax |
813
|
|
|
))); |
814
|
|
|
|
815
|
|
|
$li->appendChild($pgform); |
816
|
|
|
$ul->appendChild($li); |
817
|
|
|
|
818
|
|
|
// Next |
819
|
|
|
$li = new XMLElement('li'); |
820
|
|
|
|
821
|
|
|
if ($current_page < $entries['total-pages']) { |
822
|
|
|
$li->appendChild(Widget::Anchor(__('Next →'), Administration::instance()->getCurrentPageURL(). '?pg=' . ($current_page + 1).($filter_querystring ? "&" . $filter_querystring : ''))); |
|
|
|
|
823
|
|
|
} else { |
824
|
|
|
$li->setValue(__('Next →')); |
825
|
|
|
} |
826
|
|
|
|
827
|
|
|
$ul->appendChild($li); |
828
|
|
|
|
829
|
|
|
// Last |
830
|
|
|
$li = new XMLElement('li'); |
831
|
|
|
|
832
|
|
|
if ($current_page < $entries['total-pages']) { |
833
|
|
|
$li->appendChild(Widget::Anchor(__('Last'), Administration::instance()->getCurrentPageURL(). '?pg=' . $entries['total-pages'].($filter_querystring ? "&" . $filter_querystring : ''))); |
|
|
|
|
834
|
|
|
} else { |
835
|
|
|
$li->setValue(__('Last')); |
836
|
|
|
} |
837
|
|
|
|
838
|
|
|
$ul->appendChild($li); |
839
|
|
|
|
840
|
|
|
$this->Contents->appendChild($ul); |
841
|
|
|
} |
842
|
|
|
} |
843
|
|
|
|
844
|
|
|
public function __actionIndex() |
845
|
|
|
{ |
846
|
|
|
$checked = (is_array($_POST['items'])) ? array_keys($_POST['items']) : null; |
847
|
|
|
|
848
|
|
|
if (is_array($checked) && !empty($checked)) { |
849
|
|
|
/** |
850
|
|
|
* Extensions can listen for any custom actions that were added |
851
|
|
|
* through `AddCustomPreferenceFieldsets` or `AddCustomActions` |
852
|
|
|
* delegates. |
853
|
|
|
* |
854
|
|
|
* @delegate CustomActions |
855
|
|
|
* @since Symphony 2.3.2 |
856
|
|
|
* @param string $context |
857
|
|
|
* '/publish/' |
858
|
|
|
* @param array $checked |
859
|
|
|
* An array of the selected rows. The value is usually the ID of the |
860
|
|
|
* the associated object. |
861
|
|
|
*/ |
862
|
|
|
Symphony::ExtensionManager()->notifyMembers('CustomActions', '/publish/', array( |
863
|
|
|
'checked' => $checked |
864
|
|
|
)); |
865
|
|
|
|
866
|
|
|
switch ($_POST['with-selected']) { |
867
|
|
|
case 'delete': |
868
|
|
|
/** |
869
|
|
|
* Prior to deletion of entries. An array of Entry ID's is provided which |
870
|
|
|
* can be manipulated. This delegate was renamed from `Delete` to `EntryPreDelete` |
871
|
|
|
* in Symphony 2.3. |
872
|
|
|
* |
873
|
|
|
* @delegate EntryPreDelete |
874
|
|
|
* @param string $context |
875
|
|
|
* '/publish/' |
876
|
|
|
* @param array $entry_id |
877
|
|
|
* An array of Entry ID's passed by reference |
878
|
|
|
*/ |
879
|
|
|
Symphony::ExtensionManager()->notifyMembers('EntryPreDelete', '/publish/', array('entry_id' => &$checked)); |
880
|
|
|
|
881
|
|
|
EntryManager::delete($checked); |
882
|
|
|
|
883
|
|
|
/** |
884
|
|
|
* After the deletion of entries, this delegate provides an array of Entry ID's |
885
|
|
|
* that were deleted. |
886
|
|
|
* |
887
|
|
|
* @since Symphony 2.3 |
888
|
|
|
* @delegate EntryPostDelete |
889
|
|
|
* @param string $context |
890
|
|
|
* '/publish/' |
891
|
|
|
* @param array $entry_id |
892
|
|
|
* An array of Entry ID's that were deleted. |
893
|
|
|
*/ |
894
|
|
|
Symphony::ExtensionManager()->notifyMembers('EntryPostDelete', '/publish/', array('entry_id' => $checked)); |
895
|
|
|
|
896
|
|
|
redirect(server_safe('REQUEST_URI')); |
897
|
|
|
break; |
898
|
|
|
default: |
899
|
|
|
list($option, $field_id, $value) = explode('-', $_POST['with-selected'], 3); |
900
|
|
|
|
901
|
|
|
if ($option == 'toggle') { |
902
|
|
|
$field = FieldManager::fetch($field_id); |
|
|
|
|
903
|
|
|
$fields = array($field->get('element_name') => $value); |
904
|
|
|
|
905
|
|
|
$section = SectionManager::fetch($field->get('parent_section')); |
906
|
|
|
|
907
|
|
|
foreach ($checked as $entry_id) { |
908
|
|
|
$entry = EntryManager::fetch($entry_id); |
909
|
|
|
$existing_data = $entry[0]->getData($field_id); |
910
|
|
|
$entry[0]->setData($field_id, $field->toggleFieldData(is_array($existing_data) ? $existing_data : array(), $value, $entry_id)); |
911
|
|
|
|
912
|
|
|
/** |
913
|
|
|
* Just prior to editing of an Entry |
914
|
|
|
* |
915
|
|
|
* @delegate EntryPreEdit |
916
|
|
|
* @param string $context |
917
|
|
|
* '/publish/edit/' |
918
|
|
|
* @param Section $section |
919
|
|
|
* @param Entry $entry |
920
|
|
|
* @param array $fields |
921
|
|
|
*/ |
922
|
|
|
Symphony::ExtensionManager()->notifyMembers('EntryPreEdit', '/publish/edit/', array( |
923
|
|
|
'section' => $section, |
924
|
|
|
'entry' => &$entry[0], |
925
|
|
|
'fields' => $fields |
926
|
|
|
)); |
927
|
|
|
|
928
|
|
|
$entry[0]->commit(); |
929
|
|
|
|
930
|
|
|
/** |
931
|
|
|
* Editing an entry. Entry object is provided. |
932
|
|
|
* |
933
|
|
|
* @delegate EntryPostEdit |
934
|
|
|
* @param string $context |
935
|
|
|
* '/publish/edit/' |
936
|
|
|
* @param Section $section |
937
|
|
|
* @param Entry $entry |
938
|
|
|
* @param array $fields |
939
|
|
|
*/ |
940
|
|
|
Symphony::ExtensionManager()->notifyMembers('EntryPostEdit', '/publish/edit/', array( |
941
|
|
|
'section' => $section, |
942
|
|
|
'entry' => $entry[0], |
943
|
|
|
'fields' => $fields |
944
|
|
|
)); |
945
|
|
|
} |
946
|
|
|
|
947
|
|
|
unset($field); |
948
|
|
|
redirect(server_safe('REQUEST_URI')); |
949
|
|
|
} |
950
|
|
|
} |
951
|
|
|
} |
952
|
|
|
} |
953
|
|
|
|
954
|
|
|
public function __viewNew() |
955
|
|
|
{ |
956
|
|
|
if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) { |
957
|
|
|
Administration::instance()->throwCustomError( |
958
|
|
|
__('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')), |
959
|
|
|
__('Unknown Section'), |
960
|
|
|
Page::HTTP_STATUS_NOT_FOUND |
961
|
|
|
); |
962
|
|
|
} |
963
|
|
|
|
964
|
|
|
$section = SectionManager::fetch($section_id); |
965
|
|
|
|
966
|
|
|
$this->setPageType('form'); |
967
|
|
|
$this->setTitle(__('%1$s – %2$s', array(General::sanitize($section->get('name')), __('Symphony')))); |
|
|
|
|
968
|
|
|
|
969
|
|
|
// Ensure errored entries still maintain any prepopulated values [#2211] |
970
|
|
|
$this->Form->setAttribute('action', $this->Form->getAttribute('action') . $this->getPrepopulateString()); |
971
|
|
|
$this->Form->setAttribute('enctype', 'multipart/form-data'); |
972
|
|
|
|
973
|
|
|
$sidebar_fields = $section->fetchFields(null, 'sidebar'); |
974
|
|
|
$main_fields = $section->fetchFields(null, 'main'); |
975
|
|
|
|
976
|
|
|
if (!empty($sidebar_fields) && !empty($main_fields)) { |
977
|
|
|
$this->Form->setAttribute('class', 'two columns'); |
978
|
|
|
} else { |
979
|
|
|
$this->Form->setAttribute('class', 'columns'); |
980
|
|
|
} |
981
|
|
|
|
982
|
|
|
// Only show the Edit Section button if the Author is a developer. #938 ^BA |
983
|
|
|
if (Symphony::Author()->isDeveloper()) { |
984
|
|
|
$this->appendSubheading(__('Untitled'), |
985
|
|
|
Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/', __('Edit Section Configuration'), 'button') |
|
|
|
|
986
|
|
|
); |
987
|
|
|
} else { |
988
|
|
|
$this->appendSubheading(__('Untitled')); |
989
|
|
|
} |
990
|
|
|
|
991
|
|
|
// Build filtered breadcrumb [#1378} |
992
|
|
|
$this->insertBreadcrumbs(array( |
993
|
|
|
Widget::Anchor(General::sanitize($section->get('name')), SYMPHONY_URL . '/publish/' . $this->_context['section_handle'] . '/' . $this->getFilterString()), |
994
|
|
|
)); |
995
|
|
|
|
996
|
|
|
$this->Form->appendChild(Widget::Input('MAX_FILE_SIZE', Symphony::Configuration()->get('max_upload_size', 'admin'), 'hidden')); |
|
|
|
|
997
|
|
|
|
998
|
|
|
// If there is post data floating around, due to errors, create an entry object |
999
|
|
|
if (isset($_POST['fields'])) { |
1000
|
|
|
$entry = EntryManager::create(); |
1001
|
|
|
$entry->set('section_id', $section_id); |
1002
|
|
|
$entry->setDataFromPost($_POST['fields'], $error, true); |
1003
|
|
|
|
1004
|
|
|
// Brand new entry, so need to create some various objects |
1005
|
|
|
} else { |
1006
|
|
|
$entry = EntryManager::create(); |
1007
|
|
|
$entry->set('section_id', $section_id); |
1008
|
|
|
} |
1009
|
|
|
|
1010
|
|
|
// Check if there is a field to prepopulate |
1011
|
|
|
if (isset($_REQUEST['prepopulate'])) { |
1012
|
|
|
foreach ($_REQUEST['prepopulate'] as $field_id => $value) { |
1013
|
|
|
$this->Form->prependChild(Widget::Input( |
1014
|
|
|
"prepopulate[{$field_id}]", |
|
|
|
|
1015
|
|
|
rawurlencode($value), |
1016
|
|
|
'hidden' |
1017
|
|
|
)); |
1018
|
|
|
|
1019
|
|
|
// The actual pre-populating should only happen if there is not existing fields post data |
1020
|
|
|
// and if the field allows it |
1021
|
|
|
if (!isset($_POST['fields']) && ($field = FieldManager::fetch($field_id)) && $field->canPrePopulate()) { |
1022
|
|
|
$entry->setData( |
1023
|
|
|
$field->get('id'), |
1024
|
|
|
$field->processRawFieldData($value, $error, $message, true) |
|
|
|
|
1025
|
|
|
); |
1026
|
|
|
unset($field); |
1027
|
|
|
} |
1028
|
|
|
} |
1029
|
|
|
} |
1030
|
|
|
|
1031
|
|
|
$primary = new XMLElement('fieldset'); |
1032
|
|
|
$primary->setAttribute('class', 'primary column'); |
1033
|
|
|
|
1034
|
|
|
if ((!is_array($main_fields) || empty($main_fields)) && (!is_array($sidebar_fields) || empty($sidebar_fields))) { |
1035
|
|
|
$message = __('Fields must be added to this section before an entry can be created.'); |
1036
|
|
|
|
1037
|
|
|
if (Symphony::Author()->isDeveloper()) { |
1038
|
|
|
$message .= ' <a href="' . SYMPHONY_URL . '/blueprints/sections/edit/' . $section->get('id') . '/" accesskey="c">' |
|
|
|
|
1039
|
|
|
. __('Add fields') |
1040
|
|
|
. '</a>'; |
1041
|
|
|
} |
1042
|
|
|
|
1043
|
|
|
$this->pageAlert($message, Alert::ERROR); |
1044
|
|
|
} else { |
1045
|
|
|
if (is_array($main_fields) && !empty($main_fields)) { |
1046
|
|
|
foreach ($main_fields as $field) { |
1047
|
|
|
$primary->appendChild($this->__wrapFieldWithDiv($field, $entry)); |
1048
|
|
|
} |
1049
|
|
|
|
1050
|
|
|
$this->Form->appendChild($primary); |
1051
|
|
|
} |
1052
|
|
|
|
1053
|
|
|
if (is_array($sidebar_fields) && !empty($sidebar_fields)) { |
1054
|
|
|
$sidebar = new XMLElement('fieldset'); |
1055
|
|
|
$sidebar->setAttribute('class', 'secondary column'); |
1056
|
|
|
|
1057
|
|
|
foreach ($sidebar_fields as $field) { |
1058
|
|
|
$sidebar->appendChild($this->__wrapFieldWithDiv($field, $entry)); |
1059
|
|
|
} |
1060
|
|
|
|
1061
|
|
|
$this->Form->appendChild($sidebar); |
1062
|
|
|
} |
1063
|
|
|
|
1064
|
|
|
$div = new XMLElement('div'); |
1065
|
|
|
$div->setAttribute('class', 'actions'); |
1066
|
|
|
$div->appendChild(Widget::Input('action[save]', __('Create Entry'), 'submit', array('accesskey' => 's'))); |
1067
|
|
|
|
1068
|
|
|
$this->Form->appendChild($div); |
1069
|
|
|
|
1070
|
|
|
// Create a Drawer for Associated Sections |
1071
|
|
|
$this->prepareAssociationsDrawer($section); |
|
|
|
|
1072
|
|
|
} |
1073
|
|
|
} |
1074
|
|
|
|
1075
|
|
|
public function __actionNew() |
1076
|
|
|
{ |
1077
|
|
|
if (is_array($_POST['action']) && (array_key_exists('save', $_POST['action']) || array_key_exists('done', $_POST['action']))) { |
1078
|
|
|
$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle']); |
1079
|
|
|
|
1080
|
|
|
if (!$section = SectionManager::fetch($section_id)) { |
1081
|
|
|
Administration::instance()->throwCustomError( |
1082
|
|
|
__('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')), |
1083
|
|
|
__('Unknown Section'), |
1084
|
|
|
Page::HTTP_STATUS_NOT_FOUND |
1085
|
|
|
); |
1086
|
|
|
} |
1087
|
|
|
|
1088
|
|
|
$entry = EntryManager::create(); |
1089
|
|
|
$entry->set('author_id', Symphony::Author()->get('id')); |
1090
|
|
|
$entry->set('section_id', $section_id); |
1091
|
|
|
$entry->set('creation_date', DateTimeObj::get('c')); |
1092
|
|
|
$entry->set('modification_date', DateTimeObj::get('c')); |
1093
|
|
|
|
1094
|
|
|
$fields = $_POST['fields']; |
1095
|
|
|
|
1096
|
|
|
// Combine FILES and POST arrays, indexed by their custom field handles |
1097
|
|
|
if (isset($_FILES['fields'])) { |
1098
|
|
|
$filedata = General::processFilePostData($_FILES['fields']); |
1099
|
|
|
|
1100
|
|
|
foreach ($filedata as $handle => $data) { |
1101
|
|
|
if (!isset($fields[$handle])) { |
1102
|
|
|
$fields[$handle] = $data; |
1103
|
|
|
} elseif (isset($data['error']) && $data['error'] == UPLOAD_ERR_NO_FILE) { |
1104
|
|
|
$fields[$handle] = null; |
1105
|
|
|
} else { |
1106
|
|
|
foreach ($data as $ii => $d) { |
1107
|
|
|
if (isset($d['error']) && $d['error'] == UPLOAD_ERR_NO_FILE) { |
1108
|
|
|
$fields[$handle][$ii] = null; |
1109
|
|
|
} elseif (is_array($d) && !empty($d)) { |
1110
|
|
|
foreach ($d as $key => $val) { |
1111
|
|
|
$fields[$handle][$ii][$key] = $val; |
1112
|
|
|
} |
1113
|
|
|
} |
1114
|
|
|
} |
1115
|
|
|
} |
1116
|
|
|
} |
1117
|
|
|
} |
1118
|
|
|
|
1119
|
|
|
// Initial checks to see if the Entry is ok |
1120
|
|
|
if (Entry::__ENTRY_FIELD_ERROR__ == $entry->checkPostData($fields, $this->_errors)) { |
1121
|
|
|
$this->pageAlert(__('Some errors were encountered while attempting to save.'), Alert::ERROR); |
1122
|
|
|
|
1123
|
|
|
// Secondary checks, this will actually process the data and attempt to save |
1124
|
|
|
} elseif (Entry::__ENTRY_OK__ != $entry->setDataFromPost($fields, $errors)) { |
1125
|
|
|
foreach ($errors as $field_id => $message) { |
1126
|
|
|
$this->pageAlert($message, Alert::ERROR); |
1127
|
|
|
} |
1128
|
|
|
|
1129
|
|
|
// Everything is awesome. Dance. |
1130
|
|
|
} else { |
1131
|
|
|
/** |
1132
|
|
|
* Just prior to creation of an Entry |
1133
|
|
|
* |
1134
|
|
|
* @delegate EntryPreCreate |
1135
|
|
|
* @param string $context |
1136
|
|
|
* '/publish/new/' |
1137
|
|
|
* @param Section $section |
1138
|
|
|
* @param Entry $entry |
1139
|
|
|
* @param array $fields |
1140
|
|
|
*/ |
1141
|
|
|
Symphony::ExtensionManager()->notifyMembers('EntryPreCreate', '/publish/new/', array('section' => $section, 'entry' => &$entry, 'fields' => &$fields)); |
1142
|
|
|
|
1143
|
|
|
$entry->set('modification_author_id', Symphony::Author()->get('id')); |
1144
|
|
|
|
1145
|
|
|
// Check to see if the dancing was premature |
1146
|
|
|
if (!$entry->commit()) { |
1147
|
|
|
$this->pageAlert(null, Alert::ERROR); |
1148
|
|
|
} else { |
1149
|
|
|
/** |
1150
|
|
|
* Creation of an Entry. New Entry object is provided. |
1151
|
|
|
* |
1152
|
|
|
* @delegate EntryPostCreate |
1153
|
|
|
* @param string $context |
1154
|
|
|
* '/publish/new/' |
1155
|
|
|
* @param Section $section |
1156
|
|
|
* @param Entry $entry |
1157
|
|
|
* @param array $fields |
1158
|
|
|
*/ |
1159
|
|
|
Symphony::ExtensionManager()->notifyMembers('EntryPostCreate', '/publish/new/', array('section' => $section, 'entry' => $entry, 'fields' => $fields)); |
1160
|
|
|
|
1161
|
|
|
$prepopulate_querystring = $this->getPrepopulateString(); |
1162
|
|
|
redirect(sprintf( |
1163
|
|
|
'%s/publish/%s/edit/%d/created/%s', |
1164
|
|
|
SYMPHONY_URL, |
|
|
|
|
1165
|
|
|
$this->_context['section_handle'], |
1166
|
|
|
$entry->get('id'), |
|
|
|
|
1167
|
|
|
(!empty($prepopulate_querystring) ? $prepopulate_querystring : null) |
1168
|
|
|
)); |
1169
|
|
|
} |
1170
|
|
|
} |
1171
|
|
|
} |
1172
|
|
|
} |
1173
|
|
|
|
1174
|
|
|
public function __viewEdit() |
1175
|
|
|
{ |
1176
|
|
|
if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) { |
1177
|
|
|
Administration::instance()->throwCustomError( |
1178
|
|
|
__('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')), |
1179
|
|
|
__('Unknown Section'), |
1180
|
|
|
Page::HTTP_STATUS_NOT_FOUND |
1181
|
|
|
); |
1182
|
|
|
} |
1183
|
|
|
|
1184
|
|
|
$section = SectionManager::fetch($section_id); |
1185
|
|
|
$entry_id = intval($this->_context['entry_id']); |
1186
|
|
|
$base = '/publish/'.$this->_context['section_handle'] . '/'; |
1187
|
|
|
$new_link = $base . 'new/'; |
1188
|
|
|
$filter_link = $base; |
1189
|
|
|
$canonical_link = $base . 'edit/' . $entry_id . '/'; |
1190
|
|
|
|
1191
|
|
|
EntryManager::setFetchSorting('id', 'DESC'); |
|
|
|
|
1192
|
|
|
|
1193
|
|
|
$existingEntry = EntryManager::fetch($entry_id); |
1194
|
|
|
if (empty($existingEntry)) { |
1195
|
|
|
Administration::instance()->throwCustomError( |
1196
|
|
|
__('Unknown Entry'), |
1197
|
|
|
__('The Entry, %s, could not be found.', array($entry_id)), |
1198
|
|
|
Page::HTTP_STATUS_NOT_FOUND |
1199
|
|
|
); |
1200
|
|
|
} |
|
|
|
|
1201
|
|
|
$existingEntry = $existingEntry[0]; |
1202
|
|
|
|
1203
|
|
|
// If the entry does not belong in the context's section |
1204
|
|
|
if ($section_id != $existingEntry->get('section_id')) { |
1205
|
|
|
Administration::instance()->throwCustomError( |
1206
|
|
|
__('Wrong section'), |
1207
|
|
|
__('The Entry, %s, does not belong in section %s', array($entry_id, $section_id)), |
1208
|
|
|
Page::HTTP_STATUS_BAD_REQUEST |
1209
|
|
|
); |
1210
|
|
|
} |
1211
|
|
|
|
1212
|
|
|
// If there is post data floating around, due to errors, create an entry object |
1213
|
|
|
if (isset($_POST['fields'])) { |
1214
|
|
|
$fields = $_POST['fields']; |
1215
|
|
|
|
1216
|
|
|
$entry = EntryManager::create(); |
1217
|
|
|
$entry->set('id', $entry_id); |
1218
|
|
|
$entry->set('author_id', $existingEntry->get('author_id')); |
1219
|
|
|
$entry->set('modification_author_id', $existingEntry->get('modification_author_id')); |
1220
|
|
|
$entry->set('section_id', $existingEntry->get('section_id')); |
1221
|
|
|
$entry->set('creation_date', $existingEntry->get('creation_date')); |
1222
|
|
|
$entry->set('modification_date', $existingEntry->get('modification_date')); |
1223
|
|
|
$entry->setDataFromPost($fields, $errors, true); |
1224
|
|
|
|
1225
|
|
|
$timestamp = isset($_POST['action']['timestamp']) |
1226
|
|
|
? $_POST['action']['timestamp'] |
|
|
|
|
1227
|
|
|
: $entry->get('modification_date'); |
1228
|
|
|
|
1229
|
|
|
// Editing an entry, so need to create some various objects |
1230
|
|
|
} else { |
1231
|
|
|
$entry = $existingEntry; |
1232
|
|
|
$fields = array(); |
1233
|
|
|
|
1234
|
|
|
if (!$section) { |
1235
|
|
|
$section = SectionManager::fetch($entry->get('section_id')); |
1236
|
|
|
} |
1237
|
|
|
|
1238
|
|
|
$timestamp = $entry->get('modification_date'); |
1239
|
|
|
} |
1240
|
|
|
|
1241
|
|
|
/** |
1242
|
|
|
* Just prior to rendering of an Entry edit form. |
1243
|
|
|
* |
1244
|
|
|
* @delegate EntryPreRender |
1245
|
|
|
* @param string $context |
1246
|
|
|
* '/publish/edit/' |
1247
|
|
|
* @param Section $section |
1248
|
|
|
* @param Entry $entry |
1249
|
|
|
* @param array $fields |
1250
|
|
|
*/ |
1251
|
|
|
Symphony::ExtensionManager()->notifyMembers('EntryPreRender', '/publish/edit/', array( |
1252
|
|
|
'section' => $section, |
1253
|
|
|
'entry' => &$entry, |
1254
|
|
|
'fields' => $fields |
1255
|
|
|
)); |
1256
|
|
|
|
1257
|
|
|
// Iterate over the `prepopulate` parameters to build a URL |
1258
|
|
|
// to remember this state for Create New, View all Entries and |
1259
|
|
|
// Breadcrumb links. If `prepopulate` doesn't exist, this will |
1260
|
|
|
// just use the standard pages (ie. no filtering) |
1261
|
|
|
if (isset($_REQUEST['prepopulate'])) { |
1262
|
|
|
$new_link .= $this->getPrepopulateString(); |
1263
|
|
|
$filter_link .= $this->getFilterString(); |
1264
|
|
|
$canonical_link .= $this->getPrepopulateString(); |
1265
|
|
|
} |
1266
|
|
|
|
1267
|
|
|
if (isset($this->_context['flag'])) { |
1268
|
|
|
// These flags are only relevant if there are no errors |
1269
|
|
|
if (empty($this->_errors)) { |
1270
|
|
|
$time = Widget::Time(); |
1271
|
|
|
|
1272
|
|
|
switch ($this->_context['flag']) { |
1273
|
|
|
case 'saved': |
1274
|
|
|
$message = __('Entry updated at %s.', array($time->generate())); |
1275
|
|
|
break; |
1276
|
|
|
case 'created': |
1277
|
|
|
$message = __('Entry created at %s.', array($time->generate())); |
1278
|
|
|
} |
1279
|
|
|
|
1280
|
|
|
$this->pageAlert( |
1281
|
|
|
$message |
|
|
|
|
1282
|
|
|
. ' <a href="' . SYMPHONY_URL . $new_link . '" accesskey="c">' |
|
|
|
|
1283
|
|
|
. __('Create another?') |
1284
|
|
|
. '</a> <a href="' . SYMPHONY_URL . $filter_link . '" accesskey="a">' |
1285
|
|
|
. __('View all Entries') |
1286
|
|
|
. '</a>', |
1287
|
|
|
Alert::SUCCESS |
1288
|
|
|
); |
1289
|
|
|
} |
1290
|
|
|
} |
1291
|
|
|
|
1292
|
|
|
// Determine the page title |
1293
|
|
|
$field_id = Symphony::Database()->fetchVar('id', 0, sprintf(" |
|
|
|
|
1294
|
|
|
SELECT `id` |
1295
|
|
|
FROM `tbl_fields` |
1296
|
|
|
WHERE `parent_section` = %d |
1297
|
|
|
ORDER BY `sortorder` LIMIT 1", |
1298
|
|
|
$section->get('id') |
|
|
|
|
1299
|
|
|
)); |
1300
|
|
|
if (!is_null($field_id)) { |
1301
|
|
|
$field = FieldManager::fetch($field_id); |
|
|
|
|
1302
|
|
|
} |
1303
|
|
|
|
1304
|
|
|
if ($field) { |
|
|
|
|
1305
|
|
|
$title = $field->prepareReadableValue($existingEntry->getData($field->get('id')), $entry_id, true); |
1306
|
|
|
} else { |
1307
|
|
|
$title = ''; |
1308
|
|
|
} |
1309
|
|
|
|
1310
|
|
|
if (trim($title) == '') { |
1311
|
|
|
$title = __('Untitled'); |
1312
|
|
|
} |
1313
|
|
|
|
1314
|
|
|
// Check if there is a field to prepopulate |
1315
|
|
|
if (isset($_REQUEST['prepopulate'])) { |
1316
|
|
|
foreach ($_REQUEST['prepopulate'] as $field_id => $value) { |
1317
|
|
|
$this->Form->prependChild(Widget::Input( |
1318
|
|
|
"prepopulate[{$field_id}]", |
|
|
|
|
1319
|
|
|
rawurlencode($value), |
1320
|
|
|
'hidden' |
1321
|
|
|
)); |
1322
|
|
|
} |
1323
|
|
|
} |
1324
|
|
|
|
1325
|
|
|
$this->setPageType('form'); |
1326
|
|
|
$this->Form->setAttribute('enctype', 'multipart/form-data'); |
1327
|
|
|
$this->setTitle(__('%1$s – %2$s – %3$s', array($title, General::sanitize($section->get('name')), __('Symphony')))); |
|
|
|
|
1328
|
|
|
$this->addElementToHead(new XMLElement('link', null, array( |
1329
|
|
|
'rel' => 'canonical', |
1330
|
|
|
'href' => SYMPHONY_URL . $canonical_link, |
1331
|
|
|
))); |
1332
|
|
|
|
1333
|
|
|
$sidebar_fields = $section->fetchFields(null, 'sidebar'); |
1334
|
|
|
$main_fields = $section->fetchFields(null, 'main'); |
1335
|
|
|
|
1336
|
|
|
if (!empty($sidebar_fields) && !empty($main_fields)) { |
1337
|
|
|
$this->Form->setAttribute('class', 'two columns'); |
1338
|
|
|
} else { |
1339
|
|
|
$this->Form->setAttribute('class', 'columns'); |
1340
|
|
|
} |
1341
|
|
|
|
1342
|
|
|
// Only show the Edit Section button if the Author is a developer. #938 ^BA |
1343
|
|
|
if (Symphony::Author()->isDeveloper()) { |
1344
|
|
|
$this->appendSubheading($title, Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/', __('Edit Section Configuration'), 'button')); |
1345
|
|
|
} else { |
1346
|
|
|
$this->appendSubheading($title); |
1347
|
|
|
} |
1348
|
|
|
|
1349
|
|
|
$this->insertBreadcrumbs(array( |
1350
|
|
|
Widget::Anchor(General::sanitize($section->get('name')), SYMPHONY_URL . (isset($filter_link) ? $filter_link : $base)), |
1351
|
|
|
)); |
1352
|
|
|
|
1353
|
|
|
$this->Form->appendChild(Widget::Input('MAX_FILE_SIZE', Symphony::Configuration()->get('max_upload_size', 'admin'), 'hidden')); |
|
|
|
|
1354
|
|
|
|
1355
|
|
|
$primary = new XMLElement('fieldset'); |
1356
|
|
|
$primary->setAttribute('class', 'primary column'); |
1357
|
|
|
|
1358
|
|
|
if ((!is_array($main_fields) || empty($main_fields)) && (!is_array($sidebar_fields) || empty($sidebar_fields))) { |
1359
|
|
|
$message = __('Fields must be added to this section before an entry can be created.'); |
1360
|
|
|
|
1361
|
|
|
if (Symphony::Author()->isDeveloper()) { |
1362
|
|
|
$message .= ' <a href="' . SYMPHONY_URL . '/blueprints/sections/edit/' . $section->get('id') . '/" accesskey="c">' |
|
|
|
|
1363
|
|
|
. __('Add fields') |
1364
|
|
|
. '</a>'; |
1365
|
|
|
} |
1366
|
|
|
|
1367
|
|
|
$this->pageAlert($message, Alert::ERROR); |
1368
|
|
|
} else { |
1369
|
|
|
if (is_array($main_fields) && !empty($main_fields)) { |
1370
|
|
|
foreach ($main_fields as $field) { |
1371
|
|
|
$primary->appendChild($this->__wrapFieldWithDiv($field, $entry)); |
1372
|
|
|
} |
1373
|
|
|
|
1374
|
|
|
$this->Form->appendChild($primary); |
1375
|
|
|
} |
1376
|
|
|
|
1377
|
|
|
if (is_array($sidebar_fields) && !empty($sidebar_fields)) { |
1378
|
|
|
$sidebar = new XMLElement('fieldset'); |
1379
|
|
|
$sidebar->setAttribute('class', 'secondary column'); |
1380
|
|
|
|
1381
|
|
|
foreach ($sidebar_fields as $field) { |
1382
|
|
|
$sidebar->appendChild($this->__wrapFieldWithDiv($field, $entry)); |
1383
|
|
|
} |
1384
|
|
|
|
1385
|
|
|
$this->Form->appendChild($sidebar); |
1386
|
|
|
} |
1387
|
|
|
|
1388
|
|
|
$div = new XMLElement('div'); |
1389
|
|
|
$div->setAttribute('class', 'actions'); |
1390
|
|
|
$div->appendChild(Widget::Input('action[save]', __('Save Changes'), 'submit', array('accesskey' => 's'))); |
1391
|
|
|
|
1392
|
|
|
$button = new XMLElement('button', __('Delete')); |
1393
|
|
|
$button->setAttributeArray(array('name' => 'action[delete]', 'class' => 'button confirm delete', 'title' => __('Delete this entry'), 'type' => 'submit', 'accesskey' => 'd', 'data-message' => __('Are you sure you want to delete this entry?'))); |
1394
|
|
|
$div->appendChild($button); |
1395
|
|
|
|
1396
|
|
|
$div->appendChild(Widget::Input('action[timestamp]', $timestamp, 'hidden')); |
1397
|
|
|
$div->appendChild(Widget::Input('action[ignore-timestamp]', 'yes', 'checkbox', array('class' => 'irrelevant'))); |
1398
|
|
|
|
1399
|
|
|
$this->Form->appendChild($div); |
1400
|
|
|
|
1401
|
|
|
// Create a Drawer for Associated Sections |
1402
|
|
|
$this->prepareAssociationsDrawer($section); |
|
|
|
|
1403
|
|
|
} |
1404
|
|
|
} |
1405
|
|
|
|
1406
|
|
|
public function __actionEdit() |
1407
|
|
|
{ |
1408
|
|
|
$entry_id = intval($this->_context['entry_id']); |
1409
|
|
|
|
1410
|
|
|
if (is_array($_POST['action']) && (array_key_exists('save', $_POST['action']) || array_key_exists('done', $_POST['action']))) { |
1411
|
|
|
$ret = EntryManager::fetch($entry_id); |
1412
|
|
|
if (empty($ret)) { |
1413
|
|
|
Administration::instance()->throwCustomError( |
1414
|
|
|
__('The Entry, %s, could not be found.', array($entry_id)), |
1415
|
|
|
__('Unknown Entry'), |
1416
|
|
|
Page::HTTP_STATUS_NOT_FOUND |
1417
|
|
|
); |
1418
|
|
|
} |
1419
|
|
|
|
1420
|
|
|
$entry = $ret[0]; |
1421
|
|
|
|
1422
|
|
|
$section = SectionManager::fetch($entry->get('section_id')); |
1423
|
|
|
|
1424
|
|
|
$post = General::getPostData(); |
1425
|
|
|
$fields = $post['fields']; |
1426
|
|
|
|
1427
|
|
|
$canProceed = $this->validateTimestamp($entry_id, true); |
1428
|
|
|
|
1429
|
|
|
// Timestamp validation |
1430
|
|
|
if (!$canProceed) { |
1431
|
|
|
$this->addTimestampValidationPageAlert($this->_errors['timestamp'], $entry, 'save'); |
1432
|
|
|
|
1433
|
|
|
// Initial checks to see if the Entry is ok |
1434
|
|
|
} elseif (Entry::__ENTRY_FIELD_ERROR__ == $entry->checkPostData($fields, $this->_errors)) { |
1435
|
|
|
$this->pageAlert(__('Some errors were encountered while attempting to save.'), Alert::ERROR); |
1436
|
|
|
|
1437
|
|
|
// Secondary checks, this will actually process the data and attempt to save |
1438
|
|
|
} elseif (Entry::__ENTRY_OK__ != $entry->setDataFromPost($fields, $errors)) { |
|
|
|
|
1439
|
|
|
foreach ($errors as $field_id => $message) { |
1440
|
|
|
$this->pageAlert($message, Alert::ERROR); |
1441
|
|
|
} |
1442
|
|
|
|
1443
|
|
|
// Everything is awesome. Dance. |
1444
|
|
|
} else { |
1445
|
|
|
/** |
1446
|
|
|
* Just prior to editing of an Entry. |
1447
|
|
|
* |
1448
|
|
|
* @delegate EntryPreEdit |
1449
|
|
|
* @param string $context |
1450
|
|
|
* '/publish/edit/' |
1451
|
|
|
* @param Section $section |
1452
|
|
|
* @param Entry $entry |
1453
|
|
|
* @param array $fields |
1454
|
|
|
*/ |
1455
|
|
|
Symphony::ExtensionManager()->notifyMembers('EntryPreEdit', '/publish/edit/', array('section' => $section, 'entry' => &$entry, 'fields' => $fields)); |
1456
|
|
|
|
1457
|
|
|
$entry->set('modification_author_id', Symphony::Author()->get('id')); |
1458
|
|
|
|
1459
|
|
|
// Check to see if the dancing was premature |
1460
|
|
|
if (!$entry->commit()) { |
1461
|
|
|
$this->pageAlert(null, Alert::ERROR); |
1462
|
|
|
} else { |
1463
|
|
|
/** |
1464
|
|
|
* Just after the editing of an Entry |
1465
|
|
|
* |
1466
|
|
|
* @delegate EntryPostEdit |
1467
|
|
|
* @param string $context |
1468
|
|
|
* '/publish/edit/' |
1469
|
|
|
* @param Section $section |
1470
|
|
|
* @param Entry $entry |
1471
|
|
|
* @param array $fields |
1472
|
|
|
*/ |
1473
|
|
|
Symphony::ExtensionManager()->notifyMembers('EntryPostEdit', '/publish/edit/', array('section' => $section, 'entry' => $entry, 'fields' => $fields)); |
1474
|
|
|
|
1475
|
|
|
redirect(sprintf( |
1476
|
|
|
'%s/publish/%s/edit/%d/saved/%s', |
1477
|
|
|
SYMPHONY_URL, |
|
|
|
|
1478
|
|
|
$this->_context['section_handle'], |
1479
|
|
|
$entry->get('id'), |
1480
|
|
|
$this->getPrepopulateString() |
1481
|
|
|
)); |
1482
|
|
|
} |
1483
|
|
|
} |
1484
|
|
|
} elseif (is_array($_POST['action']) && array_key_exists('delete', $_POST['action']) && is_numeric($entry_id)) { |
1485
|
|
|
/** |
1486
|
|
|
* Prior to deletion of entries. An array of Entry ID's is provided which |
1487
|
|
|
* can be manipulated. This delegate was renamed from `Delete` to `EntryPreDelete` |
1488
|
|
|
* in Symphony 2.3. |
1489
|
|
|
* |
1490
|
|
|
* @delegate EntryPreDelete |
1491
|
|
|
* @param string $context |
1492
|
|
|
* '/publish/' |
1493
|
|
|
* @param array $entry_id |
1494
|
|
|
* An array of Entry ID's passed by reference |
1495
|
|
|
*/ |
1496
|
|
|
$checked = array($entry_id); |
1497
|
|
|
Symphony::ExtensionManager()->notifyMembers('EntryPreDelete', '/publish/', array('entry_id' => &$checked)); |
1498
|
|
|
|
1499
|
|
|
$canProceed = $this->validateTimestamp($entry_id); |
1500
|
|
|
|
1501
|
|
|
if ($canProceed) { |
1502
|
|
|
EntryManager::delete($checked); |
1503
|
|
|
|
1504
|
|
|
/** |
1505
|
|
|
* After the deletion of entries, this delegate provides an array of Entry ID's |
1506
|
|
|
* that were deleted. |
1507
|
|
|
* |
1508
|
|
|
* @since Symphony 2.3 |
1509
|
|
|
* @delegate EntryPostDelete |
1510
|
|
|
* @param string $context |
1511
|
|
|
* '/publish/' |
1512
|
|
|
* @param array $entry_id |
1513
|
|
|
* An array of Entry ID's that were deleted. |
1514
|
|
|
*/ |
1515
|
|
|
Symphony::ExtensionManager()->notifyMembers('EntryPostDelete', '/publish/', array('entry_id' => $checked)); |
1516
|
|
|
|
1517
|
|
|
redirect(SYMPHONY_URL . '/publish/'.$this->_context['section_handle'].'/'); |
1518
|
|
|
} else { |
1519
|
|
|
$ret = EntryManager::fetch($entry_id); |
1520
|
|
|
if (!empty($ret)) { |
1521
|
|
|
$entry = $ret[0]; |
1522
|
|
|
$this->addTimestampValidationPageAlert($this->_errors['timestamp'], $entry, 'delete'); |
1523
|
|
|
} |
1524
|
|
|
} |
1525
|
|
|
} |
1526
|
|
|
} |
1527
|
|
|
|
1528
|
|
|
/** |
1529
|
|
|
* Given a Field and Entry object, this function will wrap |
1530
|
|
|
* the Field's displayPublishPanel result with a div that |
1531
|
|
|
* contains some contextual information such as the Field ID, |
1532
|
|
|
* the Field handle and whether it is required or not. |
1533
|
|
|
* |
1534
|
|
|
* @param Field $field |
1535
|
|
|
* @param Entry $entry |
1536
|
|
|
* @return XMLElement |
1537
|
|
|
*/ |
1538
|
|
|
private function __wrapFieldWithDiv(Field $field, Entry $entry) |
1539
|
|
|
{ |
1540
|
|
|
$is_hidden = $this->isFieldHidden($field); |
1541
|
|
|
$div = new XMLElement('div', null, array('id' => 'field-' . $field->get('id'), 'class' => 'field field-'.$field->handle().($field->get('required') == 'yes' ? ' required' : '').($is_hidden === true ? ' irrelevant' : ''))); |
|
|
|
|
1542
|
|
|
|
1543
|
|
|
$field->setAssociationContext($div); |
1544
|
|
|
|
1545
|
|
|
$field->displayPublishPanel( |
1546
|
|
|
$div, $entry->getData($field->get('id')), |
|
|
|
|
1547
|
|
|
(isset($this->_errors[$field->get('id')]) ? $this->_errors[$field->get('id')] : null), |
1548
|
|
|
null, null, (is_numeric($entry->get('id')) ? $entry->get('id') : null) |
|
|
|
|
1549
|
|
|
); |
1550
|
|
|
|
1551
|
|
|
/** |
1552
|
|
|
* Allows developers modify the field before it is rendered in the publish |
1553
|
|
|
* form. Passes the `Field` object, `Entry` object, the `XMLElement` div and |
1554
|
|
|
* any errors for the entire `Entry`. Only the `$div` element |
1555
|
|
|
* will be altered before appending to the page, the rest are read only. |
1556
|
|
|
* |
1557
|
|
|
* @since Symphony 2.5.0 |
1558
|
|
|
* @delegate ModifyFieldPublishWidget |
1559
|
|
|
* @param string $context |
1560
|
|
|
* '/backend/' |
1561
|
|
|
* @param Field $field |
1562
|
|
|
* @param Entry $entry |
1563
|
|
|
* @param array $errors |
1564
|
|
|
* @param Widget $widget |
1565
|
|
|
*/ |
1566
|
|
|
Symphony::ExtensionManager()->notifyMembers('ModifyFieldPublishWidget', '/backend/', array( |
1567
|
|
|
'field' => $field, |
1568
|
|
|
'entry' => $entry, |
1569
|
|
|
'errors' => $this->_errors, |
1570
|
|
|
'widget' => &$div |
1571
|
|
|
)); |
1572
|
|
|
|
1573
|
|
|
return $div; |
1574
|
|
|
} |
1575
|
|
|
|
1576
|
|
|
/** |
1577
|
|
|
* Check whether the given `$field` will be hidden because it's been |
1578
|
|
|
* prepopulated. |
1579
|
|
|
* |
1580
|
|
|
* @param Field $field |
1581
|
|
|
* @return boolean |
1582
|
|
|
*/ |
1583
|
|
|
public function isFieldHidden(Field $field) |
1584
|
|
|
{ |
1585
|
|
|
if ($field->get('hide_when_prepopulated') == 'yes') { |
1586
|
|
|
if (isset($_REQUEST['prepopulate'])) { |
1587
|
|
|
foreach ($_REQUEST['prepopulate'] as $field_id => $value) { |
1588
|
|
|
if ($field_id == $field->get('id')) { |
1589
|
|
|
return true; |
1590
|
|
|
} |
1591
|
|
|
} |
1592
|
|
|
} |
1593
|
|
|
} |
1594
|
|
|
|
1595
|
|
|
return false; |
1596
|
|
|
} |
1597
|
|
|
|
1598
|
|
|
/** |
1599
|
|
|
* Prepare a Drawer to visualize section associations |
1600
|
|
|
* |
1601
|
|
|
* @param Section $section The current Section object |
1602
|
|
|
* @throws InvalidArgumentException |
1603
|
|
|
* @throws Exception |
1604
|
|
|
*/ |
1605
|
|
|
private function prepareAssociationsDrawer($section) |
1606
|
|
|
{ |
1607
|
|
|
$entry_id = (!is_null($this->_context['entry_id'])) ? $this->_context['entry_id'] : null; |
1608
|
|
|
$show_entries = Symphony::Configuration()->get('association_maximum_rows', 'symphony'); |
1609
|
|
|
|
1610
|
|
|
if (is_null($entry_id) && !isset($_GET['prepopulate']) || is_null($show_entries) || $show_entries == 0) { |
1611
|
|
|
return; |
1612
|
|
|
} |
1613
|
|
|
|
1614
|
|
|
$parent_associations = SectionManager::fetchParentAssociations($section->get('id'), true); |
|
|
|
|
1615
|
|
|
$child_associations = SectionManager::fetchChildAssociations($section->get('id'), true); |
|
|
|
|
1616
|
|
|
$content = null; |
1617
|
|
|
$drawer_position = 'vertical-right'; |
1618
|
|
|
|
1619
|
|
|
/** |
1620
|
|
|
* Prepare Associations Drawer from an Extension |
1621
|
|
|
* |
1622
|
|
|
* @since Symphony 2.3.3 |
1623
|
|
|
* @delegate PrepareAssociationsDrawer |
1624
|
|
|
* @param string $context |
1625
|
|
|
* '/publish/' |
1626
|
|
|
* @param integer $entry_id |
1627
|
|
|
* The entry ID or null |
1628
|
|
|
* @param array $parent_associations |
1629
|
|
|
* Array of Sections |
1630
|
|
|
* @param array $child_associations |
1631
|
|
|
* Array of Sections |
1632
|
|
|
* @param string $drawer_position |
1633
|
|
|
* The position of the Drawer, defaults to `vertical-right`. Available |
1634
|
|
|
* values of `vertical-left, `vertical-right` and `horizontal` |
1635
|
|
|
*/ |
1636
|
|
|
Symphony::ExtensionManager()->notifyMembers('PrepareAssociationsDrawer', '/publish/', array( |
1637
|
|
|
'entry_id' => $entry_id, |
1638
|
|
|
'parent_associations' => &$parent_associations, |
1639
|
|
|
'child_associations' => &$child_associations, |
1640
|
|
|
'content' => &$content, |
1641
|
|
|
'drawer-position' => &$drawer_position |
1642
|
|
|
)); |
1643
|
|
|
|
1644
|
|
|
// If there are no associations, return now. |
1645
|
|
|
if ( |
|
|
|
|
1646
|
|
|
(is_null($parent_associations) || empty($parent_associations)) |
1647
|
|
|
&& |
1648
|
|
|
(is_null($child_associations) || empty($child_associations)) |
1649
|
|
|
) { |
1650
|
|
|
return; |
1651
|
|
|
} |
1652
|
|
|
|
1653
|
|
|
if (!($content instanceof XMLElement)) { |
1654
|
|
|
$content = new XMLElement('div', null, array('class' => 'content')); |
1655
|
|
|
$content->setSelfClosingTag(false); |
1656
|
|
|
|
1657
|
|
|
// backup global sorting |
1658
|
|
|
$sorting = EntryManager::getFetchSorting(); |
1659
|
|
|
|
1660
|
|
|
// Process Parent Associations |
1661
|
|
|
if (!is_null($parent_associations) && !empty($parent_associations)) { |
1662
|
|
|
$title = new XMLElement('h2', __('Linked to') . ':', array('class' => 'association-title')); |
1663
|
|
|
$content->appendChild($title); |
1664
|
|
|
|
1665
|
|
|
foreach ($parent_associations as $as) { |
1666
|
|
|
if (empty($as['parent_section_field_id'])) { |
1667
|
|
|
continue; |
1668
|
|
|
} |
|
|
|
|
1669
|
|
|
if ($field = FieldManager::fetch($as['parent_section_field_id'])) { |
1670
|
|
|
// Get the related section |
1671
|
|
|
$parent_section = SectionManager::fetch($as['parent_section_id']); |
1672
|
|
|
|
1673
|
|
|
if (!($parent_section instanceof Section)) { |
1674
|
|
|
continue; |
1675
|
|
|
} |
1676
|
|
|
|
1677
|
|
|
// set global sorting for associated section |
1678
|
|
|
EntryManager::setFetchSorting( |
1679
|
|
|
$parent_section->getSortingField(), |
|
|
|
|
1680
|
|
|
$parent_section->getSortingOrder() |
1681
|
|
|
); |
1682
|
|
|
|
1683
|
|
|
if (isset($_GET['prepopulate'])) { |
1684
|
|
|
$prepopulate_field = key($_GET['prepopulate']); |
1685
|
|
|
} |
1686
|
|
|
|
1687
|
|
|
// get associated entries if entry exists, |
1688
|
|
|
if ($entry_id) { |
1689
|
|
|
$relation_field = FieldManager::fetch($as['child_section_field_id']); |
1690
|
|
|
$entry_ids = $relation_field->findParentRelatedEntries($as['parent_section_field_id'], $entry_id); |
1691
|
|
|
|
1692
|
|
|
// get prepopulated entry otherwise |
1693
|
|
|
} elseif (isset($_GET['prepopulate']) && is_array($_GET['prepopulate']) && isset($_GET['prepopulate'][$as['child_section_field_id']])) { |
1694
|
|
|
$entry_ids = array(intval($_GET['prepopulate'][$as['child_section_field_id']])); |
1695
|
|
|
} else { |
1696
|
|
|
$entry_ids = array(); |
1697
|
|
|
} |
1698
|
|
|
|
1699
|
|
|
// Use $schema for perf reasons |
1700
|
|
|
$schema = array($field->get('element_name')); |
1701
|
|
|
$where = (!empty($entry_ids)) ? sprintf(' AND `e`.`id` IN (%s)', implode(', ', $entry_ids)) : null; |
1702
|
|
|
$entries = (!empty($entry_ids) || isset($_GET['prepopulate']) && $field->get('id') === $prepopulate_field) |
|
|
|
|
1703
|
|
|
? EntryManager::fetchByPage(1, $as['parent_section_id'], $show_entries, $where, null, false, false, true, $schema) |
|
|
|
|
1704
|
|
|
: array(); |
1705
|
|
|
$has_entries = !empty($entries) && $entries['total-entries'] != 0; |
1706
|
|
|
|
1707
|
|
|
// Create link |
1708
|
|
|
$link = SYMPHONY_URL . '/publish/' . $as['handle'] . '/'; |
|
|
|
|
1709
|
|
|
$aname = General::sanitize($as['name']); |
1710
|
|
|
if ($has_entries) { |
1711
|
|
|
$aname .= ' <span>(' . $entries['total-entries'] . ')</span>'; |
1712
|
|
|
} |
|
|
|
|
1713
|
|
|
$a = new XMLElement('a', $aname, array( |
1714
|
|
|
'class' => 'association-section', |
1715
|
|
|
'href' => $link, |
1716
|
|
|
'title' => strip_tags($aname), |
1717
|
|
|
)); |
1718
|
|
|
|
1719
|
|
|
if (!$has_entries) { |
1720
|
|
|
unset($field); |
1721
|
|
|
continue; |
1722
|
|
|
} |
1723
|
|
|
|
1724
|
|
|
$element = new XMLElement('section', null, array('class' => 'association parent')); |
1725
|
|
|
$header = new XMLElement('header'); |
1726
|
|
|
$header->appendChild(new XMLElement('p', $a->generate())); |
1727
|
|
|
$element->appendChild($header); |
1728
|
|
|
|
1729
|
|
|
$ul = new XMLElement('ul', null, array( |
1730
|
|
|
'class' => 'association-links', |
1731
|
|
|
'data-section-id' => $as['child_section_id'], |
1732
|
|
|
'data-association-ids' => implode(', ', $entry_ids) |
1733
|
|
|
)); |
1734
|
|
|
|
1735
|
|
|
foreach ($entries['records'] as $e) { |
1736
|
|
|
// let the field create the mark up |
1737
|
|
|
$li = $field->prepareAssociationsDrawerXMLElement($e, $as); |
1738
|
|
|
// add it to the unordered list |
1739
|
|
|
$ul->appendChild($li); |
1740
|
|
|
} |
1741
|
|
|
|
1742
|
|
|
$element->appendChild($ul); |
1743
|
|
|
$content->appendChild($element); |
1744
|
|
|
unset($field); |
1745
|
|
|
} |
1746
|
|
|
} |
1747
|
|
|
} |
1748
|
|
|
|
1749
|
|
|
// Process Child Associations |
1750
|
|
|
if (!is_null($child_associations) && !empty($child_associations)) { |
|
|
|
|
1751
|
|
|
$title = new XMLElement('h2', __('Links in') . ':', array('class' => 'association-title')); |
1752
|
|
|
$content->appendChild($title); |
1753
|
|
|
|
1754
|
|
|
foreach ($child_associations as $as) { |
1755
|
|
|
// Get the related section |
1756
|
|
|
$child_section = SectionManager::fetch($as['child_section_id']); |
1757
|
|
|
|
1758
|
|
|
if (!($child_section instanceof Section)) { |
1759
|
|
|
continue; |
1760
|
|
|
} |
1761
|
|
|
|
1762
|
|
|
// set global sorting for associated section |
1763
|
|
|
EntryManager::setFetchSorting( |
1764
|
|
|
$child_section->getSortingField(), |
1765
|
|
|
$child_section->getSortingOrder() |
1766
|
|
|
); |
1767
|
|
|
|
1768
|
|
|
// Get the visible field instance (using the sorting field, this is more flexible than visibleColumns()) |
1769
|
|
|
// Get the link field instance |
1770
|
|
|
$visible_field = current($child_section->fetchVisibleColumns()); |
1771
|
|
|
$relation_field = FieldManager::fetch($as['child_section_field_id']); |
1772
|
|
|
|
1773
|
|
|
$entry_ids = $relation_field->findRelatedEntries($entry_id, $as['parent_section_field_id']); |
1774
|
|
|
|
1775
|
|
|
$schema = $visible_field ? array($visible_field->get('element_name')) : array(); |
|
|
|
|
1776
|
|
|
$where = sprintf(' AND `e`.`id` IN (%s)', implode(', ', $entry_ids)); |
1777
|
|
|
|
1778
|
|
|
$entries = (!empty($entry_ids)) ? EntryManager::fetchByPage(1, $as['child_section_id'], $show_entries, $where, null, false, false, true, $schema) : array(); |
1779
|
|
|
$has_entries = !empty($entries) && $entries['total-entries'] != 0; |
1780
|
|
|
|
1781
|
|
|
// Build the HTML of the relationship |
1782
|
|
|
$element = new XMLElement('section', null, array('class' => 'association child')); |
1783
|
|
|
$header = new XMLElement('header'); |
1784
|
|
|
|
1785
|
|
|
// Get the search value for filters and prepopulate |
1786
|
|
|
$filter = ''; |
1787
|
|
|
$prepopulate = ''; |
1788
|
|
|
$entry = current(EntryManager::fetch($entry_id)); |
1789
|
|
|
if ($entry) { |
1790
|
|
|
$search_value = $relation_field->fetchAssociatedEntrySearchValue( |
1791
|
|
|
$entry->getData($as['parent_section_field_id']), |
1792
|
|
|
$as['parent_section_field_id'], |
1793
|
|
|
$entry_id |
1794
|
|
|
); |
1795
|
|
|
if (is_array($search_value)) { |
1796
|
|
|
$search_value = $entry_id; |
1797
|
|
|
} |
|
|
|
|
1798
|
|
|
$filter = '?filter[' . $relation_field->get('element_name') . ']=' . $search_value; |
1799
|
|
|
$prepopulate = '?prepopulate[' . $as['child_section_field_id'] . ']=' . $search_value; |
1800
|
|
|
} |
1801
|
|
|
|
1802
|
|
|
// Create link with filter or prepopulate |
1803
|
|
|
$link = SYMPHONY_URL . '/publish/' . $as['handle'] . '/' . $filter; |
1804
|
|
|
$aname = General::sanitize($as['name']); |
1805
|
|
|
if ($has_entries) { |
1806
|
|
|
$aname .= ' <span>(' . $entries['total-entries'] . ')</span>'; |
1807
|
|
|
} |
|
|
|
|
1808
|
|
|
$a = new XMLElement('a', $aname, array( |
1809
|
|
|
'class' => 'association-section', |
1810
|
|
|
'href' => $link, |
1811
|
|
|
'title' => strip_tags($aname), |
1812
|
|
|
)); |
1813
|
|
|
|
1814
|
|
|
// Create new entries |
1815
|
|
|
$create = new XMLElement('a', __('New'), array( |
1816
|
|
|
'class' => 'button association-new', |
1817
|
|
|
'href' => SYMPHONY_URL . '/publish/' . $as['handle'] . '/new/' . $prepopulate |
1818
|
|
|
)); |
1819
|
|
|
|
1820
|
|
|
// Display existing entries |
1821
|
|
|
if ($has_entries) { |
1822
|
|
|
$header->appendChild(new XMLElement('p', $a->generate())); |
1823
|
|
|
|
1824
|
|
|
$ul = new XMLElement('ul', null, array( |
1825
|
|
|
'class' => 'association-links', |
1826
|
|
|
'data-section-id' => $as['child_section_id'], |
1827
|
|
|
'data-association-ids' => implode(', ', $entry_ids) |
1828
|
|
|
)); |
1829
|
|
|
|
1830
|
|
|
foreach ($entries['records'] as $key => $e) { |
1831
|
|
|
// let the first visible field create the mark up |
1832
|
|
|
if ($visible_field) { |
1833
|
|
|
$li = $visible_field->prepareAssociationsDrawerXMLElement($e, $as, $prepopulate); |
1834
|
|
|
} |
1835
|
|
|
// or use the system:id if no visible field exists. |
1836
|
|
|
else { |
1837
|
|
|
$li = Field::createAssociationsDrawerXMLElement($e->get('id'), $e, $as, $prepopulate); |
1838
|
|
|
} |
1839
|
|
|
|
1840
|
|
|
// add it to the unordered list |
1841
|
|
|
$ul->appendChild($li); |
1842
|
|
|
} |
1843
|
|
|
|
1844
|
|
|
$element->appendChild($ul); |
1845
|
|
|
|
1846
|
|
|
// If we are only showing 'some' of the entries, then show this on the UI |
1847
|
|
|
if ($entries['total-entries'] > $show_entries) { |
1848
|
|
|
$pagination = new XMLElement('li', null, array( |
1849
|
|
|
'class' => 'association-more', |
1850
|
|
|
'data-current-page' => '1', |
1851
|
|
|
'data-total-pages' => ceil($entries['total-entries'] / $show_entries), |
1852
|
|
|
'data-total-entries' => $entries['total-entries'] |
1853
|
|
|
)); |
1854
|
|
|
$counts = new XMLElement('a', __('Show more entries'), array( |
1855
|
|
|
'href' => $link |
1856
|
|
|
)); |
1857
|
|
|
|
1858
|
|
|
$pagination->appendChild($counts); |
1859
|
|
|
$ul->appendChild($pagination); |
1860
|
|
|
} |
1861
|
|
|
|
1862
|
|
|
// No entries |
1863
|
|
|
} else { |
1864
|
|
|
$element->setAttribute('class', 'association child empty'); |
1865
|
|
|
$header->appendChild(new XMLElement('p', __('No links in %s', array($a->generate())))); |
1866
|
|
|
} |
1867
|
|
|
|
1868
|
|
|
$header->appendChild($create); |
1869
|
|
|
$element->prependChild($header); |
1870
|
|
|
$content->appendChild($element); |
1871
|
|
|
} |
1872
|
|
|
} |
1873
|
|
|
|
1874
|
|
|
// reset global sorting |
1875
|
|
|
EntryManager::setFetchSorting( |
1876
|
|
|
$sorting->field, |
1877
|
|
|
$sorting->direction |
1878
|
|
|
); |
1879
|
|
|
} |
1880
|
|
|
|
1881
|
|
|
$drawer = Widget::Drawer('section-associations', __('Show Associations'), $content); |
1882
|
|
|
$this->insertDrawer($drawer, $drawer_position, 'prepend'); |
1883
|
|
|
} |
1884
|
|
|
|
1885
|
|
|
/** |
1886
|
|
|
* If this entry is being prepopulated, this function will return the prepopulated |
1887
|
|
|
* fields and values as a query string. |
1888
|
|
|
* |
1889
|
|
|
* @since Symphony 2.5.2 |
1890
|
|
|
* @return string |
1891
|
|
|
*/ |
1892
|
|
|
public function getPrepopulateString() |
1893
|
|
|
{ |
1894
|
|
|
$prepopulate_querystring = ''; |
1895
|
|
|
|
1896
|
|
|
if (isset($_REQUEST['prepopulate']) && is_array($_REQUEST['prepopulate'])) { |
1897
|
|
|
foreach ($_REQUEST['prepopulate'] as $field_id => $value) { |
1898
|
|
|
// Properly decode and re-encode value for output |
1899
|
|
|
$value = rawurlencode(rawurldecode($value)); |
1900
|
|
|
$prepopulate_querystring .= sprintf("prepopulate[%s]=%s&", $field_id, $value); |
|
|
|
|
1901
|
|
|
} |
|
|
|
|
1902
|
|
|
$prepopulate_querystring = trim($prepopulate_querystring, '&'); |
1903
|
|
|
} |
1904
|
|
|
|
1905
|
|
|
// This is to prevent the value being interpreted as an additional GET |
1906
|
|
|
// parameter. eg. prepopulate[cat]=Minx&June, would come through as: |
1907
|
|
|
// $_GET['cat'] = Minx |
1908
|
|
|
// $_GET['June'] = '' |
1909
|
|
|
$prepopulate_querystring = preg_replace("/&$/", '', $prepopulate_querystring); |
|
|
|
|
1910
|
|
|
|
1911
|
|
|
return $prepopulate_querystring ? '?' . $prepopulate_querystring : null; |
|
|
|
|
1912
|
|
|
} |
1913
|
|
|
|
1914
|
|
|
/** |
1915
|
|
|
* If the entry is being prepopulated, we may want to filter other views by this entry's |
1916
|
|
|
* value. This function will create that filter query string. |
1917
|
|
|
* |
1918
|
|
|
* @since Symphony 2.5.2 |
1919
|
|
|
* @return string |
1920
|
|
|
*/ |
1921
|
|
|
public function getFilterString() |
1922
|
|
|
{ |
1923
|
|
|
$filter_querystring = ''; |
1924
|
|
|
|
1925
|
|
|
if (isset($_REQUEST['prepopulate']) && is_array($_REQUEST['prepopulate'])) { |
1926
|
|
|
foreach ($_REQUEST['prepopulate'] as $field_id => $value) { |
1927
|
|
|
$handle = FieldManager::fetchHandleFromID($field_id); |
1928
|
|
|
// Properly decode and re-encode value for output |
1929
|
|
|
$value = rawurlencode(rawurldecode($value)); |
1930
|
|
|
$filter_querystring .= sprintf('filter[%s]=%s&', $handle, $value); |
1931
|
|
|
} |
|
|
|
|
1932
|
|
|
$filter_querystring = trim($filter_querystring, '&'); |
1933
|
|
|
} |
1934
|
|
|
|
1935
|
|
|
// This is to prevent the value being interpreted as an additional GET |
1936
|
|
|
// parameter. eg. filter[cat]=Minx&June, would come through as: |
1937
|
|
|
// $_GET['cat'] = Minx |
1938
|
|
|
// $_GET['June'] = '' |
1939
|
|
|
$filter_querystring = preg_replace("/&$/", '', $filter_querystring); |
|
|
|
|
1940
|
|
|
|
1941
|
|
|
return $filter_querystring ? '?' . $filter_querystring : null; |
|
|
|
|
1942
|
|
|
} |
1943
|
|
|
|
1944
|
|
|
/** |
1945
|
|
|
* Given $_POST values, this function will validate the current timestamp |
1946
|
|
|
* and set the proper error messages. |
1947
|
|
|
* |
1948
|
|
|
* @since Symphony 2.7.0 |
1949
|
|
|
* @param int $entry_id |
1950
|
|
|
* The entry id to validate |
1951
|
|
|
* @return boolean |
1952
|
|
|
* true if the timestamp is valid |
1953
|
|
|
*/ |
1954
|
|
|
protected function validateTimestamp($entry_id, $checkMissing = false) |
|
|
|
|
1955
|
|
|
{ |
1956
|
|
|
if (!isset($_POST['action']['ignore-timestamp'])) { |
1957
|
|
|
if ($checkMissing && !isset($_POST['action']['timestamp'])) { |
1958
|
|
|
if (isset($this->_errors) && is_array($this->_errors)) { |
1959
|
|
|
$this->_errors['timestamp'] = __('The entry could not be saved due to conflicting changes'); |
1960
|
|
|
} |
|
|
|
|
1961
|
|
|
return false; |
1962
|
|
|
} elseif (isset($_POST['action']['timestamp'])) { |
1963
|
|
|
$tv = new TimestampValidator('entries'); |
1964
|
|
|
if (!$tv->check($entry_id, $_POST['action']['timestamp'])) { |
1965
|
|
|
if (isset($this->_errors) && is_array($this->_errors)) { |
1966
|
|
|
$this->_errors['timestamp'] = __('The entry could not be saved due to conflicting changes'); |
1967
|
|
|
} |
|
|
|
|
1968
|
|
|
return false; |
1969
|
|
|
} |
1970
|
|
|
} |
1971
|
|
|
} |
|
|
|
|
1972
|
|
|
return true; |
1973
|
|
|
} |
1974
|
|
|
} |
1975
|
|
|
|
Classes in PHP are usually named in CamelCase.
In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. The whole name starts with a capital letter as well.
Thus the name database provider becomes
DatabaseProvider
.