diff --git a/workflow/engine/classes/class.configuration.php b/workflow/engine/classes/class.configuration.php
index 0d4b7bdf1..1bec05267 100755
--- a/workflow/engine/classes/class.configuration.php
+++ b/workflow/engine/classes/class.configuration.php
@@ -2,10 +2,10 @@
/**
* class.configuration.php
* @package workflow.engine.ProcessMaker
- *
+ *
* ProcessMaker Open Source Edition
* Copyright (C) 2004 - 2011 Colosa Inc.
- *
+ *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
@@ -15,13 +15,13 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
- *
- * For more information, contact Colosa Inc, 2566 Le Jeune Rd.,
+ *
+ * For more information, contact Colosa Inc, 2566 Le Jeune Rd.,
* Coral Gables, FL, 33134, USA, or email info@colosa.com.
- *
+ *
*/
//
// It works with the table CONFIGURATION in a WF dataBase
@@ -47,29 +47,29 @@ require_once 'classes/model/Configuration.php';
* @copyright 2007 COLOSA
* @version Release: @package_version@
* @package workflow.engine.ProcessMaker
- */
+ */
class Configurations // extends Configuration
{
var $aConfig = array();
private $Configuration = null;
private $UserConfig = null;
-
+
/**
* Set Configurations
* @return void
- */
+ */
function Configurations()
{
$this->Configuration = new Configuration();
}
-
+
/**
* arrayClone
*
* @param array &$object Source array
* @param array &$cloneObject Array duplicate
* @return void
- */
+ */
function arrayClone( &$object, &$cloneObject )
{
if (is_array($object)) {
@@ -84,56 +84,56 @@ class Configurations // extends Configuration
}
}
}
-
+
/**
* configObject
*
- * @param object &$object
+ * @param object &$object
* @param array &$from
* @return void
- */
+ */
function configObject( &$object, &$from )
{
- if (!(is_object($object) || is_array($object)))
+ if (!(is_object($object) || is_array($object)))
return;
- if (!isset($from))
+ if (!isset($from))
$from = &$this->aConfig;
foreach($from as $k => $v ) {
if (isset($v) && array_key_exists($k,$object)) {
- if (is_object($v))
+ if (is_object($v))
throw new Exception( 'Object is not permited inside configuration array.' );
-
+
if (is_object($object)) {
- if (is_array($v))
+ if (is_array($v))
$this->configObject($object->{$k}, $v);
- else
+ else
$object->{$k} = $v;
- }
- else {
+ }
+ else {
if (is_array($object)) {
- if (is_array($v))
+ if (is_array($v))
$this->configObject($object[$k], $v);
- else
+ else
$object[$k] = $v;
}
}
}
}
}
-
+
/**
* loadConfig
*
- * @param object &$object
- * @param string $cfg
- * @param object $obj
- * @param string $pro
- * @param string $usr
- * @param string $app
+ * @param object &$object
+ * @param string $cfg
+ * @param object $obj
+ * @param string $pro
+ * @param string $usr
+ * @param string $app
* @return void
- */
+ */
function loadConfig(&$object, $cfg, $obj='', $pro = '', $usr = '', $app = '')
{
$this->load($cfg, $obj, $pro, $usr, $app);
@@ -143,13 +143,13 @@ class Configurations // extends Configuration
/**
* loadConf
*
- * @param string $cfg
- * @param object $obj
- * @param string $pro
- * @param string $usr
- * @param string $app
+ * @param string $cfg
+ * @param object $obj
+ * @param string $pro
+ * @param string $usr
+ * @param string $app
* @return void
- */
+ */
function load($cfg, $obj='', $pro = '', $usr = '', $app = '')
{
$this->Fields = array();
@@ -158,23 +158,23 @@ class Configurations // extends Configuration
$this->Fields = $this->Configuration->load($cfg, $obj, $pro, $usr, $app);
}
catch(Exception $e) {} // the configuration does not exist
-
+
if (isset($this->Fields['CFG_VALUE']))
$this->aConfig = unserialize($this->Fields['CFG_VALUE']);
-
- if (!is_array($this->aConfig))
+
+ if (!is_array($this->aConfig))
$this->aConfig = Array();
-
+
return $this->aConfig;
}
-
+
/**
* saveConfig
*
- * @param object &$object
+ * @param object &$object
* @param array &$from
* @return void
- */
+ */
function saveConfig($cfg,$obj,$pro='',$usr='',$app='')
{
$aFields = array(
@@ -192,14 +192,14 @@ class Configurations // extends Configuration
$this->Configuration->update($aFields);
}
}
-
+
/**
* saveObject
*
- * @param object &$object
+ * @param object &$object
* @param array &$from
* @return void
- */
+ */
function saveObject(&$object,$cfg,$obj,$pro='',$usr='',$app='')
{
$aFields = array(
@@ -222,13 +222,13 @@ class Configurations // extends Configuration
* loadObject
* this function is deprecated, we dont know why return an object, use the function getConfiguration below
*
- * @param string $cfg
+ * @param string $cfg
* @param object $obj
* @param string $pro
* @param string $usr
* @param string $app
* @return void
- */
+ */
function loadObject($cfg, $obj, $pro = '', $usr = '', $app = '')
{
$objectContainer=array((object) array());
@@ -236,26 +236,26 @@ class Configurations // extends Configuration
if ($this->Configuration->exists( $cfg, $obj, $pro, $usr, $app ))
$this->Fields = $this->Configuration->load( $cfg, $obj, $pro, $usr, $app );
else
- return $objectContainer[0];
-
+ return $objectContainer[0];
+
if (isset($this->Fields['CFG_VALUE']))
$objectContainer = unserialize($this->Fields['CFG_VALUE']);
- if (!is_array($objectContainer)||sizeof($objectContainer)!=1)
+ if (!is_array($objectContainer)||sizeof($objectContainer)!=1)
return (object) array();
else
return $objectContainer[0];
}
-
+
/**
* getConfiguration
*
- * @param string $cfg
+ * @param string $cfg
* @param object $obj
* @param string $pro
* @param string $usr
* @param string $app
* @return void
- */
+ */
function getConfiguration($cfg, $obj, $pro = '', $usr = '', $app = '')
{
try {
@@ -266,7 +266,7 @@ class Configurations // extends Configuration
if ( is_array($result) && sizeof($result)==1 ) {
$arrayKeys = Array_keys( $result );
return $result[ $arrayKeys[0]];
- }
+ }
else {
return $result;
}
@@ -279,16 +279,16 @@ class Configurations // extends Configuration
return null;
}
}
-
+
/**
* usersNameFormat
* @author Enrique Ponce de Leon enrique@colosa.com
* @param string $username
- * @param string $firstname
+ * @param string $firstname
* @param string $lastname
* @return string User Name Well-Formatted
*/
-
+
function usersNameFormat($username, $firstname, $lastname){
try{
if (!isset($this->UserConfig)) $this->UserConfig = $this->getConfiguration('ENVIRONMENT_SETTINGS', '');
@@ -297,59 +297,69 @@ class Configurations // extends Configuration
$aux = str_replace('@userName', $username, $this->UserConfig['format']);
$aux = str_replace('@firstName', $firstname, $aux);
$aux = str_replace('@lastName', $lastname, $aux);
- return $aux;
+ return $aux;
}else{
- return $username;
+ return $username;
}
}catch(Exception $oError){
return null;
}
}
-
- /**
- * getFormats
- * @author Enrique Ponce de Leon enrique@colosa.com
- * @return FORMATS array
- */
-
- function getFormats(){
- if (!isset($this->UserConfig)) {
- $this->UserConfig = $this->getConfiguration('ENVIRONMENT_SETTINGS', '');
+
+ /**
+ * getFormats
+ * @author Enrique Ponce de Leon enrique@colosa.com
+ * @return FORMATS array
+ */
+ public function getFormats()
+ {
+ if (!isset($this->UserConfig)) {
+ $this->UserConfig = $this->getConfiguration("ENVIRONMENT_SETTINGS", "");
+ }
+
+ //Setting defaults
+ if (!isset($this->UserConfig["format"])) {
+ $this->UserConfig["format"] = "@lastName, @firstName (@userName)";
+ }
+
+ if (!isset($this->UserConfig["dateFormat"])) {
+ $this->UserConfig["dateFormat"] = "Y-m-d H:i:s";
+ }
+
+ if (!isset($this->UserConfig["startCaseHideProcessInf"])) {
+ $this->UserConfig["startCaseHideProcessInf"] = false;
+ }
+
+ if (!isset($this->UserConfig["casesListDateFormat"])) {
+ $this->UserConfig["casesListDateFormat"] = "Y-m-d H:i:s";
+ }
+
+ if (!isset($this->UserConfig["casesListRowNumber"])) {
+ $this->UserConfig["casesListRowNumber"] = 25;
+ }
+
+ if (!isset($this->UserConfig["casesListRefreshTime"]) ||
+ (isset($this->UserConfig["casesListRefreshTime"]) && empty($this->UserConfig["casesListRefreshTime"]))
+ ) {
+ $this->UserConfig["casesListRefreshTime"] = 120; //2 minutes
+ }
+
+ $this->UserConfig["TimeZone"] = date("T");
+
+ return $this->UserConfig;
}
- // setting defaults
- if (!isset($this->UserConfig['format'])) {
- $this->UserConfig['format'] = '@lastName, @firstName (@userName)';
- }
- if (!isset($this->UserConfig['dateFormat'])) {
- $this->UserConfig['dateFormat'] = 'Y-m-d H:i:s';
- }
- if (!isset($this->UserConfig['casesListDateFormat'])) {
- $this->UserConfig['casesListDateFormat'] = 'Y-m-d H:i:s';
- }
- if (!isset($this->UserConfig['casesListRowNumber'])) {
- $this->UserConfig['CasesListRowNumber'] = '25';
- }
- if (!isset($this->UserConfig['startCaseHideProcessInf'])) {
- $this->UserConfig['startCaseHideProcessInf'] = false;
- }
- $this->UserConfig['TimeZone'] = date('T');
-
- return $this->UserConfig;
- }
-
-
/**
* setConfig
*
- * @param string $route
+ * @param string $route
* @param object &$object
* @param object &$to
* @return void
- */
+ */
function setConfig( $route , &$object , &$to )
{
- if (!isset($to))
+ if (!isset($to))
$to = &$this->aConfig;
$routes = explode(',',$route);
foreach($routes as $r) {
@@ -373,18 +383,18 @@ class Configurations // extends Configuration
}
} else {
if (is_object($object)) {
- if (!isset($to[$ro[0]]))
+ if (!isset($to[$ro[0]]))
$to[$ro[0]] = array();
$this->setConfig(implode('/',$rou),$object->{$ro[0]},$to[$ro[0]]);
} else {
if (is_array($object)) {
- if (!isset($to[$ro[0]]))
+ if (!isset($to[$ro[0]]))
$to[$ro[0]] = array();
$this->setConfig(implode('/',$rou),$object[$ro[0]],$to[$ro[0]]);
} else {
$to = $object;
}
- }
+ }
}
} else {
@@ -416,7 +426,7 @@ class Configurations // extends Configuration
}
}
}
-
+
function getDateFormats(){
$formats[] = Array(
'id'=>'Y-m-d H:i:s', //the id , don't translate
@@ -470,7 +480,7 @@ class Configurations // extends Configuration
'id'=>'d m, Y',
'name'=>G::loadTranslation('ID_DATE_FORMAT_13')//'d m, Y' i.e:'17 11, 2010'
);
-
+
return $formats;
}
@@ -506,13 +516,13 @@ class Configurations // extends Configuration
return $formats;
}
-
+
function getSystemDate($dateTime)
{
$oConf = new Configurations;
$oConf->loadConfig($obj, 'ENVIRONMENT_SETTINGS','');
$creationDateMask = isset($oConf->aConfig['dateFormat'])? $oConf->aConfig['dateFormat']: '';
-
+
if( $creationDateMask != '' ) {
if( strpos($dateTime, ' ') !== false ) {
list($date, $time) = explode(' ', $dateTime);
@@ -522,22 +532,22 @@ class Configurations // extends Configuration
} else {
list($y, $m, $d) = explode('-', $dateTime);
$dateTime = date($creationDateMask, mktime(0, 0, 0, $m, $d, $y));
- }
- }
-
+ }
+ }
+
return $dateTime;
}
-
+
function getEnvSetting($key=null, $data=null)
{
$this->loadConfig($obj, 'ENVIRONMENT_SETTINGS','');
-
+
if( isset($key) ) {
if( isset($this->aConfig[$key]) ) {
if( isset($data) && is_array($data) )
foreach($data as $k=>$v)
$this->aConfig[$key] = str_replace('@'.$k, $v, $this->aConfig[$key]);
-
+
return $this->aConfig[$key];
} else
return '';
@@ -545,4 +555,4 @@ class Configurations // extends Configuration
return $this->aConfig;
}
}
-?>
\ No newline at end of file
+
diff --git a/workflow/engine/methods/cases/main_init.php b/workflow/engine/methods/cases/main_init.php
index 4fcdd7640..158be7e1b 100755
--- a/workflow/engine/methods/cases/main_init.php
+++ b/workflow/engine/methods/cases/main_init.php
@@ -23,53 +23,64 @@
*
*/
- $oHeadPublisher =& headPublisher::getSingleton();
- $oHeadPublisher->addExtJsScript('cases/main', false ); //adding a javascript file .js
- $oHeadPublisher->addContent( 'cases/main'); //adding a html file .html.
-
- $keyMem = 'USER_PREFERENCES'.$_SESSION['USER_LOGGED'];
- $memcache = & PMmemcached::getSingleton(SYS_SYS);
- if ( ($arrayConfig = $memcache->get($keyMem)) === false ) {
- G::loadClass('configuration');
- $oConf = new Configurations;
- $oConf->loadConfig($x, 'USER_PREFERENCES','','',$_SESSION['USER_LOGGED'],'');
- $arrayConfig = $oConf->aConfig;
- $memcache->set( $keyMem, $arrayConfig, PMmemcached::ONE_HOUR);
- }
+G::LoadClass("configuration");
+
+
+
+
+
+$conf = new Configurations();
+
+$oHeadPublisher = &headPublisher::getSingleton();
+$oHeadPublisher->addExtJsScript("cases/main", false); //Adding a javascript file .js
+$oHeadPublisher->addContent("cases/main"); //Adding a html file .html.
+
+$keyMem = "USER_PREFERENCES" . $_SESSION["USER_LOGGED"];
+$memcache = &PMmemcached::getSingleton(SYS_SYS);
+
+if (($arrayConfig = $memcache->get($keyMem)) === false) {
+ $conf->loadConfig($x, "USER_PREFERENCES", "", "", $_SESSION["USER_LOGGED"], "");
+ $arrayConfig = $conf->aConfig;
+ $memcache->set($keyMem, $arrayConfig, PMmemcached::ONE_HOUR);
+}
+
+$confDefaultOption = "";
+
+if (isset($arrayConfig["DEFAULT_CASES_MENU"])) { //this user has a configuration record
+ $confDefaultOption = $arrayConfig["DEFAULT_CASES_MENU"];
- $confDefaultOption='';
- if( isset($arrayConfig['DEFAULT_CASES_MENU']) ){ #this user has a configuration record
- $confDefaultOption = $arrayConfig['DEFAULT_CASES_MENU'];
global $G_TMP_MENU;
+
$oMenu = new Menu();
- $oMenu->load('cases');
- $defaultOption = '';
- foreach($oMenu->Id as $i=>$id){
- if( $id == $confDefaultOption ){
- $defaultOption = $oMenu->Options[$i];
- break;
- }
+ $oMenu->load("cases");
+ $defaultOption = "";
+
+ foreach ($oMenu->Id as $i => $id) {
+ if ($id == $confDefaultOption) {
+ $defaultOption = $oMenu->Options[$i];
+ break;
+ }
}
- $defaultOption = $defaultOption != '' ? $defaultOption : 'casesListExtJs';
-
- }
- else {
- $defaultOption = 'casesListExtJs';
- $confDefaultOption = 'CASES_INBOX';
- }
- if (isset($_GET['id']) && isset($_GET['id'])) {
- $defaultOption = '../cases/open?APP_UID=' . $_GET['id'] . '&DEL_INDEX=' . $_GET['i'];
+ $defaultOption = ($defaultOption != "")? $defaultOption : "casesListExtJs";
+} else {
+ $defaultOption = "casesListExtJs";
+ $confDefaultOption = "CASES_INBOX";
+}
- if (isset($_GET['a'])) {
- $defaultOption .= '&action=' . $_GET['a'];
+if (isset($_GET["id"]) && isset($_GET["id"])) {
+ $defaultOption = "../cases/open?APP_UID=" . $_GET["id"] . "&DEL_INDEX=" . $_GET["i"];
+
+ if (isset($_GET["a"])) {
+ $defaultOption .= "&action=" . $_GET["a"];
}
- }
-
- $oHeadPublisher->assign( 'defaultOption', $defaultOption); // user menu permissions
- $oHeadPublisher->assign( '_nodeId', isset($confDefaultOption)?$confDefaultOption:'PM_USERS'); // user menu permissions
+}
+
+$oHeadPublisher->assign("defaultOption", $defaultOption); //User menu permissions
+$oHeadPublisher->assign("_nodeId", isset($confDefaultOption)? $confDefaultOption : "PM_USERS"); //User menu permissions
+$oHeadPublisher->assign("FORMATS", $conf->getFormats());
+
+$_SESSION["current_ux"] = "NORMAL";
+
+G::RenderPage("publish", "extJs");
- $_SESSION['current_ux'] = 'NORMAL';
-
- G::RenderPage('publish', 'extJs');
-
\ No newline at end of file
diff --git a/workflow/engine/methods/setup/environmentSettingsAjax.php b/workflow/engine/methods/setup/environmentSettingsAjax.php
index 0ae801352..df906ef8c 100755
--- a/workflow/engine/methods/setup/environmentSettingsAjax.php
+++ b/workflow/engine/methods/setup/environmentSettingsAjax.php
@@ -1,58 +1,62 @@
- * @date Sept 13th, 2010
- */
-
- $request = isset($_POST['request'])? $_POST['request']: (isset($_GET['request'])? $_GET['request']: null);
-
- switch($request){
- case 'getUserMaskList':
- G::loadClass('configuration');
- $result->rows = Configurations::getUserNameFormats();
- print(G::json_encode($result));
- break;
+/**
+ * @author Erik A.O.
+ * @date Sept 13th, 2010
+ *
+ */
- case 'getDateFormats':
- G::loadClass('configuration');
- $result->rows = Configurations::getDateFormats();
- print(G::json_encode($result));
- break;
+G::LoadClass("configuration");
- case 'getCasesListDateFormat':
- G::loadClass('configuration');
- $result->rows = Configurations::getDateFormats();;
- print(G::json_encode($result));
- break;
-
- case 'getCasesListRowNumber':
- for($i=10; $i<=50; $i+=5){
- $formats[] = Array('id'=>"$i", 'name'=>"$i");
- }
- $result->rows = $formats;
- print(G::json_encode($result));
- break;
- case 'save':
-
- G::LoadClass('configuration');
- $conf = new Configurations;
- $conf->aConfig = Array(
- 'format'=>$_POST['userFormat'],
- 'dateFormat'=>$_POST['dateFormat'],
- 'casesListDateFormat' =>$_POST['casesListDateFormat'],
- 'casesListRowNumber' =>$_POST['casesListRowNumber']
- );
- $conf->aConfig['startCaseHideProcessInf'] = isset($_POST['hideProcessInf']) ? true : false;
- $conf->saveConfig('ENVIRONMENT_SETTINGS', '');
- $response = new StdClass();
- $response->success = true;
- $response->msg = G::LoadTranslation('ID_SAVED_SUCCESSFULLY');
-
- echo G::json_encode($response);
- break;
- }
-
\ No newline at end of file
+$request = isset($_POST["request"])? $_POST["request"] : (isset($_GET["request"])? $_GET["request"] : null);
+
+switch ($request) {
+ case "getUserMaskList":
+ $result->rows = Configurations::getUserNameFormats();
+ print(G::json_encode($result));
+ break;
+
+ case "getDateFormats":
+ $result->rows = Configurations::getDateFormats();
+ print(G::json_encode($result));
+ break;
+
+ case "getCasesListDateFormat":
+ $result->rows = Configurations::getDateFormats();;
+ print(G::json_encode($result));
+ break;
+
+ case "getCasesListRowNumber":
+ for ($i = 10; $i <= 50; $i+=5) {
+ $formats[] = array("id" => "$i", "name" => "$i");
+ }
+
+ $result->rows = $formats;
+ print(G::json_encode($result));
+ break;
+
+ case "save":
+ $conf = new Configurations();
+
+ $conf->aConfig = array(
+ "format" => $_POST["userFormat"],
+ "dateFormat" => $_POST["dateFormat"],
+ "startCaseHideProcessInf" => ((isset($_POST["hideProcessInf"]))? true : false),
+ "casesListDateFormat" => $_POST["casesListDateFormat"],
+ "casesListRowNumber" => intval($_POST["casesListRowNumber"]),
+ "casesListRefreshTime" => intval($_POST["txtCasesRefreshTime"])
+ );
+
+ $conf->saveConfig("ENVIRONMENT_SETTINGS", "");
+
+ $response = new stdclass();
+ $response->success = true;
+ $response->msg = G::LoadTranslation("ID_SAVED_SUCCESSFULLY");
+
+ echo G::json_encode($response);
+ break;
+}
+
diff --git a/workflow/engine/templates/cases/main.js b/workflow/engine/templates/cases/main.js
index 717e25f1a..a46cb6732 100755
--- a/workflow/engine/templates/cases/main.js
+++ b/workflow/engine/templates/cases/main.js
@@ -1,5 +1,4 @@
var PANEL_EAST_OPEN = false;
-var timerMinutes = 2*60*1000; //every 2 minutes, this should be customized also,
var currentSelectedTreeMenuItem = null;
var centerPanel;
var menuTree;
@@ -11,7 +10,7 @@ var detailsText = '';
var debugTriggersDetailTpl = new Ext.Template('{code}
');
debugTriggersDetailTpl.compile();
-
+
var propStore;
var triggerStore;
@@ -21,7 +20,7 @@ var NOTIFIER_FLAG = false;
var result;
var _action = '';
var _CASE_TITLE;
-
+
Ext.onReady(function(){
new Ext.KeyMap(document, {
key: Ext.EventObject.F5,
@@ -32,8 +31,8 @@ Ext.onReady(function(){
}
e.stopEvent();
updateCasesTree();
- }
- else
+ }
+ else
Ext.Msg.alert('Refresh', 'You clicked: CTRL-F5');
}
});
@@ -41,7 +40,7 @@ Ext.onReady(function(){
Ext.QuickTips.init();
Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
- var resetGrid = function() {
+ var resetGrid = function() {
propStore.load();
};
@@ -52,16 +51,16 @@ Ext.onReady(function(){
var debugVariablesFilterSystem = function(){
propStore.load({params:{filter:'sys'}});
}
-
+
var resetTriggers = function(){
triggerStore.load();
}
-
+
propStore = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({url: 'debug_vars'}),
reader: new Ext.data.DynamicJsonReader({root: 'data'})
});
-
+
propStore.on('load', function(){
propStore.fields = propStore.recordType.prototype.fields;
debugVariables.setSource(propStore.getAt(0).data);
@@ -76,14 +75,14 @@ Ext.onReady(function(){
width: 400,
region: 'center',
margins: '2 2 0 2',
-
+
border: true,
stripeRows: true,
listeners: {
beforeedit: function(event) { //Cancel editing - read only
event.cancel = true;
}
- },
+ },
tbar: [
{text: TRANSLATIONS.ID_ALL, handler: resetGrid},
{text: TRANSLATIONS.ID_DYNAFORM, handler: debugVariablesFilterDynaform},
@@ -95,18 +94,18 @@ Ext.onReady(function(){
}
});
-
+
//set debug variable details
debugVariables.getSelectionModel().on('rowselect', function(sm, rowIdx, r) {
var detailPanel = Ext.getCmp('debug-details-panel');
var d = {}
-
+
d.name = r.data.name;
d.value = parent.parent.htmlentities ? parent.parent.htmlentities(r.data.value) : r.data.value;
debugVarTpl.overwrite(detailPanel.body, d);
detailPanel.setTitle(r.data.name);
-
+
if(r.data.value == '