diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-84214-AddCheckIfFieldsAreEditableForLinkvalidator.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-84214-AddCheckIfFieldsAreEditableForLinkvalidator.rst new file mode 100644 index 0000000000000000000000000000000000000000..b8386ce16e974949e7e88cd91fce7ae576c19ce7 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-84214-AddCheckIfFieldsAreEditableForLinkvalidator.rst @@ -0,0 +1,42 @@ +.. include:: ../../Includes.txt + +==================================================================== +Feature: #84214 - Add check if fields are editable for Linkvalidator +==================================================================== + +See :issue:`84214` + +Description +=========== + +Broken links should only be shown in the list of broken links, +if current backend user has edit access to the field. This way +the editor will no longer get an error message on trying to +edit records he has no permission to edit. + +Whether the editor has access depends on a number of factors. + +We check the following: + +* The current permissions of the page. For editing the page, the editor must have Permission::PAGE_EDIT, for editing content Permission::CONTENT_EDIT must be available +* The user has write access to the table. We check if the table + is in 'tables_modify' for the group(s) +* The user has write access to the field. We check if the field + is an exclude field. If yes, it must be included in + 'non_exclude_fields' for the group(s). +* The user has write permission for the language of the record +* For tt_content: The CType is in list of explicitly allowed + values for authMode. + +Impact +====== + +* Broken links for fields that are not editable for the current backend + user will no longer be shown. +* Fields were added to the tx_linkvalidator_link table. "Analyze + Database Structure" must be executed. +* After an update to the new version, checking of broken links should + be reinitiated for the entire site. Until this is done, some broken + links may not be displayed for editors in the broken link report. + +.. index:: Backend, ext:linkvalidator diff --git a/typo3/sysext/linkvalidator/Classes/LinkAnalyzer.php b/typo3/sysext/linkvalidator/Classes/LinkAnalyzer.php index 90c7ac12c26973cc94f5b4fe014015d882e10235..7d368915512c744f66d3cd9b1cb30697cf111258 100644 --- a/typo3/sysext/linkvalidator/Classes/LinkAnalyzer.php +++ b/typo3/sysext/linkvalidator/Classes/LinkAnalyzer.php @@ -158,6 +158,12 @@ class LinkAnalyzer // Re-init selectFields for table $selectFields = array_merge(['uid', 'pid', $GLOBALS['TCA'][$table]['ctrl']['label']], $fields); + if ($GLOBALS['TCA'][$table]['ctrl']['languageField'] ?? false) { + $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['languageField']; + } + if ($GLOBALS['TCA'][$table]['ctrl']['type'] ?? false) { + $selectFields[] = $GLOBALS['TCA'][$table]['ctrl']['type']; + } $result = $queryBuilder->select(...$selectFields) ->from($table) @@ -196,6 +202,16 @@ class LinkAnalyzer $record['link_title'] = $entryValue['link_title']; $record['field'] = $entryValue['field']; $record['last_check'] = time(); + $typeField = $GLOBALS['TCA'][$table]['ctrl']['type'] ?? false; + if ($entryValue['row'][$typeField] ?? false) { + $record['element_type'] = $entryValue['row'][$typeField]; + } + $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'] ?? false; + if ($languageField && isset($entryValue['row'][$languageField])) { + $record['language'] = $entryValue['row'][$languageField]; + } else { + $record['language'] = -1; + } $this->recordReference = $entryValue['substr']['recordRef']; if (!empty($entryValue['pageAndAnchor'] ?? '')) { // Page with anchor, e.g. 18#1580 @@ -462,13 +478,7 @@ class LinkAnalyzer */ public function getLinkCounts() { - $groupedResult = $this->brokenLinkRepository->getNumberOfBrokenLinksForRecordsOnPages($this->pids); - $data = []; - foreach ($groupedResult as $linkType => $amount) { - $data[$linkType] = $amount; - $data['brokenlinkCount'] += $amount; - } - return $data; + return $this->brokenLinkRepository->getNumberOfBrokenLinksForRecordsOnPages($this->pids, $this->searchFields); } /** diff --git a/typo3/sysext/linkvalidator/Classes/Linktype/InternalLinktype.php b/typo3/sysext/linkvalidator/Classes/Linktype/InternalLinktype.php index 4708aaae57f06b24f15239f657cd9bc61c6b23fc..e438979edc739979a76f8ceaed506df60f466666 100644 --- a/typo3/sysext/linkvalidator/Classes/Linktype/InternalLinktype.php +++ b/typo3/sysext/linkvalidator/Classes/Linktype/InternalLinktype.php @@ -105,8 +105,8 @@ class InternalLinktype extends AbstractLinktype $this->responseContent = $this->checkContent($page, $anchor); } if ( - is_array($this->errorParams['page']) && !$this->responsePage - || is_array($this->errorParams['content']) && !$this->responseContent + (is_array($this->errorParams['page']) && !$this->responsePage) + || (is_array($this->errorParams['content']) && !$this->responseContent) ) { $this->setErrorParams($this->errorParams); } diff --git a/typo3/sysext/linkvalidator/Classes/QueryRestrictions/EditableRestriction.php b/typo3/sysext/linkvalidator/Classes/QueryRestrictions/EditableRestriction.php new file mode 100644 index 0000000000000000000000000000000000000000..9d6a6d1138a9cfa2d513aa52dd7b9006c15e421d --- /dev/null +++ b/typo3/sysext/linkvalidator/Classes/QueryRestrictions/EditableRestriction.php @@ -0,0 +1,231 @@ +<?php +declare(strict_types = 1); + +namespace TYPO3\CMS\Linkvalidator\QueryRestrictions; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Core\Database\Connection; +use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression; +use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; +use TYPO3\CMS\Core\Database\Query\QueryBuilder; +use TYPO3\CMS\Core\Database\Query\QueryHelper; +use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionInterface; +use TYPO3\CMS\Core\Type\Bitmask\Permission; + +class EditableRestriction implements QueryRestrictionInterface +{ + /** + * Specify which database fields the current user is allowed to edit + * + * @var array + */ + protected $allowedFields = []; + + /** + * Specify which languages the current user is allowed to edit + * + * @var array + */ + protected $allowedLanguages = []; + + /** + * Explicit allow fields + * + * @var array + */ + protected $explicitAllowFields = []; + + /** + * @var QueryBuilder + */ + protected $queryBuilder; + + /** + * @param array $searchFields array of 'table' => 'field1, field2' + * in which linkvalidator searches for broken links. + * @param QueryBuilder $queryBuilder + */ + public function __construct(array $searchFields, QueryBuilder $queryBuilder) + { + $this->allowedFields = $this->getAllowedFieldsForCurrentUser($searchFields); + $this->allowedLanguages = $this->getAllowedLanguagesForCurrentUser(); + foreach ($searchFields as $table => $fields) { + if ($table !== 'pages' && ($GLOBALS['TCA'][$table]['ctrl']['type'] ?? false)) { + $type = $GLOBALS['TCA'][$table]['ctrl']['type']; + $this->explicitAllowFields[$table][$type] = $this->getExplicitAllowFieldsForCurrentUser($table, $type); + } + } + $this->queryBuilder = $queryBuilder; + } + + /** + * Gets all allowed language ids for current backend user + * + * @return array + */ + protected function getAllowedLanguagesForCurrentUser(): array + { + if (!(is_string($GLOBALS['BE_USER']->groupData['allowed_languages'] ?? false))) { + return []; + } + + return array_map('intval', explode(',', $GLOBALS['BE_USER']->groupData['allowed_languages'])); + } + + protected function getExplicitAllowFieldsForCurrentUser(string $table, string $field): array + { + $allowDenyOptions = []; + $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$field]['config']; + // Check for items + if ($fieldConfig['type'] === 'select' && is_array($fieldConfig['items'] ?? false)) { + foreach ($fieldConfig['items'] as $iVal) { + $itemIdentifier = (string)$iVal[1]; + if ($GLOBALS['BE_USER']->checkAuthMode($table, $field, $itemIdentifier, $GLOBALS['TYPO3_CONF_VARS']['BE']['explicitADmode'])) { + $allowDenyOptions[] = $itemIdentifier; + } + } + } + return $allowDenyOptions; + } + + /** + * Get allowed table / fieldnames for current backend user. + * Only consider table / fields in $searchFields + * + * @param array $searchFields array of 'table' => ['field1, field2', ....] + * in which linkvalidator searches for broken links + * @return array + */ + protected function getAllowedFieldsForCurrentUser(array $searchFields = []): array + { + if (!$searchFields) { + return []; + } + + $allowedFields = []; + + foreach ($searchFields as $table => $fieldList) { + if (!$GLOBALS['BE_USER']->isAdmin() && !$GLOBALS['BE_USER']->check('tables_modify', $table)) { + // table not allowed + continue; + } + foreach ($fieldList as $field) { + $isExcludeField = $GLOBALS['TCA'][$table]['columns'][$field]['exclude'] ?? false; + if (!$GLOBALS['BE_USER']->isAdmin() + && $isExcludeField + && !$GLOBALS['BE_USER']->check('non_exclude_fields', $table . ':' . $field)) { + continue; + } + $allowedFields[$table][$field] = true; + } + } + return $allowedFields; + } + + public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression + { + $constraints = []; + + if ($this->allowedFields) { + $constraints = [ + $expressionBuilder->orX( + // broken link is in page and page is editable + $expressionBuilder->andX( + $expressionBuilder->eq( + 'tx_linkvalidator_link.table_name', + $this->queryBuilder->createNamedParameter('pages') + ), + QueryHelper::stripLogicalOperatorPrefix($GLOBALS['BE_USER']->getPagePermsClause(Permission::PAGE_EDIT)) + ), + // OR broken link is in content and content is editable + $expressionBuilder->andX( + $expressionBuilder->neq( + 'tx_linkvalidator_link.table_name', + $this->queryBuilder->createNamedParameter('pages') + ), + QueryHelper::stripLogicalOperatorPrefix($GLOBALS['BE_USER']->getPagePermsClause(Permission::CONTENT_EDIT)) + ) + ) + ]; + + // check if fields are editable + $additionalWhere = []; + foreach ($this->allowedFields as $table => $fields) { + foreach ($fields as $field => $value) { + $additionalWhere[] = $expressionBuilder->andX( + $expressionBuilder->eq( + 'tx_linkvalidator_link.table_name', + $this->queryBuilder->createNamedParameter($table) + ), + $expressionBuilder->eq( + 'tx_linkvalidator_link.field', + $this->queryBuilder->createNamedParameter($field) + ) + ); + } + } + if ($additionalWhere) { + $constraints[] = $expressionBuilder->orX(...$additionalWhere); + } + } else { + // add a constraint that will always return zero records because there are NO allowed fields + $constraints[] = $expressionBuilder->isNull('tx_linkvalidator_link.table_name'); + } + + foreach ($this->explicitAllowFields as $table => $field) { + $additionalWhere = []; + $additionalWhere[] = $expressionBuilder->andX( + $expressionBuilder->eq( + 'tx_linkvalidator_link.table_name', + $this->queryBuilder->createNamedParameter($table) + ), + $expressionBuilder->in( + 'tx_linkvalidator_link.element_type', + $this->queryBuilder->createNamedParameter( + array_unique(current($field)), + Connection::PARAM_STR_ARRAY + ) + ) + ); + $additionalWhere[] = $expressionBuilder->neq( + 'tx_linkvalidator_link.table_name', + $this->queryBuilder->createNamedParameter($table) + ); + if ($additionalWhere) { + $constraints[] = $expressionBuilder->orX(...$additionalWhere); + } + } + + if ($this->allowedLanguages) { + $additionalWhere = []; + foreach ($this->allowedLanguages as $langId) { + $additionalWhere[] = $expressionBuilder->orX( + $expressionBuilder->eq( + 'tx_linkvalidator_link.language', + $this->queryBuilder->createNamedParameter($langId, \PDO::PARAM_INT) + ), + $expressionBuilder->eq( + 'tx_linkvalidator_link.language', + $this->queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT) + ) + ); + } + $constraints[] = $expressionBuilder->orX(...$additionalWhere); + } + // If allowed languages is empty: all languages are allowed, so no constraint in this case + + return $expressionBuilder->andX(...$constraints); + } +} diff --git a/typo3/sysext/linkvalidator/Classes/Report/LinkValidatorReport.php b/typo3/sysext/linkvalidator/Classes/Report/LinkValidatorReport.php index 7243128656149561d3d62de0e13e5c3426a33571..a66d17d8bdbe9c354ffe6d57acb39dc2b9f226cc 100644 --- a/typo3/sysext/linkvalidator/Classes/Report/LinkValidatorReport.php +++ b/typo3/sysext/linkvalidator/Classes/Report/LinkValidatorReport.php @@ -143,6 +143,11 @@ class LinkValidatorReport */ protected $view; + public function __construct() + { + $this->brokenLinkRepository = GeneralUtility::makeInstance(BrokenLinkRepository::class); + } + /** * Init, called from parent object * @@ -262,8 +267,8 @@ class LinkValidatorReport } else { // mark broken links for last edited record as needing a recheck $this->brokenLinkRepository->setNeedsRecheckForRecord( - $this->lastEditedRecord['table'], - (int)$this->lastEditedRecord['uid'] + (int)$this->lastEditedRecord['uid'], + $this->lastEditedRecord['table'] ); } } @@ -324,7 +329,7 @@ class LinkValidatorReport } $this->pageRecord = BackendUtility::readPageAccess($this->id, $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW)); - if ($this->id && is_array($this->pageRecord) || !$this->id && $this->getBackendUser()->isAdmin()) { + if (($this->id && is_array($this->pageRecord)) || (!$this->id && $this->getBackendUser()->isAdmin())) { $this->isAccessibleForCurrentUser = true; } // Don't access in workspace @@ -402,7 +407,11 @@ class LinkValidatorReport $items = []; $rootLineHidden = $this->linkAnalyzer->getRootLineIsHidden($this->pObj->pageinfo); if (!$rootLineHidden || (bool)$this->modTS['checkhidden'] && !empty($linkTypes)) { - $brokenLinks = $this->brokenLinkRepository->getAllBrokenLinksForPages($this->getPageList(), $linkTypes); + $brokenLinks = $this->brokenLinkRepository->getAllBrokenLinksForPages( + $this->getPageList(), + $linkTypes, + $this->searchFields + ); foreach ($brokenLinks as $row) { $items[] = $this->renderTableRow($row['table_name'], $row); } @@ -570,7 +579,7 @@ class LinkValidatorReport { $variables = []; $variables['totalCountLabel'] = BackendUtility::wrapInHelp('linkvalidator', 'checkboxes', $this->getLanguageService()->getLL('overviews.nbtotal')); - $variables['totalCount'] = $brokenLinkOverView['brokenlinkCount'] ?: '0'; + $variables['totalCount'] = $brokenLinkOverView['total'] ?: '0'; $variables['optionsByType'] = []; $linkTypes = GeneralUtility::trimExplode(',', $this->modTS['linktypes'] ?? '', true); $availableLinkTypes = array_keys($this->hookObjectsArr); diff --git a/typo3/sysext/linkvalidator/Classes/Repository/BrokenLinkRepository.php b/typo3/sysext/linkvalidator/Classes/Repository/BrokenLinkRepository.php index 36bf60b38079a521a823aeef3e71f0a60dfc057b..38c85c01dffc95b5969774f6c676a7763695f76f 100644 --- a/typo3/sysext/linkvalidator/Classes/Repository/BrokenLinkRepository.php +++ b/typo3/sysext/linkvalidator/Classes/Repository/BrokenLinkRepository.php @@ -19,6 +19,7 @@ namespace TYPO3\CMS\Linkvalidator\Repository; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Linkvalidator\QueryRestrictions\EditableRestriction; /** * Repository for finding broken links that were detected previously. @@ -90,17 +91,27 @@ class BrokenLinkRepository * grouped by the link_type. * * @param array $pageIds + * @param array $searchFields [ table => [field1, field2, ...], ...] * @return array */ - public function getNumberOfBrokenLinksForRecordsOnPages(array $pageIds): array + public function getNumberOfBrokenLinksForRecordsOnPages(array $pageIds, array $searchFields): array { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) ->getQueryBuilderForTable(static::TABLE); $queryBuilder->getRestrictions()->removeAll(); - + if (!$GLOBALS['BE_USER']->isAdmin()) { + $queryBuilder->getRestrictions() + ->add(GeneralUtility::makeInstance(EditableRestriction::class, $searchFields, $queryBuilder)); + } $statement = $queryBuilder->select('link_type') - ->addSelectLiteral($queryBuilder->expr()->count('uid', 'amount')) + ->addSelectLiteral($queryBuilder->expr()->count(static::TABLE . '.uid', 'amount')) ->from(static::TABLE) + ->join( + static::TABLE, + 'pages', + 'pages', + $queryBuilder->expr()->eq('record_pid', $queryBuilder->quoteIdentifier('pages.uid')) + ) ->where( $queryBuilder->expr()->orX( $queryBuilder->expr()->andX( @@ -122,9 +133,12 @@ class BrokenLinkRepository ->groupBy('link_type') ->execute(); - $result = []; + $result = [ + 'total' => 0 + ]; while ($row = $statement->fetch()) { $result[$row['link_type']] = $row['amount']; + $result['total']+= $row['amount']; } return $result; } @@ -205,17 +219,31 @@ class BrokenLinkRepository /** * Prepare database query with pageList and keyOpt data. * + * This takes permissions of current BE user into account + * * @param int[] $pageIds Pages to check for broken links * @param string[] $linkTypes Link types to validate + * @param string[] $searchFields table => [fields1, field2, ...], ... : fields in which linkvalidator should + * search for broken links * @return array */ - public function getAllBrokenLinksForPages(array $pageIds, array $linkTypes): array + public function getAllBrokenLinksForPages(array $pageIds, array $linkTypes, array $searchFields = []): array { $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) ->getQueryBuilderForTable(self::TABLE); + if (!$GLOBALS['BE_USER']->isAdmin()) { + $queryBuilder->getRestrictions() + ->add(GeneralUtility::makeInstance(EditableRestriction::class, $searchFields, $queryBuilder)); + } $records = $queryBuilder - ->select('*') + ->select(self::TABLE . '.*') ->from(self::TABLE) + ->join( + 'tx_linkvalidator_link', + 'pages', + 'pages', + $queryBuilder->expr()->eq('tx_linkvalidator_link.record_pid', $queryBuilder->quoteIdentifier('pages.uid')) + ) ->where( $queryBuilder->expr()->orX( $queryBuilder->expr()->andX( @@ -238,13 +266,13 @@ class BrokenLinkRepository $queryBuilder->createNamedParameter($linkTypes, Connection::PARAM_STR_ARRAY) ) ) - ->orderBy('record_uid') - ->addOrderBy('uid') + ->orderBy('tx_linkvalidator_link.record_uid') + ->addOrderBy('tx_linkvalidator_link.uid') ->execute() ->fetchAll(); foreach ($records as &$record) { $response = json_decode($record['url_response'], true); - // Fallback mechansim to still support the old serialized data, could be removed in TYPO3 v12 or later + // Fallback mechanism to still support the old serialized data, could be removed in TYPO3 v12 or later if ($response === null) { $response = unserialize($record['url_response'], ['allowed_classes' => false]); } diff --git a/typo3/sysext/linkvalidator/Classes/Task/ValidatorTask.php b/typo3/sysext/linkvalidator/Classes/Task/ValidatorTask.php index ef246de3329c37812f15d88c9ac6bc42bfe218ca..30d9362d51737566bee6b889b802baa97304a2fa 100644 --- a/typo3/sysext/linkvalidator/Classes/Task/ValidatorTask.php +++ b/typo3/sysext/linkvalidator/Classes/Task/ValidatorTask.php @@ -23,6 +23,7 @@ use TYPO3\CMS\Core\Utility\ArrayUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MailUtility; use TYPO3\CMS\Linkvalidator\LinkAnalyzer; +use TYPO3\CMS\Linkvalidator\Repository\BrokenLinkRepository; use TYPO3\CMS\Scheduler\Task\AbstractTask; /** @@ -128,6 +129,9 @@ class ValidatorTask extends AbstractTask */ protected $languageFile = 'LLL:EXT:linkvalidator/Resources/Private/Language/locallang.xlf'; + /** @var BrokenLinkRepository */ + protected $brokenLinkRepository; + /** * Get the value of the protected property email * @@ -256,6 +260,7 @@ class ValidatorTask extends AbstractTask */ public function execute() { + $this->brokenLinkRepository = GeneralUtility::makeInstance(BrokenLinkRepository::class); $this->setCliArguments(); $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class); $successfullyExecuted = true; @@ -343,12 +348,12 @@ class ValidatorTask extends AbstractTask $processor->init($searchFields, $pageIds, $modTs); if (!empty($this->email)) { $oldLinkCounts = $processor->getLinkCounts(); - $this->oldTotalBrokenLink += $oldLinkCounts['brokenlinkCount']; + $this->oldTotalBrokenLink += $oldLinkCounts['total']; } $processor->getLinkStatistics($linkTypes, $modTs['checkhidden']); if (!empty($this->email)) { $linkCounts = $processor->getLinkCounts(); - $this->totalBrokenLink += $linkCounts['brokenlinkCount']; + $this->totalBrokenLink += $linkCounts['total']; $pageSections = $this->buildMail($page, $pageIds, $linkCounts, $oldLinkCounts); } } @@ -547,7 +552,7 @@ class ValidatorTask extends AbstractTask BackendUtility::getRecord('pages', $curPage) ); $content = ''; - if ($markerArray['brokenlinkCount'] > 0) { + if ($markerArray['total'] > 0) { $content = $this->templateService->substituteMarkerArray( $pageSectionHtml, $markerArray, diff --git a/typo3/sysext/linkvalidator/Tests/Functional/Repository/BrokenLinkRepositoryTest.php b/typo3/sysext/linkvalidator/Tests/Functional/Repository/BrokenLinkRepositoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..77fafb17e68ed5d3f1746f195cf45ed5ba421939 --- /dev/null +++ b/typo3/sysext/linkvalidator/Tests/Functional/Repository/BrokenLinkRepositoryTest.php @@ -0,0 +1,723 @@ +<?php +declare(strict_types = 1); + +namespace TYPO3\CMS\Linkvalidator\Tests\Functional\Repository; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use Psr\EventDispatcher\EventDispatcherInterface; +use TYPO3\CMS\Core\Core\Bootstrap; +use TYPO3\CMS\Linkvalidator\LinkAnalyzer; +use TYPO3\CMS\Linkvalidator\Repository\BrokenLinkRepository; +use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; + +class BrokenLinkRepositoryTest extends FunctionalTestCase +{ + /** + * @var array + */ + protected $coreExtensionsToLoad = [ + 'linkvalidator', + 'seo' + ]; + + /** + * @var BrokenLinkRepository + */ + protected $brokenLinksRepository; + + protected $beusers = [ + 'admin' => [ + 'fixture' => 'PACKAGE:typo3/testing-framework/Resources/Core/Functional/Fixtures/be_users.xml', + 'uid' => 1, + 'groupFixture' => '' + ], + 'no group' => [ + 'fixture' => 'EXT:linkvalidator/Tests/Functional/Repository/Fixtures/be_users.xml', + 'uid' => 2, + 'groupFixture' => '' + ], + // write access to pages, tt_content + 'group 1' => [ + 'fixture' => 'EXT:linkvalidator/Tests/Functional/Repository/Fixtures/be_users.xml', + 'uid' => 3, + 'groupFixture' => 'EXT:linkvalidator/Tests/Functional/Repository/Fixtures/be_groups.xml' + ], + // write access to pages, tt_content, exclude field pages.header_link + 'group 2' => [ + 'fixture' => 'EXT:linkvalidator/Tests/Functional/Repository/Fixtures/be_users.xml', + 'uid' => 4, + 'groupFixture' => 'EXT:linkvalidator/Tests/Functional/Repository/Fixtures/be_groups.xml' + ], + // write access to pages, tt_content (restricted to default language) + 'group 3' => [ + 'fixture' => 'EXT:linkvalidator/Tests/Functional/Repository/Fixtures/be_users.xml', + 'uid' => 5, + 'groupFixture' => 'EXT:linkvalidator/Tests/Functional/Repository/Fixtures/be_groups.xml' + ], + // group 6: access to all, but restricted via explicit allow to CType=texmedia and text + 'group 6' => [ + 'fixture' => 'EXT:linkvalidator/Tests/Functional/Repository/Fixtures/be_users.xml', + 'uid' => 6, + 'groupFixture' => 'EXT:linkvalidator/Tests/Functional/Repository/Fixtures/be_groups.xml' + ], + + ]; + + protected function setUp(): void + { + parent::setUp(); + + Bootstrap::initializeLanguageObject(); + + $GLOBALS['TYPO3_CONF_VARS']['BE']['explicitADmode'] = 'explicitAllow'; + $this->brokenLinksRepository = new BrokenLinkRepository(); + } + + public function getLinkCountsForPagesAndLinktypesReturnsCorrectCountForUserDataProvider() + { + yield 'Admin user should see all broken links' => + [ + // backendUser: 1=admin + $this->beusers['admin'], + // input file for DB + __DIR__ . '/Fixtures/input.xml', + //pids + [1], + // expected result: + [ + 'db' => 1, + 'file' => 1, + 'external' => 2, + 'total' => 4, + ] + ]; + yield 'User with no group should see none' => + [ + // backend user + $this->beusers['no group'], + // input file for DB + __DIR__ . '/Fixtures/input.xml', + //pids + [1], + // expected result: + [ + 'total' => 0, + ] + ]; + yield 'User with permission to pages but not to specific tables should see none' => + [ + // backend user + $this->beusers['no group'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_2.xml', + //pids + [1], + // expected result: + [ + 'total' => 0, + ] + ]; + yield 'User with permission to pages and to specific tables, but no exclude fields should see 3 of 4 broken links' => + [ + // backend user + $this->beusers['group 1'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_3.xml', + //pids + [1], + // expected result: + [ + 'db' => 1, + 'file' => 1, + 'external' => 1, + 'total' => 3 + ] + ]; + yield 'User with permission to pages, specific tables and exclude fields should see all broken links' => + [ + // backend user + $this->beusers['group 2'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_4.xml', + //pids + [1], + // expected result: + [ + 'db' => 1, + 'file' => 1, + 'external' => 2, + 'total' => 4 + ] + ]; + yield 'User has write permission only for Ctype textmedia and text, should see only broken links from textmedia records' => + [ + // backend user + $this->beusers['group 6'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_6_explicit_allow.xml', + //pids + [1], + // expected result: + [ + 'external' => 1, + 'total' => 1 + ] + ]; + + yield 'User has write permission only for default language and should see only 1 of 2 broken links' => + [ + // backend user + $this->beusers['group 3'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_5.xml', + //pids + [1], + // expected result: + [ + 'external' => 1, + 'total' => 1 + ] + ]; + } + + /** + * @test + * @dataProvider getLinkCountsForPagesAndLinktypesReturnsCorrectCountForUserDataProvider + */ + public function getLinkCountsForPagesAndLinktypesReturnsCorrectCountForUser( + array $beuser, + string $inputFile, + array $pidList, + array $expectedOutput + ) { + $config = [ + 'db' => '1', + 'file' => '1', + 'external' => '1', + 'linkhandler' => '1' + + ]; + + $tsConfig = [ + 'searchFields.' => [ + 'pages' => 'media,url,canonical_link', + 'tt_content' => 'bodytext,header_link,records' + ], + 'linktypes' => 'db,file,external,linkhandler', + 'checkhidden' => '0', + 'linkhandler' => [ + 'reportHiddenRecords' => '0' + ] + ]; + + $searchFields = $tsConfig['searchFields.']; + foreach ($searchFields as $table => $fields) { + $searchFields[$table] = explode(',', $fields); + } + + $this->setupBackendUser($beuser['uid'], $beuser['fixture'], $beuser['groupFixture']); + + $this->importDataSet($inputFile); + + $linkAnalyzer = new LinkAnalyzer( + $this->prophesize(EventDispatcherInterface::class)->reveal(), + $this->brokenLinksRepository + ); + $linkAnalyzer->init($searchFields, implode(',', $pidList), $tsConfig); + $linkAnalyzer->getLinkStatistics($config); + $result = $this->brokenLinksRepository->getNumberOfBrokenLinksForRecordsOnPages( + $pidList, + $searchFields + ); + + self::assertEquals($expectedOutput, $result); + } + + public function getAllBrokenLinksForPagesReturnsCorrectCountForUserDataProvider() + { + yield 'Admin user should see all broken links' => + [ + // backendUser: 1=admin + $this->beusers['admin'], + // input file for DB + __DIR__ . '/Fixtures/input.xml', + //pids + [1], + // count + 4 + ]; + + yield 'User with no group should see none' => + [ + // backend user + $this->beusers['no group'], + // input file for DB + __DIR__ . '/Fixtures/input.xml', + //pids + [1], + // count + 0 + ]; + yield 'User with permission to pages but not to specific tables should see none' => + [ + // backend user + $this->beusers['no group'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_2.xml', + //pids + [1], + // count + 0 + ]; + yield 'User with permission to pages and to specific tables, but no exclude fields should see 3 of 4 broken links' => + [ + // backend user + $this->beusers['group 1'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_3.xml', + //pids + [1], + // count + 3 + ]; + yield 'User with permission to pages, specific tables and exclude fields should see all broken links' => + [ + // backend user + $this->beusers['group 2'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_4.xml', + //pids + [1], + // count + 4 + ]; + yield 'User has write permission only for Ctype textmedia and text, should see only broken links from textmedia records' => + [ + // backend user + $this->beusers['group 6'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_6_explicit_allow.xml', + //pids + [1], + // count + 1 + ]; + + yield 'User has write permission only for default language and should see only 1 of 2 broken links' => + [ + // backend user + $this->beusers['group 3'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_5.xml', + //pids + [1], + // count + 1 + ]; + } + + /** + * @test + * @dataProvider getAllBrokenLinksForPagesReturnsCorrectCountForUserDataProvider + */ + public function getAllBrokenLinksForPagesReturnsCorrectCountForUser( + array $beuser, + string $inputFile, + array $pidList, + int $expectedCount + ) { + $config = [ + 'db' => '1', + 'file' => '1', + 'external' => '1', + 'linkhandler' => '1' + + ]; + + $linkTypes = [ + 'db', + 'file', + 'external' + ]; + + $tsConfig = [ + 'searchFields.' => [ + 'pages' => 'media,url,canonical_link', + 'tt_content' => 'bodytext,header_link,records' + ], + 'linktypes' => 'db,file,external,linkhandler', + 'checkhidden' => '0', + 'linkhandler' => [ + 'reportHiddenRecords' => '0' + ] + ]; + + $searchFields = $tsConfig['searchFields.']; + foreach ($searchFields as $table => $fields) { + $searchFields[$table] = explode(',', $fields); + } + + $this->setupBackendUser($beuser['uid'], $beuser['fixture'], $beuser['groupFixture']); + + $this->importDataSet($inputFile); + + $linkAnalyzer = new LinkAnalyzer( + $this->prophesize(EventDispatcherInterface::class)->reveal(), + $this->brokenLinksRepository + ); + $linkAnalyzer->init($searchFields, implode(',', $pidList), $tsConfig); + $linkAnalyzer->getLinkStatistics($config); + + $results = $this->brokenLinksRepository->getAllBrokenLinksForPages( + $pidList, + $linkTypes, + $searchFields + ); + + self::assertEquals($expectedCount, count($results)); + } + + public function getAllBrokenLinksForPagesReturnsCorrectValuesForUserDataProvider() + { + yield 'Admin user should see all broken links' => + [ + // backendUser: 1=admin + $this->beusers['admin'], + // input file for DB + __DIR__ . '/Fixtures/input.xml', + //pids + [1], + // expected result: + [ + [ + 'record_uid' => 1, + 'record_pid' => 1, + 'language' => 0, + 'headline' => 'link', + 'field' => 'bodytext', + 'table_name' => 'tt_content', + 'element_type' => 'textmedia', + 'link_title' => 'link', + 'url' => 'https://sfsfsfsfdfsfsdfsf/sfdsfsds', + 'link_type' => 'external', + 'needs_recheck' => 0 + ], + [ + 'record_uid' => 2, + 'record_pid' => 1, + 'language' => 0, + 'headline' => '[No title]', + 'field' => 'header_link', + 'table_name' => 'tt_content', + 'element_type' => 'textmedia', + 'link_title' => null, + 'url' => 'https://sfsfsfsfdfsfsdfsf/sfdsfsds', + 'link_type' => 'external', + 'needs_recheck' => 0 + ], + [ + 'record_uid' => 3, + 'record_pid' => 1, + 'language' => 0, + 'headline' => 'broken link', + 'field' => 'bodytext', + 'table_name' => 'tt_content', + 'element_type' => 'textmedia', + 'link_title' => 'broken link', + 'url' => '85', + 'link_type' => 'db', + 'needs_recheck' => 0 + ], + [ + 'record_uid' => 5, + 'record_pid' => 1, + 'language' => 0, + 'headline' => 'broken link', + 'field' => 'bodytext', + 'table_name' => 'tt_content', + 'element_type' => 'textmedia', + 'link_title' => 'broken link', + 'url' => 'file:88', + 'link_type' => 'file', + 'needs_recheck' => 0 + ], + ] + ]; + + yield 'User with no group should see none' => + [ + // backend user + $this->beusers['no group'], + // input file for DB + __DIR__ . '/Fixtures/input.xml', + //pids + [1], + // expected result: + [] + ]; + yield 'User with permission to pages but not to specific tables should see none' => + [ + // backend user + $this->beusers['no group'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_2.xml', + //pids + [1], + // expected result: + [] + ]; + yield 'User with permission to pages and to specific tables, but no exclude fields should see 3 of 4 broken links' => + [ + // backend user + $this->beusers['group 1'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_3.xml', + //pids + [1], + // expected result: + [ + [ + 'record_uid' => 1, + 'record_pid' => 1, + 'language' => 0, + 'headline' => 'link', + 'field' => 'bodytext', + 'table_name' => 'tt_content', + 'element_type' => 'textmedia', + 'link_title' => 'link', + 'url' => 'https://sfsfsfsfdfsfsdfsf/sfdsfsds', + 'link_type' => 'external', + 'needs_recheck' => 0 + ], + [ + 'record_uid' => 3, + 'record_pid' => 1, + 'language' => 0, + 'headline' => 'broken link', + 'field' => 'bodytext', + 'table_name' => 'tt_content', + 'element_type' => 'textmedia', + 'link_title' => 'broken link', + 'url' => '85', + 'link_type' => 'db', + 'needs_recheck' => 0 + ], + [ + 'record_uid' => 5, + 'record_pid' => 1, + 'language' => 0, + 'headline' => 'broken link', + 'field' => 'bodytext', + 'table_name' => 'tt_content', + 'element_type' => 'textmedia', + 'link_title' => 'broken link', + 'url' => 'file:88', + 'link_type' => 'file', + 'needs_recheck' => 0 + ], + ] + ]; + yield 'User with permission to pages, specific tables and exclude fields should see all broken links' => + [ + // backend user + $this->beusers['group 2'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_4.xml', + //pids + [1], + // expected result: + [ + [ + 'record_uid' => 1, + 'record_pid' => 1, + 'language' => 0, + 'headline' => 'link', + 'field' => 'bodytext', + 'table_name' => 'tt_content', + 'element_type' => 'textmedia', + 'link_title' => 'link', + 'url' => 'https://sfsfsfsfdfsfsdfsf/sfdsfsds', + 'link_type' => 'external', + 'needs_recheck' => 0 + ], + [ + 'record_uid' => 2, + 'record_pid' => 1, + 'language' => 0, + 'headline' => '[No title]', + 'field' => 'header_link', + 'table_name' => 'tt_content', + 'element_type' => 'textmedia', + 'link_title' => null, + 'url' => 'https://sfsfsfsfdfsfsdfsf/sfdsfsds', + 'link_type' => 'external', + 'needs_recheck' => 0 + ], + [ + 'record_uid' => 3, + 'record_pid' => 1, + 'language' => 0, + 'headline' => 'broken link', + 'field' => 'bodytext', + 'table_name' => 'tt_content', + 'element_type' => 'textmedia', + 'link_title' => 'broken link', + 'url' => '85', + 'link_type' => 'db', + 'needs_recheck' => 0 + ], + [ + 'record_uid' => 5, + 'record_pid' => 1, + 'language' => 0, + 'headline' => 'broken link', + 'field' => 'bodytext', + 'table_name' => 'tt_content', + 'element_type' => 'textmedia', + 'link_title' => 'broken link', + 'url' => 'file:88', + 'link_type' => 'file', + 'needs_recheck' => 0 + ], + ] + ]; + yield 'User has write permission only for Ctype textmedia and text, should see only broken links from textmedia records' => + [ + // backend user + $this->beusers['group 6'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_6_explicit_allow.xml', + //pids + [1], + // expected result: + [ + [ + 'record_uid' => 1, + 'record_pid' => 1, + 'language' => 0, + 'headline' => 'link', + 'field' => 'bodytext', + 'table_name' => 'tt_content', + 'element_type' => 'textmedia', + 'link_title' => 'link', + 'url' => 'https://sfsfsfsfdfsfsdfsf/sfdsfsds', + 'link_type' => 'external', + 'needs_recheck' => 0 + ], + ] + ]; + + yield 'User has write permission only for default language and should see only 1 of 2 broken links' => + [ + // backend user + $this->beusers['group 3'], + // input file for DB + __DIR__ . '/Fixtures/input_permissions_user_5.xml', + //pids + [1], + // expected result: + [ + [ + 'record_uid' => 1, + 'record_pid' => 1, + 'language' => 0, + 'headline' => 'link', + 'field' => 'bodytext', + 'table_name' => 'tt_content', + 'element_type' => 'textmedia', + 'link_title' => 'link', + 'url' => 'https://sfsfsfsfdfsfsdfsf/sfdsfsds', + 'link_type' => 'external', + 'needs_recheck' => 0 + ], + ] + ]; + } + + /** + * @test + * @dataProvider getAllBrokenLinksForPagesReturnsCorrectValuesForUserDataProvider + */ + public function getAllBrokenLinksForPagesReturnsCorrectValuesForUser( + array $beuser, + string $inputFile, + array $pidList, + array $expectedResult + ) { + $config = [ + 'db' => '1', + 'file' => '1', + 'external' => '1', + 'linkhandler' => '1' + + ]; + + $linkTypes = [ + 'db', + 'file', + 'external' + ]; + + $tsConfig = [ + 'searchFields.' => [ + 'pages' => 'media,url,canonical_link', + 'tt_content' => 'bodytext,header_link,records' + ], + 'linktypes' => 'db,file,external,linkhandler', + 'checkhidden' => '0', + 'linkhandler' => [ + 'reportHiddenRecords' => '0' + ] + ]; + + $searchFields = $tsConfig['searchFields.']; + foreach ($searchFields as $table => $fields) { + $searchFields[$table] = explode(',', $fields); + } + + $this->setupBackendUser($beuser['uid'], $beuser['fixture'], $beuser['groupFixture']); + + $this->importDataSet($inputFile); + + $linkAnalyzer = new LinkAnalyzer( + $this->prophesize(EventDispatcherInterface::class)->reveal(), + $this->brokenLinksRepository + ); + $linkAnalyzer->init($searchFields, implode(',', $pidList), $tsConfig); + $linkAnalyzer->getLinkStatistics($config); + + $results = $this->brokenLinksRepository->getAllBrokenLinksForPages( + $pidList, + $linkTypes, + $searchFields + ); + + foreach ($results as &$result) { + unset($result['url_response']); + unset($result['uid']); + unset($result['last_check']); + } + self::assertEquals($expectedResult, $results); + } + + protected function setupBackendUser(int $uid, string $fixtureFile, string $groupFixtureFile) + { + if ($groupFixtureFile) { + $this->importDataSet($groupFixtureFile); + } + $this->backendUserFixture = $fixtureFile; + $this->setUpBackendUserFromFixture($uid); + } +} diff --git a/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/be_groups.xml b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/be_groups.xml new file mode 100644 index 0000000000000000000000000000000000000000..511c79f6e21acd406a40407b700cf12f279f44e9 --- /dev/null +++ b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/be_groups.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<dataset> + <be_groups> + <uid>1</uid> + <pid>0</pid> + <tstamp>1366642540</tstamp> + <tables_modify>pages,tt_content</tables_modify> + <explicit_allowdeny>tt_content:CType:text:ALLOW,tt_content:CType:textmedia:ALLOW</explicit_allowdeny> + </be_groups> + <be_groups> + <uid>2</uid> + <pid>0</pid> + <tstamp>1366642540</tstamp> + <tables_modify>pages,tt_content</tables_modify> + <non_exclude_fields>tt_content:header_link</non_exclude_fields> + <explicit_allowdeny>tt_content:CType:text:ALLOW,tt_content:CType:textmedia:ALLOW</explicit_allowdeny> + </be_groups> + <be_groups> + <uid>3</uid> + <pid>0</pid> + <tstamp>1366642540</tstamp> + <tables_modify>pages,tt_content</tables_modify> + <allowed_languages>0</allowed_languages> + <explicit_allowdeny>tt_content:CType:text:ALLOW,tt_content:CType:textmedia:ALLOW</explicit_allowdeny> + </be_groups> + + <!-- group 6: editors with access to all, but only CType=textmedia and text via explicit allow --> + <be_groups> + <uid>6</uid> + <pid>0</pid> + <tstamp>1366642540</tstamp> + <tables_modify>pages,tt_content</tables_modify> + <explicit_allowdeny>tt_content:CType:text:ALLOW,tt_content:CType:textmedia:ALLOW</explicit_allowdeny> + </be_groups> +</dataset> diff --git a/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/be_users.xml b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/be_users.xml new file mode 100644 index 0000000000000000000000000000000000000000..e8df1d0f7e7fbbbf0cb09b0dcd18ba373eafd5f0 --- /dev/null +++ b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/be_users.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8"?> +<dataset> + <be_users> + <uid>2</uid> + <pid>0</pid> + <username>plain_editor</username> + <realName>Editor with no group</realName> + <password>$1$tCrlLajZ$C0sikFQQ3SWaFAZ1Me0Z/1</password> <!-- password --> + <admin>0</admin> + <disableIPlock>1</disableIPlock> + <lastlogin>1371033743</lastlogin> + <createdByAction>0</createdByAction> + <workspace_id>0</workspace_id> + </be_users> + + <!-- editor with access to tables via group --> + <be_users> + <uid>3</uid> + <pid>0</pid> + <username>simple_editor1</username> + <realName>Editor with group 1</realName> + <usergroup>1</usergroup> + <password>$1$tCrlLajZ$C0sikFQQ3SWaFAZ1Me0Z/1</password> <!-- password --> + <admin>0</admin> + <disableIPlock>1</disableIPlock> + <lastlogin>1371033743</lastlogin> + <createdByAction>0</createdByAction> + <workspace_id>0</workspace_id> + </be_users> + + <!-- editor with access to tables and excludefiels via group --> + <be_users> + <uid>4</uid> + <pid>0</pid> + <username>simple_editor2</username> + <realName>Editor with group 2</realName> + <usergroup>2</usergroup> + <password>$1$tCrlLajZ$C0sikFQQ3SWaFAZ1Me0Z/1</password> <!-- password --> + <admin>0</admin> + <disableIPlock>1</disableIPlock> + <lastlogin>1371033743</lastlogin> + <createdByAction>0</createdByAction> + <workspace_id>0</workspace_id> + </be_users> + + <!-- editor with access to tables, limited to default language --> + <be_users> + <uid>5</uid> + <pid>0</pid> + <username>simple_editor3</username> + <realName>Editor with group 3</realName> + <usergroup>3</usergroup> + <password>$1$tCrlLajZ$C0sikFQQ3SWaFAZ1Me0Z/1</password> <!-- password --> + <admin>0</admin> + <disableIPlock>1</disableIPlock> + <lastlogin>1371033743</lastlogin> + <createdByAction>0</createdByAction> + <workspace_id>0</workspace_id> + </be_users> + + <!-- user 6: editor with access to all, but only CType=textmedia and text via explicit allow --> + <be_users> + <uid>6</uid> + <pid>0</pid> + <username>simple_editor6</username> + <realName>Editor with group 6</realName> + <usergroup>6</usergroup> + <password>$1$tCrlLajZ$C0sikFQQ3SWaFAZ1Me0Z/1</password> <!-- password --> + <admin>0</admin> + <disableIPlock>1</disableIPlock> + <lastlogin>1371033743</lastlogin> + <createdByAction>0</createdByAction> + <workspace_id>0</workspace_id> + </be_users> +</dataset> diff --git a/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input.xml b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input.xml new file mode 100644 index 0000000000000000000000000000000000000000..7dd9c7e3dc44c8e96b7ebe7e71c15239f3586a16 --- /dev/null +++ b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<dataset> + <pages> + <uid>1</uid> + <pid>0</pid> + </pages> + <!-- external --> + <tt_content> + <uid>1</uid> + <pid>1</pid> + <bodytext><p><a href="https://sfsfsfsfdfsfsdfsf/sfdsfsds">link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + <tt_content> + <uid>2</uid> + <pid>1</pid> + <header_link>https://sfsfsfsfdfsfsdfsf/sfdsfsds</header_link> + <CType>textmedia</CType> + </tt_content> + + <!-- page --> + <tt_content> + <uid>3</uid> + <pid>1</pid> + <bodytext><p><a href="t3://page?uid=85">broken link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + + <!-- file --> + <tt_content> + <uid>5</uid> + <pid>1</pid> + <bodytext><p><a href="t3://file?uid=88">broken link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> +</dataset> diff --git a/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_group.xml b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_group.xml new file mode 100644 index 0000000000000000000000000000000000000000..d29a39db86a33d0246c9121efc7d131954dd58f4 --- /dev/null +++ b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_group.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<dataset> + <pages> + <uid>1</uid> + <pid>0</pid> + <perms_group>1</perms_group> + <!-- Permission::CONTENT_EDIT | Permission::PAGE_EDIT --> + <perms_user>18</perms_user> + </pages> + + <!-- external --> + <tt_content> + <uid>1</uid> + <pid>1</pid> + <bodytext><p><a href="https://sfsfsfsfdfsfsdfsf/sfdsfsds">link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + + <!-- page --> + <tt_content> + <uid>3</uid> + <pid>1</pid> + <bodytext><p><a href="t3://page?uid=85">broken link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + + <!-- file --> + <tt_content> + <uid>5</uid> + <pid>1</pid> + <bodytext><p><a href="t3://file?uid=88">broken link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + +</dataset> diff --git a/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_2.xml b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_2.xml new file mode 100644 index 0000000000000000000000000000000000000000..c45d3aad817260c047cfc4dd7647e79ec40c542b --- /dev/null +++ b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_2.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<dataset> + <pages> + <uid>1</uid> + <pid>0</pid> + <perms_userid>2</perms_userid> + <!-- Permission::CONTENT_EDIT | Permission::PAGE_EDIT --> + <perms_user>18</perms_user> + </pages> + + <!-- external --> + <tt_content> + <uid>1</uid> + <pid>1</pid> + <bodytext><p><a href="https://sfsfsfsfdfsfsdfsf/sfdsfsds">link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + + <!-- page --> + <tt_content> + <uid>3</uid> + <pid>1</pid> + <bodytext><p><a href="t3://page?uid=85">broken link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + + <!-- file --> + <tt_content> + <uid>5</uid> + <pid>1</pid> + <bodytext><p><a href="t3://file?uid=88">broken link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + +</dataset> diff --git a/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_3.xml b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_3.xml new file mode 100644 index 0000000000000000000000000000000000000000..b9eb96931d2daf6da96ad726ece6ff1dfeb70ffe --- /dev/null +++ b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_3.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<dataset> + <pages> + <uid>1</uid> + <pid>0</pid> + <perms_userid>3</perms_userid> + <!-- Permission::CONTENT_EDIT | Permission::PAGE_EDIT --> + <perms_user>18</perms_user> + </pages> + + <!-- external --> + <tt_content> + <uid>1</uid> + <pid>1</pid> + <bodytext><p><a href="https://sfsfsfsfdfsfsdfsf/sfdsfsds">link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + <tt_content> + <uid>2</uid> + <pid>1</pid> + <header_link>https://sfsfsfsfdfsfsdfsf/sfdsfsds</header_link> + <CType>textmedia</CType> + </tt_content> + + <!-- page --> + <tt_content> + <uid>3</uid> + <pid>1</pid> + <bodytext><p><a href="t3://page?uid=85">broken link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + + <!-- file --> + <tt_content> + <uid>5</uid> + <pid>1</pid> + <bodytext><p><a href="t3://file?uid=88">broken link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + +</dataset> diff --git a/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_4.xml b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_4.xml new file mode 100644 index 0000000000000000000000000000000000000000..9aebe00c563694f63c1502f6a3982ee654562e83 --- /dev/null +++ b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_4.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<dataset> + <pages> + <uid>1</uid> + <pid>0</pid> + <perms_userid>4</perms_userid> + <!-- Permission::CONTENT_EDIT | Permission::PAGE_EDIT --> + <perms_user>18</perms_user> + </pages> + + <!-- external --> + <tt_content> + <uid>1</uid> + <pid>1</pid> + <bodytext><p><a href="https://sfsfsfsfdfsfsdfsf/sfdsfsds">link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + <tt_content> + <uid>2</uid> + <pid>1</pid> + <header_link>https://sfsfsfsfdfsfsdfsf/sfdsfsds</header_link> + <CType>textmedia</CType> + </tt_content> + + <!-- page --> + <tt_content> + <uid>3</uid> + <pid>1</pid> + <bodytext><p><a href="t3://page?uid=85">broken link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + + <!-- file --> + <tt_content> + <uid>5</uid> + <pid>1</pid> + <bodytext><p><a href="t3://file?uid=88">broken link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + +</dataset> diff --git a/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_5.xml b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_5.xml new file mode 100644 index 0000000000000000000000000000000000000000..e69790676137425fd54f0f92b636f6114bf87abe --- /dev/null +++ b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_5.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<dataset> + <pages> + <uid>1</uid> + <pid>0</pid> + <perms_userid>5</perms_userid> + <!-- Permission::CONTENT_EDIT | Permission::PAGE_EDIT --> + <perms_user>18</perms_user> + </pages> + + <!-- default language --> + <tt_content> + <uid>1</uid> + <pid>1</pid> + <bodytext><p><a href="https://sfsfsfsfdfsfsdfsf/sfdsfsds">link</a></p></bodytext> + <CType>textmedia</CType> + <sys_language_uid>0</sys_language_uid> + </tt_content> + + <!-- other language --> + <tt_content> + <uid>2</uid> + <pid>1</pid> + <header_link>https://sfsfsfsfdfsfsdfsf/sfdsfsds</header_link> + <CType>textmedia</CType> + <sys_language_uid>1</sys_language_uid> + </tt_content> + +</dataset> diff --git a/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_6_explicit_allow.xml b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_6_explicit_allow.xml new file mode 100644 index 0000000000000000000000000000000000000000..7624e10cdb3cb55618a1e137a4ebebc5980328c9 --- /dev/null +++ b/typo3/sysext/linkvalidator/Tests/Functional/Repository/Fixtures/input_permissions_user_6_explicit_allow.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<dataset> + <pages> + <uid>1</uid> + <pid>0</pid> + <perms_userid>6</perms_userid> + <!-- Permission::CONTENT_EDIT | Permission::PAGE_EDIT --> + <perms_user>18</perms_user> + </pages> + + <!-- textmedia --> + <tt_content> + <uid>1</uid> + <pid>1</pid> + <bodytext><p><a href="https://sfsfsfsfdfsfsdfsf/sfdsfsds">link</a></p></bodytext> + <CType>textmedia</CType> + </tt_content> + + <!-- textpic --> + <tt_content> + <uid>2</uid> + <pid>1</pid> + <bodytext><p><a href="https://sfsfsfsfdfsfsdfsf/sfdsfsds">link</a></p></bodytext> + <CType>textpic</CType> + </tt_content> +</dataset> diff --git a/typo3/sysext/linkvalidator/ext_tables.sql b/typo3/sysext/linkvalidator/ext_tables.sql index 401cc85e9c3076ed5c61f23b782fc7978d818dbe..91b4b19d6564c89d77e54bfbe8b305b664299569 100644 --- a/typo3/sysext/linkvalidator/ext_tables.sql +++ b/typo3/sysext/linkvalidator/ext_tables.sql @@ -2,9 +2,11 @@ CREATE TABLE tx_linkvalidator_link ( uid int(11) NOT NULL auto_increment, record_uid int(11) DEFAULT '0' NOT NULL, record_pid int(11) DEFAULT '0' NOT NULL, + language int(11) DEFAULT '-1' NOT NULL, headline varchar(255) DEFAULT '' NOT NULL, field varchar(255) DEFAULT '' NOT NULL, table_name varchar(255) DEFAULT '' NOT NULL, + element_type varchar(255) DEFAULT '' NOT NULL, link_title text, url text, url_response text,