TYPO3 Plugin:

cq_base

TYPO3 7.6

Das Update auf 7.6 bietet die chance einige Altlasten loszuwerden. Dazu gehören auch Funktionen, die nicht mehr benötigt werden. Die Features, die ab der Version 3.x nicht mehr unterstützt werden, sollen hier aufgeführt werden

includeAssetViewHelper mit neuem default Wert

Assets werden nun standardmäßig nicht mehr "onTop" geladen. Daher muss einmal geprüft werden, ob es einen validen Grund gibt, dass assets "onTop" geladen werden. Ist dies der Fall, muss das Attribut "loadOnTop" bei der Verwendung des ViewHelpers "includeAssetViewHelper" entsprechend gesetzt werden. In 99% der Fälle sollte der default Wert ausreichen. Sollte es zu Problemem kommen, kann es auch sinnvoll sein zu prüfen ob im Typoscript der TYPO3 Instanz die Verwendung von "IncludeJSLibs" anstelle von "IncludeJS" möglich ist.

Klasse TyposcriptConditionExtender für Typoscript Erweiterungen erstellt

Es gibt eine handvoll Funktionen, die innerhalb von Typoscript Conditions genutzt werden. Damit das Autoloading korrekt funktioniert, wurde nun eine dafür Klasse erstellt.

Am besten sucht man nach der Umstellung auf 7.6 einmal nach den Funktionen im Typoscript und stellt die Aufrufe entsprechend um. Was man dabei genau tun muss, sollte hier dokumentiert werden, wenn es das erste Mal umgestellt wurde.

Die Funktion heißen:

  • user_tx_cqbase_useragent
  • user_tx_cqbase_ismobile
  • user_tx_cqbase_requestParam

Die Umstellung wurde im Zuge der Migrationstätigkeiten für die Hamm Internet Instanz 7.6 am Beispiel der Extension cq_reportdef (Mängelmelder) vorgenommen.

Folgende Erkenntnis wurde festgestellt:

Die alten Namens-Konventionen (Methode muss mit "user" beginnen...) entfallen.
Die include Syntax hat sich in TYPO3 7.6 geändert.

config.uncludeLibrary und config.includeLibs wurden entfernt!

Verwendung von userfuncs:

Userfuncs müssen demnach folgendermaßen eingebunden werden (Bespiel cq_reportdef):

page.20 = USER_INT
page.20 {
 userfunc = CITEQ\CqBase\UserFunc\TyposcriptConditionExtender->user_tx_cqbase_ismobile(isnotmobile_device)
 }

Bei Fallunterscheidungen in Typoscript sieht der Aufruf von userfuncs folgendermaßen aus:

[globalVar = TSFE:id=9041]&&[userFunc=CITEQ\CqBase\UserFunc\TyposcriptConditionExtender::user_tx_cqbase_ismobile(isnotmobile_device)]
    page.headerData  {
        4125 = TEXT
        4125.value = <script src="typo3conf/ext/cq_reportdef/Resources/Public/JavaScript/slider.js" type="text/javascript"></script>
        4126 = TEXT
        4126.value = <link rel="stylesheet" type="text/css"  media="all" />
    }
[GLOBAL] 

AjaxDispatcher rausgeflogen

Der AjaxDispatcher sollte nicht mehr verwendet werden, da es hierfür bereits Core Funktionalitäten gibt. Z.B. Über die Typenum order EID Funktion.

Rest Server

Es gab eine Rest Server Funktionalität, die es aber nie in die Produktion geschafft hat. Diese Funktion wurde ebenfalls entfernt, damit die Extension möglichst schlank bleibt. In diesem Zusammenhang wurde auch die eigene caching Umgebung cqbase_digest entfernt, welche für das Caching der Authentifizierungen genutzt wurde.

AdditionalLanguageViewHelper

Dieser ViewHelper funktioniert in dieser Form nicht mehr. Er wirft aktuell eine Exception, wenn man ihn aufruft, weil es nun einen zusätzlichen Parameter extKey geben muss. Dazu muss aber erstmal herausgefunden werden, wofür dieser Viewhelper im einzelnen gedacht ist und wo er aufgerufen wird.

Hooks

TCAdefaults werden auch ohne Berichtungen auf das Feld berücksichtigt

Wie in diesem Bug beschrieben übernimmt TYPO3 standardmäßig die Werte aus TCAdefaults nur, wenn man auch Berechtigungen auf das Feld hat. Dies ist nicht immer der Fall. CqBase enthält einen Hook, der genau das überprüft und sicherstellt, dass für neue Elemente auch der definierte Standardwert übernommen wird.

Siehe dazu auch den Bug: http://bugzilla.citeq.de/show_bug.cgi?id=3879

Feature Dokumentation

Data Manipulation API

Über eine CLI API gibt es die Möglichkeit Datensätze einer TYPO3 Instanz zu manipulieren. Aktuell ist lediglich das Löschen von Datensätzen vorgesehen, da dies im Rahmen von Behat Tests benötigt wurde.

Dazu kann bequem folgender Command auf der Kommandozeile aufgerufen werden:

typo3/cli_dispatch.phpsh extbase datamanipulation:deletedata --table='tt_content' --where='pid = 123456'

Die API kann aber sehr einfach erweitert werden.

Frontend im Backend booten

In einigen Fällen möchte man im Backend Links für zum Frontend erzeugen. Dazu gibt es die Möglichkeit das Frontend zu booten.

Folgender Code Schnipsel kann dazu verwendet werden:

/** @var \CITEQ\CqBase\Utility\FrontEndUtility $frontendUtility */
$frontendUtility = GeneralUtility::makeInstance(\CITEQ\CqBase\Utility\FrontEndUtility::class);
$frontendUtility->bootFrontend();

Configuration Utility (Ab Version 3.2.1)

Um an beliebiger Stelle auf Typoscript Konfiguration zurückzugreifen, gibt es folgende Möglichkeit. Unterschieden wird nach Frontend oder Backend:

Frontend:

 /** @var ConfigurationUtility $configurationUtility */
 $configurationUtility = $this->objectManager->get(ConfigurationUtility::class, "MyExtension");
 $setting = $configurationUtility->getSetting("path.to.my.setting");
 echo "this is my setting: $setting";

Backend:

 /** @var ConfigurationUtility $configurationUtility */
 $configurationUtility = $objectManager->get(ConfigurationUtility::class, "CqBase");
 $setting = $configurationUtility->getBackendSetting("cqws.wsLocation");
 echo "this is my setting: $setting";

Bugfix für RealUrl 1.x unter TYPO3 7.6 (ab Version 3.x)

Verwendet man realurl in dem 1.x branch unter der TYPO3 Version 7.6 ist die Option "gesamten Pfad überschreiben" nicht mehr vorhanden. Dies liegt daran, dass das Verhalten der palettes sich geändert hat. Dimitry Dulepov hat allerdings den support von realurl 1.x eingestellt. Daher müssen wir das irgendwie selbst fixen. Damit kommt dieser bugfix ins spiel. Es wird einfach nur in der ext_tables.php das entsprechende Feld im TCA wieder "sichtbar" gemacht. Im Prinzip könnte man das auch irgendwo anders machen, aber cq_base schien mir ein guter Ort dafür, da wir die fast überall im Einsatz haben.

Csv Parsing (noch nicht released, vorraussichtlich ab Version 2.6.0)

Gelegentlich kommt es vor, dass man eine csv Datei angeliefert bekommt, die es zu importieren gilt. Dazu ist es nötig, die einzelnen Felder der csv Datei auf die einzelnen Tabellenspalten mappen. In komplexeren Datenstrukturen reicht ein einfaches 1:1 Mapping aber nicht mehr aus, da viele Tabellen miteinander verknüpft sind. Das Konzept sieht dabei vor, dass man den Content einer CSV Datei so aufbereitet, dass man daraus ein entsprechendes Extbase Model erzeugen kann, welches problemlos über die Extbase Persistenz-Schicht in die Datenbank eingefügt werden kann.

