Beruflich Dokumente
Kultur Dokumente
* @ @param String $key * @ @param Mixed $value * @ @param String $expireTime * @ @return Boolean - indicating if the key was correctly saved in the cache */ public function
set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; //
DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’:
$expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE
CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. * @ @param String $key * @ @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file
params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp
=substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer);
@ if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache
for a certain key. * @ @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @
@return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if
(is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this-
>getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @ @return Boolean - indicating if the cache was completely destroyed * @ @see
\Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @ @return Array */ public function getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details
$params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’];
$return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10); $return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); //
get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @ @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin.
*@ @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the
concrete classes. * @ @param String $triggerName - the trigger name * @ @param \Engine\Plugins\Observer $obs - the observer object * @ @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The
trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @ @return multitype: */ public
function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers * @ @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @ @var Array */ protected $exportFields = array(‘alias’, ‘company’); /** * Extra
filters - deleted and duplicated suppliers are not allwed. * @ @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @ @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @ @see
\App\EntityMatch\BaseAbstract::setTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @ @param String $value - the supplier alias. * @ @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setAlias($value,
$useWildcard=true) { $this->setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @ @param String $value - the supplier company name. * @ @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this-
>setSearchCriterion(‘company’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @ @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @ @param String $value * @ @param String $codeName
- indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } //
add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manually set in the external reservations system configs. * @ @param String $name - the supplier name * @ @param Int $idExternalResSystem - the external system */ public function
setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the rest of the criteria, it uses the parent call. * @ @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected
function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field, $value,
$useWildcard, $options); } } /** * Searches a supplier by its code. * @ @param String $value - the supplier code * @ @param Array $options - stores the code name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT
“.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx)
{ writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’); } // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external reservation
systems. * @ @param unknown $valuem * @ @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE
systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation systems code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by the external reservation systems
code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file
(relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @@var String */ protected $fileName=null; /** * The filename with its full path. * @ @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @ @see
\App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName;
} /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @ @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if
(isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this->changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads
$S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList; $S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @ @see \App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this-
>fileNameWithPath)) return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it) $xml=@simplexml_load_file($this->fileNameWithPath);
@ if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this-
>fileNameWithPath.’).’); // return the array from the parsed XML content return $this->getAll _rawData _extract DataFromFile($xml); } /** * Sets the default output in case the XML file is missing. * @ @return Array */ protected function getAll_rawData_fileNotExists() { return array(); } /** * Returns the extracted data after the XML content
is parsed. * @ @param \SimpleXMLElement $xml - the parsed xml * @ @return Array - the items list or whatever output is needed. */ abstract protected function getAll_ rawData _extractData FromFile (\ Simple XMLElement $xml); /** * Sets an element in the internal attribute $this->itemsList. The method can be called multiple * times and they
will overwrite the older values. * @ @param String $var - or the name of one key (a string); * @ @param Mixed $value - the value of the key; * @ @throws Exception - if input is bad */ public function set($var, $value) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get _called_
class().’::’.__FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_string($var)) throw new \Exception(“You did not set() a correct name for the key.”); // (re)set one key $this->itemsList[$var]=$value; // update the changed flag $this->changedFlag=true; } /** * Sets all the elements in the internal attribute $this-
>itemsList (rewrites any old data * already stored). The parameter is an associative array and the keys will be * stored one by one. * @ @param Array $vars - associative array of keys * @ @throws Exception - if input is bad */ public function setAll($vars) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You
can use the method `”.get_c alled_class().’::’.__ FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_array($vars)) throw new \Exception(“You did not setAll() a correct list of elements - the parameter is not an array.”); // (re)set a list of keys $this->itemsList=$vars; // update the changed flag $this->changed
Flag=true; } /** * (non-PHPdoc) * @ @see \App\Config\BaseAbstract::save_write_do() */ protected function save_write_do() { // GETS THE SIMPLEXML OBJECT TO BE WRITTEN ON DISK gets the simplexml object $xmlObj=$this->save_write_do _getXml(); // checks that the object is correct (it is a valid simplexml object) if (!($xmlObj
instanceof \SimpleXMLElement)) throw new \Exception(‘The output of the method `’.get_ called_class() .’::save_write_do_getXml` must be a `SimpleXMLElement` object.’); // (RE)WRITE FILE ON DISK // check if the file is writeable $fileChmod=false; if (file_exists($this->fileNameWithPath)) { if (!is_writeable($this->fileNameWithPath)) $this-
>addErrorMessage(‘The config file exists but it is not writeable.’); } else { $fileChmod=true; } // (OVER)WRITE THE FILE CONTENT - if (!file_put_contents($this->fileNameWithPath, $xmlObj->asXml())) { $this->addErrorMessage(‘The config file could not be (over)written.’); } // if the file did not exist - chmod it if ($fileChmod) { if
(!chmod($this->fileNameWithPath, 0777)) { $this->addErrorMessage(‘The config file could not have its acess rights updated.’); } } } /** * Returns the SimpleXML object to be written on disk. * @ @return \SimpleXMLElement */ abstract protected function save_write_do_getXml(); } <?php namespace Engine\Cache; /** * The class is used to
manage the cache stored on disk. * @ @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @ @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @ @var String */ protected
$keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @ @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache
caches the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @ @param Array
$key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’;
$return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @ @param String $key * @ @param Mixed $value * @ @param String $expireTime * @ @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS
// if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if
(is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case
‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return
\File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. * @ @param String $key * @ @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params
$params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr
($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer);
@ if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache for a
certain key. * @ @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @@return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if
(is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileN
(!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this-
>getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @ @return Boolean - indicating if the cache was completely destroyed * @ @see
\Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @ @return Array */ public function getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details
$params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’];
$return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10); $return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); //
get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @ @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin.
*@ @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the
concrete classes. * @ @param String $triggerName - the trigger name * @ @param \Engine\Plugins\Observer $obs - the observer object * @ @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The
trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @ @return multitype: */ public
function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers * @ @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @ @var Array */ protected $exportFields = array(‘alias’, ‘company’); /** * Extra
filters - deleted and duplicated suppliers are not allwed. * @ @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @ @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @ @see
\App\EntityMatch\BaseAbstract::setTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @ @param String $value - the supplier alias. * @ @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setAlias($value,
$useWildcard=true) { $this->setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @ @param String $value - the supplier company name. * @ @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this-
>setSearchCriterion(‘company’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @ @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @ @param String $value * @ @param String $codeName
- indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } //
add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manually set in the external reservations system configs. * @ @param String $name - the supplier name * @ @param Int $idExternalResSystem - the external system */ public function
setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the rest of the criteria, it uses the parent call. * @ @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected
function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field, $value,
$useWildcard, $options); } } /** * Searches a supplier by its code. * @ @param String $value - the supplier code * @ @param Array $options - stores the code name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT
“.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx)
{ writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’); } // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external reservation
systems. * @ @param unknown $valuem * @ @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE
systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation systems code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by the external reservation systems
code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file
(relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @@var String */ protected $fileName=null; /** * The filename with its full path. * @ @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @ @see
\App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName;
} /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @ @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if
@ @
@ @
@
@ @ @ @
@ @
@
@ @
@ @
@
@
@
@ @ @
@
@ @
@ @ @
@ @
@ @
@ @ @
@ @
@
@ @
@ @
@ @ @
@
@
Business critical
@
@ @
@ @ @
@ @
@
@ @ @
@
@
@ @
@
@
@
@
@ @
@
end-to-end solutions
@ @
@
@
@
@ @ @
@
@ @
@ @ @
@ @
<?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Exception Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was
updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) -
\App\Entity\Cache caches the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @param Array
$key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’;
$return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if
(!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) {
$expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break;
default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. *
@param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params
[‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and
return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the
cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file
$return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /**
* Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see \Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function
getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys
$return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10);
$return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Exception
*/ abstract class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this
plugin (the method is called from the defined method register() * in the concrete classes. * @param String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger
name if (!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current
* @return multitype: */ public function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers * @author Exception */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var Array */ protected $exportFields = array(‘alias’, ‘company’);
/** * Extra filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @see
\App\EntityMatch\BaseAbstract::setTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setAlias($value, $useWildcard=true) {
$this->setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this->setSearchCriterion(‘company’, $value, $useWildcard); } /**
* Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @param String $value * @param String $codeName - indicate the type of code (iata, tktCode, etc) */ public function setCode($value,
$codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, false,
array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manually set in the external reservations system configs. * @param String $name - the supplier name * @param Int $idExternalResSystem - the external system */ public function setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name,
false, array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the rest of the criteria, it uses the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return
$this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field, $value, $useWildcard, $options); } } /** * Searches a supplier by its code. * @param String $value - the supplier code * @param Array $options - stores the code
name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier`
{$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx) { writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’); } // STORE RESULTS $return = array();
foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external reservation systems. * @param unknown $valuem * @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db =
\DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external
reservation systems code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by the external reservation systems code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace
App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file (relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path. * @var String */ protected
$fileNameWithPath=null; /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH .
FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class
attributes if (isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this->changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads
$S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList; $S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this->fileNameWithPath))
return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it) $xml=@simplexml_load_file($this->fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this->fileNameWithPath.’).’); // return the array from the
parsed XML content return $this->getAll _rawData _extract DataFromFile($xml); } /** * Sets the default output in case the XML file is missing. * @return Array */ protected function getAll_rawData_fileNotExists() { return array(); } /** * Returns the extracted data after the XML content is parsed. * @param \SimpleXMLElement $xml - the parsed xml *
@return Array - the items list or whatever output is needed. */ abstract protected function getAll_ rawData _extractData FromFile (\ Simple XMLElement $xml); /** * Sets an element in the internal attribute $this->itemsList. The method can be called multiple * times and they will overwrite the older values. * @param String $var - or the name of one key (a
string); * @param Mixed $value - the value of the key; * @throws Exception - if input is bad */ public function set($var, $value) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get _called_ class().’::’.__FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_string($var))
throw new \Exception(“You did not set() a correct name for the key.”); // (re)set one key $this->itemsList[$var]=$value; // update the changed flag $this->changedFlag=true; } /** * Sets all the elements in the internal attribute $this->itemsList (rewrites any old data * already stored). The parameter is an associative array and the keys will be * stored one by
one. * @param Array $vars - associative array of keys * @throws Exception - if input is bad */ public function setAll($vars) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get_c alled_class().’::’.__ FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_array($vars))
throw new \Exception(“You did not setAll() a correct list of elements - the parameter is not an array.”); // (re)set a list of keys $this->itemsList=$vars; // update the changed flag $this->changed Flag=true; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::save_write_do() */ protected function save_write_do() { // GETS THE SIMPLEXML OBJECT TO BE
WRITTEN ON DISK gets the simplexml object $xmlObj=$this->save_write_do _getXml(); // checks that the object is correct (it is a valid simplexml object) if (!($xmlObj instanceof \SimpleXMLElement)) throw new \Exception(‘The output of the method `’.get_ called_class() .’::save_write_do_getXml` must be a `SimpleXMLElement` object.’); // (RE)WRITE FILE
ON DISK // check if the file is writeable $fileChmod=false; if (file_exists($this->fileNameWithPath)) { if (!is_writeable($this->fileNameWithPath)) $this->addErrorMessage(‘The config file exists but it is not writeable.’); } else { $fileChmod=true; } // (OVER)WRITE THE FILE CONTENT - if (!file_put_contents($this->fileNameWithPath, $xmlObj->asXml())) { $this-
>addErrorMessage(‘The config file could not be (over)written.’); } // if the file did not exist - chmod it if ($fileChmod) { if (!chmod($this->fileNameWithPath, 0777)) { $this->addErrorMessage(‘The config file could not have its acess rights updated.’); } } } /** * Returns the SimpleXML object to be written on disk. * @return \SimpleXMLElement */ abstract
protected function save_write_do_getXml(); } <?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Exception Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid
the reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because
cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data
determined from the key * @param Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’;
$return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if
(!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) {
$expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break;
default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. *
@param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params
[‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and
return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the
cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file
$return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /**
* Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see \Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function
getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys
$return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10);
$return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Exception
*/ abstract class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this
plugin (the method is called from the defined method register() * in the concrete classes. * @param String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger
name if (!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current
* @return multitype: */ public function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers * @author Exception */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var Array */ protected $exportFields = array(‘alias’, ‘company’);
<?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Exception */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. *
@var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) -
\App\Entity\Cache caches the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @param Array
$key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’;
$return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if
(!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) {
$expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break;
default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. *
@param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params
[‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and
return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the
cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file
$return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /**
* Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see \Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function
getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys
$return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10);
$return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */
abstract class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin
(the method is called from the defined method register() * in the concrete classes. * @param String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if
(!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current *
@return multitype: */ public function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers * @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var Array */ protected $exportFields = array(‘alias’, ‘company’); /**
* Extra filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @see
\App\EntityMatch\BaseAbstract::setTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setAlias($value, $useWildcard=true) {
$this->setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this->setSearchCriterion(‘company’, $value, $useWildcard); } /**
* Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @param String $value * @param String $codeName - indicate the type of code (iata, tktCode, etc) */ public function setCode($value,
$codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, false,
array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manually set in the external reservations system configs. * @param String $name - the supplier name * @param Int $idExternalResSystem - the external system */ public function setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name,
false, array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the rest of the criteria, it uses the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return
$this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field, $value, $useWildcard, $options); } } /** * Searches a supplier by its code. * @param String $value - the supplier code * @param Array $options - stores the code
name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier`
{$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx) { writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’); } // STORE RESULTS $return = array();
foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external reservation systems. * @param unknown $valuem * @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db =
\DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external
reservation systems code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by the external reservation systems code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace
App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file (relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path. * @var String */ protected
$fileNameWithPath=null; /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH .
FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class
attributes if (isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this->changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads
$S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList; $S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this->fileNameWithPath))
return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it) $xml=@simplexml_load_file($this->fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this->fileNameWithPath.’).’); // return the array from the
parsed XML content return $this->getAll _rawData _extract DataFromFile($xml); } /** * Sets the default output in case the XML file is missing. * @return Array */ protected function getAll_rawData_fileNotExists() { return array(); } /** * Returns the extracted data after the XML content is parsed. * @param \SimpleXMLElement $xml - the parsed xml *
@return Array - the items list or whatever output is needed. */ abstract protected function getAll_ rawData _extractData FromFile (\ Simple XMLElement $xml); /** * Sets an element in the internal attribute $this->itemsList. The method can be called multiple * times and they will overwrite the older values. * @param String $var - or the name of one key (a
string); * @param Mixed $value - the value of the key; * @throws Exception - if input is bad */ public function set($var, $value) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get _called_ class().’::’.__FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_string($var))
throw new \Exception(“You did not set() a correct name for the key.”); // (re)set one key $this->itemsList[$var]=$value; // update the changed flag $this->changedFlag=true; } /** * Sets all the elements in the internal attribute $this->itemsList (rewrites any old data * already stored). The parameter is an associative array and the keys will be * stored one by
one. * @param Array $vars - associative array of keys * @throws Exception - if input is bad */ public function setAll($vars) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get_c alled_class().’::’.__ FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_array($vars))
throw new \Exception(“You did not setAll() a correct list of elements - the parameter is not an array.”); // (re)set a list of keys $this->itemsList=$vars; // update the changed flag $this->changed Flag=true; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::save_write_do() */ protected function save_write_do() { // GETS THE SIMPLEXML OBJECT TO BE
WRITTEN ON DISK gets the simplexml object $xmlObj=$this->save_write_do _getXml(); // checks that the object is correct (it is a valid simplexml object) if (!($xmlObj instanceof \SimpleXMLElement)) throw new \Exception(‘The output of the method `’.get_ called_class() .’::save_write_do_getXml` must be a `SimpleXMLElement` object.’); // (RE)WRITE FILE
ON DISK // check if the file is writeable $fileChmod=false; if (file_exists($this->fileNameWithPath)) { if (!is_writeable($this->fileNameWithPath)) $this->addErrorMessage(‘The config file exists but it is not writeable.’); } else { $fileChmod=true; } // (OVER)WRITE THE FILE CONTENT - if (!file_put_contents($this->fileNameWithPath, $xmlObj->asXml())) { $this-
Business critical
>addErrorMessage(‘The config file could not be (over)written.’); } // if the file did not exist - chmod it if ($fileChmod) { if (!chmod($this->fileNameWithPath, 0777)) { $this->addErrorMessage(‘The config file could not have its acess rights updated.’); } } } /** * Returns the SimpleXML object to be written on disk. * @return \SimpleXMLElement */ abstract
protected function save_write_do_getXml(); } <?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Exception */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the
reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd
may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data
determined from the key * @param Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’;
$return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if
(!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) {
$expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break;
default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. *
@param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params
[‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and
return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the
end-to-end solutions
cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file
$return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /**
* Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see \Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function
getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys
$return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10);
$return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */
abstract class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin
(the method is called from the defined method register() * in the concrete classes. * @param String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if
(!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current *
@return multitype: */ public function getObservers( @ @
Contents Introduction 4
WHAT WE OFFER 6
RELIABLE SERVICES 22
Consultancy 22
Operational Audit 23
Customizations 24
Trainings 25
Courses 26
PRODUCTS INTERACTION 40
Global Presence 44
Amadeus Select Partner 46
Lufthansa City Center Partnership 47
1
www.dcsplus.net
The change is taking us
to new heights
In the last years, the travel industry has become 2015 - Scaling new heights
overwhelming in its dimensions, diversity and complexity.
These years have been intensive and exciting for dcs plus. We In an industry as dynamic as the one we operate in, even if
have managed to strengthen our global position on the market you are among the leaders of the market, you must always be
by embracing innovation and deliver it through our innovative and come up with fresh and new ideas. The
technologies, by being in a permanent motion and mostly by impressive growth that our company had known in the last
remaining true and close to every partner and client. years determined us to take things to the next level.
2014 - Full speed ahead After carefully analyzing the strategic goals of dcs plus, we
decided to join efforts with Earlybird, an international venture
For dcs plus, 2014 was the year of speed. Even if it came with capital firm, which became a shareholder in the company. We
changes, it didn’t change our core beliefs: high standards, are confident that having a strong partner such as Earlybird,
professionalism, team effort and innovation. will allow us to explore new opportunities together with our
Great achievements were added to our portfolio - the customers and to step up our growth plan at a greater pace.
consolidation of our business-critical solutions, new Significant investments will be made in both technology and
developments, the implementation of ambitious projects, the people.
expansion into new markets and important strategic
partnerships. As part of the strategy for 2015, we are planning to extend
our portfolio with new products and services meant to
Among the most notable accomplishments in 2014 was the leverage our clients’ businesses.
launch of dcs plus BPO. Having the ability to anticipate the
market’s needs, we dared to explore the travel market beyond Dear partners and customers, thank you for making all these
technology and to share our expertise with the aim to improve years great and memorable ones. dcs plus knows how to
the business processes of travel agencies and tour operators. create success stories and for sure you are the proof of our
success.
The new company of the group, was created to optimize our
clients’ business processes, to add value to the travel Cristian Dinca,
technology and to improve its performance through advanced CEO of dcs plus
connected services. We are now able to fully support the core
components of travel agencies in a collaborative environment
and with integrated tools and strategies.
3
www.dcsplus.net
More than a decade of passion
Passion
One might think that software and software engineers
have nothing to do with passion. One might also think that
the Corporate World is all about figures, profit, tough Courage
negotiations, long meetings and time spent inside the
office. One might even think that what we do every day is We talk about courage in our story because with every
to take a huge amount of data, apply some complex little success we had more and more courage to be bolder
mathematical functions and create algorithms. and explore beyond… to challenge people and mentalities
even harder.
Here at dcs plus, we believe that one might not know the
whole scenery when making these assumptions. We state
this very clearly because we know a different story; a story
about passion, a story with long nights of hard work that Experience
seemed to be only minutes. We know a story full of
challenges and successes from overcoming them. We know As the years passed, part of the passion turned into
a story that seems to have no limits. experience; part of the disappointments transformed into
new challenges to overcome and so, in 2004 we have
We talk about a story full of passion because this is how started to work on the ERP system TINA. Back then, it was
we came into existence back in 2002 when the company hard to decide if we should embrace the new emerging
was founded by its current owner at the University web technologies or use the well proven client-server
campus. We know it was passion because everything that architecture. We took the chance and developed
was done back then by a small group of computer science everything in web-based technologies. In December
graduates, was done with the desire to make a difference; 2005 we have officially launched the product inside the
to stand out from the day to day stories and challenge Romanian market. We put all our dreams and hopes, all
everything and everyone. our disappointments and passion, all our courage in this.
We gave everything that we could to create an innovative
product from every single point of view (technology,
customer care, business model customization, consultancy
services, implementation model). It was great. In 6
months, most of the top travel agencies inside the
4
www.dcsplus.net
Romanian market were using TINA. 6 months later we Today, we are as passionate about what we are doing as
were signing an exclusive distribution agreement with we were in day one. We still dream to change everything
Passion drives performance
Amadeus IT Group, which became the sole distributor of and we challenge ourselves on a daily basis. And if one
TINA across the CESE region as early as 2007. This story might assume that we had a lot of fun doing it, one would
At dcs plus, we see the changes in
is still to be written as we kept on deploying TINA in more finally be correct.
the industry and markets as
and more markets every year.
remarkable opportunities to grow.
We truly believe that our dedicated
and passionate team can take you to
the next level in this complex and
40 markets
Professionalism fast changing environment.
The successful launch of TINA was only the beginning of global & proven solutions
the journey, as we realized there are so many areas in the
travel industry that we can explore and improve. This is
how we started developing TBS (Travel Booking System), hundreds of travel agencies
AIDA (Advanced Inventory and Distribution Application) with thousands of users
and TRIP (TRavel Internet Presentation).
7
www.dcsplus.net
<?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /**
Susta nab ty
* The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this-
>cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @param Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-
’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime *
@return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’]))
return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’:
$expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a
certain key. * @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ
THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null;
else return $value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if
Growth
(is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this->getFileDetails($key); // check the file if
(!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see \Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /**
Reliability
* Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or
Power
!is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10);
$return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list
of observers for a registered plugin. * @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the concrete classes. * @param
String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this-
>observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype: */ public function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers *
@author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var Array */ protected $exportFields = array(‘alias’, ‘company’); /** * Extra filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used
for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @see \App\EntityMatch\BaseAbstract::setTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard - uses wildcard for this
search or not. */ public function setAlias($value, $useWildcard=true) { $this->setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this->setSearchCriterion(‘company’, $value,
$useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @param String $value * @param String $codeName - indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check
the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manually set in the
external reservations system configs. * @param String $name - the supplier name * @param Int $idExternalResSystem - the external system */ public function setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the rest of the criteria, it uses
the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field,
$value, $useWildcard, $options); } } /** * Searches a supplier by its code. * @param String $value - the supplier code * @param Array $options - stores the code name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes`
Quality
AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx) { writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’);
} // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external reservation systems. * @param unknown $valuem * @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db =
\DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation systems code.”, ‘events’, 9); throw new
Performance
\Exception(‘Could not match supplier by the external reservation systems code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file
(relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is
okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function
constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if (isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this-
Expertise
>changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList; $S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return
the output in case the file does not exist if (!file_exists($this->fileNameWithPath)) return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it) $xml=@simplexml_load_file($this->fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this-
>fileNameWithPath.’).’); // return the array from the parsed XML content return $this->getAll _rawData _extract DataFromFile($xml); } /** * Sets the default output in case the XML file is missing. * @return Array */ protected function getAll_rawData_fileNotExists() { return array(); } /** * Returns the extracted data after the XML content is parsed. * @param \SimpleXMLElement $xml - the parsed xml *
@return Array - the items list or whatever output is needed. */ abstract protected function getAll_ rawData _extractData FromFile (\ Simple XMLElement $xml); /** * Sets an element in the internal attribute $this->itemsList. The method can be called multiple * times and they will overwrite the older values. * @param String $var - or the name of one key (a string); * @param Mixed $value - the value of the
key; * @throws Exception - if input is bad */ public function set($var, $value) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get _called_ class().’::’.__FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_string($var)) throw new \Exception(“You did not set() a correct name for the key.”); // (re)set one key $this-
>itemsList[$var]=$value; // update the changed flag $this->changedFlag=true; } /** * Sets all the elements in the internal attribute $this->itemsList (rewrites any old data * already stored). The parameter is an associative array and the keys will be * stored one by one. * @param Array $vars - associative array of keys * @throws Exception - if input is bad */ public function setAll($vars) { // check
Trust
constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get_c alled_class().’::’.__ FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_array($vars)) throw new \Exception(“You did not setAll() a correct list of elements - the parameter is not an array.”); // (re)set a list of keys $this->itemsList=$vars; // update the changed flag $this-
>changed Flag=true; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::save_write_do() */ protected function save_write_do() { // GETS THE SIMPLEXML OBJECT TO BE WRITTEN ON DISK gets the simplexml object $xmlObj=$this->save_write_do _getXml(); // checks that the object is correct (it is a valid simplexml object) if (!($xmlObj instanceof \SimpleXMLElement)) throw new \Exception(‘The
output of the method `’.get_ called_class() .’::save_write_do_getXml` must be a `SimpleXMLElement` object.’); // (RE)WRITE FILE ON DISK // check if the file is writeable $fileChmod=false; if (file_exists($this->fileNameWithPath)) { if (!is_writeable($this->fileNameWithPath)) $this->addErrorMessage(‘The config file exists but it is not writeable.’); } else { $fileChmod=true; } // (OVER)WRITE THE FILE
CONTENT - if (!file_put_contents($this->fileNameWithPath, $xmlObj->asXml())) { $this->addErrorMessage(‘The config file could not be (over)written.’); } // if the file did not exist - chmod it if ($fileChmod) { if (!chmod($this->fileNameWithPath, 0777)) { $this->addErrorMessage(‘The config file could not have its acess rights updated.’); } } } /** * Returns the SimpleXML object to be written on disk. *
@return \SimpleXMLElement */ abstract protected function save_write_do_getXml(); } <?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the
reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) -
\App\Entity\Cache caches the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @param Array $key */ private function getFileDetails($key) {
$return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key *
@param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the
directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’:
Control
$expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return
\File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. * @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if
(!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; }
// get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public
function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); }
} // ONLY ONE KEY else { // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see
\Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this->getFileDetails($key);
$fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’];
$return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10); $return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for
registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this-
>register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the concrete classes. * @param String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger
name if (!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype: */ public function
getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers * @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var Array */ protected $exportFields = array(‘alias’, ‘company’); /** * Extra filters - deleted and duplicated suppliers are not allwed. * @var String */
protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @see \App\EntityMatch\BaseAbstract::setTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers;
} /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setAlias($value, $useWildcard=true) { $this->setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard -
uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this->setSearchCriterion(‘company’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @param String
Values
$value * @param String $codeName - indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this-
>setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manually set in the external reservations system configs. * @param String $name - the supplier name * @param Int $idExternalResSystem - the external system */ public function setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false,
array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the rest of the criteria, it uses the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($value, $options); break;
case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field, $value, $useWildcard, $options); } } /** * Searches a supplier by its code. * @param String $value - the supplier code * @param Array $options - stores the code name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes;
$db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if
Innovation
(!$matchSupplierCodeEx) { writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’); } // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external reservation systems. * @param unknown $valuem *
@param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value,
$options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation systems code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by the external reservation systems code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this-
>searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file (relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its
full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH .
FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if
(isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this->changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList;
$S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this->fileNameWithPath)) return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it)
$xml=@simplexml_load_file($this->fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this->fileNameWithPath.’).’
<?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /**
* The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this-
>cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @param Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-
’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime *
@return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’]))
Passion
return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’:
$expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a
certain key. * @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ
THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null;
else return $value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if
(is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this->getFileDetails($key); // check the file if
(!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see \Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /**
* Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or
!is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10);
$return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list
of observers for a registered plugin. * @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the concrete classes. * @param
Ethics
String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this-
>observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype: */ public function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers *
@author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var Array */ protected $exportFields = array(‘alias’, ‘company’); /** * Extra filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used
for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @see \App\EntityMatch\BaseAbstract::setTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard - uses wildcard for this
search or not. */ public function setAlias($value, $useWildcard=true) { $this->setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this->setSearchCriterion(‘company’, $value,
$useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @param String $value * @param String $codeName - indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check
the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manually set in the
Goals
external reservations system configs. * @param String $name - the supplier name * @param Int $idExternalResSystem - the external system */ public function setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the rest of the criteria, it uses
the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field,
$value, $useWildcard, $options); } } /** * Searches a supplier by its code. * @param String $value - the supplier code * @param Array $options - stores the code name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes`
AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx) { writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’);
} // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external reservation systems. * @param unknown $valuem * @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db =
\DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation systems code.”, ‘events’, 9); throw new
\Exception(‘Could not match supplier by the external reservation systems code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file
(relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is
okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function
constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if (isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this-
>changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList; $S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return
the output in case the file does not exist if (!file_exists($this->fileNameWithPath)) return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it) $xml=@simplexml_load_file($this->fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this-
>fileNameWithPath.’).’); // return the array from the parsed XML content return $this->getAll _rawData _extract DataFromFile($xml); } /** * Sets the default output in case the XML file is missing. * @return Array */ protected function getAll_rawData_fileNotExists() { return array(); } /** * Returns the extracted data after the XML content is parsed. * @param \SimpleXMLElement $xml - the parsed xml *
Determination
@return Array - the items list or whatever output is needed. */ abstract protected function getAll_ rawData _extractData FromFile (\ Simple XMLElement $xml); /** * Sets an element in the internal attribute $this->itemsList. The method can be called multiple * times and they will overwrite the older values. * @param String $var - or the name of one key (a string); * @param Mixed $value - the value of the
key; * @throws Exception - if input is bad */ public function set($var, $value) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get _called_ class().’::’.__FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_string($var)) throw new \Exception(“You did not set() a correct name for the key.”); // (re)set one key $this-
>itemsList[$var]=$value; // update the changed flag $this->changedFlag=true; } /** * Sets all the elements in the internal attribute $this->itemsList (rewrites any old data * already stored). The parameter is an associative array and the keys will be * stored one by one. * @param Array $vars - associative array of keys * @throws Exception - if input is bad */ public function setAll($vars) { // check
constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get_c alled_class().’::’.__ FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_array($vars)) throw new \Exception(“You did not setAll() a correct list of elements - the parameter is not an array.”); // (re)set a list of keys $this->itemsList=$vars; // update the changed flag $this-
>changed Flag=true; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::save_write_do() */ protected function save_write_do() { // GETS THE SIMPLEXML OBJECT TO BE WRITTEN ON DISK gets the simplexml object $xmlObj=$this->save_write_do _getXml(); // checks that the object is correct (it is a valid simplexml object) if (!($xmlObj instanceof \SimpleXMLElement)) throw new \Exception(‘The
output of the method `’.get_ called_class() .’::save_write_do_getXml` must be a `SimpleXMLElement` object.’); // (RE)WRITE FILE ON DISK // check if the file is writeable $fileChmod=false; if (file_exists($this->fileNameWithPath)) { if (!is_writeable($this->fileNameWithPath)) $this->addErrorMessage(‘The config file exists but it is not writeable.’); } else { $fileChmod=true; } // (OVER)WRITE THE FILE
CONTENT - if (!file_put_contents($this->fileNameWithPath, $xmlObj->asXml())) { $this->addErrorMessage(‘The config file could not be (over)written.’); } // if the file did not exist - chmod it if ($fileChmod) { if (!chmod($this->fileNameWithPath, 0777)) { $this->addErrorMessage(‘The config file could not have its acess rights updated.’); } } } /** * Returns the SimpleXML object to be written on disk. *
@return \SimpleXMLElement */ abstract protected function save_write_do_getXml(); } <?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the
reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified @
@
@
@ @ @
@ @
@ @ @
@ @
@
@ @
8 @ @ @
@
@ @ www dcsp
@ us ne
@ @
@ @ @ @
@ @
@
@ @
@
@ @
@
@
Management for travel agencies
ERP system for travel agencies
ERP (Enterprise Resource Planning) is a software By using TINA, you have the following benefits:
architecture that facilitates the flow of information between
different functions within an enterprise. Similarly, ERP • Higher level of optimization for all business processes
software facilitates information sharing across • More accurate and increased visibility over these
organizational units and geographical locations. It enables processes
decision-makers to have an enterprise-wide view of the • Higher level of control over the activities that take place
information they need in a timely, reliable and consistent inside the travel agency
fashion.
With these advantages, together with the possibility to
Many companies that purchased TINA mid TINA is an ERP application developed by dcs plus for the estimate and forecast using powerful reporting tools inside
office system in the last 12 months had a
travel industry. Currently being deployed in hundreds of the application, you can better organize, plan, control and
large, ineffective system that was standalone
or part of a broader ERP. Those systems either travel agencies across 17 markets, TINA is suitable for any make qualified decisions aimed to increase the overall
became too costly to support or simply failed to kind of travel agency (TMC, OTA, Leisure, Tour Operator or business.
produce a reliable company forecast or demand Retailer) that needs a robust and efficient system. More
plan. TINA is the solution for these companies. than a piece of software, TINA includes know-how
Our sales, implementation, training and
gathered in more than 10 years and covers virtually any
customer care teams can have operations back
on track inside of three months. process inside a travel agency. Permanently updated and Business Plus - Lufthansa City Center
(part of Thomas Cook Czech Republic)
Andrei Raileanu - Director Mid Back Office with around 1.000 features added every year, TINA offers
Solutions at dcs plus you: dcs plus won our international tender for a new mid office system for
the Business Travel department and till today, two years after
• Complete management of clients and suppliers implementation, we are convinced that this was absolutely the best
decision. Especially the willingness to react promptly to our additional
• Management of requests and sales through every requirements is very convincing and we love many features of the
possible channel software, especially the comprehensive reporting tool. Not only the
• Generation and management of travel and fiscal software but also the people in dcs plus are the reason that we
strongly recommend a cooperation.
documents
• Powerful reporting tools Udo Wichert, CEO Thomas Cook s.r.o.
• Connection to 3rd party software via webservices
• Complete customizable interface through the Custom
Workspace Module
9
www.dcsplus.net
Online reservations and distribution,
IBE and XML connectivity
Technology at work for you
dcs plus’ product, TBS (Travel Booking System), is an Your preferred content providers can be
advanced front office system that integrates the largest also integrated in TBS.
travel suppliers on the market, processing a wide range of
services (air, hotel, cruise, car rental, transfers, insurance, Our reservation system focuses on:
activities, packages etc). The travel service passes from • Content quality
the supplier through the travel agency rules, filters and • Fast availability
restrictions to the selling platform (resellers, online, • Volume management
corporate, website etc.). TBS consolidates the content • Automation
from the providers, lively makes the booking with the • Best price result
suppliers and keeps track of the reservations all the way to • Credit card payment
the invoices and reporting.
TBS facilitates sales on multiple selling
The reservation system is built on a versatile platform, channels at once - resellers, corporate
being able to integrate any travel service and supplier. accounts, online sales or website sales,
Currently, TBS works with the following services and with credit card payments/bank
TBS
suppliers: transfers etc.
operates reservations
for any type of travel service: • HOTELS: Expedia, GTA, Miki, Hotelbeds, HotelsPro,
HotelsPro Turkey, Go Global, JacTravel, Jumbo, Tourico,
Travco, Special Tours, DOTW, Eurotours, HS Travel, Hua
Min, Daiei Tokyo, ATI, ASA, Hotusa/Restel, RoomsXML,
Flight Hotel Car/Transfer Insurance Cruise Activity Package Academservice, AC Tours, AIC Travel, Hana Tours, Hong A platform flexible enough to accommodate any
Thai Travel, WHL travel service and any supplier with fast
• AIR: Amadeus, Worldspan, Galileo, Travelfusion integration time, without losing its robustness.
Alexandru Bararu - Director API/ XML Front
• CRUISES: MSC Cruises
Office Solutions at dcs plus
• CAR HIRE: Sixt
• TRANSFERS: GTA
• INSURANCE: Mondial Assistance, Generali, Omniasig
• ACTIVITIES: Hotelbeds, GTA, Tourico
• PACKAGES: own packages (from AIDA)
10
www.dcsplus.net
Tour operator software
From inventory management
and package pricing to online sales
Due to their complexity and large range of possibilities, the • Provide a high level customer service
composed travel offers - packages, tours, charters etc. - • Handle complex packaging, both static and dynamic
are very difficult to handle and manage. But we want to • Configure capacities, prices, availability, promotions
make your job easier and more efficient. • Use a comprehensive module for price definition
• Define complex cancellation policies with multiple rules
When you are a tour operator, you need to create and • Automatically manage the reservations, together with
update inventories and distribute travel services to the voids and refunds and issue invoices for the resellers
resellers or directly to your customers… and you need to • Use advanced document templates for vouchers,
do all this in a unitary fashion. This motivated dcs plus to proformas and travel documents, thus ensuring flexibility
develop AIDA, a software application that covers both and adaptability
inventories and distribution for tour operators, resellers • Use advanced reporting tools
and third party front offices. • Distribute services through your own selling platform or
via webservice
By using the specific modules inside AIDA, you can easily
and effectively manage situations such as:
AIDA
provides inventory management
• Inventory availability and pricing
and distribution for services such as:
• Hotel room and cruise ship cabin allotment and
guaranteed inventory
• Transportation inventory and seat management
Having a detailed, yet flexible and • Transfers Accommodation Transportation Other services Packages
comprehensive tool to manage their own
• Local excursions, multi-day tours etc.
products, enables the tour operators or DMC
companies to increase the sales while having a
transparent control of their costs. Functionally rich and easy to use, AIDA helps you to:
Octav Stan - Director Tour Operator
Solutions • Enjoy the benefits offered by a flexible and modular
structure
• Sell travel packages efficiently
11
www.dcsplus.net
Inventory and distribution
AIDA - Advanced Inventory and Distribution companies or groups, through resellers, own travel agents,
Application - successfully comprehends all the features third party front offices and online websites, AIDA is the
needed for travel distributors, for all travel services: solution you were waiting for.
• Accommodation (hotels, villas, apartments etc.) Even if it started from the idea of a complete travel
• Transportation (air, bus, car etc.) distribution system and front office, now AIDA includes
• Transfer services (car, van, minibus etc.) also mid office and reporting features in order to create a
• Other services (meals, cruises, trips, tickets etc.) comprehensive solution and gives you, as a tour operator,
• Packages (tours, vacations, city breaks, shopping, all the tools needed to fully control the business (automatic
adventure, health etc.) invoicing, reservation management, full control over the
documents layout generated within the system, report
This inventory and distribution solution can easily bring templates builder etc.).
you ahead of competition.
AIDA is a very flexible platform, offering fast changes
As a plus, being developed using the latest technology and to components structure:
concepts, AIDA is very flexible and allows immediate • Capacity
changes to components’ structure (capacities, pricing, • Pricing
service links and restrictions, packages etc.), keeping the • Service links and restrictions
pace with the dynamic market trends. • Packages
• Last minute
AIDA is ideal for tour operating agencies that create their • Early booking
own packages (holidays, tours, city breaks, trips etc.) as • Special offers
well as resell other services and packages from third party
tour operators, and require a way to automatically manage
the availability, inventory and prices.
12
www.dcsplus.net
Static and dynamic packaging
13
www.dcsplus.net
Multiple points of sale
14
www.dcsplus.net
Select between various distribution channels
the one(s) that best match your business strategy
in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’];
calling
is $CFG;
is specified
used because
set() * when
// realpath
@var calling
cwd may is used
int */ protected
change
because
set() * @var $defaultExpireT
in __destructor(s)
cwd may change
// sets the mem cache
int */ prot
- \Ain
//
TBS
DETERMINE THE EXPIRE DETERMINE
TIMESTAMP THE EXPIRE
if (is_numeric($expireTime)
TIMESTAMP if (is_numeric($expireTime)
and $expireTime>0) { and $expireTimestamp=TIMESTAMP+$expireTime;
$expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (sub
‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break;
‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break;
case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: case ‘h’:
Hotel providers $expireTimestamp=TIMESTAMP+$expireTime*3600;break;
$expireTimestamp=TIMESTAMP+$expireTime*3600;break;
(!$expireTimestamp) (!$expireTimestamp)
$expireTimestamp = $expireTimestamp
case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break;
TIMESTAMP + $this->defaultExpireTime;
case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break;
= TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent // FILE CONTENT =$expire$fileContent
default: $expireTimestam
Timestamp =$expire .serialize($value)Timestamp
defau
; /
B2B
return \File::create($params[‘fileLocation’].$params[‘fileName’],
return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /**$fileContent); * Gets a value} stored /** *inGets the a cache
valuefor stored
a certainin the key.cache * @param
for a certain String ke
3 Party Suppliers
Selling Channels
Corporate ($expirationTimestamp<TIMESTAMP)
string
delete
null;
key
from
e===false)
thedisk
- deleted
* @return
expired
else return
$this->delete($key);
return
onlyBoolean
cache from disk $this->delete($key);
$value;null; }else
that key- indicating
/**return
* Deletes
* @return ifBoolean
return null; } // getreturn
$value; the }value
the key-was
/** stored
indicating
* Deletes
correctly
contents,
in the
if thedeleted
the
null;unserialize
cache
key was
valuefor
in the
} // get contents,
stored
it and return
a certain
correctly
in the
cache deleted
key.
*/ public
cache
unse
*@
infun
it
fo
the
Travel Booking System • Interface for their account INPUTS if (!$key) return INPUTS true;if (!$key)
>getFileDetails($k); >getFileDetails($k);
// CHECK return
THE true;
FILE $return=1;
// CHECK THE // AFILE
// check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’]))
LIST$return=1;
OF KEYS TO//BE
// check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’]))
A LIST
DELETED OF KEYS if (is_array($key))
TO BE DELETED{ ifforeach($key
continue; // delete the
(is_array($key))
continue;
file
as $k)
// delete the file
{ {foreach($key
// get file p
$return=array();
the whole
the key //
* @return
\Dir::delete($this->cachePath);
cacheBoolean
content.- indicating
} /** * Returns an associative
$return[‘key’]=$key;
* @return ifBoolean
} /** * Returns
associate the key $return[‘key’]=$key;
the cache
array of andetails
- indicating
was completely
associative for a array
if the cache
certainofkey:
// get more details $params=$this->getFileDetails($key);
destroyed
details
was completely
thefor
* @see \Engine\Cache\BaseInterface::flush()
filealocation,
certain key:
// get more details $params=$this->getFileDetails($key);
destroyed * @see \Engine\Cache\BaseIn
the expire
the filetimestamp
location, the * @return
$fullFileName=$params[‘fileLocation
expire timestamp Array */
$fullFileName=$
*/ p
XML
$return[‘keyHash’]=$params[‘keyHash’];
$return[‘keyHash’]=$params[‘keyHash’];$return[‘fileLocation’]=$params[‘fileLocation’];
$return[‘fileLocation’]=$params[‘fileLocation’];
$return[‘fileName’]=$params[‘fileName’];$return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesiz $re
time $fc=\File::getContents($fullFileName);
time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc,
$return[‘expireTimestamp’]=substr($fc,
0, 10); $return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_F
0, 10); $return[‘expireTimestampReadable’]=date
Selling rules
$return[‘expireTimestamp’]);
$return[‘expireTimestamp’]);
// get the value $return // get [‘cachedValue’]
the value $return =unserialize
[‘cachedValue’]
(substr($fc, =unserialize
10)); } (substr($fc,
return $return; 10));} } <?php return namespace
$return; } }Engine\Plugi <?php nam
registering a new plugin.registering
It is extended
a new plugin.in a class It iswhich
extended is automatically
in a class which * executed.
is automatically * @author * executed.
Gabriel */* abstract @authorclass Gabriel RegisterAbstract
*/ abstract class { /** RegisterAbstra
* The list o
plugin. * @var Arrayplugin.- associative
* @var array
Array of -[triggerName][index][\Engine\Plugins\Observer
associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected object] $observers=array();
*/ protected $observers=array();
/** * The constructor /*
* Returns
$obs) { // check trigger
} $this->observers[$triggerName][]
register();
the list/** of observers
* Returns for
$obs)
} $this->observers[$triggerName][]
name
{ // ifcheck
= $obs; } /** * The=method
thethe
(!$triggerName)
listcurrent
of observers
trigger name{ ifthrow
$obs; }will
* @return
/**
for themultitype:
add
(!$triggerName)
* new
current * */
new \Exception(‘The
The methodobservers
@return
{ throw new
willtoadd
public multitype:
thenew
trigger
current
function getObservers()
\Exceptio
observers
name
plugin t(
*/ public fu
} <?php namespace App\EntityMatch;
} <?php namespace /**App\EntityMatch;
* The class manages /** *the The matching
class manages of suppliers the matching
* @author of suppliers
Gabriel */* class @author Supplier Gabriel extends */ class \App\EntityMatch\Bas
Supplier extends
$entityClassName = ‘\Entity\Supplier’;
suppliers.
$matchingFilteringSQL
$entityClassName =
*/ protected
= ‘AND ent.deleted=0=AND
/**
* @var$exportFields
Array */ protected
‘ANDent.idRealSupplier=0’;
= array(‘alias’,
$exportFields
ent.deleted=0 AND ent.idRealSupplier=0’;
‘\Entity\Supplier’;
* (non-PHPdoc) /** * @see* (non-PHPdoc)
‘company’);= array(‘alias’,
/** * The class name
/** * Extra
\App\EntityMatch\BaseAbstract::setTableName()
/**
‘company’);
which * The
filters - deleted
willclass
/** * Extra
instantiate
* @see \App\EntityMatch\BaseAbstract::setTableName()
name which
and duplicated
the Entity
filters - deleted
will instantiate
suppliers
- used for primaryKey
*/ protected function*/
and are duplicated
not allwed.
the Entity - search
setTableName()
protected func{
used f
sup
setFiscalCode($value)setFiscalCode($value)
name.supplier
>setSearchCriterion(‘company’,
* @paramcompany
$value, $useWildcard);
{ $this->setSearchCriterion(‘fiscalCode’,
Boolean
name.$useWildcard
$value,
* @param Boolean
} /**$useWildcard);
* Sets a search
{ $this->setSearchCriterion(‘fiscalCode’,
- uses wildcard
} by
$useWildcard
/**supplier
$value, false); } /**$value,
for this
* Sets fiscal
- uses
search
a search
* Searches false);
code.
a}
wildcard
by *
or not.for*/this
supplier
supplier
@param
/** *by
public
fiscal
Searches
search
String
function
code. $value
its code.a supplier
or not.
* @param
* @param
setCompany($value,
*/ public function setComp
- the supplier
by String
String $value
its code.
fiscal code.
$value
* @param
$useWild
- the */
* @param
supp
Strin
>setSearchCriterion(‘code’,
code */
\Exception(“The
(iata,
code `{$codeName}`
public
>setSearchCriterion(‘code’,
tktCode,
function
supplier code
etc)
is not
setCode($value,
*/ public function
`{$codeName}`
$value, false, array(‘codeName’=>$codeName));
valid - you can isonly
$codeName)
setCode($value,
notuse:
{ // check
valid“.implode(‘,
$value, false, array(‘codeName’=>$codeName));
- you can only
} /** * Searches a }
$codeName)
the code name { // check global the$CFG;
‘, array_keys($CFG[‘travel’][‘standardCodes’])));
code name if (!$CFG[‘travel’][‘standardCodes’]
use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’])
supplier
/** *by Searches
the matching a supplier manually
global $CFG; if (!$CFG[‘trav
by theset matching
} // add the sea
in the externalmanually reser
set
@param String $name @param
- the supplier
String $name name * - the
@paramsupplier Int name
$idExternalResSystem
* @param Int $idExternalResSystem
- the external system- the */ public
external function
system setExtResSysName($name,
*/ public function setExtResSysN $idExter
• Online booking platform >setSearchCriterion(‘extResSys’,
‘extResSys’:
dispatchSearchRecords($field,
/** a*special
protected function dispatchSearchRecords($field,
break; return case ‘extResSys’:
This method searchdoes for supplier
$value, $useWildcard,$valu
$this->searchRecords_extResSys($value,
return $this->searchRecords_
a special cod
Arr
$o
s
return parent::dispatchSearchRecords($field,
return parent::dispatchSearchRecords($field,
$value, $useWildcard,$value, $options); $useWildcard,
} } /** $options);* Searches}a } supplier
/** *by Searches
its code.a supplier* @param by String
its code. $value * @param
- the sup S
B2C
`ent`.`id`=`sc`.`idSupplier`
`ent`.`id`=`sc`.`idSupplier`
{$this->matchingFilteringSQL} {$this->matchingFilteringSQL}
“; $matchSupplierCodeEx “; $matchSupplierCodeEx
= $db->Execute($matchSupplierCodeSQL, = $db->Execute($matchSupplierCodeSQL, array($options[‘codeNam arra
(!$matchSupplierCodeEx) (!$matchSupplierCodeEx)
{ writeLog(“Could not { match
writeLog(“Could
supplier bynot theirmatch
code.”, supplier
‘events’, by their
9); throw
code.”,new ‘events’,
\Exception(‘Could
9); throw new not\Exception(‘Could
match supplier bynot theirmatchcode.’);
sup
rd
= array(); foreach($matchSupplierCodeEx
= array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] as $supp) { = $return[$supp[‘id’]]
$supp; } return $return; = $supp;} }/** return * Searches
$return;a } supplier
/** *by Searches
its matching a supplierin theb
* @param unknown $valuem * @param* unknown @param array $valuem $options* @param */ private
arrayfunction
$optionssearchRecords_extResSys($value,
*/ private function searchRecords_extResSys($value, Array $options) { global Array$DBTImportRes_supp
$options) { global $
code.’);
THE RESULT
{ match
writeLog(
} // RETURN $supp=
sup
`’.get_called_class().’`.’);
String */ protected
*/ protected function*/
for the class `’.get_called_class().’`.’);
/** $fileName=null;
* The filename with
constructInit()
protected function { // check
} // set the $this->fileNameWithPath=MODULE_REL_PATH
/** its * The
constructInit()
full path.
filename
if the file name
} // set the $this->fileNameWithPath=MODULE_REL_PATH
* @var
{ // check
with its
is okay
String
if the
full path.
*/ protected* @var$f
if (!$this->fileName
file name is okay
. FILEPATH_APPCONF.
S
Package providers
<?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The
default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this-
>cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @param Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; //
Webservice (!($xml instanceof \SimpleXMLElement))
(!($xml instanceof \SimpleXMLElement))
array from the parsedarray XML from
content the parsed
protected function getAll_rawData_fileNotExists()
throw new \Exception(‘The
return $this->getAll
XML content _rawData
protected function getAll_rawData_fileNotExists()
throw new
the}extracted
file XML
_rawData _extract DataFromFile($xml);
is not (class
a well
} /** * Sets the default
/** * Returns data after the the
= formed
‘.get_called_class().’
extracted
XML (class = ;‘.get_called_class().’
} /**
XML content
output
data after
* Sets in the
is parsed.
file = ‘.$this->fileName
casedefault
the XML
the XML
* content
output
file isinmissi
@paramis\SimpleX parsed.
; fil
case
(AIDA)
the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean -
indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE
THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’:
xml * @return Arrayxml
element in the internal
string); * @param Mixed
- the
element
string);
* @return
items listArray
attribute
$value
in the
* @param
or whatever
$this->itemsList.
- the value Mixed
- the items
internal attribute
of $value
output
the key;
listisorneeded.
The$this->itemsList.
- the
whatever
method can be called
* @throws
value of the
*/ output
abstract
The method
Exception
is protected
key; *- @throws
needed. */
multiplecan * times
function
be called
if input isException
abstract
bad */ public
getAll_
andmultiple
protected rawData
they will *overwrite
- if inputfunction
function
times and
is bad
_extractData
the
set($var,
getAll_ rawData
they
*/ public
older
FromFile
willvalues.
$value)
overwrite
function { //
_extractData
* @param
set($var,
(\ Simple XMLEleme
the olderString
check constructor
FromFil
values.
$value) { type
$var *
// c
$expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. >constructType!=’admin’) >constructType!=’admin’)
throw new \Exception(“You throw new can use\Exception(“You
the method `”.get can use _called_
the method class().’::’.__FUNCTION__.”`
`”.get _called_ class().’::’.__FUNCTION__.”` only if the constructoronly type if is
the `admin`
constru
* @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE (!is_string($var)) throw (!is_string($var))
new \Exception(“You throw new did not \Exception(“You
set() a correctdid name
not set()
for the a correct
key.”); name // (re)setfor the onekey.”);
key $this->itemsList[$var]=$value;
// (re)set one key $this->itemsList[$var]=$val // update the ch
FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return >changedFlag=true; >changedFlag=true;
} /** * Sets all the }elements /** * Sets in the allinternal
the elements
attribute in the
$this->itemsList
internal attribute (rewrites
$this->itemsList
any old data(rewrites * already anystored).
old data The * parameter
already stored). is an as Th
$value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { be * stored one by one. be **stored
@param oneArray
by one. $vars * @param
- associative Arrayarray
$vars of -keys
associative
* @throws arrayException
of keys *- @throws if input isException bad */ public - if inputfunction is bad setAll($vars)
*/ public function { // check setAc
foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) >constructType!=’admin’) >constructType!=’admin’)
throw new \Exception(“You throw new can use\Exception(“You
the method `”.get_c can use alled_class().’::’.__
the method `”.get_cFUNCTION__.”` alled_class().’::’.__ onlyFUNCTION__.”`
if the constructoronly type if is
the `admin`
constr
return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see \Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file (!is_array($vars)) throw (!is_array($vars))
new \Exception(“You throw new did not \Exception(“You
setAll() a correct did notlist of
setAll()
elements a correct
- the listparameter
of elements is not - the
an array.”);
parameter // is(re)set
not ana array.”);
list of keys // (re)set
$this->item a list
location, the expire timestamp * @return Array */ public function getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more changed flag $this->changed
changed flag Flag=true;
$this->changed
} /** * (non-PHPdoc)
Flag=true; } /** * @see* (non-PHPdoc)
\App\Config\BaseAbstract::save_write_do()
* @see \App\Config\BaseAbstract::save_write_do() */ protected function*/ save_write_do()
protected funct
keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10); $return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get OBJECT TO BE WRITTEN OBJECT ON DISK
TO BEgets WRITTEN the simplexml
ON DISKobject gets the $xmlObj=$this->save_write_do
simplexml object $xmlObj=$this->save_write_do _getXml(); // checks_getXml(); that the object // checksis correct that (ittheisobject
a validissim cor
the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of instanceof \SimpleXMLElement))
instanceof \SimpleXMLElement))
throw new \Exception(‘The throw new output \Exception(‘The
of the methodoutput `’.get_ofcalled_class()
the method `’.get_ .’::save_write_do_getXml`
called_class() .’::save_write_do_getXml` must be a `SimpleXMLElem must
[triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the concrete classes. * @param String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs - FILE ON DISK // check FILEif the
ON file
DISK is writeable
// check if$fileChmod=false;
the file is writeable if$fileChmod=false;
(file_exists($this->fileNameWithPath))
if (file_exists($this->fileNameWithPath))
{ if (!is_writeable($this->fileNameWithPath))
{ if (!is_writeable($this->fileN $this-
the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using file exists but it is notfile
writeable.’);
exists but it} is else
not{writeable.’);
$fileChmod=true; } else {} $fileChmod=true;
// (OVER)WRITE THE } // FILE
(OVER)WRITE
CONTENT - THE if (!file_put_contents($this->fileNameWithPath,
FILE CONTENT - if (!file_put_contents($this->file $xm
the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype: */ public function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers * @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var >addErrorMessage(‘The >addErrorMessage(‘The
config file could not be config
(over)written.’);
file could not }be//(over)written.’);
if the file did not}exist // if -thechmod
file did it if not($fileChmod)
exist - chmod { if it (!chmod($this->fileNameWithPath
if ($fileChmod) { if (!chmod($th
Array */ protected $exportFields = array(‘alias’, ‘company’); /** * Extra filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @see >addErrorMessage(‘The >addErrorMessage(‘The
config file could not have config itsfile
acesscouldrights
not have
updated.’);
its acess } rights
} } /** updated.’);
* Returns } the} }SimpleXML
/** * Returns objectthe to SimpleXML
be written on object
disk.to*be @return
written\S o
\App\EntityMatch\BaseAbstract::setTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setAlias($value, $useWildcard=true) { $this->setSearchCriterion(‘alias’, $value, $useWildcard); } protected function save_write_do_getXml();
protected function save_write_do_getXml();} <?php namespace }Engine\Cache; <?php namespace /** *Engine\Cache;
The class is used /**to*manage The class theis cache
used to stored
manage on disk.the cache * @author storedGab on
/** * Sets a search by supplier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this->setSearchCriterion(‘company’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function implements \Engine\Cache\BaseInterface
implements \Engine\Cache\BaseInterface
{ /** * The full location { /** where* The the full
cache
locationfileswhere
are stored.the cache * @var filesString
are stored. */ protected* @var$cachePath=’’;
String */ protected /** * $cache
The p
setFiscalCode($value) { $this->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @param String $value * @param String $codeName - indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` reusing of keys if thereusing
application of keyswasifupdated.
the application * @varwas String
updated.
*/ protected
* @var$keyPrefix=’’;
String */ protected /** *$keyPrefix=’’;
The default expiration /** * The time default
limit, expiration
if no param timeis specified
limit, if no whp
is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manually set in the external reservations system configs. * @param String $name - the supplier name * @param Int $idExternalResSystem - the external system */ protected $defaultExpireTime
protected=$defaultExpireTime
2592000; // 2592000 = 2592000;
= one month // 2592000
/** * = The one class
month constructor.
/** * The */ class
publicconstructor.
function __construct() */ public function { global __construct()
$CFG; // realpath { glob
public function setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the rest of the criteria, it uses the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array change in __destructor(s)change - \App\Entity\Cache
in __destructor(s) caches - \App\Entity\Cache
the entity object caches in destructors.
the entity object $this-in destructors. $this-
>cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’];
>cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached // setskey the prefix mem $this-
cached key prefix $
AIDA AIDA
$options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field, $value, $useWildcard, $options); } } /** * Searches a supplier by its code. * @param String $value - the supplier code * @param Array $options - stores the
code name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; >keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’];
>keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** *return Returns true;
some } /**associative
* Returns array some of data associative
determined arrayfrom of data the determined
key * @param fromArth
$matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx) { writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’); } // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a getFileDetails($key) {getFileDetails($key)
$return=array(); // { rewrite
$return=array();
the key $key=$this->keyPrefix.’-’.$key;
// rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key);
// the key hash $return[‘keyHash’]=md5($ // the subdir fo
supplier by its matching in the external reservation systems. * @param unknown $valuem * @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE systemSupplierId=? AND $keyDir=substr($return[‘keyHash’],
$keyDir=substr($return[‘keyHash’],
0, 2); // the full file location 0, 2); // $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’;
the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’ $return[‘fileName
systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation systems code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by the external reservation systems code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if * Sets a value in the * cache
Sets fora value
a certain
in the key. cache* @param
for a certain Stringkey.$key * @param
* @param String
Mixed $key $value* @param* @param Mixed String
$value $expireTime
* @param*String @return $expireTime
Boolean - indicating* @return ifB
($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file (relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected the cache */ public function
the cache set($key,
*/ public $value,
function $expireTime=0)
set($key, $value, { //$expireTime=0)
CHECK INPUTS {////if CHECK key is not INPUTS set if // (!$key)
if key return
is not set false; if (!$key)
// do not return
storefalse;‘null’ values
// do not if
$fileName=null; /** * The filename with its full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this- // FILE AND DIRECTORY // FILEMANAGEMENT
AND DIRECTORY $params=$this->getFileDetails($key);
MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the // attempt
directory tochain
createifthe (!\Dir::create($params[‘fileLocation
directory chain if (!\Dir::create(
>fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if DETERMINE THE EXPIRE DETERMINE
TIMESTAMP THE EXPIRE
if (is_numeric($expireTime)
TIMESTAMP if (is_numeric($expireTime)
and $expireTime>0) { and $expireTimestamp=TIMESTAMP+$expireTime;
$expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (sub
(isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this->changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList;
$S[‘admin’][‘configApp’][$className][‘chan Selling Platform ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break;
‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break;
$expireTimestamp=TIMESTAMP+$expireTime*3600;break;
$expireTimestamp=TIMESTAMP+$expireTime*3600;break;
(!$expireTimestamp) (!$expireTimestamp)
$expireTimestamp = $expireTimestamp
case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break;
TIMESTAMP + $this->defaultExpireTime;
case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break;
case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break;
case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break;
= TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent // FILE CONTENT =$expire$fileContent
case ‘h’:
default: $expireTimestam
Timestamp =$expire .serialize($value)
case ‘h’:
Timestamp
defau
; /
return \File::create($params[‘fileLocation’].$params[‘fileName’],
return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /**$fileContent); * Gets a value} stored /** *inGets the a cache
valuefor stored
a certainin the key.cache * @param
for a certain String ke
stored value or null ifstored
the key value
was or not null
foundif the*/key public
wasfunction
not found get($key)
*/ public { function
// CHECK get($key)
INPUTS {if // (!$key)
CHECKreturn INPUTS null;if (!$key)
// CHECK return
THE FILE null; // // get
CHECK file params
THE FIL
>getFileDetails($key); >getFileDetails($key);
// check the file if (!file_exists($params[‘fileLocation’]
// check the file if (!file_exists($params[‘fileLocation’]
.$params[‘fileName’]).$params[‘fileName’])
or !is_readable($params or !is_readable($params
[‘fileLocation’].$params[‘fileNam [‘fileLocatio
string
delete
null;
key
from
e===false)
thedisk
- deleted
* @return
// get and check expiration
expired
else return
$this->delete($key);
return
onlyBoolean
that key- indicating
// get and
cache from disk $this->delete($key);
$value;null; }else /**return
* Deletes
* @return ifBoolean
timecheck $expirationTimestamp
the }value
the key-was
expiration time $expirationTimestamp
return null; } // getreturn
$value; /** stored
indicating
* Deletes
correctly
contents,
in the
if thedeleted
the
=substr ($fc, 0, 10); =subs
null;unserialize
cache
key was
valuefor
in the
} // get contents,
stored
it and return
a certain
correctly
in the
cache deleted
key.
*/ public
cache
if
unse
*@
infun
it
fo
the
TRIP
INPUTS if (!$key) return INPUTS true;if (!$key)
// CHECK return
THE true;
FILE $return=1;
// CHECK THE // AFILE
LIST$return=1;
OF KEYS TO//BE A LIST
DELETED OF KEYS if (is_array($key))
TO BE DELETED{ ifforeach($key (is_array($key)) as $k) { {foreach($key
// get file p
>getFileDetails($k); >getFileDetails($k);
// check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’]))
// check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the continue;
file // delete the file
Application
Modular Structure
15
www.dcsplus.net
Sales flexibility
Webservice - also known as API/ XML interface • Design a custom made front office selling interface - for a
special demanding reseller or for own agents, or even own
For maximum flexibility on client-side, webservices have site - the tour operator is free to build its own selling
been developed. They offer the travel agency the interface
opportunity to build custom-made front office interface or • Integration with 3rd party distribution systems. Large
fully customized integration with a website for online sales. distributors can be interested in reselling the tour
The webservices output opens the gate to unlimited operator's products. TBS can also be a distribution channel
possibilities in selling and distribution area. From a simple for AIDA - where AIDA can play the role of the wholesaler.
concept like custom websites for the B2C sales, we can
easily go to the next level - mobile apps world or even Whitelabel
other chains of distribution networks for resellers.
Whitelabel (B2B2C platform) - the extension of the online
Our reservation system, TBS, communicates with each of selling platform. It helps any reseller to open its own
the suppliers' systems over the network, based on online selling platform, based on the TBS client technology.
communication protocols (XML files/webservices) This feature gives travel agencies the chance to sell online
extracting the data from their database, in order to build via catchy websites.
its own database, later used to serve content to the
resellers. This ensures real time transactions with the You can open the online booking interface in whitelabel, in
connected parties. any website, whether travel oriented or not. This helps
you, as a TBS client, to increase the number of visitors, by
The B2C interface TRIP communicates via webservices opening the online sales platform to high rated websites.
with TBS, AIDA and TINA to send/receive data about the
travel services, to send booking requests and to extract
the fiscal documents for the online customer.
16
www.dcsplus.net
@ @
@
Website opened in
B2B Interface
@ @
Reseller
@
@
@
whitelabel
@ @
@
@
@
@
@ @
@ @ @
B2C Interface
@
@ @
website online booking if the file is writeable $fileChmod=false; if (file_exists($this->fileNameWithPath)) { if (!is_writeable($this-
>fileNameWithPath)) $this->addErrorMessage(‘The config file exists but it is not writeable.’); } else { $fileChmod=true; } //
(OVER)WRITE THE FILE CONTENT - if (!file_put_contents($this->fileNameWithPath, $xmlObj->asXml())) { $this-
>addErrorMessage(‘The config file could not be (over)written.’); } // if the file did not exist - chmod it if ($fileChmod) { if
(!chmod($this->fileNameWithPath, 0777)) { $this->addErrorMessage(‘The config file could not have its acess rights
updated.’); } } } /** * Returns the SimpleXML object to be written on disk. * @return \SimpleXMLElement */ abstract
@ @ @ protected function save_write_do_getXml(); } <?php namespace Engine\Cache; /** * The class is used to manage the cache
stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location
@ where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the
reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time
limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one
@ month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may
@ change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this-
>cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached
@ @ key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array
@ @ of data determined from the key * @param Array $key */ private function getFileDetails($key) { $return=array(); // rewrite
the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key
$keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’;
@ @ $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. *
@param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was
TRIP
correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if
B2C Interface
(!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT
$params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’]))
return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) {
$expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’:
$expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case
@ @ ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’:
@ $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp)
$expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp
@ .serialize($value) ; // WRITE THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent);
@ } /** * Gets a value stored in the cache for a certain key. * @param String $key * @return Mixed - the stored value or null
if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get
file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’]
.$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE
FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time
$expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk
@ @ $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10);
$value=@unserialize($valueSer); if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in
the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that
@ @ key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK
INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) {
foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if
@ @ (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file
$return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params
@ $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return
true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** *
Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see
\E gine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an
@ associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function
getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this-
@ @ @ >getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if
@ (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more
keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’];
@ @ @ $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time
@ @ $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10);
$return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return
@ @ [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is
@ @ used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */ abstract
3rd Party Suppliers
Website
class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of
[triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register
@ @ the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the
method is called from the defined method register() * in the concrete classes. * @param String $triggerName - the trigger
@ name * @param \Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not correct
@ @
Webservice @ Webservice developed
*/ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName)
{ throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this-
>observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the
addObserver() method. *
@
according to your
@ <?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the
preferences
prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var in
The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this-
>cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; }
Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyD
$return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $
correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE
attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTA
$expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimes
} if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return \File::create($par
stored in the cache for a certain key. * @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK T
file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘
0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unser
Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly d
return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exis
$return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].
TBS
$return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see
\Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function getInfo($key) { $return=array(); // ass
>getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else {
$return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getCont
$return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespa
extended in a class which is automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of [triggerName][
The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the
\Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger na
‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected
multitype: */ public function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers * @author Gabriel */ class Supplier extends \A
Array */ protected $exportFields = array(‘alias’, ‘company’); /** * Extra filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.
for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @see \App\EntityMatch\BaseAbstract::setTableName() */ protected function setTableName() { gl
by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setAlias($value, $useWildcard=true) { $this->setSearchCrit
name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this->setSearc
fiscal code. * @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @param
tktCode, etc) */ public function setCode($value, $codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}`
array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manu
supplier name * @param Int $idExternalResSystem - the external system */ public function setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtR
for supplier code (for the rest of the criteria, it uses the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array $optio
>searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field, $value, $useWildcar
$value - the supplier code * @param Array $options - stores the code name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN
FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Exe
(!$matchSupplierCodeEx) { writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’); } // STORE RESULTS $return = array(); foreach($ma
} /** * Searches a supplier by its matching in the external reservation systems. * @param unknown $valuem * @param array $options */ private function searchRecords_extResSys($value, Array $options) { gl
IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array
not match supplier by the external reservation systems code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by the external reservation systems code.’); } // RETURN THE RESULT $supp=$searchS
$return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file
the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PH
constructInit() { // check if the file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPa
a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_ca
into class attributes if (isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changed
>changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList; $S
* (non-PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this->fileNameWithPath)) return $t
(and check it) $xml=@simplexml_load_file($this->fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file =
return $this->getAll _rawData _extract DataFromFile($xml); } /** * Sets the default output in case the XML file is missing. * @return Array */ protected function getAll_rawData_fileNotExists() { return array();
Webservice Webservice
\SimpleXMLElement $xml - the parsed xml * @return Array - the items list or whatever output is needed. */ abstract protected function getAll_ rawData _extractData FromFile (\ Simple XMLElement $xml); /**
called multiple * times and they will overwrite the older values. * @param String $var - or the name of one key (a string); * @param Mixed $value - the value of the key; * @throws Exception - if input is bad */
>constructType!=’admin’) throw new \Exception(“You can use the method `”.get _called_ class().’::’.__FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_string($var)) throw new \Exc
>itemsList[$var]=$value; // update the changed flag $this->changedFlag=true; } /** * Sets all the elements in the internal attribute $this->itemsList (rewrites any old data * already stored). The parameter is a
$vars - associative array of keys * @throws Exception - if input is bad */ public function setAll($vars) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method
`admin`.”); // check inputs if (!is_array($vars)) throw new \Exception(“You did not setAll() a correct list of elements - the parameter is not an array.”); // (re)set a list of keys $this->itemsList=$vars; // update t
\App\Config\BaseAbstract::save_write_do() */ protected function save_write_do() { // GETS THE SIMPLEXML OBJECT TO BE WRITTEN ON DISK gets the simplexml object $xmlObj=$this->save_write_do _getXml
TRIP
instanceof \SimpleXMLElement)) throw new \Exception(‘The output of the method `’.get_ called_class() .’::save_write_do_getXml` must be a `SimpleXMLElement` object.’); // (RE)WRITE FILE ON DISK // check if
AIDA
if (!is_writeable($this->fileNameWithPath)) $this->addErrorMessage(‘The config file exists but it is not writeable.’); } else { $fileChmod=true; } // (OVER)WRITE THE FILE CONTENT - if (!file_put_contents($this->
could not be (over)written.’); } // if the file did not exist - chmod it if ($fileChmod) { if (!chmod($this->fileNameWithPath, 0777)) { $this->addErrorMessage(‘The config file could not have its acess rights update
\SimpleXMLElement */ abstract protected function save_write_do_getXml(); } <?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk
cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The de
protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) - \A
>cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; }
Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyD
Webservice
17
www.dcsplus.net
Solutions for PCOs
Are you a professional congress organizer? Then, for sure type of travel agencies. Starting with the definition of the
you need robust solutions to manage congresses, Congress Project, TINA delivers mainly no touch processes
conferences, seminars and other similar events. Make sure which take care of the mid-back office part of the sales
that you have all the necessary tools to ensure your process. In this respect: I would like to emphasize that we are very
conferences have resounding success. • all sales made, are automatically imported by TINA satisfied with your service and your team.
Frida Mizrahi, Global Sales Manager -
• invoices and payment information is automatically Kenes International
Flexible and efficient, the software solutions provided by managed and all information is sent to the Ledger
dcs plus are capable of meeting the most challenging Application for further consolidation
demands of PCOs. • the standard reporting module of TINA offers access to
all data related to sales and CRM by using easily definable
TBS XML consolidator allows you to immediately bring report templates that can be generated manually or
available travel services from 3rd party providers into the automatically.
sales mix. An XML connection to several wholesalers might
simplify your work by bringing extended availability You can also benefit of our B2C enterprise platform TRIP.
directly into the reservations system. You, as a congress organizer, have a set of particular
requests, which are not standard for an OTA, but critical
With the right professional
Later on, adding flights or other ground services (transfers, for congresses, such as:
tools, your event cannot be
activities etc.) would reduce the amount of work on the • Scalability - the possibility to add new services
anything but a success.
travel services purchasing, allowing you to focus on the • Diversity - create different interfaces for the specific of
congress itself and its participants. each congress – multiple interfaces can connect to a single
platform engine
Moreover, when planning great events with large numbers • Split Reporting - with the congress module, reports can
of participants, you have a good opportunity to negotiate be generated for sales per each congress
good rates with the hoteliers. Manage this content within • Multiple payment accounts - a different account can
your own inventory system - AIDA - with the possibility to be defined for every congress
further distribute this content to different selling channels, • Intuitive interface - features specific for congresses:
both B2B and B2C. venue icon on the hotels map, automatic calculation for
distance between hotels and venue location, venue
As part of the Professional Congress Organizer Platform, description etc.
TINA delivers unparalleled automation features for this
18
www.dcsplus.net
Supplier 1 Congress 1 booking website
AIDA TINA
in the external reservation systems. * @param unknown $valuem * @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searc
systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation systems code.”, ‘events’, 9); throw new
$supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract {
subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @see \App\Config\BaseAbstract:
throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it c
constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if (isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$classN
>changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList; $S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$th
function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this->fileNameWithPath)) return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it) $xml=@simplexml_load_file($this->fileName
XML (class = ‘.get_called_class().’ ; file = ‘.$this->fileNameWithPath.’).’); // return the array from the parsed XML content return $this->getAll _rawData _extract DataFromFile($xml); } /** * Sets the default output in case the XML file is missing. * @return Array */
The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this-
XML content is parsed. * @param \SimpleXMLElement $xml - the parsed xml * @return Array - the items list or whatever output is needed. */ abstract protected function getAll_ rawData _extractData FromFile (\ Simple XMLElement $xml); /** * Sets an element in t
TH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @param Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key
older values. * @param String $var - or the name of one key (a string); * @param Mixed $value - the value of the key; * @throws Exception - if input is bad */ public function set($var, $value) { // check constructor type if ($this->constructType!=’admin’) throw new
lic function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default:
`admin`.”); // check inputs if (!is_string($var)) throw new \Exception(“You did not set() a correct name for the key.”); // (re)set one key $this->itemsList[$var]=$value; // update the changed flag $this->changedFlag=true; } /** * Sets all the elements in the intern
Timestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. * @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check
the keys will be * stored one by one. * @param Array $vars - associative array of keys * @throws Exception - if input is bad */ public function setAll($vars) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method
=substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if
(!is_array($vars)) throw new \Exception(“You did not setAll() a correct list of elements - the parameter is not an array.”); // (re)set a list of keys $this->itemsList=$vars; // update the changed flag $this->changed Flag=true; } /** * (non-PHPdoc) * @see \App\Confi
Distribution
was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function
useWildcard=true) { $this->setSearchCriterion(‘company’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @param String $value * @param String $codeName - indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, false,
\App\Entity\Cache caches the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return
** * Searches a supplier by the matching manually set in the external reservations system configs. * @param String $name - the supplier name * @param Int $idExternalResSystem - the external system */ public function setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the rest of the criteria, it uses the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value,
function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation
ent::dispatchSearchRecords($field, $value, $useWildcard, $options); } } /** * Searches a supplier by its code. * @param String $value - the supplier code * @param Array $options - stores the code name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx) { writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new
a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS //
their code.’); } // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external reservation systems. * @param unknown $valuem * @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation systems code.”, ‘events’, 9);
DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) { $expireTimesta
supplier by the external reservation systems code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file (relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this-
$expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default
u did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if (isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this->changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will
>defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. * @para
$S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList; $S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this->fileNameWithPath)) return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it) $xml=@simplexml_load_file($this->fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this->fileNameWithPath.’).’); // return the array from the parsed XML content return $this->getAll _rawData _extract DataFromFile($xml); } /** *
// CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return nu
ile is missing. * @return Array */ protected function getAll_rawData_fileNotExists() { return array(); } /** * Returns the extracted data after the XML content is parsed. * @param \SimpleXMLElement $xml - the parsed xml * @return Array - the items list or whatever output is needed. */ abstract protected function getAll_ rawData _extractData FromFile (\ Simple XMLElement $xml); /** * Sets an element in the internal attribute $this->itemsList. The method can be called multiple * times and they will overwrite the older values. * @param String $var - or the name of one key (a string); * @param Mixed $value - the value of the key; * @throws Exception - if input is bad */ public function set($var, $value) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can
expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserializ
:’.__FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_string($var)) throw new \Exception(“You did not set() a correct name for the key.”); // (re)set one key $this->itemsList[$var]=$value; // update the changed flag $this->changedFlag=true; } /** * Sets all the elements in the internal attribute $this->itemsList (rewrites any old data * already stored). The parameter is an associative array and the keys will be * stored one by one. * @param Array $vars - associative array of keys * @throws Exception - if input is bad */ public function setAll($vars) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get_c alled_class().’::’.__ FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if
cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that k
(“You did not setAll() a correct list of elements - the parameter is not an array.”); // (re)set a list of keys $this->itemsList=$vars; // update the changed flag $this->changed Flag=true; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::save_write_do() */ protected function save_write_do() { // GETS THE SIMPLEXML OBJECT TO BE WRITTEN ON DISK gets the simplexml object $xmlObj=$this->save_write_do _getXml(); // checks that the object is correct (it is a valid simplexml object) if (!($xmlObj instanceof \SimpleXMLElement)) throw new \Exception(‘The output of the method `’.get_ called_class() .’::save_write_do_getXml` must be a `SimpleXMLElement` object.’); // (RE)WRITE FILE ON DISK // check if the file is writeable $fileChmod=false; if (file_exists($this->fileNameWithPath)) { if
) $this->addErrorMessage(‘The config file exists but it is not writeable.’); } else { $fileChmod=true; } // (OVER)WRITE THE FILE CONTENT - if (!file_put_contents($this->fileNameWithPath, $xmlObj->asXml())) { $this->addErrorMessage(‘The config file could not be (over)written.’); } // if the file did not exist - chmod it if ($fileChmod) { if (!chmod($this->fileNameWithPath, 0777)) { $this->addErrorMessage(‘The config file could not have its acess rights updated.’); } } } /** * Returns the SimpleXML object to be written on disk. * @return \SimpleXMLElement */ abstract protected function save_write_do_getXml(); } <?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** *
e stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data
ay $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT
// attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return
rams[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. * @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it
nserialize($valueSer); if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this-
if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see \Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or
fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10); $return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin.
gerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the concrete classes. * @param String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to
er() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype: */ public function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers * @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var Array */ protected $exportFields = array(‘alias’, ‘company’); /** * Extra filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) *
etTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setAlias($value, $useWildcard=true) { $this->setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this->setSearchCriterion(‘company’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function
chCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @param String $value * @param String $codeName - indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manually set in the external reservations system configs. * @param String $name - the supplier name * @param Int $idExternalResSystem - the external system */ public function
ResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the rest of the criteria, it uses the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field, $value, $useWildcard, $options); } } /** * Searches a supplier by its code. * @param String $value - the supplier code * @param Array $options - stores the code name used */ private
ray $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx) { writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’); } // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external reservation
* @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation systems code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by the external reservation systems code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this-
SupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file (relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName; }
ta in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if (isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this->changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList; $S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @see
ata() */ protected function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this->fileNameWithPath)) return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it) $xml=@simplexml_load_file($this->fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this->fileNameWithPath.’).’
19
www.dcsplus.net
Corporate travel
As several TMC representatives are using TINA worldwide, • Clients Feedback - based on pre-selected criteria, TINA
it includes a multitude of tools to help with the builds satisfaction forms and sends them to the customer
management of your corporate customers: for feedback. The responses are stored, monitored and
Local TMC representatives
that use dcs plus products: interpreted (while the extreme responses are automatically
• Corporate Extranet - a website that helps your highlighted).
corporate customer to communicate efficiently and • Reporting - TINA can provide specialized corporate
secured, directly with the ERP system; this allows for reports in a variety of formats (iBank, Amex BTA, Tamara,
automated management of travel policy, quality control, MIS, GROs, Mastercard, GEMS, PRISM, HRG etc.).
approval workflow as well as a full record of the
communication between the corporate customer and the Additionally, the specialized reports have a quality control
travel agent (response time, request details etc.) mechanism built-in, which signal any errors before
• Corporate customer profile - allows the storage of submission.
your corporate customers’ profiles inside TINA (travel
policy, service fee, discount, deposit and credit, One possible solution to all of the above benefits (and
contracts management, KAM management, quality many more) is the Corporate Extranet extension module of
control etc.) TINA. In a nutshell, the Corporate Extranet is a website
• Credit Limit Management - automated management that helps your corporate customer to communicate
of client’s debts level with automated warning efficiently and secured, directly with the ERP system
messages (customized templates) for both Credit (TINA).
Controller and customer
Offer your corporate clients • Sales Force Automation - includes the monitoring of Ready to take it one step forward?
the possibility to: the sales process together with real-time reporting of
customers’ activity (volumes, payments, evolution over Give your corporate customer a powerful self-booking
• Input orders in a structured way
time, delayed payments etc.) tool by using the TBS self-booking tool.
• View their status
• Customers segmentation - can be done manually or in
• Get real time information about invoices,
payments, reports an automated manner (the application checks for the
• Manage their cost centers and other cost desired criteria and automatically upgrades or
related information etc. downgrades the customer status)
20
www.dcsplus.net
Make sure that your travel offers are available
to anyone, anywhere, anytime - quickly and easily
Custom Apps
• Customized website selling platform • Attract new customers on mobile, whilst more travelers
• Mobile applications use their smartphones or tablets to book their trips
• Booking kiosks • Distribute your travel services through the mobile
... all based on TRIP engine channel
• Let you customers plan their trip using their favorite
Increase your market reach and mobile devices
• Make it easier for your customers to manage their travel
revenue potential by selling itineraries
your services across a diversity • Diversify and boost the mobile sales with applications on
Android, iOS, Windows Mobile, BlackBerry etc.
of channels.
Booking kiosks
Customized selling platform
• Make your services and offers available anytime,
• Stand out in the crowded marketplace with a fine-tuned anyplace
online selling platform • Meet customers’ demand for self-service
Travellers are increasingly confortable and • Impress the audience and potential customers by • Enjoy significant cost savings with reduced front desk
savvy at planning, researching and booking offering multiple travel services on a different, unique and staffing
trips on the go. And because new trip fun
attractive website • Impress your customers and differentiate from
begins during its planning, offer your
customers a feature-reach experience, • Funnel end-users in the direction you want competitors with offers displayed on wide touch screens
combined with accessibility, usability, flexibility • Design it according to the identity of your company and sophisticated interfaces
at the right time and in the right place. • Use features that cannot be found in your competitors’ • Surprise and engage customers with outstanding widgets
Alexandra Luca, Marketing Specialist at websites (e.g. weekend flights, destination information, and gadgets etc.
dcs plus
instant results etc.)
... and many more benefits
21
www.dcsplus.net
Reliable services
Consultancy
Above the complete suite of business critical travel dcs plus has been designing and developing business
software solutions, dcs plus offers you an extended service critical travel software solutions since 2002. Major travel
portfolio: agencies in over 40 markets are using solutions provided
by dcs plus, such as web based travel ERP, inventory
'At dcs plus, expertise translates • Consultancy and distribution applications, online booking
into professional and reliable systems, B2B, B2C and B2B2C interfaces.
• Operational audit
consultancy'.
• Customizations
Alexandru Bararu - Director API/ • Trainings We answer the evolving needs of the market:
XML Front Office Solutions at dcs • Courses
plus • Design and development of business critical travel
applications
• Deployments in more than 40 markets
• Market assessments and market adaptations
• ERP system deployment and implementations into some
During the past 13 years of the largest travel agencies in Eastern Europe (hundreds
of agents)
of solutions design in • Online reservations systems
• B2C and B2B sales, credit card payment
travel business, we • Inventory and distribution, dynamic packaging
• XML communication and integrations
gathered an extensive • Travel business and opportunities consultancy
• Applications maintenance
experience in the field • Call center and support
22
www.dcsplus.net
Operational audit
23
www.dcsplus.net
Custom solutions are
as individual
Customizations as your business is
Products opened for personalization Solutions adaptable to your business needs
As clients’ demands become more sophisticated and Customizations and enhancements to software solutions
channeled to market evolution, the necessity of being are often needed. Your preferences and expectations will
flexible and adaptable becomes essential. We always seek be met after a careful analysis made by our professionals.
to understand, meet and even exceed our customers’
unique needs, as we are aware that this has a significant General improvement versus customization policy
impact on our competitiveness. So we are always opened
to adapt our products and services to your individual Each additional requirement, regardless of its nature
requirements. (functionality modification, layout etc.) is thoroughly
analyzed by dcs plus team of consultants together with
TINA, TBS and AIDA are delivered with standard you. This process classifies the development into 3 major
interfaces, with core functionalities and plug-ins. Since all categories:
of them are business critical applications and are deeply
integrated into the client’s company structure, most of our 1. General product improvement
major clients required custom-made modifications in order 2. Partial product improvement
to better adapt the products to their particular business 3. Custom development for you
profile.
Depending on the category the development fits into, it
The online platform needs to offer a highly personalized can be:
experience to your customer, so we've built TRIP using a • Scheduled for general product development plan and
modular structure, enabling a high level of customization delivered free of charge for each user of the product
to meet your requirements. • Specially developed for you, as additional plug-in.
24
www.dcsplus.net
Effective and comprehensive
training sessions
Trainings
25
www.dcsplus.net
We do not only develop and maintain
the software, but we also provide the
Courses services that surround it
The business environment (and especially the travel While the courses have a managerial orientation, the
industry) is in a continuous motion and change. Thus, the training sessions are focused on the selling and operational
ability to make fast decisions, to be permanently up to processes.
date with the industry peaks and nevertheless to have a
team of professionals, who know and understand all the Having a wide experience in the travel area and getting in
business mechanisms in depth, can make the difference touch with various business profiles from different
between success and failure. markets, our consultants can help your team in acquiring a
professional global overview on the industry. In addition to
Travel software courses this, they can assist in defining the optimal workflow for
your business, so that the software technology can be used
Our consultants are always ready to share their experience at its full efficiency.
by offering customized courses adapted to the specific of
your business. These courses capture both detailed Your needs are at the basis of our products and we want to
presentations of the applications’ administration and help you achieve your goals and figures. We truly hope you
reporting tools. Furthermore, we present our own vision will take advantage of our knowledge and experience to
regarding business concepts that are essential for travel make your business bloom.
industry, such as:
• Determining key control points and implementing them We are permanently connected
in specific reports to the changes in many markets
• Defining advanced corporate profiles
• Applying a unitary workflow able to cover the specific around the world, as well as to
elements of a service or activity that your agency provides
• Being able to effectively adjust the workflow based on
your needs and always ready to
market requirements help you grow and evolve by
adding value to your business.
26
www.dcsplus.net
PRODUCTS’ STRENGTHS, USABILITY, KEY FEATURES & BENEFITS
TINA is perfect for: interactive reporting, specialized reports for 3rd party
partners (GRO, PRISM, iBank, HRG, Tamara, MIS, GEMS,
• Travel management companies
Mastercard, SAP etc.).
• Business travel agencies
• Forecasting tools - It combines historical data, real-
• Leisure travel agencies
time data, access to the entire database, the powerful
• Online travel agencies
reporting module and sales force automation with data
• Single-site travel agencies
mining and business intelligence algorithms. This makes
• Travel search companies
TINA the most suitable tool for your business forecast.
27
www.dcsplus.net
Benefits
• Automation - TINA is able to automate every single or manager, can define by himself or with the help of the
process inside your travel agency. For example: client customer care department the report templates needed.
invoicing, supplier invoices import, automatic settlements • Exporting and importing to and from accounting
between client and supplier invoices, quality control check software - TINA includes in its default configuration an
for extra information, reporting and travel policy. By import/export mechanism for integration with pure
allowing TINA to handle these processes in an automatic accounting software.
manner, you can focus the existing resources to more • 3rd party software connection via web services -
creative and productive activities for your business. Above Your travel agent is able to launch multiple parallel
this, you avoid the human error factor. searches from TINA into the suppliers' systems, receive
• Management of customers - All types (resellers, price quotation, create bookings and import all the data
corporate, individuals), general and fiscal details, inside TINA in order to use it.
management of offices, customer profile management,
statistics, credit limits, deposits, management of Extra Benefits
duplicated accounts, frequent buyer cards management TINA also offers you the following optional modules:
etc. • Corporate Extranet - A website able to communicate
• Management of suppliers - General details, fiscal with TINA via webservices in order to extract and display
details, product names, prices from contract, invoicing different pieces of information from the mid office
profiles, commission management, CRM data etc. application (TINA).
• Management of request and sales - Input of all • Credit limit management - Enables your travel agency
requests (no matter the channel), processing the requests to use automatic segmentation and setup the credit limit
according to the approved workflows and procedures, for each client inside TINA.
parallel multiple searches inside 3rd party systems, • Supplier price management - Enables you to input
searching inside own inventory etc. prices for hotels that have a direct contract with your
• Management of travel documents - Your travel agent travel agency. Full and ergonomic price definition is
has the possibility to generate the travel document he possible (even fixed periods). Now, the prices can be easily
needs in a variety of formats (pdf, doc, docx, xls, rtf, html used inside the service offer module.
etc.). • XML 3rd party import interface - Role: to allow the
• Management of fiscal documents such as: client import of 3rd party reservation content inside TINA.
invoices, client payments, supplier invoices, supplier • Payment import functionality - Role: to allow the
payments, settlements between client and supplier integration of financial content from accounting
invoices. applications as well as from electronic invoice formats.
• Documents template manager (DTM) - Allows you to • MICE extension - Does your travel agency offer
completely customize the look and feel of the documents packages for meetings, incentives, conventions and
generated by TINA as well as to exchange the output exhibitions? This is a specially designed module that allows
format. you to manage the entire production and sales process
• Management of reports templates - Your travel agent inside TINA.
28
www.dcsplus.net
the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @param Array $key */ private function
key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean -
is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) {
expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT
eName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. * @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if
EAD THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); <?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location whe
reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultE
or a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as global $CFG; // realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]
me’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } >keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @param Array $key */ private function getFileDetails($key)
$return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$
@see \Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this- * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS //
eName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if
(substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$exp
mestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACH
will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the concrete classes. * @param String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not the cache for a certain key. * @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE
.$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expir
throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype: */ public cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return $
suppliers * @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var Array */ protected $exportFields = array(‘alias’, ‘company’); /** * Extra filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return
// get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocat
ier’; /** * (non-PHPdoc) * @see \App\EntityMatch\BaseAbstract::setTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setAlias($value, check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** *
param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this->setSearchCriterion(‘company’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function \Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestam
get more details $params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName
g $value * @param String $codeName - indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘,
Clients
$return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::g
Name’=>$codeName)); } /** * Searches a supplier by the matching manually set in the external reservations system configs. * @param String $name - the supplier name * @param Int $idExternalResSystem - the external system */ public function setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, $return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php na
automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of [triggerName][index][\Engine\Plugin
ria, it uses the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the concrete classes. * @param String $triggerName
tring $value - the supplier code * @param Array $options - stores the code name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.
Sales
xecute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx) { writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’); } // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /**
ivate function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value,
new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype: */ public function ge
matching of suppliers * @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var Array */ protected $exportFields = array(‘alias’, ‘company’
$matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @var String */ protected $entityClassName =
Channel
function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard
‘events’, 9); throw new \Exception(‘Could not match supplier by the external reservation systems code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class >setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcar
nside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this- >setSearchCriterion(‘company’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->se
@param String $codeName - indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeN
>fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session -
Sales 2
gApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this->changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList;
array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching
$idExternalResSystem - the external system */ public function setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtResSystem’=>$idExtern
Categories
parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: ret
:getAll_rawData() */ protected function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this->fileNameWithPath)) return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it) $xml=@simplexml_load_file($this->fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new
1
searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$D
>Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation systems code.”, ‘events’, 9); throw new
Details $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\
“files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path
Sales Corporate
\App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for t
FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protec
QC and invoicing
Profiles
in session - they are copied into class attributes if (isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][
Channel
>changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsL
\App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this->fileNameWithPath)) return $this->getAll_rawDa
>fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this->fileNameWithPath.’).’); // return
Suppliers 3
default output in case the XML file is missing. * @return Array */ protected function getAll_rawData_fileNotExists() { return array(); } /** * Returns the extracted data after the XML content is parsed. * @
abstract protected function getAll_ rawData _extractData FromFile (\ Simple XMLElement $xml); /** * Sets an element in the internal attribute $this->itemsList. The method can be called multiple * times
$value - the value of the key; * @throws Exception - if input is bad */ public function set($var, $value) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the
(!is_string($var)) throw new \Exception(“You did not set() a correct name for the key.”); // (re)set one key $this->itemsList[$var]=$value; // update the changed flag $this->changedFlag=true; } /** * S
an associative array and the keys will be * stored one by one. * @param Array $vars - associative array of keys * @throws Exception - if input is bad */ public function setAll($vars) { // check constructor
FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_array($vars)) throw new \Exception(“You did not setAll() a correct list of elements - the parameter is not an array.”); // (re)
Services Subagents
>fileNameWithPath)) $this->addErrorMessage(‘The config file exists but it is not writeable.’); } else { $fileChmod=true; } // (OVER)WRITE THE FILE CONTENT - if (!file_put_contents($this->fileNameWithP
fee, discount,
not exist - chmod it if ($fileChmod) { if (!chmod($this->fileNameWithPath, 0777)) { $this->addErrorMessage(‘The config file could not have its acess rights updated.’); } } } /** * Returns the SimpleXM
details } <?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location w
INPUT
reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultE
Key account
.$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expir
cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return $
Individuals deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return
Managers
// get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocat
check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** *
\Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestam
get more details $params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName
$return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::g
$return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php na
automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of [triggerName][index][\Engine\Plugin
function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the concrete classes. * @param String $triggerName
name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.
new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype: */ public function ge
matching of suppliers * @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var Array */ protected $exportFields = array(‘alias’, ‘company’
$matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @var String */ protected $entityClassName =
function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard
>setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcar
>setSearchCriterion(‘company’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->se
@param String $codeName - indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeN
array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching
Remarks support TOMA Mask \App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for t
FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protec
ir
in session - they are copied into class attributes if (isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][
• Approval process • Alert limits • Service fee profiles
nf
>changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsL
\App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this->fileNameWithPath)) return $this->getAll_rawDa
>fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this->fileNameWithPath.’).’
• Documents • Credit limits • Discount profiles
m
Co
• Reports • Automated blocking • Contracts
ati
• Orders • Statistics • Special dates
Multi GDS on • Widgets
Financial
integration and travel
Corporate extranet Credit Limit control Client profiles
documents
(Amadeus, Worldspan,
Galileo, Sirena, Sabre) • Prospects Segmentation for: • Service fee profiles
Quality • Initial approach • Corporate customers • Discount profiles
• Negotiation • Subagents • Contracts
control • Closing the deal • Individual customers • Special dates
Profiles transfer Tickets and PNRs Segmentations types:
• Automatic
• Manual
Import reservations and invoices from Sales Force Automation Segmentation Automated invoicing
3rd party systems
3rd
party • Direct data (excel, csv, • Complete customization • Supervisor
html, pivot table) of the interface by using • Duration
Hotel Wholesalers:
namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class
Packages • Specialized reports: Dashboard and widgets • Statistics
GTA, MIKI, Tourico,
mplements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */
ed $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ (TUI, Dertour etc.) Tamara, iBank, GRO, styled interface • Alerts
Academservice etc.
ed $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected
ltExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global Reports Gems, Amex BTA, etc. • Availability
// realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors.
>cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached
efix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of
Accounting
etermined from the key * @param Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key
$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key
r=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’;
Low cost carriers
n[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param
$key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in
Rail, rent a car etc.
che */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false; // do
re ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this-
Reporting CWS Task management
eDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; //
MINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) {
eTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’:
eTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’:
eTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break;
: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE
NT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return
create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key.
ram String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK
S if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if
OUTPUT
29
www.dcsplus.net
Travel Booking System - TBS
TBS is an advanced IBE system that aggregates and normalizes content from several travel services providers
(selected at your desire) and resells the services towards different selling channels (B2B, B2C, corporate, whitelabel).
30
www.dcsplus.net
Complex pricing management:
• Mark-up and commission - TBS automatically • Auto-cancels the reservations that are about to
manages all pricing components, starting from the supplier enter cancellation fee limit, according to settings
price. It adds mark-up (travel agency fee) and commission • Automatically issues invoices for certain
(reseller fee). reservations
• Promotions - You can define your own special mark-ups • Digital maps - TBS uses digital maps to place different
and promotions, on certain destinations and on certain points of interest.
markets or agents. With smart rules, you drive higher • Search by point of interest
volumes towards the areas of interest • Content quality management - You can adjust the
• Multicurrency quality of static data stored on the local database
• VAT rules (descriptions, details, images, facilities etc.).
• Fuzzy logic unassisted matching tool for hotels
Extra Benefits • Multiple views of the results
• Search by multiple types of rooms at once
• TBS is developed using very new technologies
• Documents template manager - TBS allows design of
available in web development and continuously improved
different layouts for documents (vouchers, proforma
by dcs plus team.
invoices, invoices, annexes etc.).
• It is optimized to work with many wholesalers and XML
• E-mail alerts and warnings - Configuration of different
connectors at the same time, by using parallel processes
warnings can be sent by email (e.g. communication
and multitasking (multithreading). Thus, TBS is
errors).
ensuring high-speed operation.
• Operation logs - TBS saves comprehensive logs, which
• Search results are unique - The system matches the
include each operation and message inside the application
elements replicated into multiple wholesalers (countries,
(from the communication with the wholesalers to booking
cities, hotels) by joining them into single elements. The
operations).
content quality is fully controlled by you.
• Reporting tools - Definitions of reporting templates and
• Intuitive and highly usable interface, user friendly
extraction of comprehensive reports, depending on the
• Price comparison and sorting (shows the lowest price
selected source.
first)
• Export to any mid back office
• Automated management
• Multi-language interface
• Automatically manages the reservations, keeps
track of voids and refunds
31
www.dcsplus.net
3rd Party
Core Engine Selling Channels
Suppliers
B2B
Corporate
Travel Booking System • Interface for their account
Rent a car companies
• Interface for their intranet/extranet
XML
Selling rules
Transfer providers
Webservice
Front Office
Online Booking Tool
Cruise lines
Own website (TRIP)
• Online booking platform
Insurance companies
B2C
Other partners' websites
Reservation system • Whitelabel for online booking
Activities providers
Modular structure
Package providers
che; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The
f no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this-
LE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @param Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; //
Webservice
(AIDA)
sh’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean -
ectly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE
s_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’:
MP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key.
eturn Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE
params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return
value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) {
file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’]))
$return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see \Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file
p * @return Array */ public function getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more
e; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10); $return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get
lue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of
e\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the concrete classes. * @param String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs -
ws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using
*/ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype: */ public function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers * @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var
elds = array(‘alias’, ‘company’); /** * Extra filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @see
ct::setTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setAlias($value, $useWildcard=true) { $this->setSearchCriterion(‘alias’, $value, $useWildcard); }
ier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this->setSearchCriterion(‘company’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function
s->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @param String $value * @param String $codeName - indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}`
e: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manually set in the external reservations system configs. * @param String $name - the supplier name * @param Int $idExternalResSystem - the external system */
Name($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the rest of the criteria, it uses the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array
$field) { case ‘code’: return $this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field, $value, $useWildcard, $options); } } /** * Searches a supplier by its code. * @param String $value - the supplier code * @param Array $options - stores the
unction searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “;
b->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx) { writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’); } // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a
e external reservation systems. * @param unknown $valuem * @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE systemSupplierId=? AND
erEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation systems code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by the external reservation systems code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if
return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file (relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected
ilename with its full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this-
_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if
][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this->changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList;
ssName][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this->fileNameWithPath)) return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it)
$this->fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this->fileNameWithPath.’).’); // return the array from the parsed XML content return $this->getAll _rawData _extract DataFromFile($xml); } /** * Sets the default output in case the XML file is missing. * @return Array
awData_fileNotExists() { return array(); } /** * Returns the extracted data after the XML content is parsed. * @param \SimpleXMLElement $xml - the parsed xml * @return Array - the items list or whatever output is needed. */ abstract protected function getAll_ rawData _extractData FromFile (\ Simple XMLElement $xml); /** * Sets an element in the internal attribute $this->itemsList.
ltiple * times and they will overwrite the older values. * @param String $var - or the name of one key (a string); * @param Mixed $value - the value of the key; * @throws Exception - if input is bad */ public function set($var, $value) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get _called_ class().’::’.__FUNCTION__.”`
`admin`.”); // check inputs if (!is_string($var)) throw new \Exception(“You did not set() a correct name for the key.”); // (re)set one key $this->itemsList[$var]=$value; // update the changed flag $this->changedFlag=true; } /** * Sets all the elements in the internal attribute $this->itemsList (rewrites any old data * already stored). The parameter is an associative array and the keys will
@param Array $vars - associative array of keys * @throws Exception - if input is bad */ public function setAll($vars) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get_c alled_class().’::’.__ FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_array($vars)) throw new \Exception(“You did not setAll()
e parameter is not an array.”); // (re)set a list of keys $this->itemsList=$vars; // update the changed flag $this->changed Flag=true; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::save_write_do() */ protected function save_write_do() { // GETS THE SIMPLEXML OBJECT TO BE WRITTEN ON DISK gets the simplexml object $xmlObj=$this->save_write_do _getXml(); // checks
s a valid simplexml object) if (!($xmlObj instanceof \SimpleXMLElement)) throw new \Exception(‘The output of the method `’.get_ called_class() .’::save_write_do_getXml` must be a `SimpleXMLElement` object.’); // (RE)WRITE FILE ON DISK // check if the file is writeable $fileChmod=false; if (file_exists($this->fileNameWithPath)) { if (!is_writeable($this->fileNameWithPath)) $this-
g file exists but it is not writeable.’); } else { $fileChmod=true; } // (OVER)WRITE THE FILE CONTENT - if (!file_put_contents($this->fileNameWithPath, $xmlObj->asXml())) { $this->addErrorMessage(‘The config file could not be (over)written.’); } // if the file did not exist - chmod it if ($fileChmod) { if (!chmod($this->fileNameWithPath, 0777)) { $this->addErrorMessage(‘The config file
s updated.’); } } } /** * Returns the SimpleXML object to be written on disk. * @return \SimpleXMLElement */ abstract protected function save_write_do_getXml(); } <?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files
protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global
cause cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key *
te function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in
* @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this-
empt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’:
MP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE
‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. * @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if
ation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get
turn it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - delete
deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function
UTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE
s $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see
::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this->getFileDetails($key);
ocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName);
getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10); $return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is
@author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the
ined method register() * in the concrete classes. * @param String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The trigger name
ass().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype: */ public function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch;
matching of suppliers * @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var Array */ protected $exportFields = array(‘alias’, ‘company’); /** * Extra filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The
ate the Entity - used for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @see \App\EntityMatch\BaseAbstract::setTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param
wildcard for this search or not. */ public function setAlias($value, $useWildcard=true) { $this->setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this-
y’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @param String $value * @param String $codeName - indicate the type of code (iata, tktCode, etc) */ public function
{ // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by
32
the external reservations system configs. * @param String $name - the supplier name * @param Int $idExternalResSystem - the external system */ public function setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the
e parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return
ds($field, $value, $useWildcard, $options); } } /** * Searches a supplier by its code. * @param String $value - the supplier code * @param Array $options - stores the code name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this-
ROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx) { writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new
supplier by their code.’); } // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external reservation systems. * @param unknown $valuem * @param array $options */ private function searchRecords_extResSys($value, Array $options) { global
ociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation
www.dcsplus.net
throw new \Exception(‘Could not match supplier by the external reservation systems code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract {
n file (relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::constructInit() */ protected function constructInit() {
ay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */
dmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if (isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this-
onfigApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList; $S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return the
ot exist if (!file_exists($this->fileNameWithPath)) return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it) $xml=@simplexml_load_file($this->fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this->fileNameWithPath.’).’
Partners
AIDA is perfect for: Why you need AIDA Various travel services and (static) properties
• Accommodation
• Tour operators • To manage your inventory in a detailed yet • Allowed passenger types
• DMCs comprehensive way • Available room and facilities
• To create your own products by combining different • Permitted occupancies
services from your inventory • Transportation
• To distribute your products (tailor-made services, • Departures and itinerary
Services available
packages) to an unlimited number of resellers and • Vehicles maximum capacities
through AIDA:
distributors • Transfers
• Accommodation (hotels, villas, • To track all the reservations and allow your resellers to • Pick-up - drop-off transfer segments
apartments etc.) check status, generate vouchers and proforma invoices, • Detailed pricing
• Transportation (air, bus, car etc.) amend reservations • Other (secondary) - meal, car rental, cruise,
• Transfer services (car, van, minibus • To create and generate inventory or selling reports theatre, tour etc.
etc.)
Benefits
• Other services (meals, cruises, trips, Full inventory at your service
tickets etc.) Very flexible platform, allowing fast changes • Define capacities and availability in each
• Packages (tours, vacations etc.) to components’ structure day of the month, for all months which overlap
the service’s lifespan
• Capacities • Last minute
• Use general rules to define large intervals, treat
• Pricing • Early booking
exceptions at day level
• Service links and restrictions • Special offers
• Day level inventory breakdown for each
• Packages
room type or transportation segment/sub-
AIDA supports different distribution channels itinerary
• Own selling platform • Intuitive and highly usable interface
• Webservice - you can choose the type of • Browse among the calendar and select each
configurations you desire for front office selling component at day level
interface or integration with 3rd party distribution • Manage check-ins and stay lengths
channels
34
www.dcsplus.net
Manage accommodation services in different You can easily define different prices for the same product,
ways based on the reseller market. Or why not even customize
• Bulk (management per all rooms & all features - different reseller categories (groups) and set alternative
Increasing the attractiveness of their products e.g. 20 rooms) prices?
has never been easier for the tour operators,
• Detailed (management per room type - e.g. 10
since the powerful tools of managing special
deals in AIDA were put in place. Regardless we single, 10 double) Moreover, the ‘multiple rates’ definition allows you to input
are talking of 'pay and stay' promotions, 'rolling • Extra detailed (management per room feature special deal prices, such as ‘pay and stay’ or ‘rolling early
early bird' or the standard 'early booking' - e.g. single - 5 sea view, 5 mountain view, double booking’. The modular structure of the rates management
campaigns, the tour operator can setup these
- 3 garden view, 7 sea view) section allows the extension of the ‘special deals’ types,
special offers very fast, while the products are on
according to your own customized offers.
sale.
Octav Stan - Director Tour Operator Comprehensive module for price definition
Solutions at dcs plus All price components can be defined individually: Flexible cancellation policies
• Supplier price You can define cancellation policies for each service or
• Tour operator commission package. And the penalty can be set as:
• Reseller commission • Percentage of service value
• Taxes • Fixed amount
AIDA offers both you and your • Number of nights
reseller commission based on multi-
layered schemas - different values, Also, multiple rules can be added to a single cancellation
for different activation intervals. This
policy.
enables you to easily switch between
promotional and high season
schemas, which can make your
services look more attractive for the
reseller (e.g. offer early booking, last
minute etc.).
35
www.dcsplus.net
Static packages Extra benefits
• Tours - built around a transportation
service - multiple destinations with multiple • Flexible payment mode for resellers
accommodation units • Documents template manager (DTM)
• Holidays - built around the accommodation • E-mail alerts and warnings
service - single destination, the location of the • Advanced reporting tools
accommodation service • Operations logs
• Webservice
Multiple additional services can be added to the package, • Mid/back office export
either mandatory or optional (e.g. meals, trips, concert • Multi-language content and interface
tickets etc.). • AIDA can be integrated with 3rd party reservation
system
Dynamic packages • Business to supplier interface etc.
Each travel service is added into the shopping basket. Your
customer chooses accommodation, transport and other
services and builds his custom package. All based on the TBS can represent a distribution channel
links, rules and restrictions you set up.
for AIDA - AIDA can play the role of the
wholesaler in TBS.
TRIP is perfect for: Why you need TRIP • Marketplace concept - Based on the marketplace
Online market is rapidly growing and getting your business concept, you can choose to integrate multiple widgets in
• Travel agencies that want to be your travel agency’s platform. These widgets (Top
out there is a must nowadays. The online medium is a
present online destinations, Discover city, Top activities, Recent flights,
dynamic one and more competitors appear every year.
• Travel agencies that already sell etc.) are powerful marketing tools that act as incentives
online, but want a booking tool that for your customers.
Thus, when you want to make your presence felt and be at
gives them competitive advantages • Descriptive content - TRIP has access to a wide range
top of the list, you certainly need an innovative platform to
help you get there. of descriptive content sources acting as a retention and
upselling mechanism. You can keep your customer busy
while searching for the requested travel content. He can
Services available through TRIP: browse through information like short descriptions,
• Flights • Packages population, recommended locations, time zone, weather at
• Hotels • Transfers destination etc. Providing your customer all the information
• Flights & hotels dynamically • Activities he needs will make him return to your agency’s website
combined • Car rental next time and become your loyal customer.
Benefits
• Multitasking - TRIP relies on its multitasking engine and A website needs to be easy to navigate, otherwise it is a
descriptive content suppliers in order to deliver a unique huge turnoff for visitors. Everything needs to be
experience to your online customer. When you use TRIP, convenient and intuitive, starting from the homepage and
ending up with the checkout. In order to achieve these
your travel agency can create a personalized platform
characteristics, one must consider a consistency in engine
interface to meet your target audience.
capabilities, design implementation and marketing
• Advanced SEO tools - The perfect mix of relevant
strategy.
content, complex cache mechanism and landing pages Andrei Savin - Director B2C Online Solutions
makes your website visible to search engines crawlers.
This way you will keep it on top of the search results and
attract more potential customers.
37
www.dcsplus.net
Extra benefits
Innovation User friendly environment - The combination between
• Weekend search - Most of the city break searches are new search criteria (search by activity, search by POI,
for weekend holidays. By using the multitasking weekend search etc.) and smart search capabilities creates
A customized and attractive website capabilities of the platform, your visitor has access to great a friendly user experience.
shows not only that you understand and weekend offers directly on the landing page. He discovers
take into account your customers'
new attractions and content related to target city while the Mobile compatible
wishes, but you also do anything
platform brings latest hotel offers simultaneously for all TRIP has a unique structure developed using the latest
technically possible to fulfill them.
weekends in a month. programming techniques, which makes it responsive and
Alexandra Luca - Marketing • Search by activity - Customers in the online business organic compatible with mobile browsers. Organic
Specialist at dcs plus don’t necessarily search for a specific location; they are compatible means cost efficient: only one instance to be
driven by emotions and search for places where they can maintained for all mobile platforms (iOS, Android,
live amazing experiences. We have developed the activity Windows, Symbian and Blackberry).
search criteria especially for them. You can now help your
customers find the best locations for their holidays.
• GetAway map - This is an innovative feature that gives
your customer escaping ideas. He will discover a map
dynamically filled with lots of travel destinations that come
together with prices for each location, based on recent
travel services searched by other customers on your
website. Today’s online travel industry is based on emotional
• Dynamic packaging - create dynamic packages with selling. A successful selling platform needs to be
flight & hotel, based on live searches in suppliers emotional, friendly and fast. We have designed TRIP to
databases. meet these attributes and many more.
• Online chat - Keep your clients close, using our online
Andrei Savin - Director B2C Online Solutions
chat plugin - unlimited number of sessions, unlimited
at dcs plus
number of users.
• Reservation check - your clients can easily check their
reservation details, just by using the e-mail address and
reservation number.
38
www.dcsplus.net
Custom Applications
based on TRIP engine
Mobile applications Booking kiosks
Internet users Multi-screen users New booking habits
Do you want to make a difference in this
• desktop computers competitive environment? Be the one who
• tablets creates your customers’ needs and then
• smartphones come with the right tools to meet them.
• TVs etc.
Multiple content
sources for: TRIP
Customized Booking Platform
• pictures
• descriptions
The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration
• attractions
that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if
ams[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return
• Organic SEO
le cache content. * @return Boolean - indicating if the cache was completely destroyed * @see \Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function getInfo($key) { $return=array(); // associate the key
TBS AIDA
ad */ public function set($var, $value) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get _called_ class().’::’.__FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_string($var)) throw new \Exception(“You did not set() a correct name for the key.”); // (re)set one key $this->itemsList[$var]=$value; //
Hotel
dFlag=true; } /** * Sets all the elements in the internal attribute $this->itemsList (rewrites any old data * already stored). The parameter is an associative array and the keys will be * stored one by one. * @param Array $vars - associative array of keys * @throws Exception - if input is bad */ public function setAll($vars) { // check constructor type if ($this->constructType!=’admin’) throw new
get_c alled_class().’::’.__ FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_array($vars)) throw new \Exception(“You did not setAll() a correct list of elements - the parameter is not an array.”); // (re)set a list of keys $this->itemsList=$vars; // update the changed flag $this->changed Flag=true; } /** * (non-PHPdoc) * @see
o() */ protected function save_write_do() { // GETS THE SIMPLEXML OBJECT TO BE WRITTEN ON DISK gets the simplexml object $xmlObj=$this->save_write_do _getXml(); // checks that the object is correct (it is a valid simplexml object) if (!($xmlObj instanceof \SimpleXMLElement)) throw new \Exception(‘The output of the method `’.get_ called_class() .’::save_write_do_getXml` must be a
WRITE FILE ON DISK // check if the file is writeable $fileChmod=false; if (file_exists($this->fileNameWithPath)) { if (!is_writeable($this->fileNameWithPath)) $this->addErrorMessage(‘The config file exists but it is not writeable.’); } else { $fileChmod=true; } // (OVER)WRITE THE FILE CONTENT - if (!file_put_contents($this->fileNameWithPath, $xmlObj->asXml())) { $this-
3 Party Suppliers
not be (over)written.’); } // if the file did not exist - chmod it if ($fileChmod) { if (!chmod($this->fileNameWithPath, 0777)) { $this->addErrorMessage(‘The config file could not have its acess rights updated.’); } } } /** * Returns the SimpleXML object to be written on disk. * @return \SimpleXMLElement */ abstract protected function save_write_do_getXml(); } <?php namespace
o manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no
@var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the
Transportation
x=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @param Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the
Air • Air
STANDARD XML
this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if
l’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) {
Reservation System
+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire
E THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. * @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key);
[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get
Inventory & Distribution • Bus
lueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK
K THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params
/ check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see \Engine\Cache\BaseInterface::flush() */ public function flush() { return • Car
Car rental • Selling channels: B2C, B2B, B2B2C,
* Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details $params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or
fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10); Ideal for tour operators
XML
te(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); // get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a
ative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the concrete classes. * @param String $triggerName - the trigger name *
whitelabel, corporate
- the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to
er() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype: */ public function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers * @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers.
= array(‘alias’, ‘company’); /** * Extra filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @see
eName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setAlias($value, $useWildcard=true) { $this->setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a
Cruise
he external reservation systems code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by the external reservation systems code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class
prices, availability, promotions
{ /** * Stores the configuration file (relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::constructInit() */ protected
file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see • Minibus
• E-mail alerts & warnings
min() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if (isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this-
$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList; $S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return the output in case the
fileNameWithPath)) return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it) $xml=@simplexml_load_file($this->fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this->fileNameWithPath.’).’
• Multilayered commission schemas
• Unique search results • Policies and penalties management
Insurance Packages
• Multi-lingual content • Static & dynamic packaging
• Tours
• Documents template manager • Documents template manager
rd
Activity • Vacations
• XML integration with 3rd party suppliers • E-mail alerts and warnings • City breaks
• Webservice/ API/ XML output • Advanced reporting tools
Package
<?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */
• Webservice/ API/ XML output Other services
protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */
protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache caches the entity object in
destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some
• Meals
associative array of data determined from the key * @param Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the
current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param
• Cruises
String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false;
// do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE STANDARD XML STANDARD XML • Ships
EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’:
$expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) • Tickets
<?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of ke
destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @param Array $key */ priva
$expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // F
$expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CON
value stored in the cache for a certain key. * @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp
TINA
$params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as $k) { // get file params $params=$thi
$return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see \Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Retu
FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); $return[‘fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $retur
return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observ
trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype:
$key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @see \App\EntityMatch\BaseAbstract::setTableName() */ protected funct
$return=1; // A LIST OF KEYS TO BE DELETED if (is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this->setSearchCriterion(‘company’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public fu
not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manually set in the external reservations system configs. * @param String $name
$return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete protected function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($fiel
the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see \Engine\Cache\BaseInterface::flush() `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx) { writeLog(“Could not match supplier by their code.”, ‘eve
*/ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function getInfo($key) { $return=array(); // associate the
key $return[‘key’]=$key; // get more details $params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) {
Mid Back Office $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if
namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file (relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full
>fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class att
$S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this->fileNameWithPath)) return $this->getAll_rawData_fileNotExists()
$return[‘fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; output in case the XML file is missing. * @return Array */ protected function getAll_rawData_fileNotExists() { return array(); } /** * Returns the extracted data after the XML content is parsed. * @param \SimpleXMLElement $xml - the parsed xml * @return Array - the items list or whatever output is needed. */ abstract protecte
@ • Management of customers @
if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // C
params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache
$fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation
@
• Management @ of suppliers @
@
• Task management • 3rd party software connection
* The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin. * @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=a
function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using
*/ protected $exportFields = array(‘alias’, ‘company’); /** * Extra filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @var String */ prote
@ $useWildcard=true) { $this->setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this->setSe
@
@ • Management of requests and • CRM@ via webservices
$codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } // add the search criterion $this->setSearchCriterion(‘code’, $value, fals
This method does a special search for supplier code (for the rest of the criteria, it uses the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($valu
$DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCod
@ @ @ the external reservation systems. * @param unknown $valuem * @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_sup
sales • Documents template manager $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file (relative to the “files/config/”
\Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName; } /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::co
>changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads $S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList; $S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @see \App\Config\B
(class = ‘.get_called_class().’ ; file = ‘.$this->fileNameWithPath.’).’
@ @
@
40 @
@
www dcsp us ne
@ @
dcs plus BPO - a plus value
for the travel industry
The world is witness to the impressive expansion of the
travel industry – its dimensions have become
overwhelming and its diversity seems unlimited.
• dcs MarketPlace
• Content Mapping
• Online Marketing
41
www.dcsplus.net
dcs MarketPlace Content Mapping
In order to keep up with the travel market’s diversity and dynamic, it The travel content available on the market is wide and
became mandatory for travel agencies and tour-operators to be in a increasing and every travel agency wants to enrich its
permanent run for new business opportunities. Sometimes, this run turns services portfolio by contracting as many suppliers as
out to be expensive and time consuming, with negative impact on the possible.
company’s performance.
But, integrating multiple suppliers in the booking
dcs plus BPO thought to create a global pool of business opportunities and platforms, generates a data reliability issue, as the same
make it accessible for every travel player - dcs MarketPlace. What does this country or the same city can be written in different ways, a
mean? It means that you can stop running and start choosing the certain hotel can be found under several names and so on.
partnerships and collaborations that best fit your needs. And our team of
professionals is here to support and assist you. The Content Mapping service refers to joining the static
travel content from more integrators into a single database
dcs MarketPlace is meant to be a global B2B community linking travel with unique identification for locations, hotels and hotel
players around the world (travel agencies, tour-operators, consolidators details (geocodes, addresses etc.). Duplicates are
etc.), allowing them to access, promote and distribute travel services eliminated using the matching algorithm available in the
through the dcs plus technology. TBS application, assisted by manual input. Also, the
service involves maintaining the mapped database by
Benefits:
performing regular updates and improvements of
• differentiation and growth through innovative technology bundled with
descriptive content.
content
• possibility to increase sales volumes and market share Benefits:
• access to new market niches • quality of content
• simplified operational processes • reliable and accurate search results
• global B2B visibility and increased brand awareness • optimization in terms of time and costs
• advanced promotion and distribution tool • unique records
• time and financial resources savings • reduced traffic and response time of the technological
• 13 years know-how and experience application
• competitiveness • permanently updated travel content
• diversification • database maintenance and improvement
42
www.dcsplus.net
Online Marketing
Having a strong online presence has long ago become visitors’ loyalty etc.
more than an option for travel agencies. It has become a • E-mail Marketing – database optimization, delivery and
must, as we live in a digital era driven by the consumers – template optimization etc.
demanding, informed, social. Entering the online
BPO is your strategic partner; we are the long-
environment is the easiest thing to do, but differentiating Benefits:
term ally that you can rely on, to bring new
proved not to be that simple. • increase B2C brand awareness and visibility
business opportunities, to enrich your product
portfolio and to position and sell your services • increased qualified traffic on the website
and brand in the travel marketplace. dcs plus BPO aims to support the OTAs to stand out from • increased conversion rate
the “www crowd”, through smart online marketing • improved customer relationship
Ruxandra Bararu - Chief Operating Officer at • results monitoring through professional tools
strategies applied in an innovative and coherent way. This
dcs plus BPO
means that we build and integrate Marketing on the
Technology and Content components.
• SEO/ SEM (Search Engine Marketing) - content and • explore new business opportunities (new partnerships,
Save time, html code optimizations, links, keywords etc. new market segments, new market niches and so on)
• Google AdWords - PPC promotion campaigns (selecting • add value to their core business components, through
increase your relevant keywords, setting the budget, traffic estimations integrated strategies
etc.) • get the most out of the travel industry’s dynamic and
revenues and • SMO (Social Marketing Optimization) - company diversity
profiles on social networks, communication strategy, • get connected to worldwide travel players through a
enjoy a leading community management etc. strong and professional business travel community
• Analytics (Web Analytics & Performance) - users’
position on the behavior analysis and usability, website navigation flow,
• build successful collaborations, performance and
effectiveness
market. traffic, sales, conversion, web navigation experience, • evolve through innovative and powerful tools
43
www.dcsplus.net
Global presence
Here are listed some of the markets that currently benefit Our work since 2012 with Cristian Dinca and his team at dcs plus aimed
to obtain the best integrated system of software for travel services and
from dcs plus’ travel software technology: Australia,
to reduce our operational time with 30%. We were thrilled when dcs
Bulgaria, Canada, Colombia, Cyprus, Czech Republic, plus team’s finished implementation program in just 4 months for all
Egypt, Estonia, France, Georgia, Hungary, India, Iran, three software, AIDA inventory, TBS travel booking system and TINA
Israel, Italy, Kuwait, Latvia, Lebanon, Lithuania, Romania, ERP. That was beyond our expectations in terms of cost savings and
the speed with which we accomplished our goal.
Russia, Serbia and Montenegro, Singapore, Switzerland,
What we appreciated most about working with Cristian and his team
Taiwan, Turkey, UAE, UK, Ukraine, USA, Venezuela etc. was their ability to audit our internal procedures and deliver the
expected results and then some.
TINA, TBS, AIDA and TRIP are designed with a flexible Beginning of 2014 has also a great meaning for our tide collaboration,
when Accent Travel & Events launched Traveo website, having
and modular structure, with user access per module and
integrated the newest DCS Plus program, a multitasking selling
an easy to use plug-in system. platform'.
Loredana Stanciu - Accent Travel & Events General Manage
Maintenance and support program
Being business critical software solutions, TINA, TBS, AIDA
and TRIP are fully maintained by dcs plus team, to ensure dcs plus is not just an IT company, it's a family for me. I work with dcs
that clients use the systems at full capacity at all times. plus for already 6 years, it's not just the advanced technology, it is
about the people behind.
We also offer helpdesk, call center support, trainings and
When looking for an IT company, you should count on dcs plus that you
courses to all of our clients. get the best product, the best service and real people to work with.
We use TBS hotel/flight/transfer/activity engine, we are considering
Permanent updates and general products adding more services as well as TINA to be our future Mid office. With
All the products designed and developed dcs plus, the sky is the limit. I recommend the industry to move on to
improvements
dcs plus products.
by dcs plus are web-based, offering All the products developed by dcs plus are continuously
you major advantages such as: evolving; they are updated several times a year, with Tzafrir Ben-Avinoam - Talma FIT and MICE Director
improved functionalities, additional features etc. These
• Accessibility and scalability functionalities are in the benefit of the applications and
• Low cost implementation and represent advantages for all the customers.
maintenance
44
www.dcsplus.net
dcs plus global network
Join it! 45
www.dcsplus.net
Amadeus Select Partner
46
www.dcsplus.net
Lufthansa City Center Partnership
The strategic partnership between Lufthansa City Center • TINA Core includes:
and dcs plus aims to provide the travel agencies which are - clients and supplier management, full workflow
members of the franchising network with the right coverage, client and supplier invoices, payments
software tools in order to accomplish their goals faster, made and received, automated BSP (and other
cheaper, efficiently and in a collaborative environment. In suppliers) balances, reporting, Sales Force
order to achieve this, dcs plus has created together with Automation, DTM.
Lufthansa City Center International a dedicated edition of
dcs products, tailor made to the specific needs of a • LCC Edition of TINA includes, beside the core
Lufthansa City Center travel agency. The tools included functions, the following extra features:
inside these editions aim at both leisure as well as - iBank Reporting, TAMARA reporting, Credit Limit,
business part of the activities normally conducted by a CAT integration.
Lufthansa City Center.
47
www.dcsplus.net
Please feel free to contact us in order
to get more details about our products and services.
HEADQUARTERS: HEADQUARTERS:
th nd
No. 215 Mihai Bravu Road, 5 floor, 2 District, No. 215 Mihai Bravu Road, ground floor, 2nd District,
021323, Bucharest, Romania. 021323, Bucharest, Romania.
BRANCH OFFICE:
Paris, France - 12 Rue Vivienne
Lot 3 75002
www.dcsplus.fr
© 2015 dcs plus. All rights reserved, dcs plus™ is a registered trademark in the European Union.
4th Edition, February 2015
<?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the
application was updated. * @var String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd
may change in __destructor(s) - \App\Entity\Cache caches the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative
array of data determined from the key * @param Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location
$return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function
set($key, $value, $expireTime=0) { // CHECK INPUTS // if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; //
DETERMINE THE EXPIRE TIMESTAMP if (is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’:
$expireTimestamp=TIMESTAMP+$expireTime*3600;break; case ‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE
CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. * @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file
params $params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp
=substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache
for a certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if
(is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this-
>getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see
\Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details
$params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’];
$return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10); $return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); //
get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin.
* @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the
concrete classes. * @param String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The
trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype: */ public
function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers * @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var Array */ protected $exportFields = array(‘alias’, ‘company’); /** * Extra
filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @see
\App\EntityMatch\BaseAbstract::setTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setAlias($value,
$useWildcard=true) { $this->setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this-
>setSearchCriterion(‘company’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @param String $value * @param String $codeName
- indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } //
add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manually set in the external reservations system configs. * @param String $name - the supplier name * @param Int $idExternalResSystem - the external system */ public function
setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the rest of the criteria, it uses the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected
function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field, $value,
$useWildcard, $options); } } /** * Searches a supplier by its code. * @param String $value - the supplier code * @param Array $options - stores the code name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT
“.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx)
{ writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’); } // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external reservation
systems. * @param unknown $valuem * @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE
systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation systems code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by the external reservation systems
code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file
(relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @see
\App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName;
} /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if
(isset($S[‘admin’][‘configApp’][$className][‘itemsList’])) { $this->itemsList=$S [‘admin’] [‘configApp’] [$className ][‘itemsList’]; } if (isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this->changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’]; } // these references will keep the data over different page loads
$S[‘admin’] [‘configApp’] [$className] [‘itemsList’] =&$this->itemsList; $S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->changedFlag; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData() */ protected function getAll_rawData() { // return the output in case the file does not exist if (!file_exists($this-
>fileNameWithPath)) return $this->getAll_rawData_fileNotExists(); // the file exists - load the content as XML (and check it) $xml=@simplexml_load_file($this->fileNameWithPath); if (!($xml instanceof \SimpleXMLElement)) throw new \Exception(‘The config file is not a well formed XML (class = ‘.get_called_class().’ ; file = ‘.$this-
>fileNameWithPath.’).’); // return the array from the parsed XML content return $this->getAll _rawData _extract DataFromFile($xml); } /** * Sets the default output in case the XML file is missing. * @return Array */ protected function getAll_rawData_fileNotExists() { return array(); } /** * Returns the extracted data after the XML content
is parsed. * @param \SimpleXMLElement $xml - the parsed xml * @return Array - the items list or whatever output is needed. */ abstract protected function getAll_ rawData _extractData FromFile (\ Simple XMLElement $xml); /** * Sets an element in the internal attribute $this->itemsList. The method can be called multiple * times and they
will overwrite the older values. * @param String $var - or the name of one key (a string); * @param Mixed $value - the value of the key; * @throws Exception - if input is bad */ public function set($var, $value) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You can use the method `”.get _called_
class().’::’.__FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_string($var)) throw new \Exception(“You did not set() a correct name for the key.”); // (re)set one key $this->itemsList[$var]=$value; // update the changed flag $this->changedFlag=true; } /** * Sets all the elements in the internal attribute $this-
>itemsList (rewrites any old data * already stored). The parameter is an associative array and the keys will be * stored one by one. * @param Array $vars - associative array of keys * @throws Exception - if input is bad */ public function setAll($vars) { // check constructor type if ($this->constructType!=’admin’) throw new \Exception(“You
can use the method `”.get_c alled_class().’::’.__ FUNCTION__.”` only if the constructor type is `admin`.”); // check inputs if (!is_array($vars)) throw new \Exception(“You did not setAll() a correct list of elements - the parameter is not an array.”); // (re)set a list of keys $this->itemsList=$vars; // update the changed flag $this->changed
Flag=true; } /** * (non-PHPdoc) * @see \App\Config\BaseAbstract::save_write_do() */ protected function save_write_do() { // GETS THE SIMPLEXML OBJECT TO BE WRITTEN ON DISK gets the simplexml object $xmlObj=$this->save_write_do _getXml(); // checks that the object is correct (it is a valid simplexml object) if (!($xmlObj
instanceof \SimpleXMLElement)) throw new \Exception(‘The output of the method `’.get_ called_class() .’::save_write_do_getXml` must be a `SimpleXMLElement` object.’); // (RE)WRITE FILE ON DISK // check if the file is writeable $fileChmod=false; if (file_exists($this->fileNameWithPath)) { if (!is_writeable($this->fileNameWithPath)) $this-
>addErrorMessage(‘The config file exists but it is not writeable.’); } else { $fileChmod=true; } // (OVER)WRITE THE FILE CONTENT - if (!file_put_contents($this->fileNameWithPath, $xmlObj->asXml())) { $this->addErrorMessage(‘The config file could not be (over)written.’); } // if the file did not exist - chmod it if ($fileChmod) { if
(!chmod($this->fileNameWithPath, 0777)) { $this->addErrorMessage(‘The config file could not have its acess rights updated.’); } } } /** * Returns the SimpleXML object to be written on disk. * @return \SimpleXMLElement */ abstract protected function save_write_do_getXml(); } <?php namespace Engine\Cache; /** * The class is used to
manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements \Engine\Cache\BaseInterface { /** * The full location where the cache files are stored. * @var String */ protected $cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protected
$keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $CFG; // realpath is used because cwd may change in __destructor(s) - \App\Entity\Cache
caches the entity object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key prefix $this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’]; return true; } /** * Returns some associative array of data determined from the key * @param Array
$key */ private function getFileDetails($key) { $return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the current key $keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’;
$return[‘fileName’]=$return[‘keyHash’].’.cache’; return $return; } /** * Sets a value in the cache for a certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache */ public function set($key, $value, $expireTime=0) { // CHECK INPUTS
// if key is not set if (!$key) return false; // do not store ‘null’ values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attempt to create the directory chain if (!\Dir::create($params[‘fileLocation’])) return false; // DETERMINE THE EXPIRE TIMESTAMP if
(is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTime, -1)) { case ‘s’: $expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; case
‘d’: $expireTimestamp=TIMESTAMP+$expireTime*86400;break; default: $expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return
\File::create($params[‘fileLocation’].$params[‘fileName’], $fileContent); } /** * Gets a value stored in the cache for a certain key. * @param String $key * @return Mixed - the stored value or null if the key was not found */ public function get($key) { // CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params
$params=$this->getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’] .$params[‘fileName’]) or !is_readable($params [‘fileLocation’].$params[‘fileName’])) return null; // READ THE FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration time $expirationTimestamp =substr
($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk $this->delete($key); return null; } // get contents, unserialize it and return it $valueSer=substr($fc, 10); $value=@unserialize($valueSer); if ($valu e===false) return null; else return $value; } /** * Deletes the value stored in the cache for a
certain key. * @param Mixed $key - if it is an array - deletes all those keys; if it is a string - deleted only that key * @return Boolean - indicating if the key was correctly deleted in the cache */ public function delete($key) { // CHECK INPUTS if (!$key) return true; // CHECK THE FILE $return=1; // A LIST OF KEYS TO BE DELETED if
(is_array($key)) { foreach($key as $k) { // get file params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) continue; // delete the file $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } } // ONLY ONE KEY else { // get file params $params=$this-
>getFileDetails($key); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]); } return (Bool)$return; } /** * Deletes the whole cache content. * @return Boolean - indicating if the cache was completely destroyed * @see
\Engine\Cache\BaseInterface::flush() */ public function flush() { return \Dir::delete($this->cachePath); } /** * Returns an associative array of details for a certain key: the file location, the expire timestamp * @return Array */ public function getInfo($key) { $return=array(); // associate the key $return[‘key’]=$key; // get more details
$params=$this->getFileDetails($key); $fullFileName=$params[‘fileLocation’].$params[‘fileName’]; // CHECK IF THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys $return[‘fileExists’]=true; $return[‘keyHash’]=$params[‘keyHash’];
$return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’]; $return[‘fileSize’]=filesize($fullFileName); // get expire time $fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10); $return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, $return[‘expireTimestamp’]); //
get the value $return [‘cachedValue’] =unserialize (substr($fc, 10)); } return $return; } } <?php namespace Engine\Plugins; /** * The class is used for registering a new plugin. It is extended in a class which is automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered plugin.
* @var Array - associative array of [triggerName][index][\Engine\Plugins\Observer object] */ protected $observers=array(); /** * The constructor will register the current plugin. */ public function __construct() { $this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the
concrete classes. * @param String $triggerName - the trigger name * @param \Engine\Plugins\Observer $obs - the observer object * @throws \Exception - if the triger name is not correct */ protected function addObserver($triggerName, \Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The
trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”); } $this->observers[$triggerName][] = $obs; } /** * The method will add new observers to the current plugin (using the addObserver() method. */ abstract protected function register(); /** * Returns the list of observers for the current * @return multitype: */ public
function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch; /** * The class manages the matching of suppliers * @author Gabriel */ class Supplier extends \App\EntityMatch\BaseAbstract { /** * The export fields for suppliers. * @var Array */ protected $exportFields = array(‘alias’, ‘company’); /** * Extra
filters - deleted and duplicated suppliers are not allwed. * @var String */ protected $matchingFilteringSQL = ‘AND ent.deleted=0 AND ent.idRealSupplier=0’; /** * The class name which will instantiate the Entity - used for primaryKey search. * @var String */ protected $entityClassName = ‘\Entity\Supplier’; /** * (non-PHPdoc) * @see
\App\EntityMatch\BaseAbstract::setTableName() */ protected function setTableName() { global $DBTSuppliers; $this->tableName = $DBTSuppliers; } /** * Sets a search by supplier alias. * @param String $value - the supplier alias. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setAlias($value,
$useWildcard=true) { $this->setSearchCriterion(‘alias’, $value, $useWildcard); } /** * Sets a search by supplier company name. * @param String $value - the supplier company name. * @param Boolean $useWildcard - uses wildcard for this search or not. */ public function setCompany($value, $useWildcard=true) { $this-
>setSearchCriterion(‘company’, $value, $useWildcard); } /** * Sets a search by supplier fiscal code. * @param String $value - the supplier fiscal code. */ public function setFiscalCode($value) { $this->setSearchCriterion(‘fiscalCode’, $value, false); } /** * Searches a supplier by its code. * @param String $value * @param String $codeName
- indicate the type of code (iata, tktCode, etc) */ public function setCode($value, $codeName) { // check the code name global $CFG; if (!$CFG[‘travel’][‘standardCodes’][$codeName]) { throw new \Exception(“The supplier code `{$codeName}` is not valid - you can only use: “.implode(‘, ‘, array_keys($CFG[‘travel’][‘standardCodes’]))); } //
add the search criterion $this->setSearchCriterion(‘code’, $value, false, array(‘codeName’=>$codeName)); } /** * Searches a supplier by the matching manually set in the external reservations system configs. * @param String $name - the supplier name * @param Int $idExternalResSystem - the external system */ public function
setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtResSystem’=>$idExternalResSystem)); } /** * This method does a special search for supplier code (for the rest of the criteria, it uses the parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected
function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: return $this->searchRecords_code($value, $options); break; case ‘extResSys’: return $this->searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field, $value,
$useWildcard, $options); } } /** * Searches a supplier by its code. * @param String $value - the supplier code * @param Array $options - stores the code name used */ private function searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT
“.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes` AS sc, `{$this->tableName}` AS ent WHERE sc.`code`=? AND sc.`value`=? AND `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx)
{ writeLog(“Could not match supplier by their code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by their code.’); } // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external reservation
systems. * @param unknown $valuem * @param array $options */ private function searchRecords_extResSys($value, Array $options) { global $DBTImportRes_suppliersAssociations; $db = \DB::getInstance(); // SEARCH IN THE DB $searchSupplierSQL=”SELECT tinaSupplierId FROM `$DBTImportRes_suppliersAssociations` WHERE
systemSupplierId=? AND systemId=?”; $searchSupplierEx=$db->Execute($searchSupplierSQL, array($value, $options[‘idExtResSystem’])); if (!$searchSupplierEx) { writeLog(“Could not match supplier by the external reservation systems code.”, ‘events’, 9); throw new \Exception(‘Could not match supplier by the external reservation systems
code.’); } // RETURN THE RESULT $supp=$searchSupplierEx->FetchRow(); $return = array(); if ($supp[‘tinaSupplierId’]) { $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } <?php namespace App\Config; abstract class XmlBasicAbstract extends BaseAbstract { /** * Stores the configuration file
(relative to the “files/config/” dir). For config files stored * inside subdirectories, the subdirectory is included (Ex: services/servicesList.xml). * @var String */ protected $fileName=null; /** * The filename with its full path. * @var String */ protected $fileNameWithPath=null; /** * (non-PHPdoc) * @see
\App\Config\BaseAbstract::constructInit() */ protected function constructInit() { // check if the file name is okay if (!$this->fileName) { throw new \Exception(‘You did not set the `fileName` attribute for the class `’.get_called_class().’`.’); } // set the $this->fileNameWithPath=MODULE_REL_PATH . FILEPATH_APPCONFIGS . $this->fileName;
} /** * Sets a reference to the current data in session so it can be stored during session lifetime. * @see \App\Config\BaseAbstract::constructAdmin() */ protected function constructAdmin() { global $S; $className =get_called_ class(); // if items list and changed flag are set in session - they are copied into class attributes if
2015
www.dcsplus.net