Friday, December 21, 2012

TYPO3 Extbase - mapping existing fields from fe_users

Extbase comes with an own domain model and repository to access the frontend users (fe_users) and frontend groups (fe_groups) tables. If you install sr_feuser_register, several extra fields are added to the fe_users table. Those extra fields are not included in extbase default domain model for fe_users, so you have to extend the domain model with the fields you want to access. This article show howto extend the existing domain model and some things to keep in mind.

Preperation

The first thing to do is to create a new domain model, which extends the existing one. Create a new class for this in your extensions Domain\Model folder.

class Tx_Testext1_Domain_Model_FrontendUser extends Tx_Extbase_Domain_Model_FrontendUser {

}

Next you need to create a repository for the new domain model. Create this in the Domain\Repository folder of your extension.

class Tx_Testext1_Domain_Repository_FrontendUserRepository extends Tx_Extbase_Domain_Repository_FrontendUserRepository {

}

The next thing to do is to setup the mapping for the new table, so Extbase knows from which table it has to get the fields. Add the following to your sites typoscript setup.

config.tx_extbase.persistence.classes {
    Tx_Testext1_Domain_Model_FrontendUser {
        mapping {
            tableName = fe_users
        }
    }
}

You also need to configure, in which storage pid the frontend users are stored. You can set this in the typoscript constants of your extension.

plugin.tx_testext1 { 
  persistence.storagePid = 100
}

Extending the existing domain model
Now you're ready to extend the domain model Tx_Extbase_Domain_Model_FrontendUser to access the fields, which are not included in the default domain model. Actually you can also extend the domain model with new fields, but this is not part of this article.

Lets assume, you want to access the field "comments", which was created by sr_feuser_register. The only thing to do is to add a getter and a setter for this field in the newly created domain model.

class Tx_Testext1_Domain_Model_FrontendUser extends Tx_Extbase_Domain_Model_FrontendUser {

 /**
  * Comment
  *
  * @var string
  */
 protected $comment;

 /**
  * Setter for comment
  *
  * @param string $comment
  * @return void
  */
 public function setComment ($comment) {
  $this->comment = $comment;
 }

 /**
  * Getter for comment
  *
  * @return string
  */
 public function getComment () {
  return $this->comment;
 }
}

Now you can inject the newly created repository to get a record from the database.

/**
 * @var Tx_Testext1_Domain_Repository_FrontendUserRepository
 */
protected $userRepository;

/**
 * Inject the user repository
 *
 * @param Tx_Testext1_Domain_Repository_FrontendUserRepository $userRepository
 * @return void
 */
public function injectFrontendUserRepository (Tx_Testext1_Domain_Repository_FrontendUserRepository $userRepository) {
 $this->userRepository = $userRepository;
}

/**
 * Test action
 *
 * @return void
 */
public function testAction () {
 $user = $this->userRepository->findByUid(1);
 t3lib_utility_Debug::debug($user);
}

When you call the action showed above, you should see the debug output of the user object from the user with the uid 1. In the debug output, you should see the additional field "comment", which is included in the newly created domain model.

Fields with underscore and mixed case
The extension sr_feuser_register adds some additional field to fe_users, which include underscores. The same does "felogin", which actually adds fields with underscores and mixed case (e.g. "felogin_forgotHash").

If you want to include fields with underscore in your domain model (e.g. "date_of_birth"), make sure to remove the underscore and to set a Uppercase of the next letter instead.

/**
 * Date of birth
 *
 * @var int
 */
protected $dateOfBirth;

/**
 * Setter for date_of_birth
 *
 * @param int $dateOfBirth
 * @return void
 */
public function setDateOfBirth ($dateOfBirth) {
 $this->dateOfBirth = $dateOfBirth;
}

/**
 * Getter for date_of_birth
 *
 * @return int
 */
public function getDateOfBirth () {
 return $this->dateOfBirth;
}

If you have fields with underscore and mixed case, just remove the underscore. Extbase seems to ignore the case.

Here is an example of the field "felogin_forgotHash"

/**
 * Password forgot hash
 *
 * @var string
 */
protected $feloginForgothash;



Saturday, December 15, 2012

TYPO3 Flow / Fluid - translating select fields

In a TYPO3 Flow project, I had to create a select box with countries, from which the user could select one. This task is really easy with TYPO3 Flow. Just create a domain and repository for the countries, add the necessary templates and create the country-records. Now you are ready to create a relation from a existing domain (e.g. a domain with some address-fields) to the country domain. No problems here. But what, if you want to translate the country-names in different languages?

The Fluid viewhelper "f:form.select" seems to have an argument called "translate" which can handle translation of select fields labels. I could'nt find a tutorial how to use this argument and finally found out, that everything was described directly in the viewhelpers class.

Here is a short summary on how to use the f:form.select viewhelper together with the "translate" argument.

Assume you have an domain called "country". It only has one property called "alpha2", which is a string representing the country's ISO-3166 alpha2-code. You create some records in the new domain like "DE" for Germany, "DK" for Denmark and so on. You also have an domain called "address", where you have a relation to the country domain.

The first thing to do is to create the translation files. In this example I create one for english and one for german.

<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file original="" source-language="en"  datatype="plaintext">
        <body>
            <trans-unit id="DE">
                <source>Germany</source>
            </trans-unit>
            <trans-unit id="DK">
                <source>Denmark</source>
            </trans-unit>
        </body>
    </file>
</xliff>


<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file original="" source-language="en" target-language="de"  datatype="plaintext">
        <body>
            <trans-unit id="DE">
                <source>Germany</source>
                <target>Deutschland</target>
            </trans-unit>
            <trans-unit id="DK">
                <source>Denmark</source>
                <target>Dänemark</target>
            </trans-unit>
        </body>
    </file>
</xliff>

Next you should configure your f:form.select viewhelper, so it uses the translations.

<f:form.select property="country" id="country" options="{countries}" optionLabelField="alpha2" translate="{using: 'label'}"/>

The viewhelper is configured to use "alpha2" as a label field and to use the label field for translation. There are several other options for the translate argument (e.g. the "source" attribute which enables you to select a different localization file), which are described directly in the class.

Now you just have to assign the object "countries" (containing all countries from the country-repository) through the controller to the corresponding view and you're done.

Thursday, December 6, 2012

TYPO3 CMS - Run Extbase unit tests in PHPStorm

Update 27.06.2014

With TYPO3 6.2 unit- and functional test execution is possible directly through phpunit on command line. So keep in mind, that the steps described below are obsolete, if you use TYPO3 6.2.

Original Article

TYPO3 CMS is great and since Extbase it is easy to create unit tests for your code. Just create your extension with the extension_builder (or manually), install the extension phpunit from TER, add some tests to your extension and now you´re fine to run your unit tests in the phpunit TYPO3 backend module.

If you want to run your tests directly in an IDE (in this article it´s PhpStorm), there are some things to keep care of, before the IDE is ready to run the tests. This article describes how to configure PhpStorm to run Extbase unit tests directly in the IDE and also shows some common error messages including a possible solution.

