[TASK] Turn todos into @todo to find them easier
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Module / ModuleSettings.php
1 <?php
2 namespace TYPO3\CMS\Backend;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18
19 /**
20 * Manage storing and restoring of $GLOBALS['SOBE']->MOD_SETTINGS settings.
21 * Provides a presets box for BE modules.
22 *
23 * usage inside of scbase class
24 *
25 * ....
26 *
27 * $this->MOD_MENU = array(
28 * 'function' => array(
29 * 'xxx ...
30 * ),
31 * 'tx_dam_select_storedSettings' => '',
32 *
33 * ....
34 *
35 * function main() {
36 * reStore settings
37 * $store = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Backend\ModuleSettings::class);
38 * $store->init('tx_dam_select');
39 * $store->setStoreList('tx_dam_select');
40 * $store->processStoreControl();
41 *
42 * show control panel
43 * $this->content.= $this->doc->section('Settings',$store->getStoreControl(),0,1);
44 *
45 * Format of saved settings
46 *
47 * $GLOBALS['SOBE']->MOD_SETTINGS[$this->prefix.'_storedSettings'] = serialize(
48 * array (
49 * 'any id' => array (
50 * 'title' => 'title for saved settings',
51 * 'desc' => 'description text, not mandatory',
52 * 'data' => array(), // data from MOD_SETTINGS
53 * 'user' => NULL, // can be used for extra data used by the application to identify this entry
54 * 'tstamp' => 12345, // $GLOBALS['EXEC_TIME']
55 * ),
56 * 'another id' => ...
57 *
58 * ) );
59 *
60 * @author René Fritz <r.fritz@colorcube.de>
61 */
62 class ModuleSettings {
63
64 /**
65 * If type is set 'ses' then the module data will be stored into the session and will be lost with logout.
66 * Type 'perm' will store the data permanently.
67 *
68 * @var string
69 */
70 public $type = 'perm';
71
72 /**
73 * prefix of MOD_SETTING array keys that should be stored
74 *
75 * @var string
76 */
77 public $prefix = '';
78
79 /**
80 * Names of keys of the MOD_SETTING array which should be stored
81 *
82 * @var array
83 */
84 public $storeList = array();
85
86 /**
87 * The stored settings array
88 *
89 * @var array
90 */
91 public $storedSettings = array();
92
93 /**
94 * Message from the last storage command
95 *
96 * @var string
97 */
98 public $msg = '';
99
100 /**
101 * Name of the form. Needed for JS
102 *
103 * @var string
104 */
105 public $formName = 'storeControl';
106
107 /**
108 * Write messages into the devlog?
109 *
110 * @var int
111 */
112 public $writeDevLog = 0;
113
114 /********************************
115 *
116 * Init / setup
117 *
118 ********************************/
119 /**
120 * Initializes the object
121 *
122 * @param string $prefix Prefix of MOD_SETTING array keys that should be stored
123 * @param array $storeList Additional names of keys of the MOD_SETTING array which should be stored (array or comma list)
124 * @return void
125 */
126 public function init($prefix = '', $storeList = '') {
127 $this->prefix = $prefix;
128 $this->setStoreList($storeList);
129 $this->type = 'perm';
130 // Enable dev logging if set
131 if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_modSettings.php']['writeDevLog']) {
132 $this->writeDevLog = TRUE;
133 }
134 if (TYPO3_DLOG) {
135 $this->writeDevLog = TRUE;
136 }
137 }
138
139 /**
140 * Set session type to 'ses' which will store the settings data not permanently.
141 *
142 * @param string $type Default is 'ses'
143 * @return void
144 */
145 public function setSessionType($type = 'ses') {
146 $this->type = $type;
147 }
148
149 /********************************
150 *
151 * Store list - which values should be stored
152 *
153 ********************************/
154 /**
155 * Set MOD_SETTINGS keys which should be stored
156 *
157 * @param mixed $storeList Array or string (,) - set additional names of keys of the MOD_SETTING array which should be stored
158 * @return void
159 */
160 public function setStoreList($storeList) {
161 $this->storeList = is_array($storeList) ? $storeList : GeneralUtility::trimExplode(',', $storeList, TRUE);
162 if ($this->writeDevLog) {
163 GeneralUtility::devLog('Store list:' . implode(',', $this->storeList), \TYPO3\CMS\Backend\ModuleSettings::class, 0);
164 }
165 }
166
167 /**
168 * Add MOD_SETTINGS keys to the current list
169 *
170 * @param mixed Array or string (,) - add names of keys of the MOD_SETTING array which should be stored
171 * @return void
172 */
173 public function addToStoreList($storeList) {
174 $storeList = is_array($storeList) ? $storeList : GeneralUtility::trimExplode(',', $storeList, TRUE);
175 $this->storeList = array_merge($this->storeList, $storeList);
176 if ($this->writeDevLog) {
177 GeneralUtility::devLog('Store list:' . implode(',', $this->storeList), \TYPO3\CMS\Backend\ModuleSettings::class, 0);
178 }
179 }
180
181 /**
182 * Add names of keys of the MOD_SETTING array by a prefix
183 *
184 * @param string $prefix Prefix of MOD_SETTING array keys that should be stored
185 * @return void
186 */
187 public function addToStoreListFromPrefix($prefix = '') {
188 $prefix = $prefix ? $prefix : $this->prefix;
189 $prefix = preg_quote($prefix, '/');
190 foreach ($GLOBALS['SOBE']->MOD_SETTINGS as $key => $value) {
191 if (preg_match('/^' . $prefix . '/', $key)) {
192 $this->storeList[$key] = $key;
193 }
194 }
195 unset($this->storeList[$this->prefix . '_storedSettings']);
196 if ($this->writeDevLog) {
197 GeneralUtility::devLog('Store list:' . implode(',', $this->storeList), \TYPO3\CMS\Backend\ModuleSettings::class, 0);
198 }
199 }
200
201 /********************************
202 *
203 * Process storage array
204 *
205 ********************************/
206 /**
207 * Get the stored settings from MOD_SETTINGS and set them in $this->storedSettings
208 *
209 * @return void
210 */
211 public function initStorage() {
212 $storedSettings = unserialize($GLOBALS['SOBE']->MOD_SETTINGS[$this->prefix . '_storedSettings']);
213 $this->storedSettings = $this->cleanupStorageArray($storedSettings);
214 }
215
216 /**
217 * Remove corrupted data entries from the stored settings array
218 *
219 * @param array $storedSettings The stored settings
220 * @return array Cleaned up stored settings
221 */
222 public function cleanupStorageArray($storedSettings) {
223 $storedSettings = is_array($storedSettings) ? $storedSettings : array();
224 // Clean up the array
225 foreach ($storedSettings as $id => $sdArr) {
226 if (!is_array($sdArr)) {
227 unset($storedSettings[$id]);
228 }
229 if (!is_array($sdArr['data'])) {
230 unset($storedSettings[$id]);
231 }
232 if (!trim($sdArr['title'])) {
233 $storedSettings[$id]['title'] = '[no title]';
234 }
235 }
236 return $storedSettings;
237 }
238
239 /**
240 * Creates an entry for the stored settings array
241 * Collects data from MOD_SETTINGS selected by the storeList
242 *
243 * @param array $data Should work with data from _GP('storeControl'). This is ['title']: Title for the entry. ['desc']: A description text. Currently not used by this class
244 * @return array Entry for the stored settings array
245 */
246 public function compileEntry($data) {
247 $storageData = array();
248 foreach ($this->storeList as $MS_key) {
249 $storageData[$MS_key] = $GLOBALS['SOBE']->MOD_SETTINGS[$MS_key];
250 }
251 $storageArr = array(
252 'title' => $data['title'],
253 'desc' => (string)$data['desc'],
254 'data' => $storageData,
255 'user' => NULL,
256 'tstamp' => $GLOBALS['EXEC_TIME']
257 );
258 $storageArr = $this->processEntry($storageArr);
259 return $storageArr;
260 }
261
262 /**
263 * Copies the stored data from entry $index to $writeArray which can be used to set MOD_SETTINGS
264 *
265 * @param mixed $storeIndex The entry key
266 * @param array $writeArray Preset data array. Will be overwritten by copied values.
267 * @return array Data array
268 */
269 public function getStoredData($storeIndex, $writeArray = array()) {
270 if ($this->storedSettings[$storeIndex]) {
271 foreach ($this->storeList as $k) {
272 $writeArray[$k] = $this->storedSettings[$storeIndex]['data'][$k];
273 }
274 }
275 return $writeArray;
276 }
277
278 /**
279 * Processing of the storage command LOAD, SAVE, REMOVE
280 *
281 * @param string $mconfName Name of the module to store the settings for. Default: $GLOBALS['SOBE']->MCONF['name'] (current module)
282 * @return string Storage message. Also set in $this->msg
283 */
284 public function processStoreControl($mconfName = '') {
285 $this->initStorage();
286 $storeControl = GeneralUtility::_GP('storeControl');
287 $storeIndex = $storeControl['STORE'];
288 $msg = '';
289 $saveSettings = FALSE;
290 $writeArray = array();
291 if (is_array($storeControl)) {
292 if ($this->writeDevLog) {
293 GeneralUtility::devLog('Store command: ' . GeneralUtility::arrayToLogString($storeControl), \TYPO3\CMS\Backend\ModuleSettings::class, 0);
294 }
295 // Processing LOAD
296 if ($storeControl['LOAD'] and $storeIndex) {
297 $writeArray = $this->getStoredData($storeIndex, $writeArray);
298 $saveSettings = TRUE;
299 $msg = '\'' . $this->storedSettings[$storeIndex]['title'] . '\' preset loaded!';
300 } elseif ($storeControl['SAVE']) {
301 if (trim($storeControl['title'])) {
302 // Get the data to store
303 $newEntry = $this->compileEntry($storeControl);
304 // Create an index for the storage array
305 if (!$storeIndex) {
306 $storeIndex = GeneralUtility::shortMD5($newEntry['title']);
307 }
308 // Add data to the storage array
309 $this->storedSettings[$storeIndex] = $newEntry;
310 $saveSettings = TRUE;
311 $msg = '\'' . $newEntry['title'] . '\' preset saved!';
312 } else {
313 $msg = 'Please enter a name for the preset!';
314 }
315 } elseif ($storeControl['REMOVE'] and $storeIndex) {
316 // Removing entry
317 $msg = '\'' . $this->storedSettings[$storeIndex]['title'] . '\' preset entry removed!';
318 unset($this->storedSettings[$storeIndex]);
319 $saveSettings = TRUE;
320 }
321 $this->msg = $msg;
322 if ($saveSettings) {
323 $this->writeStoredSetting($writeArray, $mconfName);
324 }
325 }
326 return $this->msg;
327 }
328
329 /**
330 * Write the current storage array and update MOD_SETTINGS
331 *
332 * @param array $writeArray Array of settings which should be overwrite current MOD_SETTINGS
333 * @param string $mconfName Name of the module to store the settings for. Default: $GLOBALS['SOBE']->MCONF['name'] (current module)
334 * @return void
335 */
336 public function writeStoredSetting($writeArray = array(), $mconfName = '') {
337 // Making sure, index 0 is not set
338 unset($this->storedSettings[0]);
339 $this->storedSettings = $this->cleanupStorageArray($this->storedSettings);
340 $writeArray[$this->prefix . '_storedSettings'] = serialize($this->storedSettings);
341 $GLOBALS['SOBE']->MOD_SETTINGS = \TYPO3\CMS\Backend\Utility\BackendUtility::getModuleData($GLOBALS['SOBE']->MOD_MENU, $writeArray, $mconfName ? $mconfName : $GLOBALS['SOBE']->MCONF['name'], $this->type);
342 if ($this->writeDevLog) {
343 GeneralUtility::devLog('Settings stored:' . $this->msg, \TYPO3\CMS\Backend\ModuleSettings::class, 0);
344 }
345 }
346
347 /********************************
348 *
349 * GUI
350 *
351 ********************************/
352 /**
353 * Returns the storage control box
354 *
355 * @param string $showElements List of elemetns which should be shown: load,remove,save
356 * @param bool $useOwnForm If set the box is wrapped with own form tag
357 * @return string HTML code
358 */
359 public function getStoreControl($showElements = 'load,remove,save', $useOwnForm = TRUE) {
360 $showElements = GeneralUtility::trimExplode(',', $showElements, TRUE);
361 $this->initStorage();
362 // Preset selector
363 $opt = array();
364 $opt[] = '<option value="0"> </option>';
365 foreach ($this->storedSettings as $id => $v) {
366 $opt[] = '<option value="' . $id . '">' . htmlspecialchars($v['title']) . '</option>';
367 }
368 $storedEntries = count($opt) > 1;
369 $codeTD = array();
370 // LOAD, REMOVE, but also show selector so you can overwrite an entry with SAVE
371 if ($storedEntries and count($showElements)) {
372 // Selector box
373 $onChange = 'document.forms[\'' . $this->formName . '\'][\'storeControl[title]\'].value= this.options[this.selectedIndex].value!=0 ? this.options[this.selectedIndex].text : \'\';';
374 $code = '
375 <select name="storeControl[STORE]" onChange="' . htmlspecialchars($onChange) . '">
376 ' . implode('
377 ', $opt) . '
378 </select>';
379 // Load button
380 if (in_array('load', $showElements)) {
381 $code .= '
382 <input type="submit" name="storeControl[LOAD]" value="Load" /> ';
383 }
384 // Remove button
385 if (in_array('remove', $showElements)) {
386 $code .= '
387 <input type="submit" name="storeControl[REMOVE]" value="Remove" /> ';
388 }
389 $codeTD[] = '<td width="1%">Preset:</td>';
390 $codeTD[] = '<td nowrap="nowrap">' . $code . '&nbsp;&nbsp;</td>';
391 }
392 // SAVE
393 if (in_array('save', $showElements)) {
394 $onClick = !$storedEntries ? '' : 'if (document.forms[\'' . $this->formName . '\'][\'storeControl[STORE]\'].options[document.forms[\'' . $this->formName . '\'][\'storeControl[STORE]\'].selectedIndex].value<0) return confirm(\'Are you sure you want to overwrite the existing entry?\');';
395 $code = '<input name="storeControl[title]" value="" type="text" max="80" width="25"> ';
396 $code .= '<input type="submit" name="storeControl[SAVE]" value="Save" onClick="' . htmlspecialchars($onClick) . '" />';
397 $codeTD[] = '<td nowrap="nowrap">' . $code . '</td>';
398 }
399 $codeTD = implode('
400 ', $codeTD);
401 if (trim($code)) {
402 $code = '
403 <!--
404 Store control
405 -->
406 <table border="0" cellpadding="3" cellspacing="0" width="100%">
407 <tr class="bgColor4">
408 ' . $codeTD . '
409 </tr>
410 </table>
411 ';
412 }
413 if ($this->msg) {
414 $code .= '
415 <div><strong>' . htmlspecialchars($this->msg) . '</strong></div>';
416 }
417 // @todo need to add parameters
418 if ($useOwnForm and trim($code)) {
419 $code = '
420 <form action="' . GeneralUtility::getIndpEnv('SCRIPT_NAME') . '" method="post" name="' . $this->formName . '" enctype="' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['form_enctype'] . '">' . $code . '</form>';
421 }
422 return $code;
423 }
424
425 /********************************
426 *
427 * Misc
428 *
429 ********************************/
430 /**
431 * Processing entry for the stored settings array
432 * Can be overwritten by extended class
433 *
434 * @param array $storageData Entry for the stored settings array
435 * @return array Entry for the stored settings array
436 */
437 public function processEntry($storageArr) {
438 return $storageArr;
439 }
440
441 }