Hier ein Beispiel:

zu importieren ist folgender csv content:

$csvContent = '
  vorname;nachname;geburtsdatum;straße;hausnummer;plz;ort
  max;mustermann;15.12.2015;musterstraße;1;12345;Musterstadt
  erika;mustermann;16.12.2015;musterstraße;2;12345;Musterstadt
';

Zuerst wird eine Konfiguration benötigt, die Aufschluss darüber gibt, wie welche CSV Spalte untergebracht werden soll. Erwartet wird Diese als Array. Für das oben gezeigt Beispiel könnte das so aussehen:

$mappingConf = array(
  // syntax: '<Spaltenname in csv Datei>' => '<Attributpfad im Extbase Model>',
  'vorname' => 'firstName',
  'nachname' => 'lastName',
  // komplexere Attribute können mit userFuncs aufbereitet werden. Dazu definiert man ein Array wie Dieses
  'geburtsdatum' => array(
    // 'field' verhält sich exakt wie die die oberen Mappings
    'field' => 'dateOfBirth',
    // 'func' gibt die Funktion an, die den Wert aufbereitet. Dazu später mehr.
    // Unterschieden wird der Trenner. Bei einem -> wird die Klasse instanziiert, bei :: nicht
    'func' => '\CITEQ\MyExt\MyMappingHelper->toTimestamp'
  ),
  // Relationen werden mit einem . angezeigt. In diesem Fall gibt es eine Relation zur 'Address' Entität
  'straße' => 'address.street',
  'hausnummer' => 'address.houseNumber',
  'plz' => 'address.zip',
  'ort' => 'address.city'
);

Dinge, die nicht in der CSV vorkommen, können mit der sog. 'postMappingConf' definiert werden. Das ist z.B. sinnvoll für eine pid. Die Syntax unterscheidet sich von der obrigen, da damit kein Mapping vorgenommen wird, sondern die Daten angereichert werden. Ein Beispiel für die pid:

$postMappingConf = array(
  // so würde jeder csv Zeile die pid zugewiesen werden, die von der Funktion geliefert wird
  'pid' => '\CITEQ\MyExt\MyMappingHelper->getPid'
);

Zur Anwendung der Konfiguration auf den csv input kann folgendes Code-Beispiel verwendet werden:

$csvDelimiter = ';';

$csvParser = $this->objectManager->get('CITEQ\\CqBase\\Helper\\CsvParser');
$csvParser->setParameters($mappingConf, $csvContent, $csvDelimiter, $postMappingsConf);

// optional kann der csv content mit einem regex gefiltert werden
$regExFilter = '.+';
$csvParser->filterContentWithRegex($regExFilter);

var_dump($csvParser->applyMappingConfiguration());

Das Code-Beispiel erzeugt folgende Ausgabe:

array(
  1 => array(
    'firstName' => 'max'
    'lastName' => 'mustermann'
    'dateOfBirth' => 1450137600
    'address' => array(
      'street' => 'musterstraße'
      'houseNumber' => 1
      'zip' => '12345'
      'city' => 'Musterstadt'
    )
  )
  2 => array(
    'firstName' => 'erika'
    'lastName' => 'mustermann'
    'dateOfBirth' => 1450224000
    'address' => array(
      'street' => 'musterstraße'
      'houseNumber' => 2
      'zip' => '12345'
      'city' => 'Musterstadt'
    )
  )
)

Die enthaltenen Arrays auf oberer Ebene können anschließend mit dem PropertyMapper zu Extbase Models konvertiert werden.

GEO Json erzeugen (ab Version 2.5.0)

Um einen sauberen bidirektionalen Datenaustausch mit dem Map Server der Stadt Münster zu implementieren, empfiehlt es sich innerhalb der Fachextension eine separate controllerAction bereitzustellen und die gewünschten Daten als Json Output zur Verfügung zu stellen. Um dabei eine einheitliche Vorgehensweise zu haben, stellt cq_base ab Version 2.5.0 dafür eine Api zur Verfügung.