Prerequisites:
  • A working TYPO3 installation with at least one page and a TS template
  • An Extbase extension with at least one test
  • TYPO3 extension "phpunit" installed 
  • The Extbase unit tests must run in TYPO3´s backend module "phpunit"
Setup:

  1. Configure the PHP interpreter and set the PHP home path to:

    /path-to-typo3-site/typo3conf/ext/phpunit/Ressources/Private/Scripts


  2. Edit your PhpStorm project to use the new PHP interpreter

  3. Adjust the PHP interpreter script.

    The script which comes with the TYPO3 extension "phpunit" seems to contain an error in the CLI path. Adjust this path, so it uses php_ide_testrunner instead of php_ide as the cliKey

    CLI_PATH="${TYPO3_SITE_PATH}/typo3/cli_dispatch.phpsh phpunit_ide_testrunner"
    
  4. Configure an environment variable in your PhpStorm project

    Edit the test configuration for the folder "Tests" of your Extbase extension and set the environment variable ENV_TYPO3_SITE_PATH to the path of your website root

  5. Create a new TYPO3 Backend user named _cli_phpunit
  6. Configure include paths

    Add the TYPO3 source and the TYPO3 extension phpunit to your PhpStorm projects include paths



    This step is optional, but I think it is always nice to have code completion working when coding TYPO3 extensions.
  7. Now you should be ready to run your Extbase unit tests directly in PhpStorm




TYPO3 6.0 issues

TYPO3 6.0 comes with a new base test class, which actually is´nt recognised by the TYPO3 Extension "phpunit" in TER (version 3.6.11). The problem has already been fixed, but it is not published yet, so you have to get the changes manually and pull a new version of the extension from the GIT repository at git://git.typo3.org/TYPO3v4/Extensions/phpunit.git


Error messages and possible solutions

While working with TYPO3, Extbase, phpunit and PhpStorm, I stumbled across some situations, where the tests were not running because of some misconfigured settings. Below are 2 possible situations with an error message an a description how I fixed them.

Situation 1

If you face the error message "Unable to attach test reporter to test framework or test framework quit unexpectedly", this may indicate, that something is wrong with your local permissions.

Solution 1 - Wrong permissions

I am developing on an Ubuntu 12.04 desktop and apache2 and TYPO3 is installed locally. I have my own user to start the desktop, so my user does not have necessary permissions to access the files owned by the webserver.

First, you should add your local user to the group "www-data" (or the group running your local webserver). Next, you should check the TYPO3 install tool for the following:

[fileCreateMask] = 664
[FolderCreateMask] = 775

This is needed, so you local user can read/write to typo3temp. Finally delete the typo3temp/ folder, so the folder is recreated with the new file- and folder permissions.

Solution 2 - Trailing slash in ENV_TYPO3_SITE_PATH

This error message can also orrur, if the ENV_TYPO3_SITE_PATH contains a trailing slash. So check if the path is correct.

Situation 2

Once I also came across this error message "PHP Fatal error:  Cannot redeclare phpunit_autoload() (previously declared in /some/path/script.php)".

The solution for this problem was, that the cliKey in the php interpreter script was set to "phpunit" instead of "phpunit_ide_testrunner"


Monday, October 29, 2012

Integrating TYPO3 Flow commands to the PhpStorm command line tool

Last week I read about the possibility to integrate composer to the PhpStorm command line tool. Inspired by this article, I created a configuration file for TYPO3 Flow commands.

TYPO3 Flow command line integration in PhpStorm
I published the configuration file on github, so it can easily be shared, extended, forked or whatever.

Sadly I did'nt get the Quick Definition View to work as it was described in the original article from Jetbrains.

Configuration:
  1. Navigate to Settings | Command Line Tool Support and add custom configuration
  2. Add the Framework Name (e.g. "TYPO3 Flow"), the Tool path (e.g. "./flow") and the Alias (e.g. "flow")
  3. Import the XML configuration file available here: https://github.com/derhansen/typo3-flow-phpstorm-cl-xml
Be sure to check, that the Tool path doesn't contain an absolute path to the flow executable.

How to use it?
  1. Open a TYPO3 Flow project in PhpStorm and press CTRL + SHIFT + X
  2. Check, that the current directory contains the TYPO3 Flow executable (in Linux systems you can use pwd to check the current directory). If not, navigate to that directory.
  3. Enter a TYPO3 Flow command by using the alias defined in the configuration (in this example "flow")

FlashMessages in TYPO3 Flow überschreiben

In TYPO3 Flow kann man Domain-Models sehr einfach per Annotations validieren. So definiert man in seinem Domain Model lediglich die Eigenschaften und die dazugehörigen Validierungsregeln. Will man nun über ein Formular Daten speichern, gibt TYPO3 Flow wie erwartet Fehlermeldungen passend zu den definierten Validierungsregeln aus.

Beispiel:

/**
 * The firstname
 *
 * @var string
 * @Flow\Validate(type="String")
 * @Flow\Validate(type="NotEmpty")
 */
 protected $firstname;

Die Eigenschaft "firstname" muss ein String und darf nicht leer sein. Erstellt man dafür nun ein Formular, würde das so aussehen.

<f:form action="create" controller="Register">
  <label for="firstname">Firstname:</label>
  <f:form.textfield=" id="firstname" name="data[firstname]" property="firstname" />
  <f:form.submit value="Submit" />
</f:form>

Um die Validierungsergebnisse auszugeben, muss das Formular wie folgt ergänzt werden.

<f:flashMessages />
<f:form.validationResults for="data">
    <f:if condition="{validationResults.flattenedErrors}">
        <div class="error">
            <f:for each="{validationResults.flattenedErrors}" key="propertyPath" as="errors">{propertyPath}: <f:for each="{errors}" as="error">{error}</f:for></f:for>
        </div>
    </f:if>
</f:form.validationResults>

Wenn man die das Formular nun absendet, erhält man in den validationResults die Meldungen bzgl. des Domain-Models. Aber im flashMessage Container erscheint noch eine nicht sehr schöne Fehlermeldung von TYPO3 Flow.
An error occurred while trying to call Your\Package\Controller\RegisterController->createAction()

Diese Meldung kann man überschreiben, indem man im Controller die Methode getErrorFlashMessage() überschreibt.

/**
 * @return \TYPO3\Flow\Error\Message
 */
protected function getErrorFlashMessage() {
 switch ($this->actionMethodName) {
  case 'createAction' :
   return new \TYPO3\Flow\Error\Message('Could not save form, because some fields are not filled out correctly');
  default:
   return parent::getErrorFlashMessage();
 }
}

Abhängig von der aufgerufenen Action, kann man die ausgegebene Fehlermeldung überschreiben.

Thursday, October 18, 2012

TYPO3 Flow 1.2 - Kickstarter Package über Composer installieren

Heute wollte ich mal die aktuelle Developer Version von TYPO3 Flow 1.2 installieren nachdem ich ein Tweet von Robert Lemke über TYPO3 flow und Composer gelesen hatte. Da mir Composer nicht nicht sehr geläufig ist, habe ich etwas länger gebraucht um das alles zum laufen zu bekommen.

Die Installation von TYPO3 Flow über Composer ist wirklich sehr einfach und mit einem simplen Befehl erhält man (fast) alle benötigten Dateien und Abhängigkeiten

composer create-project typo3/flow-base-distribution

Wer nun erwartet, dass man wie in Flow 1.1 einfach mit dem Kickstarter loslegen und Packages erstellen kann, der wird  feststellen, dass der TYPO3 Kickstarter gar nicht in der flow-base-distribution enthalten ist.

Nachdem ich dann etwas in der composer.json Datei reingeschaut hatte, fiel mir die Zeile "require-dev" auf. Dort stand unter anderem etwas vom TYPO3 Kickstarter. Die dort erwähnten Pakete installiert man mit folgendem Befehl:

composer install --dev

Nun kann man mit dem TYPO3 Kickstarter neue Packages erstellen.

Wednesday, October 3, 2012

Mapping anderer Tabellen in Extbase

Neulich stand ich vor der eigentlich recht simplen Aufgabe aus einer Extbase Extension heraus auf die erzeugten Daten einer anderen Extbase Extension zuzugreifen. Im Web gibt es auch einige Tutorials, welche beschreiben wie man dieses macht, aber fast alle zeigen, wie man auf die Tabellen fe_users und fe_groups zugreift, welche beide schon als Domain-Model bei Extbase mitgeliefert werden. Ein funktionierendes Praxisbeispiel für den Zugriff auf eigene Datenstrukturen konnte ich leider nicht finden.

In diesem Beitrag zeige ich deshalb, wie man aus einer Extbase Extension heraus auf die Daten einer anderen Extbase Extensions zugreifen kann.

Ausgangssituation:

In einer TYPO3 Installation wurden 2 Extbase Extensions (testext1 und testext2) eingerichtet, welche jeweils eigene Repositories und Storagefolder haben. Die Typoscript Constants der Extensions sind wie folgt eingerichtet:

plugin.tx_testext1 {
  persistence {
    storagePid = 5
  }
}

plugin.tx_testext2 {
  persistence {
    storagePid = 6
  }
}

Die Datensätze von testext1 werden also im Sysordner mit der Pid 5 abgelegt und die Datensätze von testext2 im Sysordner mit der Pid 6. In testext1 gibt es ein Domainmodel mit dem Namen Testext1Table und in testext2 gibt es ein Domainmodel mit dem Namen Testext2Table.

Ziel ist es nun, aus einem Controller in testext2 auf die angelegten Daten von testext1 zuzugreifen.

Vorgehensweise:

Als erstes muss in testext2 ein Domainmodel angelegt werden, welches das Domainmodel von textext1 extended. Also wird in textext2\Classes\Domain\Model\ die Datei Testext1Table.php angelegt.

class Tx_Testext2_Domain_Model_Testext1Table extends Tx_Testext1_Domain_Model_Testext1Table {

}

Somit wurde der testext2 schon mal das Domainmodel aus testext1 bekannt gemacht. Damit wir auf die Daten aus testext1 zugreifen können, wird noch ein entsprechendes Repository benötigt. Dieses wird in  textext2\Classes\Domain\Repository\Testext1TableRepository.php definiert.

class Tx_Testext2_Domain_Repository_Testext1TableRepository extends Tx_Extbase_Persistence_Repository {

}

Nun kennt die Extension testext2 das Domainmodel und hat auch ein entsprechendes Repository. Das neue Repository wird in einem Controller von testext2 per Dependency Injection implementiert.

/**
 * testext1TableRepository
 *
 * @var Tx_Testext2_Domain_Repository_Testext1TableRepository
 */
protected $testext1TableRepository;

/**
 * injectTestext1TableRepository
 *
 * @param Tx_Testext2_Domain_Repository_Testext1TableRepository $testext1TableRepository
 * @return void
 */
public function injectTestext1TableRepositoryRepository(Tx_Testext2_Domain_Repository_Testext1TableRepository $testext1TableRepository) {
 $this->testext1TableRepository = $testext1TableRepository;
}

Wer an dieser Stelle jetzt schon erste Ergebnisse aus dem implementierten Repository erwartet, wird enttäuscht und bekommt beim Versuch Daten aus dem Repository abzufragen/anzuzeigen eine Exception vom Typ Tx_Extbase_Persistence_Storage_Exception_SqlError, welche besagt, dass eine Tabelle nicht existiert.

Die Extension testext2 weiß also anscheinend noch gar nicht, aus welcher Tabelle die Daten des neuen Repositories abgefragt werden sollen. Um dieses einzurichten, muss man im Typoscript Setup von testext2 ein Mapping einrichten.

config.tx_extbase {
 persistence {
    classes { 
        Tx_Testext2_Domain_Model_Testext1Table {
            mapping.tableName = tx_testext1_domain_model_testext1table
        }
    }
 }
}

An dieser Stelle ist es wichtig, dass das Mapping direkt auf den Tabellennamen in der Datenbank stattfindet.

Auch jetzt erhalten wir über das neue Repository in testext2 noch keine Daten, da die testext2 noch nicht weiß, in welcher StoragePid die Datensätze von testext1 liegen. Hierzu ergänzt man lediglich die Typoscript Constants von testext2 wie folgt.

plugin.tx_testext2 {
  persistence {
    storagePid = 6,5
  }
}

Die StoragePid von testext1 wurde hinter der StoragePid von testext2 angehängt. Nun kann das neue Repository in testext2 angesprochen und die erwarteten Daten erfolgreich zurückgeliefert werden.


Sunday, September 16, 2012

PhpStorm und leere remote git Repositories

Nachdem ich nun auf PhpStorm umgestiegen bin, habe ich mich damit beschäftigt die Projekte aus meinem Git Repositories zu clonen. Zum Test wollte ich auch mal ausprobieren, wie man eine lokales Git Repository aus der IDE auf ein leeres remote Repository pusht. Tja, nach einigen Rumprobieren bin ich auf ein Foren-Eintrag bei Jetbrains gestoßen, der besagte, dass dieses wohl aus der IDE nicht möglich ist.

Also schnell in die Kommande-Zeile gewechselt und das lokale Git Repository in ein leeres remote Repository gepusht.

Dazu nutzt man folgende Befehle:
git remote add origin ssh://user@example.com/GIT/project.git
git push --set-upstream origin master
Nachdem man die Befehle ausgeführt hat, kann man in der IDE dann auch zukünftig die lokalen Änderungen an das remote Repository pushen. Irgendwie merkwürdig, dass diese Funktion nicht in PhpStorm implementiert ist. Oder ich habe sie einfach nicht gefunden ;-)

Tuesday, September 4, 2012

TYPO3 - Kopfgrafik als Resource per Inline CSS sliden

