0fcf59d56887cf3f243e51a2f2919377e83c7a66
[Packages/TYPO3.CMS.git] / typo3 / sysext / lowlevel / Classes / View / DatabaseIntegrityView.php
1 <?php
2 namespace TYPO3\CMS\Lowlevel\View;
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\Backend\Template\DocumentTemplate;
19 use TYPO3\CMS\Backend\Utility\IconUtility;
20 use TYPO3\CMS\Core\Database\QueryView;
21 use TYPO3\CMS\Core\Database\ReferenceIndex;
22 use TYPO3\CMS\Core\Integrity\DatabaseIntegrityCheck;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24 use TYPO3\CMS\Backend\Utility\BackendUtility;
25 use TYPO3\CMS\Fluid\View\StandaloneView;
26
27 /**
28 * Script class for the DB int module
29 *
30 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
31 */
32 class DatabaseIntegrityView extends BaseScriptClass {
33
34 /**
35 * @var string
36 */
37 protected $formName = 'queryform';
38
39 /**
40 * The name of the module
41 *
42 * @var string
43 */
44 protected $moduleName = 'system_dbint';
45
46 /**
47 * @var StandaloneView
48 */
49 protected $view;
50
51 /**
52 * @var string
53 */
54 protected $templatePath = 'EXT:lowlevel/Resources/Private/Templates/Backend/';
55
56 /**
57 * Constructor
58 */
59 public function __construct() {
60 $this->getLanguageService()->includeLLFile('EXT:lowlevel/Resources/Private/Language/locallang.xlf');
61 $this->view = GeneralUtility::makeInstance(StandaloneView::class);
62 $this->view->getRequest()->setControllerExtensionName('lowlevel');
63 }
64
65 /**
66 * Initialization
67 *
68 * @return void
69 */
70 public function init() {
71 $this->menuConfig();
72 $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
73 $this->doc->backPath = $GLOBALS['BACK_PATH'];
74 $this->doc->setModuleTemplate('EXT:lowlevel/Resources/Private/Templates/dbint.html');
75 $this->doc->form = '<form action="" method="post" name="' . $this->formName . '">';
76 }
77
78 /**
79 * Configure menu
80 *
81 * @return void
82 */
83 public function menuConfig() {
84 $lang = $this->getLanguageService();
85 // MENU-ITEMS:
86 // If array, then it's a selector box menu
87 // If empty string it's just a variable, that'll be saved.
88 // Values NOT in this array will not be saved in the settings-array for the module.
89 $this->MOD_MENU = array(
90 'function' => array(
91 0 => $lang->getLL('menuTitle', TRUE),
92 'records' => $lang->getLL('recordStatistics', TRUE),
93 'relations' => $lang->getLL('databaseRelations', TRUE),
94 'search' => $lang->getLL('fullSearch', TRUE),
95 'refindex' => $lang->getLL('manageRefIndex', TRUE)
96 ),
97 'search' => array(
98 'raw' => $lang->getLL('rawSearch', TRUE),
99 'query' => $lang->getLL('advancedQuery', TRUE)
100 ),
101 'search_query_smallparts' => '',
102 'search_result_labels' => '',
103 'labels_noprefix' => '',
104 'options_sortlabel' => '',
105 'show_deleted' => '',
106 'queryConfig' => '',
107 // Current query
108 'queryTable' => '',
109 // Current table
110 'queryFields' => '',
111 // Current tableFields
112 'queryLimit' => '',
113 // Current limit
114 'queryOrder' => '',
115 // Current Order field
116 'queryOrderDesc' => '',
117 // Current Order field descending flag
118 'queryOrder2' => '',
119 // Current Order2 field
120 'queryOrder2Desc' => '',
121 // Current Order2 field descending flag
122 'queryGroup' => '',
123 // Current Group field
124 'storeArray' => '',
125 // Used to store the available Query config memory banks
126 'storeQueryConfigs' => '',
127 // Used to store the available Query configs in memory
128 'search_query_makeQuery' => array(
129 'all' => $lang->getLL('selectRecords', TRUE),
130 'count' => $lang->getLL('countResults', TRUE),
131 'explain' => $lang->getLL('explainQuery', TRUE),
132 'csv' => $lang->getLL('csvExport', TRUE)
133 ),
134 'sword' => ''
135 );
136 // CLEAN SETTINGS
137 $OLD_MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, '', $this->moduleName, 'ses');
138 $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->moduleName, 'ses');
139 if (GeneralUtility::_GP('queryConfig')) {
140 $qA = GeneralUtility::_GP('queryConfig');
141 $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, array('queryConfig' => serialize($qA)), $this->moduleName, 'ses');
142 }
143 $addConditionCheck = GeneralUtility::_GP('qG_ins');
144 $setLimitToStart = FALSE;
145 foreach ($OLD_MOD_SETTINGS as $key => $val) {
146 if (substr($key, 0, 5) == 'query' && $this->MOD_SETTINGS[$key] != $val && $key != 'queryLimit' && $key != 'use_listview') {
147 $setLimitToStart = TRUE;
148 if ($key == 'queryTable' && !$addConditionCheck) {
149 $this->MOD_SETTINGS['queryConfig'] = '';
150 }
151 }
152 if ($key == 'queryTable' && $this->MOD_SETTINGS[$key] != $val) {
153 $this->MOD_SETTINGS['queryFields'] = '';
154 }
155 }
156 if ($setLimitToStart) {
157 $currentLimit = explode(',', $this->MOD_SETTINGS['queryLimit']);
158 if ($currentLimit[1]) {
159 $this->MOD_SETTINGS['queryLimit'] = '0,' . $currentLimit[1];
160 } else {
161 $this->MOD_SETTINGS['queryLimit'] = '0';
162 }
163 $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, $this->MOD_SETTINGS, $this->moduleName, 'ses');
164 }
165 }
166
167 /**
168 * Main functions, is rendering the content
169 *
170 * @return void
171 */
172 public function main() {
173 switch ($this->MOD_SETTINGS['function']) {
174 case 'search':
175 $templateFilename = 'CustomSearch.html';
176 $this->func_search();
177 break;
178 case 'records':
179 $templateFilename = 'RecordStatistics.html';
180 $this->func_records();
181 break;
182 case 'relations':
183 $templateFilename = 'Relations.html';
184 $this->func_relations();
185 break;
186 case 'refindex':
187 $templateFilename = 'ReferenceIndex.html';
188 $this->func_refindex();
189 break;
190 default:
191 $templateFilename = 'IntegrityOverview.html';
192 $this->func_default();
193 }
194 $this->view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($this->templatePath . $templateFilename));
195 $this->content = $this->view->render();
196
197 // Setting up the buttons and markers for docheader
198 $docHeaderButtons = $this->getButtons();
199 $markers = array(
200 'CSH' => $docHeaderButtons['csh'],
201 'FUNC_MENU' => $this->getFuncMenu(),
202 'CONTENT' => $this->content
203 );
204 // Build the <body> for the module
205 $this->content = $this->doc->moduleBody(array(), $docHeaderButtons, $markers);
206 // Renders the module page
207 $this->content = $this->doc->render($this->getLanguageService()->getLL('title'), $this->content);
208 }
209
210 /**
211 * Print content
212 *
213 * @return void
214 */
215 public function printContent() {
216 echo $this->content;
217 }
218
219 /**
220 * Create the panel of buttons for submitting the form or otherwise perform operations.
221 *
222 * @return array All available buttons as an assoc. array
223 */
224 protected function getButtons() {
225 $buttons = array(
226 'csh' => '',
227 'shortcut' => ''
228 );
229 // Shortcut
230 if ($this->getBackendUser()->mayMakeShortcut()) {
231 $buttons['shortcut'] = $this->doc->makeShortcutIcon('', 'function,search,search_query_makeQuery', $this->moduleName);
232 }
233 return $buttons;
234 }
235
236 /**
237 * Create the function menu
238 *
239 * @return string HTML of the function menu
240 */
241 protected function getFuncMenu() {
242 return BackendUtility::getFuncMenu(0, 'SET[function]', $this->MOD_SETTINGS['function'], $this->MOD_MENU['function']);
243 }
244
245 /**
246 * Creates the overview menu.
247 *
248 * @return void
249 */
250 protected function func_default() {
251 $modules = array();
252 $availableModFuncs = array('records', 'relations', 'search', 'refindex');
253 foreach ($availableModFuncs as $modFunc) {
254 $modules[$modFunc] = BackendUtility::getModuleUrl('system_dbint') . '&SET[function]=' . $modFunc;
255 }
256 $this->view->assign('availableFunctions', $modules);
257 }
258
259 /****************************
260 *
261 * Functionality implementation
262 *
263 ****************************/
264 /**
265 * Check and update reference index!
266 *
267 * @return void
268 */
269 public function func_refindex() {
270 $this->view->assign('PATH_typo3', PATH_typo3);
271
272 if (GeneralUtility::_GP('_update') || GeneralUtility::_GP('_check')) {
273 $testOnly = GeneralUtility::_GP('_check') ? TRUE : FALSE;
274 // Call the functionality
275 $refIndexObj = GeneralUtility::makeInstance(ReferenceIndex::class);
276 list(,$bodyContent) = $refIndexObj->updateIndex($testOnly);
277 $this->view->assign('content', str_replace(LF, '<br/>', $bodyContent));
278 }
279 }
280
281 /**
282 * Search (Full / Advanced)
283 *
284 * @return void
285 */
286 public function func_search() {
287 $lang = $this->getLanguageService();
288 $searchMode = $this->MOD_SETTINGS['search'];
289 $fullsearch = GeneralUtility::makeInstance(QueryView::class);
290 $fullsearch->setFormName($this->formName);
291 $submenu = BackendUtility::getFuncMenu(0, 'SET[search]', $searchMode, $this->MOD_MENU['search']);
292 if ($this->MOD_SETTINGS['search'] == 'query') {
293 $submenu .= BackendUtility::getFuncMenu(0, 'SET[search_query_makeQuery]', $this->MOD_SETTINGS['search_query_makeQuery'], $this->MOD_MENU['search_query_makeQuery']) . '<br />';
294 $submenu .= '<div class="checkbox"><label for="checkSearch_query_smallparts">' . BackendUtility::getFuncCheck($GLOBALS['SOBE']->id, 'SET[search_query_smallparts]', $this->MOD_SETTINGS['search_query_smallparts'], '', '', 'id="checkSearch_query_smallparts"') . $lang->getLL('showSQL') . '</label></div>';
295 $submenu .= '<div class="checkbox"><label for="checkSearch_result_labels">' . BackendUtility::getFuncCheck($GLOBALS['SOBE']->id, 'SET[search_result_labels]', $this->MOD_SETTINGS['search_result_labels'], '', '', 'id="checkSearch_result_labels"') . $lang->getLL('useFormattedStrings') . '</label></div>';
296 $submenu .= '<div class="checkbox"><label for="checkLabels_noprefix">' . BackendUtility::getFuncCheck($GLOBALS['SOBE']->id, 'SET[labels_noprefix]', $this->MOD_SETTINGS['labels_noprefix'], '', '', 'id="checkLabels_noprefix"') . $lang->getLL('dontUseOrigValues') . '</label></div>';
297 $submenu .= '<div class="checkbox"><label for="checkOptions_sortlabel">' . BackendUtility::getFuncCheck($GLOBALS['SOBE']->id, 'SET[options_sortlabel]', $this->MOD_SETTINGS['options_sortlabel'], '', '', 'id="checkOptions_sortlabel"') . $lang->getLL('sortOptions') . '</label></div>';
298 $submenu .= '<div class="checkbox"><label for="checkShow_deleted">' . BackendUtility::getFuncCheck($GLOBALS['SOBE']->id, 'SET[show_deleted]', $this->MOD_SETTINGS['show_deleted'], '', '', 'id="checkShow_deleted"') . $lang->getLL('showDeleted') . '</label></div>';
299 }
300 $this->view->assign('submenu', $submenu);
301 $this->view->assign('searchMode', $searchMode);
302 switch ($searchMode) {
303 case 'query':
304 $this->view->assign('queryMaker', $fullsearch->queryMaker());
305 break;
306 case 'raw':
307 default:
308 $this->view->assign('searchOptions', $fullsearch->form());
309 $this->view->assign('results', $fullsearch->search());
310 }
311 }
312
313 /**
314 * Records overview
315 *
316 * @return void
317 */
318 public function func_records() {
319
320 /** @var $admin DatabaseIntegrityCheck */
321 $admin = GeneralUtility::makeInstance(DatabaseIntegrityCheck::class);
322 $admin->genTree_makeHTML = 0;
323 $admin->backPath = $GLOBALS['BACK_PATH'];
324 $admin->genTree(0, '');
325
326 // Pages stat
327 $pageStatistic = array(
328 'total_pages' => array(
329 'icon' => IconUtility::getSpriteIconForRecord('pages', array()),
330 'count' => count($admin->page_idArray)
331 ),
332 'hidden_pages' => array(
333 'icon' => IconUtility::getSpriteIconForRecord('pages', array('hidden' => 1)),
334 'count' => $admin->recStats['hidden']
335 ),
336 'deleted_pages' => array(
337 'icon' => IconUtility::getSpriteIconForRecord('pages', array('deleted' => 1)),
338 'count' => count($admin->recStats['deleted']['pages'])
339 )
340 );
341
342 $lang = $this->getLanguageService();
343
344 // Doktype
345 $doktypes = array();
346 $doktype = $GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'];
347 if (is_array($doktype)) {
348 foreach ($doktype as $setup) {
349 if ($setup[1] != '--div--') {
350 $doktypes[] = array(
351 'icon' => IconUtility::getSpriteIconForRecord('pages', array('doktype' => $setup[1])),
352 'title' => $lang->sL($setup[0]) . ' (' . $setup[1] . ')',
353 'count' => (int)$admin->recStats['doktype'][$setup[1]]
354 );
355 }
356 }
357 }
358
359 // Tables and lost records
360 $id_list = '-1,0,' . implode(',', array_keys($admin->page_idArray));
361 $id_list = rtrim($id_list, ',');
362 $admin->lostRecords($id_list);
363 if ($admin->fixLostRecord(GeneralUtility::_GET('fixLostRecords_table'), GeneralUtility::_GET('fixLostRecords_uid'))) {
364 $admin = GeneralUtility::makeInstance(DatabaseIntegrityCheck::class);
365 $admin->backPath = $GLOBALS['BACK_PATH'];
366 $admin->genTree(0, '');
367 $id_list = '-1,0,' . implode(',', array_keys($admin->page_idArray));
368 $id_list = rtrim($id_list, ',');
369 $admin->lostRecords($id_list);
370 }
371 $tableStatistic = array();
372 $countArr = $admin->countRecords($id_list);
373 if (is_array($GLOBALS['TCA'])) {
374 foreach ($GLOBALS['TCA'] as $t => $value) {
375 if ($GLOBALS['TCA'][$t]['ctrl']['hideTable']) {
376 continue;
377 }
378 if ($t === 'pages' && $admin->lostPagesList !== '') {
379 $lostRecordCount = count(explode(',', $admin->lostPagesList));
380 } else {
381 $lostRecordCount = count($admin->lRecords[$t]);
382 }
383 if ($countArr['all'][$t]) {
384 $theNumberOfRe = (int)$countArr['non_deleted'][$t] . '/' . $lostRecordCount;
385 } else {
386 $theNumberOfRe = '';
387 }
388 $lr = '';
389 if (is_array($admin->lRecords[$t])) {
390 foreach ($admin->lRecords[$t] as $data) {
391 if (!GeneralUtility::inList($admin->lostPagesList, $data['pid'])) {
392 $lr .= '<div class="record"><a href="' . htmlspecialchars((BackendUtility::getModuleUrl('system_dbint') . '&SET[function]=records&fixLostRecords_table=' . $t . '&fixLostRecords_uid=' . $data['uid'])) . '"><img src="' . $GLOBALS['BACK_PATH'] . 'gfx/required_h.gif" width="10" height="10" title="' . $lang->getLL('fixLostRecord') . '"></a>uid:' . $data['uid'] . ', pid:' . $data['pid'] . ', ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs(strip_tags($data['title']), 20)) . '</div>';
393 } else {
394 $lr .= '<div class="record-noicon">uid:' . $data['uid'] . ', pid:' . $data['pid'] . ', ' . htmlspecialchars(GeneralUtility::fixed_lgd_cs(strip_tags($data['title']), 20)) . '</div>';
395 }
396 }
397 }
398 $tableStatistic[$t] = array(
399 'icon' => IconUtility::getSpriteIconForRecord($t, array()),
400 'title' => $lang->sL($GLOBALS['TCA'][$t]['ctrl']['title']),
401 'count' => $theNumberOfRe,
402 'lostRecords' => $lr
403 );
404 }
405 }
406
407 $this->view->assignMultiple(array(
408 'pages' => $pageStatistic,
409 'doktypes' => $doktypes,
410 'tables' => $tableStatistic
411 ));
412 }
413
414 /**
415 * Show list references
416 *
417 * @return void
418 */
419 public function func_relations() {
420 $admin = GeneralUtility::makeInstance(DatabaseIntegrityCheck::class);
421 $admin->genTree_makeHTML = 0;
422 $admin->backPath = $GLOBALS['BACK_PATH'];
423 $fkey_arrays = $admin->getGroupFields('');
424 $admin->selectNonEmptyRecordsWithFkeys($fkey_arrays);
425 $fileTest = $admin->testFileRefs();
426
427 if (is_array($fileTest['noFile'])) {
428 ksort($fileTest['noFile']);
429 }
430 $this->view->assignMultiple(array(
431 'files' => $fileTest,
432 'select_db' => $admin->testDBRefs($admin->checkSelectDBRefs),
433 'group_db' => $admin->testDBRefs($admin->checkGroupDBRefs)
434 ));
435 }
436
437 }