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