In einem Projekt wollte ich es dem Redakteur erlauben, die Kopfgrafik der Webseite auszutauschen. Die Kopfgrafik sollte sliden, sprich sich auf alle Unterseiten runter erben. Generell ist das ja auch kein Problem, wenn die Kopfgrafik ein eigenständiges Bild ist, welches direkt im Quelltext ausgegeben werden kann. Man definiert in TemplaVoila einen Marker, stellt dem User im TemplaVoila Template ein Feld zur Verfügung wo er das Bild ändern kann und schon ist man fertig. Will man das ganze noch sliden, dann nutzt man die Extension kb_tv_cont_slide. So weit, so gut.

Wenn man nun aber die Kopfgrafik per CSS festgelegt hat, dann wird es schon etwas schwieriger. Man müsste praktisch per TypoScript eine CSS generieren, welches eine vom User ausgewählte Kopfgrafik einbindet. Wie dieses geht, zeige ich in diesem Artikel.

In den Seiteneigenschaften kann der Redakteur eine (oder auch mehrere) Kopfgrafiken in den Ressourcen unter "Datei" hochladen.


Um den Verweis auf diese  Datei nun in eine CSS auszugeben, muss man im TS folgendes hinzufügen.

page.cssInline {
  10 = TEXT
  10 {
    wrap (
      .header {
        background-image: url(../'uploads/media/|');  
      }
    )
    data = levelmedia:-1,slide
  }
}

Dieses TS generiert die gewünschte CSS Datei. Glücklicherweise wird dieses auch ab TYPO3 4.5 per <link>-Tag gemacht und nicht direkt per Inline CSS ausgegeben, wie man anhand der Bezeichnung meinen könnte.

Man muss beachten, dass die CSS Datei im typo3temp-Ordner erstellt wird, was dazu führt, dass man bei der URL für das Hintergrundbild wieder eine Verzeichnis-Ebene höher muss um im upload-Ordner zu landen.

Der Wert bei "data" kann auch einfach "field:media" enthalten, wenn man den Inhalt nicht sliden möchte.

Friday, August 3, 2012

TYPO3 und Chrome 21.0.1180.60 - Flash Uploader ohne Funktion

Seit heute Morgen funktioniert bei mir in TYPO3 der Flash Uploader nicht mehr richtig - zumindest nicht in meinem Chrome (Version 21.0.1180.60) auf einem 64 Bit Windows 7. Man kann auf dem "Select Files" Button klicken, aber nichts passiert. Da der Flash Uploader in allen anderen Browsern (Firefox, Sarafi, Internet Explorer und Opera) ohne Probleme funktioniert, muss das Problem also irgendwie mit dem Chrome Update zusammen hängen, welches sich heute morgen automatisch installiert hat.

Nach einigen Testen habe ich dann einen Workaround gefunden, wie man im Chrome (Version 21.0.1180.60) den Flash Uploader wieder zum "Leben" erweckt. Ein sehr kleiner Bereich des "Select Files" Buttons scheint nämlich doch noch zu funktionieren.


Der rot markierte Bereich des Buttons funktioniert noch um Dateien für den Upload zu selektieren. Der Rest des Buttons ist ohne Funktion.

Hier zeigt sich  ein Nachteil an automatischen Updates, welche sich im Hintergrund und ohne Mitteilung an den User installieren. Da Chrome dem User nicht benachrichtigt, dass ein Update installiert wurde, wäre es auch nahe liegend gewesen, als erstes TYPO3 als Ursache für den nicht funktionierenden Flash Uploader zu vermuten.

Die Frage ist nun, wo genau das Problem zu reporten ist, denn so richtig klar ist es mir noch nicht, wer (TYPO3 oder Chrome) für das Problem verantwortlich ist. Meine Vermutung geht stark in Richtung Chrome, aber vielleicht enthält der TYPO3 Flash Uploader ja auch irgendwie einen Fehler, der sich nun durch das Chrome Update bemerkbar macht.

Sunday, July 22, 2012

ExtBase - Übersetzungen per XLIFF einbinden

Seit Version 4.6 nutzt TYPO3 das XLIFF Format zum speichern von Sprachdateien. Das neue Format ist kompatibel mit Übersetzungsservern und bietet noch einige andere Vorteile wie z.B. Pluralformen oder Übersetzungsvorschläge.

Wer mit TYPO3 4.6 oder TYPO3 4.7 eine mehrsprachige Extbase Extension entwickelt, wird sich automatisch mit dem neuen Format auseinander setzen müssen, da der Extension Builder per Default nun auch .xlf-Sprachdateien generiert. Wie aber genau bindet man eine neue Sprache in seine Extension ein?

Als Basissprache wird immer von Englisch ausgegangen. Nach dem Anlegen einer neuen Extension mit dem Extension Builder hat man in der Regel eine Sprachdatei namens locallang.xlf, welche sich im Ordner Resources\Private\Language\ befindet.

Original XLIFF Sprachdatei in englischer Sprache

<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<xliff version="1.0">
 <file source-language="en" datatype="plaintext" original="messages" date="2012-07-40T13:35:40Z" product-name="loc_test">
  <header/>
  <body>

   <trans-unit id="tx_loctest_domain_model_testtable">
    <source>Testtable</source>
   </trans-unit>
   <trans-unit id="tx_loctest_domain_model_testtable.title">
    <source>Title</source>
   </trans-unit>
   <trans-unit id="tx_loctest_domain_model_testtable.text">
    <source>Description</source>
   </trans-unit>
   
  </body>
 </file>
</xliff>

Möchte man nun z.B. eine deutsche Übersetzung für die oben gezeigte Sprachdatei generieren, erstellt man im gleichen Ordner eine Kopie der bestehenden Datei und nennt diese de.locallang.xlf

Folgende Änderungen muss man in der kopierten Datei vornehmen. Zum einen muss man die Ziel-Sprache angeben (siehe unten in Zeile 3 target-language="de") und zum anderen muss man für jeden zu übersetzenden Eintrag jeweils einen neuen Eintrag für die Zielsprache anlegen (gekennzeichnet durch das target-Tag)

Übersetzte XLIFF Sprachdatei in deutscher Sprache

<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<xliff version="1.0">
 <file source-language="en" target-language="de" datatype="plaintext" original="messages" date="2012-07-40T13:35:40Z" product-name="loc_test">
  <header/>
  <body>

   <trans-unit id="tx_loctest_domain_model_testtable">
    <source>Testtable</source>
                                <target>Testtabelle</target>
   </trans-unit>
   <trans-unit id="tx_loctest_domain_model_testtable.title">
    <source>Title</source>
                                <target>Titel</target>
   </trans-unit>
   <trans-unit id="tx_loctest_domain_model_testtable.text">
    <source>Description</source>
                                <target>Beschreibung</target>
   </trans-unit>
   
  </body>
 </file>
</xliff>

Fall man bestimmte Einträge nicht übersetzt, greift ein Fallback-System und es wird jeweils der Eintrag der Originalsprache angezeigt. Dieses funktioniert nur dann, wenn man den ganzen Eintrag beginnend ab dem entsprechenden trans-unit-Tag löscht. Es reicht nicht, einfach das target-Tag wegzulassen, denn dann wird gar nichts als Übersetzung für den Eintrag angezeigt.