Nachfolgend ein Implementationsbeispiel:

        /** @var \CITEQ\CqBase\Geo\GeoService $geoService */
        $geoService = $this->objectManager->get("\\CITEQ\\CqBase\\Geo\\GeoService");
        $allFeatures = array();

        /** @var \CITEQ\CqParking\Domain\Model\CarPark $carPark */
        foreach ($this->carparkRepository->findAll() as $carPark){
            if (!empty($carPark->getEasting()) AND !empty($carPark->getNorthing())){
                $feature = $geoService->createFeature();
                $feature->setUid($carPark->getUid());
                $feature->setEasting($carPark->getEasting());
                $feature->setNorthing($carPark->getNorthing());
                $feature->setName($carPark->getName());
                $feature->addAdditionalProperty("parkingTotal", $carPark->getParkingTotal());
                $feature->addAdditionalProperty("parkingFree", $carPark->getParkingFree());
                $feature->addAdditionalProperty("status", $carPark->getStatus());
                $feature->addAdditionalProperty("type", $carPark->getTypeString());
                $feature->setUrl($this->settings['externalUrlForGeoService']);
                $allFeatures[] = $feature;
            }
        }
        return $geoService->getGeoJsonFromFeatures($allFeatures);

Es gibt eine Pflichtfelder, die beim Aufrufen der Methode getGeoJsonFromFeatures überprüft werden. Für anwendungsspezifische Zusatzattribute kann die Methode addAdditionalProperty der Feature Klasse verwendet werden.

Wichtig: In setUrl() muss eine URL mit definiert werden, die einen festen Platzhalter ###UID### enthält. Dieser wird dann, wie man bereits richtig vermutet, durch die UID des Datensatzes ersetzt, die man mit setUid() defniert hat. Am besten macht man die URL dann konfigurierbar über TypoScript

Die URL's zur Detailseite sollten am besten über realurl konfiguriert sein, damit der cHash Parameter unterdrückt wird und die entsprechende Seite parametrisiert aufrufbar ist.

Beispiel ohne realurl:

tx_myext[controller]=organization&tx_myext[action]=detail&tx_myext[organization]=1&cHash=12123123adsasd213

Beispiel mit realurl:

/organisationen/detailansicht/organisation/1.html

PDF Erzeugung (ab Version 1.20.0)

Zur Vereinheitlichung und Zentralisierung von PDF-Generator Funktionalitäten wird die php-Library "mPDF" verwendet. Eine entsprechende Dokumentation und Beispiele befinden sich auf den Seiten der Entwickler (http://mpdf1.com/manual/index.php). Sie wurde bereits in die cq_base aufgenommen und automatisch per autoloader geladen.

Nachfolgend wird das Handling der Library in TYPO3 aufgezeigt.

Zuerst wird eine Controlleraction angelegt, in der man die Instanz erzeugt:

$tcpdf = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\CITEQ\CqBase\Libraries\Mpdf::class);

Weil die Library durch den Autoloader bereits geladen wird, ist kein "require" mehr nötig.

Es wird immer davon ausgegangen das der entsprechende Output für das PDF als Template hinterlegt wird, damit dort die entsprechenden fluid Funktionalitäten genutzt werden können:

/** @var $mpdf mPDF*/
$mpdf = t3lib_div::makeInstance(\CITEQ\CqBase\Libraries\Mpdf::class);
$mpdf->WriteHTML($this->view->render());
ob_end_flush();
ob_end_clean();
$mpdf->Output("SEPA Mandat.pdf", "D");

Damit wird der Output des Fluid-Templates nach mPDF umgeleitet, während im fluid-Template bequem Templating betrieben werden kann. Das Template befindet sich, genau wie jedes andere Template einer Controlleraction, im Resources-Ordner.

Wenn ein PDF im Backend erzeugt wird, gibt es beim Einbinden von Resourcen (z.B. Bilder und CSS-Dateien) einen Stolperstein. Die Library erwartet absolute Pfade für alle Resourcen. Da die fluid-eigenen ViewHelper <f:uri.resource .. /> und <f:image ... /> aufgrund von Bugs Schwierigkeiten beim Rendern von absoluten Pfaden haben, wurde ein entsprechender ViewHelper bereitgestellt, welcher absolute Pfade ausgeben kann. Entsprechende Implementationen im Template könnten folgendermaßen aussehen:

