From: <gem...@li...> - 2013-04-11 09:51:30
|
Revision: 1221 http://sourceforge.net/p/gemstracker/code/1221 Author: mennodekker Date: 2013-04-11 09:51:25 +0000 (Thu, 11 Apr 2013) Log Message: ----------- First working version of batch export: no longer timeouts for big excel exports. TODO: spss export + error checking etc. Modified Paths: -------------- trunk/library/classes/Gems/Default/ExportAction.php trunk/library/classes/Gems/Export/Excel.php trunk/library/classes/Gems/Export/ExportAbstract.php trunk/library/classes/Gems/Menu/MenuAbstract.php trunk/library/classes/Gems/Task/TaskRunnerBatch.php Added Paths: ----------- trunk/library/classes/Gems/Export/ExportBatchInterface.php trunk/library/classes/Gems/Task/Export/ trunk/library/classes/Gems/Task/Export/Finalize.php trunk/library/classes/Gems/Task/Export/Step.php Modified: trunk/library/classes/Gems/Default/ExportAction.php =================================================================== --- trunk/library/classes/Gems/Default/ExportAction.php 2013-04-10 10:08:36 UTC (rev 1220) +++ trunk/library/classes/Gems/Default/ExportAction.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -61,6 +61,18 @@ * @var Zend_Locale */ public $locale; + + /** + * + * @var Gems_Util_RequestCache + */ + public $requestCache; + + /** + * + * @var Zend_Session_Namespace + */ + protected $_session; public function __construct(Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response, array $invokeArgs = array()) { @@ -69,6 +81,8 @@ //Add this controller to the export so it can render view when needed $this->export->controller = $this; + + $this->_session = GemsEscort::getInstance()->session; } @@ -118,7 +132,40 @@ // Gems_Tracker::$verbose = true; return $filter; } + + /** + * Modify request to hold a cache + * + * @return array + */ + public function getCachedRequestData() + { + if (! $this->requestCache) { + $this->requestCache = $this->util->getRequestCache($this->getRequest()->getActionName(), false); + $this->requestCache->setMenu($this->menu); + $this->requestCache->setRequest($this->getRequest()); + // Button text should not be stored. + $this->requestCache->removeParams('export', 'action'); + } + + $data = $this->requestCache->getProgramParams(); + + // Clean up empty values + // + // We do this here because empty values can be valid filters that overrule the default + foreach ($data as $key => $value) { + if ((is_array($value) && empty($value)) || (is_string($value) && 0 === strlen($value))) { + unset($data[$key]); + } + } + + // Do not set, we only want to have the data as a default + //$this->getRequest()->setParams($data); + + return $data; + } + /** * Retrieve the form * @@ -234,36 +281,117 @@ */ public function handleExport($data) { - $language = $this->locale->getLanguage(); - $survey = $this->loader->getTracker()->getSurvey($data['sid']); - $filter = $this->_getFilter($data); - $answers = $survey->getRawTokenAnswerRows($filter); - $answerModel = $survey->getAnswerModel($language); - - //Now add the organization id => name mapping - $answerModel->set('organizationid', 'multiOptions', $this->loader->getCurrentUser()->getAllowedOrganizations()); - - if (count($answers) === 0) { - $answers[0] = array('' => sprintf($this->_('No %s found.'), $this->getTopic(0))); - } - if (isset($data['type'])) { //Do the logging $message = Zend_Json::encode($data); Gems_AccessLog::getLog()->log('export', $this->getRequest(), $message, null, true); - + //And delegate the export to the right class $exportClass = $this->export->getExport($data['type']); - $exportClass->handleExport($data, $survey, $answers, $answerModel, $language); + + if ($exportClass instanceof Gems_Export_ExportBatchInterface) { + // Clear possible existing batch + $batch = $this->loader->getTaskRunnerBatch('export_data'); + $batch->reset(); + // Have a batch handle the export + $this->_session->exportParams = $data; + $this->_reroute(array('action'=>'handle-export')); + + } else { + // If not possible / available, handle direct + $language = $this->locale->getLanguage(); + $survey = $this->loader->getTracker()->getSurvey($data['sid']); + $filter = $this->_getFilter($data); + $answers = $survey->getRawTokenAnswerRows($filter); + $answerModel = $survey->getAnswerModel($language); + + //Now add the organization id => name mapping + $answerModel->set('organizationid', 'multiOptions', $this->loader->getCurrentUser()->getAllowedOrganizations()); + + if (count($answers) === 0) { + $answers[0] = array('' => sprintf($this->_('No %s found.'), $this->getTopic(0))); + } + + $exportClass->handleExport($data, $survey, $answers, $answerModel, $language); + } } } + + public function handleExportAction() + { + $this->initHtml(); + $batch = $this->loader->getTaskRunnerBatch('export_data'); + $batch->minimalStepDurationMs = 2000; + if (!$batch->count()) { + $data = $this->_session->exportParams; + $filter = $this->_getFilter($data); + $language = $this->locale->getLanguage(); + + $exportClass = $this->export->getExport($data['type']); + $exportClass->handleExportBatch($batch, $filter, $language, $data); + $batch->autoStart = true; + } + + $title = $this->_('Export'); + // Not using batchrunner since we need something else + if ($batch->run($this->getRequest())) { + exit; + } else { + $controller = $this; + $controller->html->h3($title); + if ($batch->isFinished()) { + $messages = $batch->getMessages(); + if (array_key_exists('file', $messages)) { + $files = $messages['file']; + unset($messages['file']); + unset($messages['export-progress']); + } + + $controller->addMessage($messages); + + if (!empty($files)) { + // Forward to download action + $this->_session->exportFile = $files; + $this->_reroute(array('action'=>'download')); + } + } else { + if ($batch->count()) { + $controller->html->append($batch->getPanel($controller->view, $batch->getProgressPercentage() . '%')); + } else { + $controller->html->pInfo($controller->_('Nothing to do.')); + } + $controller->html->pInfo(MUtil_Html_AElement::a(MUtil_Html_UrlArrayAttribute::rerouteUrl($this->getRequest(), array('action'=>'index')), array('class'=>'actionlink'), $this->_('Back'))); + } + } + } + + public function downloadAction() + { + $this->view->layout()->disableLayout(); + $this->_helper->viewRenderer->setNoRender(true); + $files = $this->_session->exportFile; + foreach($files['headers'] as $header) { + header($header); + } + while (ob_get_level()) { + ob_end_clean(); + } + readfile(GEMS_ROOT_DIR . '/var/tmp/' . $files['file']); + // Now clean up the file + unlink(GEMS_ROOT_DIR . '/var/tmp/' . $files['file']); + exit; + } + + public function indexAction() { $this->initHtml(); + + $data = $this->getCachedRequestData(); //Hacked around to get a self-refreshing form, quite hardcoded but fine for now - if ($form = $this->processForm()) { + if ($form = $this->processForm(null, $data)) { if (!$this->getRequest()->isPost() || $form->getElement('export')->isChecked()) { if ($form->getElement('export')->isChecked()) { $data = $form->getValues(); @@ -309,7 +437,7 @@ $data = $request->getPost() + (array) $data; } else { //Set the defaults for the form here - $data = $this->export->getDefaults(); + $data = $data + $this->export->getDefaults(); } $form = $this->getForm($data); Modified: trunk/library/classes/Gems/Export/Excel.php =================================================================== --- trunk/library/classes/Gems/Export/Excel.php 2013-04-10 10:08:36 UTC (rev 1220) +++ trunk/library/classes/Gems/Export/Excel.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -45,7 +45,7 @@ * @license New BSD License * @since Class available since version 1.5 */ -class Gems_Export_Excel extends Gems_Export_ExportAbstract +class Gems_Export_Excel extends Gems_Export_ExportAbstract implements Gems_Export_ExportBatchInterface { /** * Return an array of Form Elements for this specific export @@ -135,4 +135,164 @@ $this->view->setScriptPath(GEMS_LIBRARY_DIR . '/views/scripts'); $this->export->controller->render('excel',null,true); } + + /** + * This method handles the export with the given options + * + * The method takes care of rendering the right script by using $this->export->controller to + * access the controller object. + * + * @param Gems_Task_TaskRunnerBatch $batch The batch to start + * @param array $filter The filter to use + * @param string $language The language used / to use for the export + * @param array $data The formdata + */ + public function handleExportBatch($batch, $filter, $language, $data) { + $survey = $this->loader->getTracker()->getSurvey($data['sid']); + $answerCount = $survey->getRawTokenAnswerRowsCount($filter); + $answers = $survey->getRawTokenAnswerRows(array('limit'=>1,'offset'=>1) + $filter); // Limit to one response + $filename = $survey->getName() . '.xls'; + + $files['file']= 'export-' . md5(time() . rand()); + $files['headers'][] = "Content-Type: application/download"; + $files['headers'][] = "Content-Disposition: attachment; filename=\"" . $filename . "\""; + $files['headers'][] = "Expires: Mon, 26 Jul 1997 05:00:00 GMT"; // Date in the past + $files['headers'][] = "Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"; + $files['headers'][] = "Cache-Control: must-revalidate, post-check=0, pre-check=0"; + $files['headers'][] = "Pragma: cache"; // HTTP/1.0 + + $batch->setMessage('file', $files); + $batch->setMessage('export-progress', $this->_('Initializing export')); + + $f = fopen(GEMS_ROOT_DIR . '/var/tmp/' . $files['file'], 'w'); + fwrite($f, '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"> +<head> +<meta http-equiv=Content-Type content="text/html; charset=UTF-8"> +<meta name=ProgId content=Excel.Sheet> +<meta name=Generator content="Microsoft Excel 11"> +<style> + /* Default styles for tables */ + + table { + border-collapse: collapse; + border: .5pt solid #000000; + } + + tr th { + font-weight: bold; + padding: 3px 8px; + border: .5pt solid #000000; + background: #c0c0c0 + } + tr td { + padding: 3px 8px; + border: .5pt solid #000000; + } + td {mso-number-format:"\@";} +</style> +</head> +<body>'); + + if (isset($data[$this->getName()])) { + $options = $data[$this->getName()]; + if (isset($options['format'])) { + $options = $options['format']; + } + } else { + $options = array(); + } + + $headers = array(); + if (in_array('formatVariable', $options)) { + $questions = $survey->getQuestionList($language); + //@@TODO This breaks date formatting, think of a way to fix this, check out the spss exports for + //a more direct export, also check UTF-8 differences between view / direct output + foreach ($answers[0] as $key => $value) { + if (isset($questions[$key])) { + $headers[$key] = $questions[$key]; + } else { + $headers[$key] = $key; + } + } + } else { + $headers = array_keys($answers[0]); + } + + //Only for the first row: output headers + $output = "<table>\r\n"; + $output .= "\t<thead>\r\n"; + $output .= "\t\t<tr>\r\n"; + foreach ($headers as $name => $value) { + $output .= "\t\t\t<th>$value</th>\r\n"; + } + $output .= "\t\t</tr>\r\n"; + $output .= "\t</thead>\r\n"; + $output .= "\t<tbody>\r\n"; + + fwrite($f, $output); + + fclose($f); + // Add as many steps as needed + $current = 0; + $step = 500; + do { + $filter['limit'] = $step; + $filter['offset'] = $current; + $batch->addTask('Export_Step', $data['type'], $filter, $language, $data); + $current = $current + $step; + } while ($current < $answerCount); + + $batch->addTask('Export_Finalize', $data['type']); + + return; + } + + public function handleExportBatchStep($batch, $data, $filter, $language) + { + $files = $batch->getMessage('file', array()); + $survey = $this->loader->getTracker()->getSurvey($data['sid']); + $answers = $survey->getRawTokenAnswerRows($filter); + $answerModel = $survey->getAnswerModel($language); + //Now add the organization id => name mapping + $answerModel->set('organizationid', 'multiOptions', $this->loader->getCurrentUser()->getAllowedOrganizations()); + $batch->setMessage('export-progress', sprintf($this->_('Exporting records %s and up'), $filter['offset'])); + + if (isset($data[$this->getName()])) { + $options = $data[$this->getName()]; + if (isset($options['format'])) { + $options = $options['format']; + } + } else { + $options = array(); + } + + if (in_array('formatAnswer', $options)) { + $answers = new Gems_FormattedData($answers, $answerModel); + } + + $f = fopen(GEMS_ROOT_DIR . '/var/tmp/' . $files['file'], 'a'); + foreach($answers as $answer) + { + $output = "\t\t<tr>\r\n"; + foreach ($answer as $key => $value) { + $output .= "\t\t\t<td>$value</td>\r\n"; + } + $output .= "\t\t</tr>\r\n"; + fwrite($f,$output); + } + fclose($f); + } + + public function handleExportBatchFinalize($batch, $data) + { + $files = $batch->getMessage('file', array()); + $batch->setMessage('export-progress', $this->_('Export finished')); + $f = fopen(GEMS_ROOT_DIR . '/var/tmp/' . $files['file'], 'a'); + fwrite($f, ' </tbody> + </table> + </body> +</html>'); + fclose($f); + } + } \ No newline at end of file Modified: trunk/library/classes/Gems/Export/ExportAbstract.php =================================================================== --- trunk/library/classes/Gems/Export/ExportAbstract.php 2013-04-10 10:08:36 UTC (rev 1220) +++ trunk/library/classes/Gems/Export/ExportAbstract.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -55,6 +55,12 @@ * @var Gems_Export */ public $export; + + /** + * + * @var Gems_Loader + */ + public $loader; /** * @var Zend_Translate Copied: trunk/library/classes/Gems/Export/ExportBatchInterface.php (from rev 1210, trunk/library/classes/Gems/Export/ExportInterface.php) =================================================================== --- trunk/library/classes/Gems/Export/ExportBatchInterface.php (rev 0) +++ trunk/library/classes/Gems/Export/ExportBatchInterface.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -0,0 +1,80 @@ +<?php +/** + * Copyright (c) 2011, Erasmus MC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Erasmus MC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @package Gems + * @subpackage Export + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @version $Id$ + */ + +/** + * The export interface + * + * Exporting survey-data can be done for various sorts of output formats, this interface + * describes the methods needed to implement an output format + * + * @package Gems + * @subpackage Export + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @since Class available since version 1.6.1 + */ +interface Gems_Export_ExportBatchInterface extends Gems_Export_ExportInterface +{ + /** + * This method handles setting up all needed stept for the batch export + * + * Normally this will initialize the file to download and set up as much + * steps as needed and the final job to close the file. + * + * @param Gems_Task_TaskRunnerBatch $batch The batch to start + * @param array $filter The filter to use + * @param string $language The language used / to use for the export + * @param array $data The formdata + */ + public function handleExportBatch($batch, $filter, $language, $data); + + /** + * Executes a step in exporting the data, this should be as large as possible + * without hitting max request time limits + * + * @param Gems_Task_TaskRunnerBatch $batch The batch to start + * @param array $data The formdata + * @param array $filter The filter to use + * @param string $language The language used / to use for the export + */ + public function handleExportBatchStep($batch, $data, $filter, $language); + + /** + * Final step in batch export, perform cleanup / finalize the file + * + * @param Gems_Task_TaskRunnerBatch $batch + * @param array $data + */ + public function handleExportBatchFinalize($batch, $data); +} \ No newline at end of file Modified: trunk/library/classes/Gems/Menu/MenuAbstract.php =================================================================== --- trunk/library/classes/Gems/Menu/MenuAbstract.php 2013-04-10 10:08:36 UTC (rev 1220) +++ trunk/library/classes/Gems/Menu/MenuAbstract.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -275,6 +275,8 @@ // EXPORT $export->addPage($this->_('Survey answers'), 'pr.export', 'export', 'index'); + $export->addButtonOnly('', 'pr.export', 'export', 'handle-export'); + $export->addButtonOnly('', 'pr.export', 'export', 'download'); // EXPORT TO HTML $export->addPage($this->_('Respondent archives'), 'pr.export-html', 'respondent-export', 'index'); Added: trunk/library/classes/Gems/Task/Export/Finalize.php =================================================================== --- trunk/library/classes/Gems/Task/Export/Finalize.php (rev 0) +++ trunk/library/classes/Gems/Task/Export/Finalize.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -0,0 +1,54 @@ +<?php +/** + * Copyright (c) 2011, Erasmus MC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Erasmus MC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @package Gems + * @subpackage Task_Export + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @version $Id$ + */ + +/** + * Perform one step of results export + * + * @package Gems + * @subpackage Task_Export + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @since Class available since version 1.6.1 + */ +class Gems_Task_Export_Finalize extends Gems_Task_TaskAbstract { + + public function execute($exportType = null, $filter = array(), $language = null, $data = array()) + { + // $filter now has a limit and offset + $export = $this->loader->getExport()->getExport($exportType); + $export->handleExportBatchFinalize($this->_batch, $data); + + return; + } +} \ No newline at end of file Added: trunk/library/classes/Gems/Task/Export/Step.php =================================================================== --- trunk/library/classes/Gems/Task/Export/Step.php (rev 0) +++ trunk/library/classes/Gems/Task/Export/Step.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -0,0 +1,54 @@ +<?php +/** + * Copyright (c) 2011, Erasmus MC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Erasmus MC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @package Gems + * @subpackage Task_Export + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @version $Id$ + */ + +/** + * Perform one step of results export + * + * @package Gems + * @subpackage Task_Export + * @copyright Copyright (c) 2011 Erasmus MC + * @license New BSD License + * @since Class available since version 1.6.1 + */ +class Gems_Task_Export_Step extends Gems_Task_TaskAbstract { + + public function execute($exportType = null, $filter = array(), $language = null, $data = array()) + { + // $filter now has a limit and offset + $export = $this->loader->getExport()->getExport($exportType); + $export->handleExportBatchStep($this->_batch, $data, $filter, $language); + + return; + } +} \ No newline at end of file Modified: trunk/library/classes/Gems/Task/TaskRunnerBatch.php =================================================================== --- trunk/library/classes/Gems/Task/TaskRunnerBatch.php 2013-04-10 10:08:36 UTC (rev 1220) +++ trunk/library/classes/Gems/Task/TaskRunnerBatch.php 2013-04-11 09:51:25 UTC (rev 1221) @@ -54,6 +54,16 @@ */ public $loader; + /** + * The minimal time used between send progress reports. + * + * This enables quicker processing as multiple steps can be taken in a single + * run(), without the run taking too long to answer. + * + * Set to 0 to report back on each step. + * + * @var int + */ public $minimalStepDurationMs = 1000; public function __construct($id = null) @@ -99,6 +109,23 @@ return $this; } + + /** + * Get a message from the message stack with a specific id. + * + * @param scalar $id + * @param string $default A default message + * @return string + */ + public function getMessage($id, $default = null) + { + $messages = parent::getMessages(); + if (array_key_exists($id, $messages)) { + return $messages[$id]; + } else { + return $default; + } + } public function runTask($task, $params) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |