generateFieldsLink()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
	/**
3
	 * Copyright: Deux Huit Huit 2017
4
	 * License: MIT, http://deuxhuithuit.mit-license.org
5
	 */
6
7
	if(!defined("__IN_SYMPHONY__")) die("<h2>Error</h2><p>You cannot directly access this file</p>");
8
9
	require_once(EXTENSIONS . '/entry_relationship_field/lib/class.cacheablefetch.php');
10
11
	class contentExtensionEntry_Relationship_FieldCleanup extends AdministrationPage
12
	{
13
		private $sectionManager;
14
		private $fieldManager;
15
		private $entryManager;
16
		
17
		public function __construct()
18
		{
19
			parent::__construct();
20
			$this->sectionManager = new CacheableFetch('SectionManager');
21
			$this->fieldManager = new CacheableFetch('FieldManager');
22
			$this->entryManager = new CacheableFetch('EntryManager');
23
		}
24
		
25
		private static function TableLabel($value)
26
		{
27
			$label = new XMLElement('span', __($value), array('class' => 'inactive'));
28
			return $label->generate() . '<br />';
29
		}
30
		
31
		/**
32
		 *
33
		 * Builds the content view
34
		 */
35
		public function __viewIndex()
36
		{
37
			$title = __('Entry Relationship Clean up');
38
			
39
			$this->setTitle(__('%1$s &ndash; %2$s', array(__('Symphony'), $title)));
40
			$this->addScriptToHead(URL . '/extensions/entry_relationship_field/assets/cleanup.entry_relationship_field.js', 10, false);
41
			$this->setPageType('table');
42
			$this->appendSubheading(__($title));
43
			
44
			$fieldset = new XMLElement('fieldset', null, array('class' => 'settings'));
45
			
46
			$fieldset->appendChild(new XMLElement('legend',__('List of all orphans entries')));
47
			$fieldset->appendChild(new XMLElement('p',__('Please choose what to delete', array('class' => 'help'))));
48
			
49
			$this->Form->appendChild($fieldset);
50
			
51
			$fields = $this->getAllFieldsData();
52
			$sections = $this->normalizeDataPerSection($fields);
53
			
54
			$thead = array(
55
				self::TableLabel('Section'),
56
				self::TableLabel('Fields linked to'),
57
				self::TableLabel('Number of entries'),
58
				self::TableLabel('Orphan entries'),
59
				self::TableLabel('Linked entries'),
60
			);
61
			$tbody = array();
62
63
			// If there are no fields, display default message
64
			if (!is_array($sections) || empty($sections)) {
65
				$tbody[] = Widget::TableRow(array(
66
					Widget::TableData(
67
						__('No data available.'),
68
						'inactive',
69
						null,
70
						count($thead)
71
					))
72
				);
73
			}
74
75
			// Otherwise, build table rows
76
			else {
77
				foreach ($sections as $section) {
78
					if (empty($section['orphans'])) {
79
						continue;
80
					}
81
					$tbody[] = Widget::TableRow(array(
82
						Widget::TableData(
83
							Widget::Anchor('#',
84
								'#' . $section['section']->get('handle'), null, null,
85
								$section['section']->get('handle')
86
							)->generate() . ' ' . $thead[0] .
87
							Widget::Anchor(
88
								$section['section']->get('name'),
89
								SYMPHONY_URL . '/publish/' . $section['section']->get('handle') . '/'
90
							)->generate()
91
						),
92
						Widget::TableData($thead[1] . count($section['fields']) . $this->generateFieldsLink($section)),
93
						Widget::TableData($thead[2] . count($section['all-entries'])),
94
						Widget::TableData($thead[3] . count($section['orphans'])),
95
						Widget::TableData($thead[4] . count($section['linked-entries'])),
96
					), 'js-table-section');
97
					$tbody[] = Widget::TableRow(array(
98
						Widget::TableData(
99
							$this->generateOrphanTable($section),
100
							null, null, count($thead)
101
						),
102
					), 'js-table-entries irrelevant');
103
				}
104
			}
105
			$table = Widget::Table(
106
				null, null,
107
				Widget::TableBody($tbody), '', null,
108
				array('role' => 'directory', 'aria-labelledby' => 'symphony-subheading', 'data-interactive' => 'data-interactive')
109
			);
110
			$this->Form->appendChild($table);
111
112
			if (is_array($sections) && !empty($sections)) {
113
				// Append table actions
114
				$options = array(
115
					array(null, false, __('With Selected...')),
116
					array('delete', false, __('Delete'), 'confirm', null, array(
117
						'data-message' => __('Are you sure you want to delete the selected entries?')
118
					))
119
				);
120
121
				$tableActions = new XMLElement('div');
122
				$tableActions->setAttribute('class', 'actions');
123
				$tableActions->appendChild(Widget::Apply($options));
124
				$this->Form->appendChild($tableActions);
125
			}
126
		}
127
128
129
		public function __actionIndex()
130
		{
131
			$checked = (is_array($_POST['items'])) ? array_keys($_POST['items']) : null;
132
133
			if (is_array($checked) && !empty($checked)) {
134
				switch ($_POST['with-selected']) {
135
					case 'delete':
136
						/**
137
						 * Prior to deletion of entries. An array of Entry ID's is provided which
138
						 * can be manipulated. This delegate was renamed from `Delete` to `EntryPreDelete`
139
						 * in Symphony 2.3.
140
						 *
141
						 * @delegate EntryPreDelete
142
						 * @param string $context
143
						 * '/publish/'
144
						 * @param array $entry_id
145
						 *  An array of Entry ID's passed by reference
146
						 */
147
						Symphony::ExtensionManager()->notifyMembers('EntryPreDelete', '/publish/', array('entry_id' => &$checked));
148
149
						EntryManager::delete($checked);
150
151
						/**
152
						 * After the deletion of entries, this delegate provides an array of Entry ID's
153
						 * that were deleted.
154
						 *
155
						 * @since Symphony 2.3
156
						 * @delegate EntryPostDelete
157
						 * @param string $context
158
						 * '/publish/'
159
						 * @param array $entry_id
160
						 *  An array of Entry ID's that were deleted.
161
						 */
162
						Symphony::ExtensionManager()->notifyMembers('EntryPostDelete', '/publish/', array('entry_id' => $checked));
163
164
						break;
165
				}
166
			}
167
		}
168
169
		public function generateFieldsLink(array $section)
170
		{
171
			$html = ' ';
172
			foreach ($section['fields'] as $field) {
173
				$html .= '<br />' . Widget::Anchor(
174
					$field->section->get('name') . ': ' . $field->get('label') ,
175
					SYMPHONY_URL . '/publish/' . $field->section->get('handle') . '/'
176
				)->generate();
177
			}
178
			return $html;
179
		}
180
181
		public function generateOrphanTable(array $section)
182
		{
183
			// get visible columns
184
			$visible_columns = $section['section']->fetchVisibleColumns();
185
			// extract the needed schema
186
			$element_names = array_values(array_map(function ($field) {
187
				return $field->get('element_name');
188
			}, $visible_columns));
189
			
190
			$entries = $section['orphans'];
191
			$thead = array();
192
193
			if (is_array($visible_columns) && !empty($visible_columns)) {
194
				foreach ($visible_columns as $column) {
195
					$thead[] = array($column->get('label'));
196
				}
197
			} else {
198
				$thead[] = array(__('ID'));
199
			}
200
			
201
			$tbody = array();
202
			
203
			foreach ($entries as $orphan) {
204
				$td = array();
205
				$o = $this->entryManager->fetch($orphan, $section['section']->get('id'), null, null, null, null, false, true, $element_names, false);
206
				if (empty($o)) {
207
					$td[] = Widget::TableData("Entry $orphan not found", null, null, count($thead));
208
					$tbody[] = Widget::TableRow($td);
209
					continue;
210
				}
211
				if (is_array($o)) {
212
					$o = $o[0];
213
				}
214
				if (!$o || !is_object($o)) {
215
					// wrong section
216
					throw new Exception('Fetch an entry from an invalid section');
217
				}
218
				$link = Widget::Anchor('',
219
					SYMPHONY_URL . '/publish/' . $section['section']->get('handle') . '/edit/' . $orphan . '/',
220
					$orphan,
221
					'content'
222
				);
223
				if (is_array($visible_columns) && !empty($visible_columns)) {
224
					foreach ($visible_columns as $column) {
225
						$data = $o->getData($column->get('id'));
226
						$td[] = Widget::TableData($column->prepareTableValue($data, $link, $orphan));
227
						$link = null;
228
					}
229
				} else {
230
					$link->setValue($orphan);
231
					$td[] = Widget::TableData($link);
232
				}
233
				$td[] = Widget::TableData(Widget::Input("items[$orphan]", null, 'checkbox', array(
234
					'id' => "entry-$orphan"
235
				)));
236
				$tbody[] = Widget::TableRow($td);
237
			}
238
			
239
			return Widget::Table(
240
				Widget::TableHead($thead), null,
241
				Widget::TableBody($tbody), 'selectable', null,
242
				array('role' => 'directory', 'aria-labelledby' => 'symphony-subheading', 'data-interactive' => 'data-interactive')
243
			)->generate();
244
		}
245
246
		public function normalizeDataPerSection(array $fields)
247
		{
248
			$allLinkedSections = array();
249
			foreach ($fields as $field) {
250
				foreach ($field->linkedSections as $section) {
251
					$sectionId = $section->get('id');
252
					if (!isset($allLinkedSections[$sectionId])) {
253
						$allLinkedSections[$sectionId] = array(
254
							'section' => $section,
255
							'fields' => array($field)
256
						);
257
					} else {
258
						$allLinkedSections[$sectionId]['fields'][] = $field;
259
					}
260
				}
261
			}
262
			foreach ($allLinkedSections as &$ls) {
263
				// All entries in that section
264
				$ls['all-entries'] = array_map(function ($e) {
265
					return $e['id'];
266
				}, $this->entryManager->fetch(null, $ls['section']->get('id'), null, null, null, null, false, false, null, false));
267
				// Merge all linked entries for each related field
268
				$ls['linked-entries'] = array();
269
				foreach ($ls['fields'] as $field) {
270
					$ls['linked-entries'] = array_merge($ls['linked-entries'], $field->linkedEntries);
271
				}
272
				// All linked entries that are in the all-entries (section based filtering)
273
				$ls['linked-entries'] = array_unique(array_intersect($ls['linked-entries'], $ls['all-entries']), SORT_NUMERIC);
274
				// All linked entries not in all-entries
275
				$ls['orphans'] = array_unique(array_diff($ls['all-entries'], $ls['linked-entries']), SORT_NUMERIC);
276
			}
277
			return $allLinkedSections;
278
		}
279
280
		public function getAllFieldsData()
281
		{
282
			$fields = $this->fieldManager->fetch(null, null, 'ASC', 'sortorder', 'entry_relationship');
283
			$fields = array_map(function ($f) {
284
				// Get the field's section
285
				$f->section = $this->sectionManager->fetch($f->get('parent_section'));
286
				// Get all linked entries from all entries in this field
287
				$f->linkedEntries = array();
288
				$fieldEntries = $this->entryManager->fetch(null, $f->get('parent_section'), null, null, null, null, false, true, array($f->get('element_name')), false);
289
				foreach ($fieldEntries as $fEntry) {
290
					$fedata = $fEntry->getData($f->get('id'));
291
					if (empty($fedata)) {
292
						continue;
293
					}
294
					// make section array dim
295
					$f->linkedEntries = array_merge($f->linkedEntries, array_filter(explode(',', $fedata['entries'])));
296
				}
297
				// Find all sections this field can be related with
298
				$f->linkedSections = array();
299
				$rSections = explode(',', $f->get('sections'));
300
				foreach ($rSections as $s) {
301
					$section = $this->sectionManager->fetch($s);
302
					$f->linkedSections[] = $section;
303
				}
304
				// Return new field object
305
				return $f;
306
			}, $fields);
307
			
308
			return $fields;
309
		}
310
	}
311