{namespace base=CITEQ\CqBase\ViewHelpers}

<!-- CSS -->
<link rel="stylesheet" type="text/css" >

<!-- Images -->
<!-- auf keinen Fall f:image benutzen. Der ViewHelper ist im Backend fehlerhaft. -->
<img src="{base:uri.resource(path:'Images/image.png', absolute:'1')}" alt="altText" />

SOAP (ab Version 1.18.0)

Dieses Feature erfordert mindestens Typo3 6.0!

Um ein leichteres und schnelleres Handling für SOAP-Webservices zu haben, wurde eine SOAP-API in die Extension implementiert. Es gibt zwar eine allgemeine Implementierung (\CITEQ\CqBase\Soap\Client\Base), die theoretisch auch für andere Webservices anwendbar ist, aber diese konnte mangels Testwebservices nicht getestet werden und muss bei Bedarf nachgeholt werden.

Um den citeq-Webservice (nachfolgend cqws) zu verwenden, wurde die Klasse "\CITEQ\CqBase\Soap\Client\Cqws" implementiert. Sie implementiert die bereits erwähnte Klasse "\CITEQ\CqBase\Soap\Client\Base" und ist auf den cqws geeicht. Folgender Code-Ausschnitt zeigt das Handling der cqws-API:

// Instanziierung des Clients auf das cqws-Testsystem
$cqws = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\CITEQ\CqBase\Soap\Client\Cqws::class, "https://net-ws.citeq.de/general.asmx");

anschließend werden die Login-Daten übergeben:


$cqws->setUsername("<benutzername>");
$cqws->setPassword("<passwort>"); 
$cqws->setApplicationId(<AppID des Moduls>);

//oder

$cqws->setCredentials("<benutzername>", "<passwort>", <AppID>);

Nun kann der citeq-Webservice nach Daten befragt werden. Dafür wird ein Methodenname des Moduls benötigt. Ein zweiter Parameter ist optional, damit auch Daten an den Webservice gesendet werden können. Diese müssen als Array übergeben werden.

$cqws->call("GetData");

//oder parametrisiert:

$params = array("testparameter" => "testparameter");
$cqws->call("GetData", $params);

Die Call-Methode gibt das Ergebnis der Abfrage als Mehrdimensionales Array zurück.

Logging und Exceptions

An verschiedenen Stellen werden Exceptions und Logausgaben ausgegeben. Wirft die Instanz an einer Stelle eine Exception, werden im Log detailierte Informationen hinterlegt. Das Log richtet sich je nach Konfiguration des Speicherortes in der TypoScript Konfiguration für die Typo3-Logging API.

Typoscript Referenzen auflösen (ab 1.17.0)

Es gibt im Standard von TYPO3 leider nicht die Möglichkeit richtige Referenzen im Typoscript auf andere Objekte zu definieren. Daher ist es unmöglich in einem static template einer extension komplexe Verschachtelungen zu realisieren, die im lokalen Typoscript der Instanz überschrieben werden können.

Dazu folgendes Typoscript Beispiel (Im Static Template einer Extension):

  plugin.tx_xyz.settings {
     basis {
        konfigurationEins = Das ist ein Test
        konfigurationZwei = Das ist ein Test2
     }

     weitererKnoten {
        weitereKonfiguration < plugin.tx_xyz.settings.basis.konfigurationEins
     }
  }

Durch den Zuweisungsoperator '''<''' wird lediglich eine Kopie erstellt. D.h. ändert man im lokalen Typoscript eine Instanz folgendermaßen den Wert:

  plugin.tx_xyz.settings.basis.konfigurationEins = Ich ändere den Wert in meiner lokalen TYPO3-Instanz