Friday, July 13, 2012

Infofenster bei Google Maps Karte automatisch schließen

Bei Google Maps hat man ja die Möglichkeit das Suchergebnis einer Maps-Suche als HTML Link zu kopieren und dieses aus seiner Webseite einzufügen. Das ganze sieht dann in etwas so aus.


Wie zu sehen ist, erscheint das Info-Fenster automatisch, was unter Umständen jedoch nicht gewünscht ist. Damit das Infofenster sich nicht automatisch öffnet, muss man den HTML Code von Google etwas anpassen.

HTML Code vorher

<iframe frameborder="0" height="350" marginheight="0" marginwidth="0" scrolling="no" src="https://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=Lise-Meitner-Stra%C3%9Fe+4,+Flensburg&aq=t&sll=35.101934,-95.712891&sspn=69.190487,135.263672&ie=UTF8&hq=&hnear=Lise-Meitner-Stra%C3%9Fe+4,+24941+Flensburg,+Schleswig-Holstein,+Germany&t=m&z=14&ll=54.76215,9.44458&output=embed" width="425"></iframe>
<small><a href="https://maps.google.com/maps?f=q&source=embed&hl=en&geocode=&q=Lise-Meitner-Stra%C3%9Fe+4,+Flensburg&aq=t&sll=35.101934,-95.712891&sspn=69.190487,135.263672&ie=UTF8&hq=&hnear=Lise-Meitner-Stra%C3%9Fe+4,+24941+Flensburg,+Schleswig-Holstein,+Germany&t=m&z=14&ll=54.76215,9.44458" style="color: blue; text-align: left;">View Larger Map</a></small>

HTML Code nachher

<iframe frameborder="0" height="350" marginheight="0" marginwidth="0" scrolling="no" src="https://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=Lise-Meitner-Stra%C3%9Fe+4,+Flensburg&aq=t&sll=35.101934,-95.712891&sspn=69.190487,135.263672&ie=UTF8&hq=&hnear=Lise-Meitner-Stra%C3%9Fe+4,+24941+Flensburg,+Schleswig-Holstein,+Germany&t=m&z=14&ll=54.76215,9.44458&output=embed&iwloc=near" width="425"></iframe>
<small><a href="https://maps.google.com/maps?f=q&source=embed&hl=en&geocode=&q=Lise-Meitner-Stra%C3%9Fe+4,+Flensburg&aq=t&sll=35.101934,-95.712891&sspn=69.190487,135.263672&ie=UTF8&hq=&hnear=Lise-Meitner-Stra%C3%9Fe+4,+24941+Flensburg,+Schleswig-Holstein,+Germany&t=m&z=14&ll=54.76215,9.44458" style="color: blue; text-align: left;">View Larger Map</a></small>

Durch das hinzufügen bzw. der Veränderung des Parameters &iwloc=near wird das Infofenster automatisch geschlossen. Falls der Parameter schon vorhanden ist (z.B. in Form von &iwloc=A oder &iwloc=addr), muss er auf  &iwloc=near geändert werden.

Thursday, July 12, 2012

Extbase Extension und die Übersetzung von Datensätzen im Backend

In fast jeder TYPO3 Extension müssen in irgendeiner Art und Weise Relationen abgebildet werden. Nehmen wir z.B. an, man hat eine Tabelle mit Artikeln und eine Tabelle mit Kategorien. Per n:m Relation möchte man einen Artikel mehreren Kategorien zuordnen. Das alles ist Standard und lässt sich mit dem ExtensionBuilder recht einfach konfigurieren. Mehrsprachigkeit ist im Prinzip auch kein Problem und funktioniert out-of-the-box im Frontend, im Backend kann es aber zu etwas Verwirrung für den Redakteur kommen, wenn es um die lokalisierten Datensätze (in diesem Fall die Kategorien) geht.

Der ExtensionBuilder sieht bei der Relation nicht vor, dass es sich auch um lokalisierte Datensätze handelt und zeigt jeweils den ursprünglichen Datensatz sowie die dazugehörige Übersetzung an (siehe Bild).


Damit hier nur die Datensätze der Default-Sprache angezeigt werden, muss man im TCA für das Relations-Feld eine Zeile hinzufügen.



'category' => array(
 'exclude' => 0,
 'label' => 'LLL:EXT:tx_testextension/Resources/Private/Language/locallang_db.xlf:tx_testextension_domain_model_article.category',
 'config' => array(
  'type' => 'select',
  'foreign_table' => 'tx_testextension_domain_model_category',
  'foreign_table_where' => ' AND (tx_testextension_domain_model_category.sys_language_uid = 0 OR tx_testextension_domain_model_category.l10n_parent = 0)',
  'minitems' => 0,
  'maxitems' => 1,
 ),
),

Neu hinzugekommen ist die Zeile "foreign_table_where", welche die Selektion der Datensätze auf die Default-Sprache reduziert. Nachdem man die Zeile hinzugefügt hat, werden in der Select-Box im Backend nur noch die Datensätze der Default-Sprache angezeigt.

Monday, July 9, 2012

Probleme mit TYPO3 4.7.2 und TemplaVoila

Mit TYPO3 4.7 habe ich bisher leider nur Pech gehabt. Direkt nachdem die Version 4.7 veröffentlicht wurde, habe ich diese in einem Projekt eingesetzt und musste dann feststellen, dass in CSS Styled Content ein ärgerlicher Bug enthalten war, der es unmöglich machte mit der Version zu arbeiten. Zum Glück konnte ich den Bug manuell patchen und somit erst mal mit der Version weiter arbeiten.

Nachdem nun der Bug gefixed wurde, habe ich in einem anderen Projekt auch auf TYPO3 4.7 gesetzt und stehe nun gleich vor dem nächsten großen Bug, der mir das Arbeiten mit TYPO3 erschwert.

Wenn man TYPO3 4.7.2 zusammen mit TemplaVoila 1.7.0 einsetzt, dann kann man (selbst als Admin) keine Inhalte mehr über das Seitenmodul anlegen. Es erscheint immer folgende Fehlermeldung:

Sorry, you didn't have proper permissions to perform this change.

Einen Bugfix scheint es noch nicht zu geben, der Bug ist aber schon mal im Bugtracker erfasst.

Update 11.07.2012
So, nun hat sich etwas getan und ich gehe mal davon aus, dass eine Lösung bald auf dem Weg sein wird.

Update 16.12.2012
Mittlerweile ist TemplaVoila 1.8 veröffentlicht und der Fehler wurde behoben.

Sunday, July 8, 2012

Neue Version der sf_yubikey Extension

Nachdem ich im Juni die TYPO3 Extension sf_yubikey veröffentlicht habe, habe ich bisher nur positives Feedback erhalten. Der Hersteller Yubico war so nett und hat die Extension direkt auf seiner Webseite erwähnt.

Gestern habe ich nun eine neue Version veröffentlicht, welche ein paar neue Features und Verbesserungen mitbringt. Zum einen ist es nun möglich, per Extension Settings den Authentication-Service zu aktivieren oder zu deaktivieren und zum anderen ist es nun möglich, die Extension auch für die Authentifizierung von FE-Benutzer zu nutzen. Somit kann man nun z.B. Portale mit TYPO3 Entwickeln, wo die Frontend-Benutzer sich neben dem Benutzernamen und dem Passwort zusätzlich noch mit einem OTP anmelden müssen.

Wer Kommentare oder Verbesserungsvorschläge zur Extension hat, möge diese bitte direkt im Redmine auf forge.typo3.org posten.

Thursday, July 5, 2012

FlexForms Optionen in Abhängigkeit von switchableControllerActions

Es gibt im Netz eine Vielzahl guter Tutorials wie man in einer TYPO3 Extbase Extension die Plugin-Optionen per Flexform konfigurierbar macht. Nun kann es aber vorkommen, dass man für bestimmte Konfigurations-Einstellungen unterschiedliche Einstellungsmöglichkeiten anzeigen möchte. Hierfür muss das Flexform so konfiguriert werden, dass bestimmte Einstellungsmöglichkeiten nur in Abhängigkeit von anderen Einstellungen möglich sind. Wie man dieses genau realisiert, zeige ich in diesem Artikel.

Als technische Grundlage für den Artikel dient die Extension "news" von Georg Ringer. Dort habe ich zum ersten mal bewusst diese Technik gesehen.

Als erstes erstellt man eine XML-Datei für die Flexform-Konfiguration. Als Beispiel dient die Datei /Configuration/FlexForms/flexform.xml


<T3DataStructure>
 <langDisable>1</langdisable>
 <sheets>
  <sDEF>
   <ROOT>
    <TCEforms>
     <sheetTitle>Settings</sheetTitle>
    </TCEforms>
    <type>array</type>
    <el>
     
     <switchableControllerActions>
      <TCEforms>
      <label>Mode</label>
      <onChange>reload</onChange>
      <config>
       <type>select</type>
       <items type="array">
        <numIndex index="1" type="array">
         <numIndex index="0">List-Mode</numIndex>
         <numIndex index="1">Testcontroller->list</numIndex>
        </numIndex>
        <numIndex index="2" type="array">
         <numIndex index="0">Single-Mode</numIndex>
         <numIndex index="1">Testcontroller->details</numIndex>
        </numIndex>
       </items>
      </config>
      </TCEforms>
     </switchableControllerActions>

     
     <settings.showrandom>
      <TCEforms>
       <label>Random</label>
       <config>
        <type>check</type>
       </config>
      </TCEforms>
     </settings.record>

     
     <settings.record>
      <TCEforms>
       <label>Record</label>
       <config>
        <type>group</type>
        <internal_type>db</internal_type>
        <allowed>tx_extbasetest_domain_model_test</allowed>
        <size>1</size>
        <maxitems>1</maxitems>
        <minitems>0</minitems>
        <show_thumbs>1</show_thumbs>
       </config>
      </TCEforms>
     </settings.record>

    </el>
   </ROOT>
  </sDEF>
 </sheets>
</T3DataStructure>

Wichtig sind die beiden settings-Knoten, welche später Abhängig vom ausgewählten Modus (hier der Knoten switchablecontrolleractions) angezeigt bzw. ausgeblendet werden.

Als nächstes wird das FlexForm.xml in die Extension eingebunden, sodass die Einstellungsmöglichkeiten im Plugin zur Verfügung stehen. Dafür wird in der ext_tables.php folgende Zeile hinzugefügt.


$pluginSignature = str_replace('_','',$_EXTKEY) . '_' . extbasetest;
$TCA['tt_content']['types']['list']['subtypes_addlist'][$pluginSignature] = 'pi_flexform';
t3lib_extMgm::addPiFlexFormValue($pluginSignature, 'FILE:EXT:' . $_EXTKEY . '/Configuration/FlexForms/flexform.xml');

Die FlexForm Konfiguration ist somit eigentlich schon fertig und man hat nun in den Plugin-Einstellungen zum einen eine ComboBox, wo man einstellen kann, ob man eine Listen- oder eine Detailansicht anzeigen möchte und zum anderen werden jeweils noch eine Checkbox und ein TYPO3 Datensatz Selektor angezeigt. Die letzten beiden Optionen sollen nun aber nur angezeigt werden, wenn wir in der ComboBox den Listenmodus aktiviert haben. Um dieses zu realisieren, bedienen wir uns des Hooks getFlexFormDSClass, welcher von t3lib/class.t3lib_befunc.php zur Verfügung gestellt wird.

In der ext_localconf.php muss dazu folgende Zeile hinzugefügt werden.


$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['getFlexFormDSClass'][$_EXTKEY] =
 'EXT:' . $_EXTKEY. '/Classes/Hooks/T3libBefunc.php:Tx_Extbasetest_Hooks_T3libBefunc';

Der Hook sorgt dafür, dass wir die Datenstruktur der Flexform zur Laufzeit verändern können. Wie aus der ext_localconf.php hervorgeht, wird der Hook über die Klasse Tx_Extbasetest_Hooks_T3libBefunc angesprochen. Diese muss nun angelegt werden.

class Tx_Extbasetest_Hooks_T3libBefunc {

 /**
  * Fields which are removed in details view
  *
  * @var array
  */
 public $removedFieldsInDetailsView = array(
   'sDEF' => 'record,showrandom'
  );

 /**
  * Hook function of t3lib_befunc
  * It is used to change the flexform if it is about news
  *
  * @param array &$dataStructure Flexform structure
  * @param array $conf some strange configuration
  * @param array $row row of current record
  * @param string $table table anme
  * @param string $fieldName some strange field name
  * @return void
  */
 public function getFlexFormDS_postProcessDS(&$dataStructure, $conf, $row, $table, $fieldName) {
  //t3lib_div::debug($row);
  if ($table === 'tt_content' && $row['list_type'] === 'extbasetest_extbasetest' && is_array($dataStructure)) {
   $this->updateFlexforms($dataStructure, $row);
  }
 }

 /**
  * Update flexform configuration if a action is selected
  *
  * @param array|string &$dataStructure flexform structur
  * @param array $row row of current record
  * @return void
  */
 protected function updateFlexforms(array &$dataStructure, array $row) {
  $selectedView = '';

   // get the first selected action
  $flexformSelection = t3lib_div::xml2array($row['pi_flexform']);
  if (is_array($flexformSelection) && is_array($flexformSelection['data'])) {
   $selectedView = $flexformSelection['data']['sDEF']['lDEF']['switchableControllerActions']['vDEF'];
   if (!empty($selectedView)) {
    $actionParts = t3lib_div::trimExplode(';', $selectedView, TRUE);
    $selectedView = $actionParts[0];
   }

   // new plugin element
  } elseif(t3lib_div::isFirstPartOfStr($row['uid'], 'NEW')) {
    // use List as starting view
    // @todo dynamic check, getting view from $flexformSelection
   $selectedView = 'Extbasetest->list';
  }

  if (!empty($selectedView)) {
    // modify the flexform structure depending on the first found action
   switch ($selectedView) {
    case 'Extbasetest->list':
     break;
    case 'Extbasetest->details':
     $this->deleteFromStructure($dataStructure, $this->removedFieldsInDetailsView);
     break;
   }
  }
 }

 /**
  * Remove fields from flexform structure
  *
  * @param array &$dataStructure flexform structure
  * @param array $fieldsToBeRemoved fields which need to be removed
  * @return void
  */
 private function deleteFromStructure(array &$dataStructure, array $fieldsToBeRemoved) {
  foreach ($fieldsToBeRemoved as $sheetName => $sheetFields) {
   $fieldsInSheet = t3lib_div::trimExplode(',', $sheetFields, TRUE);

   foreach ($fieldsInSheet as $fieldName) {
    unset($dataStructure['sheets'][$sheetName]['ROOT']['el']['settings.' . $fieldName]);
   }
  }
 }
}

Die Klasse habe ich fast 1:1 von der "news" Extension übernommen und angepasst. Anbei nun eine kurze Erläuterung, worauf man zu achten hat.

  • Im Array $removedFieldsInDetailsView wird festgelegt, welche Felder in der Detailansicht ausgeblendet werden sollen. Hier kann man auch mehrere Sheets angeben, falls das FlexForm über mehrere Sheets verfügt.
  • In der Methode  getFlexFormDS_postProcessDS muss man darauf achten, dass man die Variable $row['list_type'] auch mit dem Namen seines Plugins vergleicht.
  • In der Methode updateFlexforms muss man nun noch seine eigenen Controller ansprechen (in diesem Fall Extbasetest)
  • Aus der Methode deleteFromStructure geht hervor, dass die Settings im FlexForm.xml alls mit "settings." beginnen müssen, damit das Ein- bzw. Ausblenden der Optionen per Hook funktioniert.

Nachdem alle Einstellungen wie im Beispiel oben gezeigt vorgenommen wurden, müsste man in den Plugin-Optionen nun zwischen einer Listen- und einer Detail-Ansicht wechseln können und es müssten bei der Listenansicht die Zusatzoptionen angezeigt werden, welche in der Detailansicht nicht zur Verfügung stehen.

Update 18.10.2013

Das alles geht auch einfacher mit displayCond (Beispiel unten).

     <settings.record>
      <TCEforms>
       <label>Record</label>
       <displayCond>FIELD:switchableControllerActions:=:Testcontroller->list</displayCond>
       <config>
        <type>group</type>
        <internal_type>db</internal_type>
        <allowed>tx_extbasetest_domain_model_test</allowed>
        <size>1</size>
        <maxitems>1</maxitems>
        <minitems>0</minitems>
        <show_thumbs>1</show_thumbs>
       </config>
      </TCEforms>
     </settings.record>

Sunday, June 10, 2012

Zwei-Faktor-Authentifizierung für das TYPO3 Backend

Wenn ein TYPO3 Backend Login erst mal geknackt ist, hat es ein Angreifer nicht mehr allzu schwer sich Zugang zu Daten (z.B. Userdaten) oder sogar zum Webserver zu verschaffen. Glücklicherweise kann man TYPO3 auch um eigene Authentifizierungs-Dienste erweitern, sodass ein Angreifer mit gestohlenen Benutzerdaten nicht automatisch Zugriff auf das TYPO3 Backend bekommt.

So einen Authentifizierungs-Dienst habe ich mir vor ein paar Jahren mal programmiert, nachdem ein Arbeitskollege mir einen YubiKey zur Verfügung gestellt hat. Der YubiKey ist ist Token, welchen man an einen USB Port seines Rechners anschließt und welcher dann per Knopfdruck ein OTP (one time password) generiert. Das OTP kann dann von einen Cloud Authentifizierungs-Cluster des Herstellers oder auch durch einen eigenen Authentifizierungs-Server verifiziert werden. Ganz simpel und vor allem Plattform-Unabhängig, denn der USB Token funktioniert unter jedem Betriebssystem gleich.

Die Veröffentlichung meiner Extension scheiterte seinerzeit an der Erstellung der Dokumentation, da ich keine Lust hatte mir meinen Windows Rechner mit OpenOffice vollzumüllen. Nun, wo ich komplett auf Linux umgestiegen bin, habe ich wohl keine Entschuldigung mehr und habe mich nun endlich mal hingesetzt und eine Dokumentation erstellt und die Extension sf_yubikey heute veröffentlicht.

Was macht die Extension sf_yubikey?

Die Extension erweitert das TYPO3 Backend Login um ein zusätzliches Feld, wo der User beim Backend Login neben seinen Userdaten auch ein OTP eingeben muss. Da der YubiKey USB Token auf Knopfdruck das OTP "eingibt", ist der neue Login-Prozess nicht sonderlich komplizierter geworden.


Der Backend Loginprozess erhält somit eine Zwei-Faktor-Authentifizierung, wo ein Angreifer nun neben den Benutzernamen und das dazugehörige Passwort auch das YubiKey USB Token das User braucht, um sich erfolgreich am TYPO3 Backend anzumelden.

Die Zwei-Faktor-Authentifizierung kann im Backend von TYPO3 pro User festgelegt werden.

Voraussetzungen für den Gebrauch von sf_yubikey

Die Extension funktioniert leider nicht out-of-the-box. Zum einen muss der Webserver über CURL und PEAR verfügen. Das sollte heutzutage aber eigentlich zum Standard gehörten. Zum anderen muss man die PHP Klasse Auth_Yubico installiert haben, welche aber dank PEAR recht einfach zu installieren ist. Die PHP Klasse erhält man hier.

Neben den Server-Voraussetzungen braucht man natürlich auch einen YubiKey sowie einen Yubico API Key, welchen man hier kostenlos erstellen lassen kann.

Feedback erwünscht

Ich habe die Extension wie erwähnt heute veröffentlicht und bin gespannt, ob ich auch Feedback erhalten werden und ob sie überhaupt jemand nutzt. Im TYPO3 Forge habe ich für Feedback und Bugreports ein entsprechendes Projekt eingerichtet.

Sichere Userpasswörter für TYPO3

In den letzten Tagen häufen sich in der Presse Berichte über Datenlecks bei Anbietern großer Portale wie z.B. LinkedIn, Last.fm oder eHarmony. Besonders erschreckend finde ich die Tatsache, dass solch große Portale die User-Passwörter per MD5 "verschlüsseln". Das ein MD5-Hash alleine alles andere als sicher ist sollte mittlerweile jeder Entwickler wissen, der sich schon mal mit dem Thema Sicherheit beschäftigt hat.

Ab TYPO3 4.6 sind deshalb auch die Extensions rsaauth und saltedpasswords per default installiert, damit ein Abgreifen der Userpasswörter nicht gleich zu dem Erfolg führt, dass der Angreifer sich recht simpel die Userpasswörter per Rainbow-Tables zugänglich machen kann. Ohnehin empfehle ich an dieser Stelle jedem TYPO3 Admin, die beiden erwähnten Extensions zu installieren und sowohl für die Backend als auch für die Frontend-Anmeldung zu aktivieren. Somit wären die Userpasswörter dann schon mal deutlich sicherer gegen Angriffe als vorher.

Die Extension saltedpasswords sorgt dafür, dass die Userpasswörter nicht mehr als MD5-Hash, sondern als gesalzener Hash in der TYPO3 Datenbank abgespeichert werden. Durch das "salzen" der Passwort-Hashes ergibt ein und dasselbe Passwort nun nicht mehr den gleichen Hash-Wert, wie es z.B. für ungesalzene Passwörter der Fall ist.

Tuesday, June 5, 2012

Zugriff auf eigene Extension Konfiguration aus Extbase Scheduler Task

In einem Projekt wollte ich aus einem Extbase Scheduler Task heraus E-Mails versenden. Der Body dieser E-Mails sollte über Fluid Templates gerendert werden. In diesem Zusammenhang hatte ich auch schon einen entsprechenden Artikel im Wiki zu Extbase gefunden.

http://forge.typo3.org/projects/typo3v4-mvc/wiki/How_to_use_the_Fluid_Standalone_view_to_render_template_based_emails

Bei Scheduler Tasks aus Extbase muss man nun jedoch den Kontext bedenken, unter dem der Scheduler Task läuft. In diesem Fall läuft der eingerichtete Scheduler Task im Kontext der Extbase Extension, was zur Folge hat, dass der ConfigurationManager mit folgendem Code das Typoscript Setup der Extension Extbase zurückliefert.

$extbaseFrameworkConfiguration = $this->configurationManager->getConfiguration(Tx_Extbase_Configuration_ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
Um nun im Scheduler Task auf die Konfiguration seiner eigenen Extension zugreifen zu können, muss man wir folgt vorgehen.

Man holt sich die komplette Typoscript Konfiguration der TYPO3 Installation.

$extbaseFrameworkConfiguration = $this->configurationManager->getConfiguration(Tx_Extbase_Configuration_ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
Danach kann man aus dem Typoscript (welches ja nur ein großes Array ist) die Konfiguration seiner Extension auslesen.

$config = $extbaseFrameworkConfiguration['plugin.']['tx_helloworld.']['irgendeine_einstellung']
Wichtig ist, dass man die Punkte jeweils nach "plugin" und dem Extensionnamen beachtet. Im Zweifel kann man sich auch einfach das ganze Typoscript Array ausgeben und schauen, wo genau im Array sich die gewünschten Einstellungen befinden.

Sunday, June 3, 2012

Zugriff auf Frontend User Tabelle aus Extbase

Extbase bindet die TYPO3 fe_users Tabelle schon automatisch mit ein und stellt entsprechende Objekte bereit. Man muss jedoch ein paar Dinge beachten, damit man auch auf diese Daten zugreifen kann.

Als erstes muss man in seiner Klasse das Users-Repository per Dependency Injection zu Verfügung stellen.

/**
 * @var Tx_Extbase_Domain_Repository_FrontendUserRepository
 */
protected $userRepository;    

/**
 * Inject the user repository
 *
 * @param Tx_Extbase_Domain_Repository_FrontendUserRepository $userRepository
 * @return void
 */
public function injectFrontendUserRepository(Tx_Extbase_Domain_Repository_FrontendUserRepository $userRepository) {
    $this->userRepository = $userRepository;
}  

Somit hat man dann Zugriff auf das Repository. Kennt man nun z.B. die UID des FE Users, kann man wie folgt die Userdaten über das Repository auslesen.

$user = $this->userRepository->findByUid($uid);

Wer sich nun wundert, warum die Variable $user leer ist, obwohl sie einen User enthalten müsste, der muss folgendes beachten.

Extbase stellt die Daten zwar per default zur Verfügung, geht aber davon aus, dass alle FE User auch einen Record Type zugewiesen haben.

config.tx_extbase.persistence.classes {
 Tx_Extbase_Domain_Model_FrontendUser {
  mapping.recordType = Tx_Extbase_Domain_Model_FrontendUser
 }
}

Beim installieren von Extbase wird sowohl die Tabelle fe_users als auch fe_groups um genau dieses Feld (tx_extbase_type) erweitert. Existierende als auch neue User haben natürlich keinen recordType zugewiesen (siehe Bild), sodass das UserRepository demzufolge auch nichts zurückliefern kann.







Nun gibt es zwei Möglichkeiten mit diesem "Problem" umzugehen. Entweder man setzt in allen FE Usern den Record Type, oder man hebt das von Extbase vorgegebene mapping wie folgt auf.

config.tx_extbase.persistence.classes {
 Tx_Extbase_Domain_Model_FrontendUser {
  mapping.recordType >
 }
}

Ich würde aber ganz klar dazu raten, die User entsprechend richtig anzulegen (sprich das Feld Record Type gleich bei Anlage des/der FE User zu setzen). Damit man das nicht manuell machen muss, kann man die Felder schon vorbelegen. Dazu einfach folgendes TS ins PageTS der Root-Seite einfügen.

TCAdefaults.fe_users.tx_extbase_type = Tx_Extbase_Domain_Model_FrontendUser
TCAdefaults.fe_groups.tx_extbase_type = Tx_Extbase_Domain_Model_FrontendUserGroup

So kann man übrigens auch beliebige Felder bei Neuanlage von Datensätzen vorbelegen.

Saturday, June 2, 2012

TYPO3 Scheduler Task mit ExtBase 4.7

Seit Extbase 4.7 kann man TYPO3 Scheduler Tasks direkt als Command Controller registrieren. Anbei eine kurze Step-By-Step Anleitung, wie das funktioniert.

Als erstes muss man einen CommandController erstellen.

Classes/Command/HelloWorldCommandController.php
/**
 * Description of HelloWorldCommandController
 */
class Tx_HelloWorld_Command_HelloWorldCommandController extends Tx_Extbase_MVC_Controller_CommandController {
    
    /**
     * Simple Hello World Command
     * 
     * @param string $name 
     * @return void
     */
    public function HelloWorldCommand($name) {
        echo 'Hello ' . $name;
    }
}

Danach wird der CommandController registriert.

ext_localconf.php
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['extbase']['commandControllers'][] = 'Tx_HelloWorld_Command_HelloWorldCommandController';

Als nächstes erstellt man über den TYPO3 Scheduler einen neuen Task und wählt als Klasse den "Extbase CommandController Task (extbase)"

Beim CommandController wählt man den HelloWorld CommandController aus und speichert den Task ab. Um die Optionen für den Task einzustellen, muss man den Task wieder öffnen und dann die entsprechenden Optionen ausfüllen.

Update - 02.06.2012 19:24
Leider gibt es noch einen Bug für denn Fall dass man einen Task ohne Parameter anlegt:
http://forge.typo3.org/issues/37563

Ein Workaround steht im Issue beschrieben.