Sunday, July 21, 2013

TYPO3 ExtBase backend module with progressbar using AJAX


Assume you have created a TYPO3 ExtBase Extension and must enable the user to import data (e.g. CSV file) to the tables of your TYPO3 ExtBase extension. With ExtBase, you can easily create a TYPO3 backend module so the user can upload the CSV file and start the data import.

Now assume the user uploads a very big set of data (several 100 MBs) to be imported and starts the data import process. Well, the user will see the hourglass a long, long time and if the user is impacient, he will surely assume that something went wrong and will click somewhere else in the TYPO3 backend and within that cancel the data import.

So would'nt it be nice if you could show the user a progressbar or an updating informal text, that the import is still running?

When you look at the concepts of some extensions with backend modules in TER, you will often see, that PHP methods like flush() or ob_flush() is used to update data in a backend module for long taking processes.

With ExtBase you can't use flush(), since the output for the backend module is rendered with fluid and the output will first display, when the action that displays the content has finished.

In this article I will show how to use AJAX in a TYPO3 ExtBase backend module to show an updating JQuery UI progressbar.



Please note, that the example code located on Github has been created with TYPO3 6.1 and uses namespaces, so it only runs with TYPO3 > 6.x. But - the shown technique can also be adapted to TYPO3 4.5.

First of all, I've created a TYPO3 extension with the extension builder. The extension has a backend module called "mod1". This module uses the controller "Example" and the action "index".

/**
 * Registers a Backend Module
 */
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
 'derhansen.' . $_EXTKEY,
 'web', // Make module a submodule of 'web'
 'mod1', // Submodule key
 '', // Position
 array(
  'Example' => 'index',
  
 ),
 array(
  'access' => 'user,group',
  'icon'   => 'EXT:' . $_EXTKEY . '/ext_icon.gif',
  'labels' => 'LLL:EXT:' . $_EXTKEY . '/Resources/Private/Language/locallang_mod1.xlf',
 )
);

The extension builder automatically creates Typoscript files with constants and setup settings for the backend module templates. Those settings look like:


templateRootPath = EXT:extbase_bemodule_ajax/Resources/Private/Backend/Templates/

As you may notice, the templates, layouts and partials are located in a subfolder named "Backend". Using this, you always need to include the extension's Typoscript in your sites Typoscript template. If you remove the folder "Backend" and just place your templates, layouts and partials in the "normal" folder-structure of an ExtBase extension, there is no need to include the Typoscript of your extension. I have done this to keep things simple.

Next I've created the layout and the template for the backend module. Notice, that I have created a viewhelper named IncludeJQueryViewHelper, which automatically includes JQuery and JQueryUi with the nesceccary CSS files to the backend module.


{namespace h=derhansen\ExtbaseBemoduleAjax\ViewHelpers\Be}

<h:IncludeJQuery jquery="1" jqueryui="1" />

<f:be.container loadExtJs="0" loadPrototype="0" loadScriptaculous="0">
...
</f:be.container>

Also notice, that the f:be.container viewhelper is configured to disable extJs, prototype and scriptaculous.

In the template for the backend module I have placed some text, a JQuery UI Progressbar and a JQuery UI Button. I also created some Javascript code, which starts starts the progressbar and also retrieves the status of the progressbar by AJAX.

When the start-button is clicked, an AJAX request (startLongProcess) is called which starts a long taking process in our ExampleController. The AJAX request is asynchronous, so the process is not blocking the TYPO3 backend. Right after the long taking process is started, another AJAX request (checkLongProcess) is called which retrieves the status of the long taking process and updates the progressbar.

To keep things simple, the long taking action in the ExampleController just executes a for-loop 20 times and does a sleep(1) on each iteration. Below follows both the startLongProcess action, which starts the long process and the checkLongProcess action, which is used to update the progressbar.


/**
 * Example for a long process (just loops 20 seconds). Returns TRUE if process is finished
 *
 * @return bool TRUE if process is finished
 */
public function startLongProcessAction() {
 for ( $i = 1; $i <= 20; $i++) {
  /* Increase counter stored in session variable */
  session_start();
  $_SESSION['progval'] = $i * 5;
  session_write_close ();
  sleep(1);
 }

 /* Reset the counter in session variable to 0 */
 session_start();
 $_SESSION['progval'] = 0;
 session_write_close ();
 return json_encode(TRUE);
}

/**
 * Checks the status of the long process
 *
 * @return int Status of process in percent
 */
public function checkLongProcessAction() {
 session_start();
 if (!isset($_SESSION['progval'])) {
  $_SESSION['progval'] = 0;
 }
 session_write_close ();
 return json_encode($_SESSION['progval']);
}

Notice, that this example uses PHP session variables to store the actual state of the long taking process. You can also use a database or other techniques to store the actual state.

When working with PHP session variables inside a method, you have to keep in mind, that the session variable is stored, after the method is finished to prevent concurrent writes to the session. To store the session variable while the for-loop is running, you have to use session_write_close() to actually store the session variable.

Finally I updated ext_tables.php so the 2 new actions can be called by the module.

Conclusion
Creating a ExtBase backend module that uses AJAX to dynamically update content on the modules page is quite simple. You can use the same techniques for backend modules as for TYPO3 frontend plugins.

The complete code for the example shown here is available on Github

No comments:

Post a Comment