hat dies keine Auswirkung auf den Bereich '''plugin.tx_xyz.settings.weitererKnoten.weitereKonfiguration''' weil dies keine Referenz ist, sondern nur eine Kopie. D.h. es ist weiterhin der Wert "Das ist ein Test" zugewiesen.

Durch diese Erweiterung wird dies allerdings nun möglich. Im Typoscript muss folgendes definiert werden:


  plugin.tx_xyz.settings {
     basis {
        konfigurationEins = Das ist ein Test
        konfigurationZwei = Das ist ein Test2
     }

     weitererKnoten {
        weitereKonfiguration = < plugin.tx_xyz.settings.basis.konfigurationEins
     }
  }

Der Zuweisungsoperator = < stellt nun die Referenz her.

Folgender PHP-Aufruf wäre nötig um die Referenzen manuell aufzulösen.

  //$settingsArray wird als Referenz übergeben, daher wird der Wert in $settingsArray gespeichert
  \CITEQ\CqBase\Helper\TyposcriptConfig::resolveSettingsReferences("EXTENSIONKEY",$settingsArray);

Dieser Aufruf wird Base-Controller aus der Extension cq_base bereits standardmäßig aufgerufen, daher sind in allen controllern, die von \CITEQ\CqBase\Controller\Base erben, die Referenzen bereits aufgelöst. Die Referenzen sind auch in den Fluid-Templates vorhanden. Es muss lediglich in den entsprechenden Viewhelpern, falls man dort auf die Settings zugreifen möchte, noch der entsprechende Aufruf gemacht werden.

ClearCache Erweiterungen (ab 1.11.3)

Um es den Redakteuren so komfortabel wie möglich zu machen, ist es an manchen Stellen erforderlich ein automatisches Leeren des Caches zu erzwingen. Dies kann auf einem Datensatzordner im Page-TSConfig erfolgen. D.h. jedes mal wenn in dem Datensatzordner (und alle Verzeichnisse darunter) etwas geändert wird, wird das ausgeführt, was in '''TCEMAIN.clearCacheCmd''' eingetragen ist. Standardmäßig stehen nur einfache Befehle zur Verfügung (all, und einzelne PID's). In Extensions in denen die Redakteure selbst die Möglichkeit haben Plugins auf die Seiten zu legen, welche Inhalt aus den Datensatzordnern anzeigen, ist es unmöglich die einzelnen PID's zu pflegen. Daher wurde über einen Hook "'''clearCachePostProc'''" die Funktionalität erweitert. Folgende Möglichkeiten stehen zur Verfügung:

sub

Alle Unterseiten der übergebenen UIDs löschen

Syntax: sub($UID)

Beispiel: TCEMAIN.clearCacheCmd=sub(10,33,303)

contains

Leert den Cache der Seiten auf denen tt_content Elemente mit dem Wert 'cqgovservices' in der Spalte 'CType' vorhanden sind.

Syntax: contains($TABLE.$FIELD=$VALUE)

Beispiel: TCEMAIN.clearCacheCmd=contains(tt_content.CType=cqgovservices)

RespectStorage Page (ab 1.5.1)

Es ist nun nicht mehr notwendig folgende wrapper Methoden zu schreiben um zu erreichen, dass Repositories in der gesamten Instanz suchen und die Storage PID nicht berücksichtigen.

    /**
     * setRespectStoragePage to false
     * @return
     */
    public function findAll(){
        $query = $this->createQuery();
        $query->getQuerySettings()->setRespectStoragePage(false);
        return $query->execute();
    }

Dazu genügt es, dass das Repository von der Basisklasse '''\CITEQ\CqBase\Repository\BaseRepository''' ableitet und die member variable useStoragePid auf false gesetzt wird.

Beispiel:

/**
 * @package cq_mypackage
 */
class MyRepository extends \CITEQ\CqLinklistnews\Domain\Repository\BaseRepository {
    protected $useStoragePid = false;
}

Benötigen Sie schnelle Hilfe mit dieser Extension? Unser Team von erfahrenen TYPO3-Entwicklern löst Probleme unkompliziert und zum Stundensatz.