Generelles / Gut zu wissen (Siehe Clickstorm.de)
Zuerst lohnt sich ein Blick auf die Seiten von Clickstorm, in der ein Großteil der bei mir aufgetretenen Hindernisse bereits mit Lösungen beschrieben wurden
https://www.clickstorm.de/blog/typo3-12-entwickler
Danach funktionierte meine Webseite zwar immer noch nicht, aber den Rest habe ich mit den weiteren Tipps auf der Seite gelöst.
Umstellung auf CKeditor 5
Wenn man den Hinweisen auf der Clickstorm Seite gefolgt ist, dann hat man wieder einen halbwegs laufenden Backend Editor.
Um eigene “Link” Klassen für Interne, externe oder Links auf Files hinzuzufügen, muss man die Konfiguration noch erweitern.
Hinweis: ich hatte das Problem Ende 2023. Das dürfte inzwischen behoben sein.
Das fehlt zwar in den Beispielen aus dem TYPO3 Core
public/typo3/sysext/rte_ckeditor/Configuration/RTE/Full.yaml
aber in der Klasse
public/typo3/sysext/rte_ckeditor/Classes/Controller/BrowseLinksController.php
habe ich die notwendigen Hinweise gefunden:
Wie in LTS 11, braucht man in der eigenen “myConfig.yaml” zusätzlich zu “Editor:” einen eigenen Abschnitt:
Buttons:
properties:
class:
allowedClasses: ‘internalLink,externalLink,fileLink pdf,btn bt-primary.btn bt-primary….'
und um bestimmte Classen nur für Link Typen wie “File” oder "Page" zuzulassen, wie bisher
classesAnchor:
internalLink:
class: 'internalLink'
type: 'page'
filePdf:
class: 'fileLink pdf'
type: 'file'
Das Beispiel von meiner Webseite : default.yaml
Import und Namen der typoscript Dateien
Historisch gewachsen, hatte meine TYPO3 Instanz über die Jahre diverse Benennungen und deren Zugriff erlebt.
Diverse Datei Endungen wie:
'ts', 'txt', 'text' , 't3' , 't3s' , 'tscript' , 'tsconfig'
and different kind of importing each other:
f.e.
<INCLUDE_TYPOSCRIPT src="/typo3conf/ext/jve_template/Configuration/TypoScript/Setup.ts"> <INCLUDE_TYPOSCRIPT src="FILE:EXT:jve_template/Configuration/TypoScript/Setup/Setup.t3s">
but should be:
@import 'EXT:jve_template/Configuration/TypoScript/Setup.typoscript'In PHP Storm in den Localen Dateien zu ändern ist simple gelöst gewesen.In der Datenbank in den diversen Tabellen, vergisst man aber schnell einen Eintrag anzupassen. Tabellen und Felder die bei mir genutzt wurden**sys_template** -> config and constants \**pages** -> TSconfig \**fe_users** -> TSconfig \**fe_groups** -> TSconfig \**be_users** -> TSconfig \**be_groups** -> TSconfig
sicher nicht vollständig. Dafür habe ich mir einen Upgrade Wizard gebaut, den ich bisher erfolgreich in 2 Instanzen getestet habe:
composer req jvelletti/jve-upgradewizard
bitte unbedingt die readme.md beachten (erst lokal testen und nur mit backup der DB!!!)
Älteres eigenes TT Content Element mit Image Elementen
Historisch hatten die selbstgebauten TT Content Elemente , z.b. für einen Slider im Feld pi1_flexform nur eine Komma separierte Liste mit File IDs, aber es gab keine Sys_file_reference einträge, die die Verlinkung zwischen sys_file und dem tt_content element hergestellt haben.
Somit wurden im Backend keine verknüpften Bilder gezeigt.
Beispiel:
data.elements.lDEF.settings.images.vDEF = 104,105,1018,1020
sollte aber sein:
data.elements.lDEF.settings.images.vDEF = 4
und dazu die 4 Sys_fle_referenz einträge mit der UID des TT_content elements
tablenames=tt_content
table_local=sys:file
uid_local=xxx (=UID des TT_content elements)
uid_foreign=104 ( bzw. 105,1018,1020 )
ich habe mir dafür einen Upgrade Wizard gebaut. der funktioniert zwar nur mit “meinen” Feldern. Der nachfolgende Code muss somit nicht für andere Flexfor Konfigurationen angepasst werden. der kümmert sich nur um die Korrektur des Feldes “pi1_flexform”
Der ist bisher nur in einer Instanz unter PHP 8.1 getestet. Darum kein BACKUP, kein Mitleid !!
Anpassungen in Zeile 54: dem "$elements" Array mit den Flexform Feldern.
EXT:jve_template/Classes/Upgrades/UpgradeImagesWizard.php
<?phpdeclare(strict_types=1);namespace Jve\JveTemplate\Upgrades;use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;use TYPO3\CMS\Core\Database\ConnectionPool;use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;use TYPO3\CMS\Core\Service\FlexFormService;use TYPO3\CMS\Core\Utility\GeneralUtility;use TYPO3\CMS\Install\Attribute\UpgradeWizard;use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;#[UpgradeWizard('jveTemplate_upgradeImagesWizard')]final class UpgradeImagesWizard implements UpgradeWizardInterface{ public int $verboseLevel = 0 ; /** * Return the speaking name of this wizard */ public function getTitle(): string { return 'Fix sys_file_references of content elements'; } /** * Return the description for this wizard */ public function getDescription(): string { return 'Content elements like image slider had no complete sys_file_reference and direct UID (list) of such records.'; } /** * Execute the update * * Called when a wizard reports that an update is necessary */ public function executeUpdate(): bool { if ( isset( $_SERVER['argv'][3]) ) { if ( $_SERVER['argv'][3] =="-v" ) { $this->verboseLevel = 16 ; } if ( $_SERVER['argv'][3] =="-vv" ) { $this->verboseLevel = 64 ; } if ( $_SERVER['argv'][3] =="-vvv" ) { $this->verboseLevel = 128 ; } }; $elements = [ "jvetemplate_ce_imageSlider" => [ 'section' => 'elements' , 'field' => 'images' ] , "jvetemplate_ce_imageLink" => [ 'section' => 'main' , 'field' => 'images' ] , "jvetemplate_ce_countdown" => [ 'section' => 'main' , 'field' => 'image' ] ] ; $result = true; foreach ( $elements as $ctype => $elementField ) { $objCount = 0 ; $objects = $this->getContentElements( $ctype ) ; while ( $object = $objects->fetchAssociative() ) { try { $this->checkContentElement( $object , $elementField ) ; $objCount ++ ; } catch ( \Exception $e ) { $this->debugOutput( 0 , $e->getFile() . " - Line: " . $e->getLine() . $e->getMessage() ) ; $result = false ; } } $this->debugOutput( 15 , "Found " . $objCount . " of " . $ctype ) ; } $this->debugOutput( 0 , "" ) ; return $result ; } private function getContentElements($ctype) { /** @var ConnectionPool $connectionPool */ $connectionPool = GeneralUtility::makeInstance( "TYPO3\\CMS\\Core\\Database\\ConnectionPool"); $queryBuilder = $connectionPool->getConnectionForTable('tt_content')->createQueryBuilder(); $queryBuilder->getRestrictions()->removeAll()->add( GeneralUtility::makeInstance(DeletedRestriction::class)); $queryBuilder->select('uid','header','pi_flexform' , 'CType' ) ->from('tt_content') ; $expr = $queryBuilder->expr(); $queryBuilder->where( $expr->eq('CType', $queryBuilder->createNamedParameter( $ctype )) ) ; return $queryBuilder->executeQuery() ; } private function checkContentElement($ttContent , $structure ) { $settingsField = $structure['field'] ; $flexFormService = GeneralUtility::makeInstance(FlexFormService::class ) ; $data = [ 'tablenames' => 'tt_content' , 'fieldname' => 'settings.' . $settingsField , 'uid_foreign' => $ttContent['uid']] ; $settings = $flexFormService->convertFlexFormContentToArray( $ttContent['pi_flexform'] ); $this->debugOutput( 63 , $ttContent['uid'] . "-". $ttContent['CType'] . ":". $settingsField ) ; if( isset( $settings['settings'][$settingsField] ) && !isset($settings['settings']['imagesrepaired'])) { $references = GeneralUtility::trimExplode("," , $settings['settings'][$settingsField] ); if( count( $references) > 0 ) { foreach ($references as $refUid ) { $updateCount = $this->updateSysfile( $refUid , $data ) ; $this->debugOutput( 123 , "Reference " . $refUid . " updated: " . $updateCount ) ; } if( $flexFormString = $this->repairFlexForm($ttContent['pi_flexform'] , count( $references) , $structure )) { $updateCount = $this->updateContentElement($ttContent['uid'] , $flexFormString ) ; $this->debugOutput( 123 , "Flexform of tt_content : " . $ttContent['uid'] . " updated: " . $updateCount ) ; } } } } private function updateContentElement($uid , $data) { /** @var ConnectionPool $connectionPool */ $connectionPool = GeneralUtility::makeInstance( "TYPO3\\CMS\\Core\\Database\\ConnectionPool"); $queryBuilder = $connectionPool->getConnectionForTable('tt_content')->createQueryBuilder(); $queryBuilder->getRestrictions()->removeAll()->add( GeneralUtility::makeInstance(DeletedRestriction::class)); $queryBuilder->update('tt_content') ->set( "pi_flexform", $data ) ; $expr = $queryBuilder->expr(); $queryBuilder->where( $expr->eq('uid', $uid ) ) ; return $queryBuilder->executeStatement() ; } private function updateSysfile($uid , $data ) { /** @var ConnectionPool $connectionPool */ $connectionPool = GeneralUtility::makeInstance( "TYPO3\\CMS\\Core\\Database\\ConnectionPool"); $queryBuilder = $connectionPool->getConnectionForTable('sys_file_reference')->createQueryBuilder(); $queryBuilder->getRestrictions()->removeAll()->add( GeneralUtility::makeInstance(DeletedRestriction::class)); $queryBuilder->update('sys_file_reference') ->set( "tablenames" , $data['tablenames']) ->set( "fieldname" , $data['fieldname']) ->set( "table_local" , 'sys_file') ->set( "uid_foreign" , intval( $data['uid_foreign'])) ; $expr = $queryBuilder->expr(); $queryBuilder->where( $expr->eq('uid', $uid )) ; return $queryBuilder->executeStatement() ; } /** * @param string $flexString * @param int $count * @param array $settingsField * @return false|string */ private function repairFlexForm(string $flexString , int $count, array $structure): bool|string { $settingsField = $structure['field'] ; $section = $structure['section'] ; $flexFormArray = GeneralUtility::xml2array( $flexString ); if ( isset( $flexFormArray['data'][$section] )) { $imageRepaired = $flexFormArray['data'][$section] ; $flexFormArray['data'][$section]['lDEF']['settings.upgradeImagesWizardBack']['vDEF'] = $flexFormArray['data'][$section]['lDEF']['settings.' . $settingsField]['vDEF'] ; $flexFormArray['data'][$section]['lDEF']['settings.' . $settingsField]['vDEF'] = $count ; $flexFormArray['data'][$section]['lDEF']['settings.imagesrepaired']['vDEF'] = 1 ; $flexFormTools = new FlexFormTools(); return $flexFormTools->flexArray2Xml($flexFormArray, addPrologue: true); } return false ; } private function debugOutput( $minVerbosity , $text ) { if ( $this->verboseLevel > $minVerbosity ) { echo "\n" . $text ; } } /** * Is an update necessary? * * Is used to determine whether a wizard needs to be run. * Check if data for migration exists. * * @return bool Whether an update is required (TRUE) or not (FALSE) */ public function updateNecessary(): bool { return true; } /** * Returns an array of class names of prerequisite classes * * This way a wizard can define dependencies like "database up-to-date" or * "reference index updated" * * @return string[] */ public function getPrerequisites(): array { return ['database up-to-date' , 'reference index updated'] ; }}
FeLogin - Changes - Eigenes Template muss angepasst werden
Das Partial CookieWarning wird nicht mehr benötigt und besonders wichtig:
das Login Template hat sich geändert: im FORM Tag wird unbedingt der Parameter
requestToken="{requestToken}"
benötigt:
<f:form target="_top" fieldNamePrefix="" action="login" requestToken="{requestToken}" additionalAttributes="{spellcheck: 'false'}">
Content-Security-Policy benötigt zu "https:" Zugriff auf blob:
Mit V12 kann man mit den folgenden Settings das generieren der Content Security Policy TYPO3 überlassen.
$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['security.frontend.enforceContentSecurityPolicy'] = true ;
$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['security.backend.enforceContentSecurityPolicy'] = true ;
Alternativ die Einstellungen auf FALSE setzten, dann aber in den eigenen Einstellungen in z. B. via .htaccess darauf achten, dass neben https: auch “blob:” enthält, sonst werden im Backend keine Inhalte im Bereich Admin geladen.
default-src https: blob:
local in meiner Entwicklungsumbebung habe ich das:
Header set Content-Security-Policy "default-src https: blob: 'unsafe-eval' 'unsafe-inline'; font-src https: data: filesystem: 'unsafe-inline'; img-src https: data: ;"
So migriert man V11 Contentblock-reg-api elemente
Simple und einfach: Einfach diese Extension nutzen:
https://github.com/passionweb-manuel-schnabel/contentblocks-reg-api-migration
(die Issues 1-3 sind in meinem Fork behoben, sollten nach dem akzetiertem Pullrequest kein problem darstellen)
danach musste ich nur noch :
- ggf. Links zu Assets anpassen wie /typo3con/ext/template/ …
{core:normalizedUrl(pathOrUrl: 'EXT:template/Resources/Public/Logo.svg')} ggf.
<f:layout name="Default" />
ersetzen mit
<f:render section="main" arguments="{_all}"></f:render>{image.originalFile.identifier}
ändern in
{image.0.originalFile.identifier}
So ermittelt man den _asset Pfad zu einer Extension
Irgendwo einer Extension brauchst du den Pfad zu einer Datei im Public ordner und die normalen Lösungen funktionieren nicht?
vielleicht hilft dann der TYPO3 Viewhelper:
z.b. in einem Template, das java Script generiert :
var CKEDITOR_BASEPATH = ‘{core:normalizedUrl(pathOrUrl:"EXT:your_extension/Resources/Public/JavaScript/ckeditor/")}’
oder in einem Template das Bilder aus einer anderen Extension laden soll:
<f:variable name="mainlogo">{core:normalizedUrl(pathOrUrl:"EXT:your_template/Resources/Public/Logos/main.svg")}</f:variable>
<img src="{mainlogo}" alt="logo">
$GLOBALS['LANG'] existiert in V12 nicht mehr , so bekommt man einen $this->languageService
If ( !$this->languageService ) {
$this->languageService = GeneralUtility::makeInstance(LanguageServiceFactory::class)->create('default');
}
$GLOBALS['TSFE']->fe_user deprecated
somit muss man das ersetzen :
$feUser = $GLOBALS['TSFE']->fe_user;
Ab V12:
/** @var \TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication $frontendUser */
$frontendUser = $GLOBALS['TYPO3_REQUEST']->getAttribute('frontend.user');
$feUser = ($frontendUser->user ?? null )
Flashmessage Typ: severity nicht mehr INT !
Für die meisten ein “alter Hut”, den man mit rector auch automatsich behoben bekommt:
aus: \TYPO3\CMS\Core\Messaging\AbstractMessage:WARNING
wird: \TYPO3\CMS\Core\Type\ContextualFeedbackSeverity::WARNING
was rector entgangen ist:
wertet man in einem Fluid Template flashMessage.severity aus, um eine error/success klasse zu rendern, dann muss man auf flashMessage.severity.value zugreifen
also aus:
<f:if condition="{flashMessages}">
<f:for each="{flashMessages}" as="flashMessage">
<f:switch expression="{flashMessage.severity}">
wird:
<f:if condition="{flashMessages}">
<f:for each="{flashMessages}" as="flashMessage">
<f:switch expression="{flashMessage.severity.value}">