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