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