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