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}">