Commit 3d899071 authored by Marc Egger's avatar Marc Egger
Browse files

Refs #12588 pause-migration link, also match pageId, system messages,...

Refs #12588 pause-migration link, also match pageId, system messages, pageAlias deprecated exception
parent 09e6e046
Pipeline #5387 failed with stages
in 2 minutes and 36 seconds
......@@ -507,7 +507,7 @@ const SYSTEM_SQL_LOG_PATHFILENAME = 'sqlLog'; // absolute or relative to app
const SYSTEM_SQL_LOG_MODE = 'sqlLogMode'; // Mode, which statements to log.
const SYSTEM_SQL_LOG_MODE_AUTOCRON = 'sqlLogModeAutoCron'; // Mode, which statements to log in AutoCron Environments.
const FORCE_RUN_PAGE_ALIAS_MIGRATION_CHECK = 'FORCE_RUN_PAGE_ALIAS_MIGRATION_CHECK';
const FORCE_RUN_PAGE_SLUG_MIGRATION_CHECK = 'FORCE_RUN_PAGE_SLUG_MIGRATION_CHECK';
const SYSTEM_FORM_SUBMIT_LOG_MODE = 'formSubmitLogMode';
const FORM_SUBMIT_LOG_MODE_ALL = 'all';
......@@ -2027,13 +2027,15 @@ const ACTION_SPECIAL_COLUMN_UPDATE = '_scupdate'; // get parameter to set the u
const ACTION_SPECIAL_COLUMN_DO_REPLACE = 'replace'; // special columns are automatically replaced
const ACTION_SPECIAL_COLUMN_DO_SKIP_REPLACE = 'skip_replace'; // special columns are automatically replaced
// page alias migration
const ACTION_ALIAS_MIGRATION_UPDATE = '_pamupdate'; // get parameter to set the update behaviour
const ACTION_ALIAS_MIGRATION_DO_REPLACE = 'replace'; // replace page aliases by slugs
const ACTION_ALIAS_MIGRATION_DO_SKIP_FOREVER = 'skipForever'; // disable the alias migration check
const QFQ_VERSION_KEY_ALIAS_MIGRATION = 'pageAliasMigration';
const QFQ_VERSION_KEY_ALIAS_MIGRATION_DONE = 'done';
const QFQ_VERSION_KEY_ALIAS_MIGRATION_SKIP = 'skip';
// page slug migration
const ACTION_SLUG_MIGRATION_UPDATE = '_pamupdate'; // get parameter to set the update behaviour
const ACTION_SLUG_MIGRATION_DO_REPLACE = 'replace'; // replace page aliases by slugs
const ACTION_SLUG_MIGRATION_DO_PAUSE = 'pause'; // disable the slug migration check temporarily
const ACTION_SLUG_MIGRATION_DO_RESUME = 'resume'; // resume the slug migration check
const ACTION_SLUG_MIGRATION_DO_SKIP_FOREVER = 'skipForever'; // disable the slug migration check
const QFQ_VERSION_KEY_SLUG_MIGRATION = 'pageSlugMigration';
const QFQ_VERSION_KEY_SLUG_MIGRATION_DONE = 'done';
const QFQ_VERSION_KEY_SLUG_MIGRATION_SKIP = 'skip';
// tablesorter
const TABLESORTER_VIEW_SAVER = 'tablesorter-view-saver';
......
......@@ -13,6 +13,7 @@ use IMATHUZH\Qfq\Core\Form\FormAsFile;
use IMATHUZH\Qfq\Core\Helper\Logger;
use IMATHUZH\Qfq\Core\Helper\OnString;
use IMATHUZH\Qfq\Core\Helper\Path;
use IMATHUZH\Qfq\Core\QuickFormQuery;
use IMATHUZH\Qfq\Core\Store\Config;
use IMATHUZH\Qfq\Core\Store\Store;
use IMATHUZH\Qfq\Core\Store\T3Info;
......@@ -204,9 +205,9 @@ class DatabaseUpdate {
}
}
$state = $this->t3v10AliasMigration($versionInfo[QFQ_VERSION_KEY_ALIAS_MIGRATION] ?? '');
$state = $this->t3v10SlugMigration($versionInfo[QFQ_VERSION_KEY_SLUG_MIGRATION] ?? '');
if (!is_null($state)) {
$versionInfo[QFQ_VERSION_KEY_ALIAS_MIGRATION] = $state;
$versionInfo[QFQ_VERSION_KEY_SLUG_MIGRATION] = $state;
$this->setDatabaseVersion($versionInfo);
}
......@@ -260,7 +261,7 @@ class DatabaseUpdate {
* @throws \UserFormException
* @throws \UserReportException
*/
private function t3v10AliasMigration($dBMigrationFlag) {
private function t3v10SlugMigration($dBMigrationFlag) {
##################################
##### PHPUNIT: SKIP FUNCTION #####
......@@ -282,30 +283,43 @@ class DatabaseUpdate {
##################################
##### COMPUTE MIGRATION MODE #####
// read variable "RUN_PAGE_ALIAS_MIGRATION_CHECK" from qfq.json
$forceRunMigrationCheck = Config::get(FORCE_RUN_PAGE_ALIAS_MIGRATION_CHECK) === true;
// read variable "RUN_PAGE_SLUG_MIGRATION_CHECK" from qfq.json
$forceRunMigrationCheck = Config::get(FORCE_RUN_PAGE_SLUG_MIGRATION_CHECK) === true;
// if db flag "done" or "skip" then skip check
if (!$forceRunMigrationCheck && ($dBMigrationFlag === QFQ_VERSION_KEY_ALIAS_MIGRATION_DONE || $dBMigrationFlag === QFQ_VERSION_KEY_ALIAS_MIGRATION_SKIP)) {
if (!$forceRunMigrationCheck && ($dBMigrationFlag === QFQ_VERSION_KEY_SLUG_MIGRATION_DONE || $dBMigrationFlag === QFQ_VERSION_KEY_SLUG_MIGRATION_SKIP)) {
return null;
}
// get value from GET parameter ACTION_ALIAS_MIGRATION_UPDATE
$actionAliasMigration = $_GET[ACTION_ALIAS_MIGRATION_UPDATE] ?? '';
// get value from GET parameter ACTION_SLUG_MIGRATION_UPDATE
$actionSlugMigration = $_GET[ACTION_SLUG_MIGRATION_UPDATE] ?? '';
// if GET parameter "skipForever" then write "skip" to db
if (!$forceRunMigrationCheck && $actionAliasMigration === ACTION_ALIAS_MIGRATION_DO_SKIP_FOREVER) {
return QFQ_VERSION_KEY_ALIAS_MIGRATION_SKIP;
if (!$forceRunMigrationCheck && $actionSlugMigration === ACTION_SLUG_MIGRATION_DO_SKIP_FOREVER) {
return QFQ_VERSION_KEY_SLUG_MIGRATION_SKIP;
}
// if GET is "resume" and session is "pause" then resume.
if (($_SESSION[ACTION_SLUG_MIGRATION_UPDATE] ?? '') === ACTION_SLUG_MIGRATION_DO_PAUSE && $actionSlugMigration === ACTION_SLUG_MIGRATION_DO_RESUME) {
$_SESSION[ACTION_SLUG_MIGRATION_UPDATE] = '';
}
// if GET parameter or session is "pause" then skip check and show message
if ($actionSlugMigration === ACTION_SLUG_MIGRATION_DO_PAUSE || $_SESSION[ACTION_SLUG_MIGRATION_UPDATE] ?? '' === ACTION_SLUG_MIGRATION_DO_PAUSE) {
$_SESSION[ACTION_SLUG_MIGRATION_UPDATE] = ACTION_SLUG_MIGRATION_DO_PAUSE;
QuickFormQuery::$systemMessages[] = 'Page slug migration paused. '
. '<a href="?' . ACTION_SLUG_MIGRATION_UPDATE . '=' . ACTION_SLUG_MIGRATION_DO_RESUME . '">Resume migration</a>';
return null;
}
// is replacement mode?
$doReplace = $actionAliasMigration === ACTION_ALIAS_MIGRATION_DO_REPLACE;
$doReplace = $actionSlugMigration === ACTION_SLUG_MIGRATION_DO_REPLACE;
############################################################
##### THROW EXCEPTION IF BACKEND USER IS NOT LOGGED IN #####
if (!T3Info::beUserLoggedIn()) {
Thrower::dbException('Page Alias Migration: Please log in to Typo3 backend and reload this page.');
Thrower::dbException('Page Slug Migration: Please log in to Typo3 backend and reload this page.');
}
......@@ -315,7 +329,7 @@ class DatabaseUpdate {
# Caching: If multiple QFQ tt_content elements are present then only process this function once
static $hasAlreadyRunOnPage = false;
if ($hasAlreadyRunOnPage) {
Thrower::dbException('Page Alias Migration.', 'Please go to a page which contains only one QFQ content element.');
Thrower::dbException('Page Slug Migration.', 'Please go to a page which contains only one QFQ content element.');
}
$hasAlreadyRunOnPage = true;
......@@ -384,14 +398,14 @@ class DatabaseUpdate {
// Patterns for which we might be able to give a suggestion
$patternsWithSuggestions = [
'/([\'"\|]p:)((?:id=)?[a-zA-Z0-9_\-]*)([&\|"\'])/s',
'/([\'"\|]p:)((?:id=)?{{pageAlias)(:T[0E]*}}[&\|"\'])/s',
'/([\'"\|]p:)((?:id=)?{{(?:pageAlias|pageId))(:T[0E]*}}[&\|"\'])/s',
'/(href=[\'"])((?:index\.php)?\?id=[a-zA-Z0-9_\-]*)([&"\'])/s',
'/(href=[\'"])((?:index\.php)?\?id={{pageAlias)(:T[0E]*}}[&"\'])/s'
'/(href=[\'"])((?:index\.php)?\?id={{(?:pageAlias|pageId))(:T[0E]*}}[&"\'])/s'
];
// Patterns for which we can't make a suggestion (applied after the the patterns with suggestions were replaced)
$patternsNeedManualFix = [
'/({{)(pageAlias)(:T[BCDEFLMPRSUVY0]*}})/s'
'/({{)((?:pageAlias|pageId))(:T[BCDEFLMPRSUVY0]*}})/s'
];
##################################################
......@@ -418,8 +432,8 @@ class DatabaseUpdate {
$trailing = substr($postfix, -1);
$postfix = substr($postfix, 0, -1) . ($trailing === '&' ? '?' : $trailing);
// case 'p:{{pageAlias:T}}& >>> pageSlug
return [$prefix, '{{' . ($match === '{{' . TYPO3_PAGE_ALIAS ? TYPO3_PAGE_SLUG : $noSuggestionSymbol), $postfix];
# case 'p:{{pageAlias:T}}& >>> pageSlug
return [$prefix, '{{' . TYPO3_PAGE_SLUG, $postfix];
} elseif (OnString::strStartsWith($prefix, 'href=') && !OnString::strStartsWith($postfix, ':T')) {
......@@ -523,7 +537,7 @@ class DatabaseUpdate {
// make sure the control characters are not used in the QFQ code
if (OnString::strContains($qfqCode[$KEY_CONTENT], $noSuggestionSymbol) || OnString::strContains($qfqCode[$KEY_CONTENT], $replacedSymbol)) {
Thrower::dbException('Page Alias Migration.', "Unicode character $noSuggestionSymbol or $replacedSymbol found in" . $qfqCode[$KEY_TITLE] . ". The page alias migration script can't continue since it uses those characters as control characters. Please temporarily replace those characters.");
Thrower::dbException('Page Slug Migration.', "Unicode character $noSuggestionSymbol or $replacedSymbol found in" . $qfqCode[$KEY_TITLE] . ". The page slug migration script can't continue since it uses those characters as control characters. Please temporarily replace those characters.");
}
// add suggestion marks to all occurrences of regex patterns in $patternsWithSuggestions
......@@ -603,9 +617,9 @@ class DatabaseUpdate {
// if no occurrences were found then set db flag to "done"
if ($message === '' && !$doReplace) {
if ($forceRunMigrationCheck) {
Thrower::dbException('Page Alias Migration.', 'No alias patterns where found. Please disable the setting ' . FORCE_RUN_PAGE_ALIAS_MIGRATION_CHECK . ' in qfq.json');
Thrower::dbException('Page Slug Migration.', 'No alias patterns where found. Please disable the setting ' . FORCE_RUN_PAGE_SLUG_MIGRATION_CHECK . ' in qfq.json');
}
return QFQ_VERSION_KEY_ALIAS_MIGRATION_DONE;
return QFQ_VERSION_KEY_SLUG_MIGRATION_DONE;
}
###############################################
......@@ -614,13 +628,13 @@ class DatabaseUpdate {
if ($doReplace) {
// save report to file
$reportPath = Path::absoluteLog() . '/' . date("YmdHi") . '_page_alias_migration.html';
$reportPath = Path::absoluteLog() . '/' . date("YmdHi") . '_page_slug_migration.html';
Logger::logMessage('<meta charset="UTF-8">' . "<h2>Report: Replacement Marked with $replacedSymbol in Code</h2>" . $message, $reportPath);
$message = ''
. '<h2>Automatic Replacement Completed</h2>'
. 'The following report has been saved to <br>' . $reportPath
. '<br><br>Click <a href="?' . ACTION_ALIAS_MIGRATION_UPDATE . '=null">check-again</a> to search for still existing usages of page alias.'
. '<br><br>Click <a href="?' . ACTION_SLUG_MIGRATION_UPDATE . '=null">check-again</a> to search for still existing usages of page alias.'
. '<h2>Overview Replacement Suggestions</h2>'
. join('<br>', array_unique($allMatches))
. ' <br><br>(list might not be complete)'
......@@ -628,8 +642,8 @@ class DatabaseUpdate {
. $message;
} else {
$message = ''
. '<h2>Usage of Page Alias Found</h2>'
. 'The Typo3 feature "page alias" has been replaced by the "page slug" feature and can no longer be used in links with "?id=[alias]".'
. '<h2>Page Slug Migration</h2>'
. 'Usages of page alias found. The Typo3 feature "page alias" has been replaced by the "page slug" feature and can no longer be used in links with "?id=[alias]".'
. '<h2>SOLUTION</h2>'
. "<ul>"
. '<li>The special column _link now treats the "p:" parameter value as a page slug if it starts with "/".</li>'
......@@ -638,18 +652,21 @@ class DatabaseUpdate {
. '<li>Replace hardcoded aliases in QFQ code with the slugs of the pages.</li>'
. "</ul>"
. 'Click <a href="?' . ACTION_ALIAS_MIGRATION_UPDATE . '=' . ACTION_ALIAS_MIGRATION_DO_REPLACE . '">auto-replace</a>'
. ' to automatically replace occurrences of page aliases and page ids with the page slug.'
. '<br> In the report below the suggested changes are prominently marked with colour.'
. '<br>In the report below the suggested changes are prominently marked with colour.'
. "<br> If there is a $noSuggestionSymbol then there is no suggestion and you will have to fix it manually."
. "<br> Tip: use ctrl+f and copy $noSuggestionSymbol into the search bar."
. '<br><br> The following report will be saved as an HTML file to ' . Path::absoluteLog() . ' after the automatic replacement.'
. '<br><br>Click <a href="?' . ACTION_SLUG_MIGRATION_UPDATE . '=' . ACTION_SLUG_MIGRATION_DO_REPLACE . '">auto-replace</a>'
. ' to automatically replace occurrences of page aliases and page ids with the page slug.'
. '<br>An HTML report will be saved to "' . Path::absoluteLog() . '" after the automatic replacement.'
. ' <br><br>To pause this migration and to use the Form Editor click: '
. '<a href="?' . ACTION_SLUG_MIGRATION_UPDATE . '=' . ACTION_SLUG_MIGRATION_DO_PAUSE . '">Pause-migration</a>'
. ' <br><br>To skip this check forever click: '
. '<a href="?' . ACTION_ALIAS_MIGRATION_UPDATE . '=' . ACTION_ALIAS_MIGRATION_DO_SKIP_FOREVER . '">Skip-check-forever</a> (your app will probably be broken)'
. '<a href="?' . ACTION_SLUG_MIGRATION_UPDATE . '=' . ACTION_SLUG_MIGRATION_DO_SKIP_FOREVER . '">Skip-check-forever</a> (your app will probably be broken)'
. ($forceRunMigrationCheck ? '<br><br>Note: setting ' . FORCE_RUN_PAGE_ALIAS_MIGRATION_CHECK . ' is active in qfq.json.' : '')
. ($forceRunMigrationCheck ? '<br><br>Note: setting ' . FORCE_RUN_PAGE_SLUG_MIGRATION_CHECK . ' is active in qfq.json.' : '')
. '<h2>Overview Replacement Suggestions</h2>'
. join('<br>', array_unique($allMatches))
......@@ -657,7 +674,7 @@ class DatabaseUpdate {
. $message;
}
Thrower::dbException('Page Alias Migration.', $message, false, ERROR_PLAY_SQL_FILE);
Thrower::dbException('Page Slug Migration.', $message, false, ERROR_PLAY_SQL_FILE);
}
/**
......
......@@ -32,6 +32,19 @@ class OnString {
return substr($haystack, strrpos($haystack, $needle) + strlen($needle));
}
/**
* Like str_replace() but only replaces the first occurrence.
* @param $from
* @param $to
* @param $content
* @return array|string|string[]|null
*/
public static function strReplaceFirst($from, $to, $content)
{
$from = '/'.preg_quote($from, '/').'/';
return preg_replace($from, $to, $content, 1);
}
/**
* Strips the first char $c from $data if the first char is equal to $c.
* Example: with $c='_' the $data='_pId' becomes 'pId'
......
......@@ -30,6 +30,7 @@ use IMATHUZH\Qfq\Core\Store\FillStoreForm;
use IMATHUZH\Qfq\Core\Store\Session;
use IMATHUZH\Qfq\Core\Store\Sip;
use IMATHUZH\Qfq\Core\Store\Store;
use IMATHUZH\Qfq\Core\Store\T3Info;
/*
* Form will be called
......@@ -49,6 +50,8 @@ use IMATHUZH\Qfq\Core\Store\Store;
*/
class QuickFormQuery {
public static $systemMessages = [];
/**
* @var Store instantiated class
*/
......@@ -300,6 +303,13 @@ class QuickFormQuery {
$html = Support::wrapTag("<div class='$class'>", $html);
}
// Show system messages if be-user logged in
if (T3Info::beUserLoggedIn()) {
foreach (self::$systemMessages AS $message) {
$html = '<div class="alert alert-warning">' . $message . '</div>' . $html;
}
}
return $html;
}
......
......@@ -25,6 +25,7 @@ namespace IMATHUZH\Qfq\Core\Report;
use IMATHUZH\Qfq\Core\Helper\KeyValueStringParser;
use IMATHUZH\Qfq\Core\Helper\OnArray;
use IMATHUZH\Qfq\Core\Helper\OnString;
use IMATHUZH\Qfq\Core\Helper\Path;
use IMATHUZH\Qfq\Core\Helper\Sanitize;
use IMATHUZH\Qfq\Core\Helper\Support;
......@@ -1569,8 +1570,22 @@ EOF;
*/
private function buildPage($vars, $value) {
$vars[NAME_LINK_CLASS_DEFAULT] = '';
$vars[NAME_LINK_CLASS_DEFAULT] = ''; // No idea why this is here... (Marc)
// if page is given by a numeric id then prepend 'id=' and return
if (preg_match('/^((?:id=)?)([0-9]+)((?:&.*))$/s', $value,$matches)) {
$vars[NAME_PAGE] = Support::concatUrlParam('', 'id=' . $matches[2] . $matches[3]);
return $vars;
}
// replace first '&' with '?' if there is no '?'. (for convenience in case someone writes something like 'p:{{pageSlug:T}}&foo=bar&...' )
if (!OnString::strContains($value, '?') && OnString::strContains($value, '&')) {
$value = OnString::strReplaceFirst('&', '?', $value);
}
// make URL absolute by prepending host
$vars[NAME_PAGE] = Path::join($this->store->getVar(SYSTEM_BASE_URL_LANG, STORE_SYSTEM), $value);
return $vars;
}
......
......@@ -9,6 +9,7 @@
namespace IMATHUZH\Qfq\Core\Store;
use IMATHUZH\Qfq\Core\Database\Database;
use IMATHUZH\Qfq\Core\Exception\Thrower;
use IMATHUZH\Qfq\Core\Helper\HelperFile;
use IMATHUZH\Qfq\Core\Helper\KeyValueStringParser;
use IMATHUZH\Qfq\Core\Helper\Logger;
......@@ -547,6 +548,11 @@ class Store {
while ($useStores !== false && $useStores !== '') {
$store = $useStores[0]; // current store
// Deprecation check: Throw exception if pageAlias is used
if ($key === TYPO3_PAGE_ALIAS && $store === STORE_TYPO3) {
Thrower::userReportException('Deprecated functionality used.', 'Tried to access the variable ' . TYPO3_PAGE_ALIAS . ' from Typo3 store. Since Typo3 version 9 page slugs are used instead of page aliases. Please use ' . TYPO3_PAGE_SLUG);
}
$finalKey = $key;
if ($store == STORE_LDAP) {
$finalKey = strtolower($key); // in STORE_LDAP all keys are lowercase
......
......@@ -16,8 +16,8 @@ dbIndex={{indexQfq:Y}}
# List of Forms: Do not show this list of forms if there is a form given by SIP.
# Table header.
sql = SELECT '<th data-sorter="false" class="filter-false">'
, CONCAT('p:{{pageAlias:T}}&form=form&') as _pagen
, CONCAT('p:{{pageAlias:T}}&form=formJson&|t:json') as _pagen
, CONCAT('p:{{pageSlug:T}}?form=form&') as _pagen
, CONCAT('p:{{pageSlug:T}}?form=formJson&|t:json') as _pagen
, '</th><th>Name'
, '</th><th>Title'
, '</th><th>Table'
......@@ -27,7 +27,7 @@ dbIndex={{indexQfq:Y}}
, '</th><th><em>PageId</em></th>'
FROM (SELECT '') AS fake
WHERE {{formIdHistory:S0}}=0
head = <table class="table table-hover qfq-table-50 tablesorter tablesorter-filter" id="{{pageAlias:T}}-form">
head = <table class="table table-hover qfq-table-50 tablesorter tablesorter-filter" id="{{pageSlug:T}}-form">
rbeg = <thead class="qfq-sticky"><tr>
rend = </tr></thead><tbody>
tail = </tbody></table>
......@@ -35,13 +35,13 @@ dbIndex={{indexQfq:Y}}
20 {
# All forms
sql = SELECT '<td>'
, CONCAT('p:{{pageAlias:T}}&form=form&r=', f.id) as _pagee
, CONCAT('p:{{pageAlias:T}}&form=formJson&r=', f.id, '|t:json') as _pagee
, CONCAT('p:{{pageSlug:T}}?form=form&r=', f.id) as _pagee
, CONCAT('p:{{pageSlug:T}}?form=formJson&r=', f.id, '|t:json') as _pagee
, '</td>'
, CONCAT(f.name, ' <span class="text-muted">(', f.id, ')</span>')
, QMORE(strip_tags(f.title),50)
, f.tableName
, CONCAT('p:{{pageAlias:T}}&formIdHistory=', f.id, '|s|b|t:<span class="badge">', COUNT(fsl.id), '</span>'
, CONCAT('p:{{pageSlug:T}}?formIdHistory=', f.id, '|s|b|t:<span class="badge">', COUNT(fsl.id), '</span>'
, IF(COUNT(fsl.id)=0, '|r:3','') ) as _link
, CONCAT( '<em><span title="',MIN(fsl.created), '">', DATE_FORMAT( MIN( fsl.created), '%d.%m.%Y'), '</span></em>')
, CONCAT( '<em><span title="',MAX(fsl.created), '">', DATE_FORMAT( MAX( fsl.created), '%d.%m.%Y'), '</span></em>')
......@@ -73,7 +73,7 @@ dbIndex={{indexQfq:Y}}
AND fsl.formId=f.id
ORDER BY fsl.created DESC
head = <table class="table table-hover qfq-table-50 tablesorter tablesorter-filter" id="{{pageAlias:T}}-formHistory">
head = <table class="table table-hover qfq-table-50 tablesorter tablesorter-filter" id="{{pageSlug:T}}-formHistory">
<thead class="qfq-sticky"><tr><th>Form</th><th>feUser</th><th>recordId</th><th>pageId</th><th>Submit</th></tr></thead><tbody>
tail = </tbody></table>
rbeg = <tr>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment