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>

2 comments:

  1. Genau dafür gibts doch das Feld "displayCond" im Flexform?

    ReplyDelete
  2. Stimmt, danke für den Hinweis.

    Ich schätze mal, dass der hier gezeigte Ansatz bei der "news" Extension gewählt wurde, weil das Flexform dort entsprechend groß ist und die Abhängigkeiten innerhalb der wählbaren Optionen zu komplex für die "displayCond" sind.

    ReplyDelete