Sie sind auf Seite 1von 50

$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[‘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

FLEXIBLE SOLUTIONS FOR YOUR BUSINESS NEEDS 6


Online Solutions 7
Management for Travel Agencies 9
Online Reservations and Distribution 10
Tour Operator Software 11
Inventory and Distribution 12
Static and Dynamic Packaging 13
Multiple Points of Sale 14
Sales Flexibility 16
Solutions for PCOs 18
Corporate Travel 20
Custom Apps 21

RELIABLE SERVICES 22
Consultancy 22
Operational Audit 23
Customizations 24
Trainings 25
Courses 26

PRODUCTS’ STRENGTHS, USABILITY, KEY FEATURES & BENEFITS 27

Travel IntraNet Application - TINA 27


Travel Booking System - TBS 30
Advanced Inventory and Distribution Application - AIDA 34
TRavel Internet Presentation - TRIP 37

PRODUCTS INTERACTION 40

dcs plus BPO - THE NEW COMPANY OF THE GROUP 41

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).

first full web-based travel ERP


Expertise
During all these years, dcs plus team acquired experience, 5 billion EUR
know-how and business expertise, managing to create a in transactions made
complete range of fully optimized and scalable solutions. through our solutions
We are able to cover any scenario that may appear in the
activity of a travel agency. Dealing with so many different
business models led us to a high level of industry innovative technology
understanding. So we expanded our activity and started
offering business analysis and recommendations,
13 years of experience
processes audit and consultancy. The support, guidance
and follow-up offered by dcs plus team are meant to bring
added value to those who want to improve and grow. no transaction fees charged
5
www.dcsplus.net
What we offer
Flexible solutions for your business needs
13 years of experience in the travel industry allowed dcs dcs plus platforms that cover your technology requirements
plus team to develop a complete range of software
products designed to cover all the necessities of a travel TINA - the ERP of choice for the travel TBS - advanced travel booking system
agency, regardless of size, business model or market. industry across 17 markets - enables you with XML/API/ webservice GDS
to have an enterprise-wide view of the connection. TBS operates reservations
Either you are a: information you need. for any type of travel service:
• Online travel agency TINA helps your travel agency to: • Flight
• Tour operator • Automate processes • Hotel
• Travel management company • Increase business efficiency • Car
• Business travel agency • Control the business by implementing • Transfer
• Leisure travel agency the strategy directly inside the • Insurance
• Single-site travel agency application • Cruise
• Travel search company • Report all the information using • Activity
• Destination management company different types of reports • Vacation package etc.
• Professional congress organizer • Change the business strategy on the go
etc.
or you need:
• Reservations and sales AIDA - designed for tour operators and TRIP - a B2C enterprise platform
• Online selling travel package retailers, AIDA provides focused on selling travel products to
• Management for travel agencies inventory and stock management for consumers through a friendly and
• Tour operator software travel services such as: innovative interface. The core of the
• Inventory & stock management • Accommodation platform is a multitasking engine with
• Static & dynamic packaging • Transportation native integration via webservices with
• Multiple points of sale • Transfer services the other technology platforms (TBS,
• Sales flexibility • Additional services AIDA). TRIP engine focuses on:
• Customized selling platforms • Travel packages etc. • Descriptive content integration
• Mobile apps • Advanced SEO tools
• Booking kiosks • Mobile compatibility
• Corporate travel • Innovative features
• Solutions for PCOs • Smart search concept etc.

... you can find in our complete range of end-to-end


solutions the answer to your individual needs.
6
www.dcsplus.net
Online solutions
Online business has been the fastest growing sector during The online booking interface can be opened in any website,
the last decade and it continues to grow, due to the major whether travel oriented or not, using whitelabel. This gives In the online market, the success of
cost reductions, customers’ affiliations and accessibility. you the opportunity, as a TBS client, to increase the a travel agency consists in the ability
number of visitors by opening the online sales platform to of standing out through a
Consolidating the online selling channels is a major step high rated websites. differentiated and strong web
forward for any travel agency. With the right and presence.
affordable instruments, you can complete the process fast Also, for maximum flexibility in distributing products from
enough to catch-up with the other competitors that have your own inventory, output webservices were built-in,
already made their move. allowing you to connect to virtually any other distribution
platform. When using TBS or AIDA, you can design a
Our online travel solutions - AIDA, TBS and TRIP - are custom-made front office selling interface through these
designed for the online environment, using emerging webservices, and sell your own travel packages online.
technologies. Being web-based, our products bring you
more advantages: accessibility and scalability, low cost With webservices output, you can reach the desired level
implementation and maintenance, low TCO etc. of flexibility and functionality on your public interface.

Beyond the possibility of selling through a reseller network


or through corporate accounts, our Travel Booking
System (TBS) facilitates online sales - the travel agency
can sell its services on a predefined or fully customized
website (TRIP). Webservice /
Whitelabel
Your customer has the possibility to:
TBS
• Search throughout a large database with services
Webservice /
• Make the booking Whitelabel
• Select the payment option AIDA
• Get the voucher or ticket
... and enjoy the trip.

Simple for your visitor, effortless for you!

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.

If your travel agency wants to sell all these products


(either in travel packages or individually) to individuals,

12
www.dcsplus.net
Static and dynamic packaging

Advanced Inventory and Distribution Application Dynamic packaging


(AIDA) represents a major breakthrough in technology for
travel business. The complexity of storing inventories and Dynamic packages - accommodation, transport and other
dynamically joining and distributing travel services was a services can be chosen for custom packages, based on the
great challenge to all software solutions. links, rules and restrictions that you set up. This way of
creating virtual links among the services brings you a great
Our inventory and distribution solution offers both types of flexibility; you can model the dynamic packages on the fly,
packaging for travel agencies: by adding/removing restrictions among services while on
sale. The sales platform is actually a shopping basket
• Static packaging - services joined statically, under a where your client chooses different services to book his
fixed, predefined package own customized package.
• Dynamic packaging - links among the services and
dynamically created packages

Static packaging Join services statically, under


fixed, predefined packages or
Fixed components packages can be built based on
accommodation, transport and other services. They can dynamically create packages by
be: linking different travel services.
• Tour - Built starting from the transportation service, such
a package has multiple destinations with multiple
accommodation units. Multiple additional services can be
added to the package, either mandatory or optional.
• Holiday - Built starting from the accommodation service,
these packages have a single destination, the targeted city
of the accommodation service. Multiple additional services
can be added to the package, either mandatory or
optional.

13
www.dcsplus.net
Multiple points of sale

Both AIDA and TBS applications can provide points of sale


such as:

• B2B selling platform - facilitates selling travel services


through a network of resellers. By having their own
interface, resellers can search for services, make
reservations, extract vouchers and invoices and generate
reports, based on given credentials.

• B2B for corporate clients - provides travel managers


inside a corporation access to self booking tool

• B2C selling platform - online selling platform - off the


shelf booking engine, which easily integrates in any
website, allowing consumers to make bookings, get travel
documentation etc. Of course this is the simple alternative
for sales to consumer, the bigger brother being the fully
custom website connected via webservices - TRIP.

The standardized, OTA compliant – AIDA web


• B2E channel - access via intranet, for corporate
services XML connector – provides the tour
operator with the most powerful tool for
employees - a company’s employee can book directly on
expanding its clients portfolio, by distributing the company’s behalf, for himself, using the online selling
the content through an unlimited number of platform opened into the company’s intranet
selling channels.
Octav Stan - Director Tour Operator
• TINA Corporate Extranet - suitable for the use of
Solutions
corporate customers who want to send order forms for the
agencies in order to be manually processed (require
normal touch channels).

14
www.dcsplus.net
Select between various distribution channels
the one(s) that best match your business strategy

<?php namespace Engine\Cache;


<?php namespace /** *Engine\Cache;
The class is used /**to*manage
The class the is cache
used to stored
manage on disk.
the cache * @authorstoredGabriel on disk.Grosu * @author */ class Gabriel
Disk implements
Grosu */ class \Engine\Ca
Disk im
full location where the full
cache
location
fileswhere
are stored.
the cache * @var filesString
are stored.
*/ protected
* @var $cachePath=’’;
String */ protected /** * $cachePath=’’;
The prefix key/** is used * The to avoid
prefix the keyreusing
is used of to keys
avoidifthe thereusi
appl

GDS Air content Resellers String */ protected $keyPrefix=’’;


one month /** * The
String */ protected
one
entity object in destructors.
class
monthconstructor.
entity object
/** *$keyPrefix=’’;
/** * The
The default expiration
*/ class
publicconstructor.
/** * The
function __construct()
timedefault
limit, expiration
*/ public function
if no param
{ global
time
__construct()
is specified
limit, if no
$CFG; // realpath
$this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’];
when
{ global
param

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
//

• Interface for internal sales


>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

Mark-up | Commission | Promotion schemas


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
$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
* Sets a value in the *cacheSets 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 i

• Whitelabel on their website the cache */ public function


// FILE AND DIRECTORY
the cache
// FILE
set($key,
MANAGEMENT
*/ public
AND DIRECTORY
$value,
function $expireTime=0)
set($key, $value,
$params=$this->getFileDetails($key);
{ //$expireTime=0)
CHECK INPUTS {////if CHECK
MANAGEMENT $params=$this->getFileDetails($key);
key is not
// attempt to create the
INPUTS set if //
// attempt
(!$key)
directory
if key return
is not set
tochain
createifthe
false; if (!$key)
// do not return
storefalse;
(!\Dir::create($params[‘fileLocation
directory chain if (!\Dir::create(
‘null’ values
// do not if

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

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
FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]);
FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]); // get and check expiration // get and timecheck $expirationTimestamp
expiration time $expirationTimestamp=substr ($fc, 0, 10); =subs if

Selling Channels
Corporate ($expirationTimestamp<TIMESTAMP)

array - deletes all those


($expirationTimestamp<TIMESTAMP)
$value=@unserialize($valueSer);
array keys;
- deletes
if it is all
{ // delete the expired
$value=@unserialize($valueSer);
if ($valu e===false) if
a string
those -keys; deleted if itonly
{ //
return
($valu
is a that
cache

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

Rent a car companies $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]);


$return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]);
(!file_exists($params[‘fileLocation’].$params[‘fileName’]))
(!file_exists($params[‘fileLocation’].$params[‘fileName’]))
} } // ONLY ONE KEY
return true; // deletereturn the file true;
} } else// {ONLY // get ONE fileKEY params else {$params=$this->getFileDetails($key);
// get file params $params=$this->
$return=\File::delete($params[‘fileLocation’].$params[‘fileName’]);
// delete the file $return=\File::delete($params[‘fileLocation’].$p

• Interface for their intranet/extranet * Deletes the whole cache* Deletes


\Dir::delete($this->cachePath);
$return=array(); // associate
content.

$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

CHECK IF THE FILE EXISTS CHECK IF if (!file_exists($fullFileName)


THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) or !is_readable($fullFileName))
{ $return[‘fileExists’]=false; { $return[‘fileExists’]=false;
} else { // associate }some else { more // assoc
keys

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 /*

Transfer providers */ public function __construct()


*/ public function
@param String $triggerName
@param String
{ $this->register();
- the trigger
__construct() {
$triggerNamename *-@param
} $this->register();
/** * Stores a new
the trigger
} observer
\Engine\Plugins\Observer
/** * Stores
name * @param \Engine\Plugins\Observer
for this a newplugin observer
(the method
$obs - the observer object
for thisisplugin
$obs - *
called(the
the @throws
frommethod
observer
the defined
\Exception
is called
object * -@throws
methodfromregister()
if the triger
the define
\Exception
name is

Webservice function addObserver($triggerName,


function addObserver($triggerName,
‘.get_called_class().’::’.__FUNCTION__.”.”);
method. */ abstract method.
protected*/ function
\Engine\Plugins\Observer
‘.get_called_class().’::’.__FUNCTION__.”.”);
abstract register();
protected /** function
\Engine\Plugins\Observer

* 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

Front Office fields for suppliers. *fields


$matchingFilteringSQL
@varfor Array

$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

Online Booking Tool


>tableName = $DBTSuppliers;
>tableName } = /** $DBTSuppliers;
* Sets a search } by/**supplier
* Sets alias.
a search * @param
by supplier String alias.
$value* @param
- the supplierString $value alias. *- @param the supplier Booleanalias.$useWildcard
* @param Boolean - uses
*/ public function setAlias($value,
*/ public function $useWildcard=true)
setAlias($value, {$useWildcard=true)
$this->setSearchCriterion(‘alias’,
{ $this->setSearchCriterion(‘alias’,
$value, $useWildcard); $value,
} /**$useWildcard);
* Sets a search } by /**supplier
* Sets companya search

Cruise lines $value - the supplier $value


company
>setSearchCriterion(‘company’,
- the

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

Own website (TRIP) type of code (iata, tktCode,


\Exception(“The supplier
type of etc)

>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’,

($field) { case ‘code’:


>setSearchCriterion(‘extResSys’,
uses the parent call. uses* @see
($field)
return
$name, false, array(‘idExtResSystem’=>$idExternalResSystem));
the \App\EntityMatch\BaseAbstract::searchRecords()
{ $this->searchRecords_code($value,
$name, false, array(‘idExtResSystem’=>$idExternalResSystem));
parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords()
case ‘code’: return $this->searchRecords_code($value, $options); break; case
*/ protected function*/
$options);
} /** * This method} does

‘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

Insurance companies $options - stores the $options


$matchSupplierCodeSQL
code name - stores
used the
$matchSupplierCodeSQL
*/ private
code name function
= “ SELECT “.$this->getSelectingFieldsSQL().”
usedsearchRecords_code($value,
*/ private function searchRecords_code($value,
= “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSupplierCodes`
Array $options) { global
FROM `$DBTSupplierCodes`
Array$DBTSupplierCodes;
$options) { global $DBTSupplierCodes;
AS sc, `{$this->tableName}`
$db = \DB::getInstance
AS sc, `{$this->tableName}`
AS ent WHERE sc.`code
$d
AS e

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 $

Other partners' websites \DB::getInstance(); \DB::getInstance();

systems code.”, ‘events’,


// SEARCH IN THE DB// $searchSupplierSQL=”SELECT
$searchSupplierEx=$db->Execute($searchSupplierSQL,
systems9); throw
code.”,new ‘events’,
SEARCH IN THE DB $searchSupplierSQL=”SELECT
$searchSupplierEx=$db->Execute($searchSupplierSQL,
\Exception(‘Could
9); throw new not\Exception(‘Could
match supplier bynot
tinaSupplierId FROM tinaSupplierId
array($value, $options[‘idExtResSystem’]));
thematch
external supplier
`$DBTImportRes_suppliersAssociations`
array($value, $options[‘idExtResSystem’]));
reservation
by the systems
FROM `$DBTImportRes_suppliersAssociations`
if (!$searchSupplierEx)
external code.’); reservation
if (!$searchSupplierEx)
{ writeLog(“Could not
} // systems
RETURN
WHERE systemSuppl

code.’);
THE RESULT
{ match
writeLog(
} // RETURN $supp=
sup

>FetchRow(); $return >FetchRow();


= array(); if $return ($supp[‘tinaSupplierId’])
= array(); if ($supp[‘tinaSupplierId’])
{ $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]);
{ $return=$this->searchRecord_primaryKey($supp[‘tinaSupplierId’]); } return $return; } } < r

Reservation system • Whitelabel for online booking abstract class XmlBasicAbstract


abstract class extends
XmlBasicAbstract
BaseAbstractextends { /** * BaseAbstract
Stores the configuration
{ /** * Stores file the
(relative
configuration
to the “files/config/”
file (relative dir). to the For “files/config/”
config files stored dir). For * inside
config s

Activities providers is included (Ex: services/servicesList.xml).


(non-PHPdoc) * @see
is included (Ex: services/servicesList.xml).
(non-PHPdoc)
did not set the `fileName`
* @var String */ protected
\App\Config\BaseAbstract::constructInit()
did notattribute
* @see \App\Config\BaseAbstract::constructInit()
set the `fileName`
for the classattribute
* @var$fileName=null;

`’.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

Modular structure Sets a reference to the


global $S; $className
Setscurrent
global
a reference
=get_called_
data intosession
$S; $className
(isset($S[‘admin’][‘configApp’][$className][‘itemsList’]))
the currentso it can
class();=get_called_
databeinstored
// if items list
(isset($S[‘admin’][‘configApp’][$className][‘itemsList’]))
session
class();
during
and changed
so it cansession
// if items
{ $this->itemsList=$S
be stored
flag list
lifetime.
areand set in
during
changed
* @seesession
sessionflag
{ $this->itemsList=$S
[‘admin’] [‘configApp’]
\App\Config\BaseAbstract::constructAdmin()
- they
lifetime. * @see \App\Config\BaseAbstract::constructAd
are are set copied
in session
[‘admin’]
[$className
into- class
they attributes
[‘configApp’]
are copied if
][‘itemsList’]; [$className
*/ protected f
into class attributes if
} if ][‘itemsList’]; } if
(isset($S[‘admin’][‘configApp’][$className][‘changedFlag’]))
(isset($S[‘admin’][‘configApp’][$className][‘changedFlag’])) { $this->changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag’];
{ $this->changedFlag=$S[‘admin’][‘configApp’][$className][‘changedFlag } // these refere
different page loads different
$S[‘admin’] page [‘configApp’]
loads $S[‘admin’] [$className] [‘configApp’]
[‘itemsList’]
[$className]
=&$this->itemsList;
[‘itemsList’] =&$this->itemsList;
$S[‘admin’][‘configApp’][$className][‘changedFlag’]=&$this->
$S[‘admin’][‘configApp’][$className][‘cha
PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData()
PHPdoc) * @see \App\Config\BaseAbstract::getAll_rawData() */ protected function*/ getAll_rawData()
protected function { //getAll_rawData()
return the output { in // case
return thethe fileoutput
does not in caseexistthe if
>fileNameWithPath))>fileNameWithPath))
return $this->getAll_rawData_fileNotExists();
return $this->getAll_rawData_fileNotExists();
// the file exists - load // the
the content
file exists as -XML load(and the content
check it)as$xml=@simplexml_load_file($th
XML (and check it) $xml=@sim

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

{ return array(); } /**


config
return $this->getAll
\Exception(‘The
file is not a well
_extract DataFromFile($xml);
{ return
* Returnsarray();
config
formed

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

Inventory Distribution FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]);

array - deletes all those


FILE$fc=\File::getContents($params[‘fileLocation’].$params[‘fileName’]);
($expirationTimestamp<TIMESTAMP)
($expirationTimestamp<TIMESTAMP)
$value=@unserialize($valueSer);
array keys;
- deletes
if it is all
{ // delete the expired
$value=@unserialize($valueSer);
if ($valu e===false) if
a string
those -keys; deleted if itonly
{ //
return
($valu
is a that
cache

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

Inventory & Distribution


Webservice $return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]);
$return*=\File::delete($params[‘fileLocation’].$params[‘fileName’]);
(!file_exists($params[‘fileLocation’].$params[‘fileName’]))
(!file_exists($params[‘fileLocation’].$params[‘fileName’]))
} } // ONLY ONE KEY
return true; // deletereturn the file true;
} } else// {ONLY // get ONE fileKEY params else {$params=$this->getFileDetails($key);
// get file params $params=$this->
$return=\File::delete($params[‘fileLocation’].$params[‘fileName’]);
// delete the file $return=\File::delete($params[‘fileLocation’].$p

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.

Full webservice for custom-made interaction with AIDA


inventory is also available. Webservices commanding AIDA
inventory can be used in two ways:

16
www.dcsplus.net
@ @

@
Website opened in
B2B Interface
@ @

Reseller
@

@
@
whitelabel
@ @

@
@
@
@
@ @
@ @ @

Partner's Whitelabel for


TBS
@ @
@ @

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

Supplier 2 Congress 2 booking website

Supplier 3 TBS TRIP Congress 3 booking website


Reservation System Customized
Booking Platform
... ...

Supplier n Congress n booking website


<?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class
$cachePath=’’; /** * The prefix key is used to avoid the reusing of keys if the application was updated. * @var String */ protect
$defaultExpireTime = 2592000; // 2592000 = one month /** * The class constructor. */ public function __construct() { global $
>cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached key p
determined from the key * @param Array $key */ private function getFileDetails($key) { $return=array(); // rewrite the key $
$keyDir=substr($return[‘keyHash’], 0, 2); // the full file location $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’; $return[‘fil
@param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly saved in the cache *
values if ($value===null) return false; // FILE AND DIRECTORY MANAGEMENT $params=$this->getFileDetails($key); // attemp
(is_numeric($expireTime) and $expireTime>0) { $expireTimestamp=TIMESTAMP+$expireTime; } else { switch (substr($expireTi
$expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break; ca
$expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($valu
<?php namespace Engine\Cache; /** * The class is used to manage the cache stored on disk. * @author Gabriel Grosu */ class Disk implements stored in \Engine\Cache\BaseInterface
the cache for a certain key. {* /** @param* The full location
String $key *where @return theMixed
cache-files are stored.
the stored value *or@var String
null if the key */ was
protected
not found$cach*
String */ protected $keyPrefix=’’; /** * The default expiration time limit, if no param is specified when calling set() * @var int */ protected $defaultExpireTime =//2592000;
>getFileDetails($key); check the//file
2592000 = one month /** * The class constructor.
if (!file_exists($params[‘fileLocation’] */ public function
.$params[‘fileName’]) __construct() { glob
or !is_readable($params [‘file
object in destructors. $this->cachePath=realpath(MODULE_REL_PATH.$CFG[‘paths’][‘files’]).’/’.$CFG[‘cache’][‘disk’][‘location’]; // sets the mem cached
and check key prefix
expiration time$this->keyPrefix=$CFG[‘global’][‘application’][‘versionBuild’];
$expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) return true; } /** * Returns some associa
{ // delete the exp
$return=array(); // rewrite the key $key=$this->keyPrefix.’-’.$key; // the key hash $return[‘keyHash’]=md5($key); // the subdir for the $value=@unserialize($valueSer);
current key $keyDir=substr($return[‘keyHash’], 0, 2); return
if ($valu e===false) // the null;
full file location
else return $return[‘fileLocation’]=$this->cachePath.$keyDir.’/’
$value; } /** * Deletes the value stored in the c
certain key. * @param String $key * @param Mixed $value * @param String $expireTime * @return Boolean - indicating if the key was correctly Booleansaved in the cache
- indicating */ public
if the key functiondeleted
was correctly set($key, $value,
in the cache $expireTime=0)
*/ public function { //delete($key)
CHECK INPUTS { // // if keyINPUTS
CHECK is not setif (!$key)
if (!$key) r
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)
params $params=$this->getFileDetails($k); // check the file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) cont { $expireTimestamp=TIMEST
$expireTimestamp=TIMESTAMP+$expireTime;break; case ‘m’: $expireTimestamp=TIMESTAMP+$expireTime*60;break; case ‘h’: $expireTimestamp=TIMESTAMP+$expireTime*3600;break;
params $params=$this->getFileDetails($key); // check case ‘d’:
the $expireTimestamp=TIMESTAMP+$expireTime*86400;break;
file if (!file_exists($params[‘fileLocation’].$params[‘fileName’])) default
retu
>defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACHE FILE return \File::create($params[‘fileLocation’].$params[‘fileName’],
the whole cache content. * @return Boolean -$fileContent); indicating if the } cache
/** *was Gets a value stored
completely in the *cache
destroyed @seefor a certain key. * @para
\Engine\Cache\BaseInterf
// CHECK INPUTS if (!$key) return null; // CHECK THE FILE // get file params $params=$this->getFileDetails($key); // check the file if certain (!file_exists($params[‘fileLocation’]
key: the file location, the expire .$params[‘fileName’])
timestamp * @return or !is_readable($params
Array */ public function [‘fileLocation’].$params[‘fileName’]))
getInfo($key) { $return=array();return nu
// assoc
expiration time $expirationTimestamp =substr ($fc, 0, 10); if ($expirationTimestamp<TIMESTAMP) { // delete the expired cache from disk$fullFileName=$params[‘fileLocation’].$params[‘fileName’];
$this->delete($key); return null; } // get contents, unserialize // CHECKit and IFreturn it $valueSer=substr($fc,
THE FILE 10); $value=@unserializ
EXISTS if (!file_exists($fullFileName) or !is_rea
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) {
$return[‘keyHash’]=$params[‘keyHash’]; $return[‘fileLocation’]=$params[‘fileLocation’]; $return[‘fileName’]=$params[‘fileName’ // CHECK INPUTS if (!$key) retu
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’]);
$return[‘expireTimestamp’]=substr($fc, 0, 10); $return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT, } } // ONLY ONE KEY e
$return[‘ex
(!file_exists($params[‘fileLocation’].$params[‘fileName’])) return true; // delete the file $return=\File::delete($params[‘fileLocation’].$params[‘fileName’]);
Engine\Plugins; /** } return
* The (Bool)$return;
class is used for } registering
/** * Deletes a new theplugin.
whole It cache content.in *a @return
is extended Boolean
class which - indicating *
is automatically if executed.
the cache w*
\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();
of [triggerName][index][\Engine\Plugins\Observer object] */ protected // associate the key $return[‘key’]=$key;
$observers=array(); /** * The constructor // getwill
more detailst
register
THE FILE EXISTS if (!file_exists($fullFileName) or !is_readable($fullFileName)) { $return[‘fileExists’]=false; } else { // associate some more keys
from the$return[‘fileExists’]=true;
defined method register() $return[‘keyHash’]=$params[‘keyHash’];
* in the concrete classes. * @param String $return[‘fileLocation’]=$params[‘fileLocation’];
$triggerName - the trigger name * @param \Eng $retu
$fc=\File::getContents($fullFileName); $return[‘expireTimestamp’]=substr($fc, 0, 10); $return[‘expireTimestampReadable’]=date(TIMESTAMP_FULL_FORMAT,
addObserver($triggerName, $return[‘expireTimestamp’]);
\Engine\Plugins\Observer // get$obs)
the value $return
{ // check [‘cachedValue’]
trigger =unserialize (substr($fc,
name if (!$triggerName) { throw new 10)); } retu
\Exception(
extended in a class which is automatically * executed. * @author Gabriel */ abstract class RegisterAbstract { /** * The list of observers for a registered
method will addplugin. * @var Array
new observers to the-current
associative
pluginarray of [triggerName][index][\Engine\Plugins\Observer
(using the addObserver() method. */ abstract protected object]
function*/ protected
register();$o
$this->register(); } /** * Stores a new observer for this plugin (the method is called from the defined method register() * in the concrete <?php classes.namespace
* @paramApp\EntityMatch;
String $triggerName /** -*the
Thetrigger name * @param
class manages the matching \Engine\Plugins\Observer
of suppliers * @author $obs - the observer
Gabriel object *exte
*/ class Supplier @t
\Engine\Plugins\Observer $obs) { // check trigger name if (!$triggerName) { throw new \Exception(‘The trigger name was not set in ‘.get_called_class().’::’.__FUNCTION__.”.”);
‘company’); /** * Extra filters - deleted } $this->observers[$triggerName][]
and duplicated suppliers are not allwed. = $obs; } /**String
* @var * The*/ method
protectedwill $matchingFilteringS
add new observers
the list of observers for the current * @return multitype: */ public function getObservers() { return $this->observers; } } <?php namespace App\EntityMatch;
* @var /** * $entityClassName
String */ protected The class manages=the matching of suppliers
‘\Entity\Supplier’; /** * *(non-PHPdoc)
@author Gabriel */ class
* @see Supplier extends \App\Entity
\App\EntityMatch\BaseAbstract::
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 primary
\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
>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,
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 - in
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->setSea
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->setSearchCriterio
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
>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
$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`.`idS
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($matchSupplierCod

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

Inventory & Mid Back Office


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 (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(); //
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
// 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
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 CONT
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) { //
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 ob
{ 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
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. * @v
ed=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.

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

dcs plus’ custom apps development focus areas: Mobile applications

• 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

In our vision, new technologies and software products are


The necessary steps to take for completing this
mandatory for any agency that wants to keep up with the
process are:
dynamic travel industry. But in order to fully assimilate the
1. Setting the goals and objectives together with your
innovation’s benefits, one needs first to have a clear view
agency’s management team and gathering basic
on the business day-to-day operations.
business information
2. Team allocation for the audit process
This is why we decided to include the operational audit in
3. Elaborating the audit plan
our range of services. Our aim is to improve your efficiency
4. Audit process
and effectiveness on the internal processes. Based on
5. Preparing the documentation containing the
years of experience and hundreds of successful
auditors’ conclusions and recommendations - review it
implementations, we are confident that we can offer you
with your travel agency’s management
new perspectives and increased control via the audit
6. Final audit report including an action plan
process, a service that we offer free of charge during the
7. Follow-up in order to have an overview of the
implementation phase.
progress made

Additionaly, we can perform an operational audit, not only


at the beginning of the implementation, but anytime you
request it - whenever you need processes' reevaluation,
changes in strategy, purchasing of a new company etc.

The operational audit is focusing on:


• Mapping the operational processes inside the travel
agency
• Identifying the weak and strong points
• Providing suggestions on how you can eliminate weak
points
• How to improve strong points
• How to increase the overall efficiency.

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

An approach that combines a well-structured and mature


application with a high level of know-how in using all the
A software solution, no matter
available features can guarantee substantial benefits for how good it is, cannot be used
your business.
at its full potential without the
Some of these benefits, such as bringing added value for
proper training.
the company services and massive cost reductions, are
more important nowadays than in the past, due to the
reorientation of the business models to efficiency instead
of growth in volume.
On behalf of GoTravel / SunMedair I would like to thank dcs plus' team
The extensive experience and the wide client portfolio for all their professional support during the training and implementation
grant our access to a large and varied range of business of TINA inside our company. [...]
models. Alongside you, we will set the tools and strategy
Catalin Musat, GoTravel - SunMedair General Manager
to find the suitable model for your business, based on the
specifics of company's activities.

The training sessions can be provided together with the


implementation of the software application or at your
request, being adapted to your agency's characteristics
such as: The quality of travel agents and their development
through training and education are major factors in
• Selling channels (front desk/online sales) increasing business efficiency and productivity.
• Targeted clients (business travel for corporations/leisure
services for individuals)
• Number of users (large agencies with hundreds of users/
small agencies with less than 10 users)

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

Travel IntraNet Application - TINA


TINA is the ERP of choice for the travel industry across 17 markets.

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.

Why you need TINA • Hundreds of successful implementations across 17 markets


• More than 1.000 analyzed business models
TINA ERP includes among other features:
• More than 6.000 man-hours of training performed for travel
• Sales processing and data repository tools - All agencies
bookings, client and supplier invoices, payments and • More than 10.000 man-hours of business processes analysis
settlements are handled inside TINA. Also, bookings made • Hundreds of add-on modules which complement the core
inside 3rd party applications (GDS, hotel wholesalers, version and expand the existing functionalities.

Airlines, etc.) can be imported through the included 3rd


TINA is a very well designed software tool that is able to
party XML import mechanism and processed according to accommodate virtually any business model, to increase its
your travel agency’s rules. efficiency and to remodel it in order to achieve the results you
• Control tools - Task management, customer profiles desire.

that are applied in an automated manner, sales force


management module, automated reporting, dashboard
reporting, calendar functions, automated alerts, credit limit
The included workflow, business processes, quality control mechanism,
management, user traces for the identification of patterns
corporate travel tools, automated invoicing and reporting work
inside the daily workflow, automated invoicing, quality seamlessly together out of the box in order to deliver the most efficient
control mechanism etc. tool a travel agency could be using.
• Reporting tools - Standard reports, customized reports Andrei Raileanu - Director Mid Back Office Solutions at dcs plus

built directly by your travel agency, automated report


generation with several formats available, dashboard and

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

Contact Channel Profiles


>searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field, $value, $useWildcard, $options); } } /** * Searches a supplier by its code. * @param String
searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSup
`ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx) { writ
code.’); } // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external

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)

Additional Contracts (service


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-
\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

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

deposit and credit)


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’]
>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’]=$
* @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 //
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
$expireTimestamp=0; } } if (!$expireTimestamp) $expireTimestamp = TIMESTAMP + $this->defaultExpireTime; // FILE CONTENT $fileContent =$expire Timestamp .serialize($value) ; // WRITE THE CACH
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

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

Added value features list


$idExternalResSystem - the external system */ public function setExtResSysName($name, $idExternalResSystem) { $this->setSearchCriterion(‘extResSys’, $name, false, array(‘idExtResSystem’=>$idExtern
parent call. * @see \App\EntityMatch\BaseAbstract::searchRecords() */ protected function dispatchSearchRecords($field, $value, $useWildcard, Array $options=array()) { switch ($field) { case ‘code’: ret
>searchRecords_extResSys($value, $options); break; default: return parent::dispatchSearchRecords($field, $value, $useWildcard, $options); } } /** * Searches a supplier by its code. * @param String
searchRecords_code($value, Array $options) { global $DBTSupplierCodes; $db = \DB::getInstance(); // RUN SQL $matchSupplierCodeSQL = “ SELECT “.$this->getSelectingFieldsSQL().” FROM `$DBTSup

Offer `ent`.`id`=`sc`.`idSupplier` {$this->matchingFilteringSQL} “; $matchSupplierCodeEx = $db->Execute($matchSupplierCodeSQL, array($options[‘codeName’], $value)); if (!$matchSupplierCodeEx) { writ


code.’); } // STORE RESULTS $return = array(); foreach($matchSupplierCodeEx as $supp) { $return[$supp[‘id’]] = $supp; } return $return; } /** * Searches a supplier by its matching in the external
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 \
$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

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).

TBS is perfect for: Why you need TBS • B2C


• Your website
• Wholesalers that automatically TBS represents: • Customized booking platform - TRIP
distribute travel services towards the • An XML aggregation engine • Other partner websites
resellers/subagents network • A booking system • Whitelabel for online booking
• Business travel agencies that • A distribution interface with multiple channels • B2E - Business to employee
service large corporates seeking • Business flow management tool • Webservice output - full webservice for custom-made
advanced self-booking tools • An automation tool interaction with TBS core engine (custom websites, mobile
• Leisure travel agencies that book • A tracking and reporting tool applications, special distribution networks, any 3rd party
from several providers to create distributor etc.)
leisure bundles for their customers TBS can process the following travel services:
• Online travel agencies that require • Air tickets • Cruise Flexible payment modes for resellers
a powerful aggregation engine to • Hotels • Insurance
feed their B2C portal • Car rental • Activities • Credit - the reseller can book within the credit limit
• Events/congress companies that • Transfers • Vacation packages without payment
seek to bring automated booking • Prepayment - the reseller can book, but if the
functions in their portfolio reservation is not paid and confirmed, it is auto-canceled
Benefits
• Any company that can use TBS as before entering the penalty period
an universal API/XMLs umbrella for Separated modular distribution channels and
• Mixed - a combination of the above situations
feeding its own distribution interfaces selling platforms
• Credit card payment with instant confirmation
with aggregated travel content
• B2B
• Resellers
NO TRANSACTION FEES paid to dcs plus
• Interface for internal sales
• Whitelabel on their website As part of dcs plus philosophy, we do not interfere in your
• Corporate business strategy by charging you for the reservations made
• Interface for their account in our systems.

• Interface for their extranet/intranet

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

GDS Air content Resellers


• Interface for internal sales

Mark-up | Commission | Promotion schemas


• Whitelabel on their website
Hotel providers
TBS

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

TBS is built on a versatile platform, being able to adapt to


any travel service and supplier. Currently, TBS works with
the following suppliers:
‘dcs plus has been the perfect IT partner for our
sales effort. The company impressed us right
from the start with their efficient, professional
manner and demonstrated a clear understanding
of Tourico Holidays’ unique position in the global
travel market and helped us execute on our
goals. The service the company provides has
been top quality. The dcs plus team is
tremendously creative and innovative and we
highly recommend them.’ Piotr Hlawiczka -
Sales Manager (Eastern Europe) at Tourico
Holidays

‘Travco has been partnered with dcs plus for


number of years and we have a number of
successful developments in operation. The dcs
plus team is a pleasure to work with and offer a
very high level of service to their suppliers and
customers. Our mutual clients are very pleased
with the TBS product and it is one of the most
popular software suites interfaced with our API.
We look forward to continuing to work closely
with dcs plus in the future as one of our key
software partners.’ Andy Brewster - XML
Development Manager at Travco

‘dcs plus is a professional and efficient partner


delivering workable solutions for clients.’ Guy TBS has been designed with a flexible and modular
Ladkin - Distribution Solutions Manager at structure, with user access per module and an easy to
GTA Travel
use plug-in system. Functionalities and modules can be
plugged or unplugged at your request. Now you can obtain
a tailor-made online travel solution that perfectly fits
your travel agency needs and budget.
33
www.dcsplus.net
Advanced Inventory and
Distribution Application - AIDA
AIDA is a solution for inventory and distribution, ideal for tour operators.

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 can represent a B2C distribution


channel for AIDA in terms of leisure
products (Holiday and Tour Packages).

NO TRANSACTION FEES paid to dcs plus.


36
www.dcsplus.net
TRavel Internet Presentation - TRIP
TRIP is a multitasking online selling platform with native integration via webservice with the other technology platforms (TBS,
AIDA, TINA) that are using the dcs plus engine.

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.

Flexible... portable... Bring your products and offers at your customers’


fingertips
The user can easily: • Meet customers’ demand for self-service
Available on: • search • Let your customers experience innovation while planning
• book their trip
• pay... • Increase revenues by displaying your services in any
...for the travel itinerary place you wish (e.g. Office buildings, Metro stations,
Lounges etc.)
Users can also: • Let the users enjoy sophisticated yet user friendly
• view existing bookings interfaces
• view e-tickets • Customize! Make it fit your brand – banners, colors,
• destination information etc. themes etc.
• Let them use interactive maps
Get Mobile! • Highlight travel service features
• Surprise customers with outstanding widgets and
• intuitive application design gadgets etc.
• rich and interactive interface
Attract • fully branded and customizable
The official website is no
• fast search results
Engage • minimal booking steps
Surprise longer the only element in the
• easy booking process
• integrated with payment solutions/ gateways online marketing strategy
39
www.dcsplus.net
Products interaction
You can manage your travel stocks in AIDA, sell the wholesalers services or your own through TBS (B2B/reseller network) or
through TRIP (B2C/end-users) and manage the mid back office activity in TINA.

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

• POIs • Multitasking online selling


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-
• Innovative features:
H.$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
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
• Organic mobile compatible
• activities platform
t($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
me>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:
◦ Advanced POI integration
imestamp) $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 • Dynamic packaging
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

• Modular architecture ◦ Search by activity


0); 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

• 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

• Wide range of descriptive content


s $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’];

• Social Media etc. ◦ Weekend search


; $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
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(); }
• Smart search
gin (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
t 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
ges 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
ity - 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
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,
y 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
s’][$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
aram 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()
ords($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 Accommodation
STANDARD XML
ray $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`
STANDARD XML
chSupplierCodeEx = $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; }
ng 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
>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
• Hotel
s->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
g */ 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 .
nce 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’]
• Villa
} 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
ta() */ 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
= ‘.$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
- 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
• Apartment

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

Transfer • Multilayered price schemas


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-
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(‘, ‘, • Management of own inventory Transfer services
s’]))); } // 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)

• Complex promotion schemas


, $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-
• Car
; 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) {
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 • Travel units configurations: capacities,
ould 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 */
• Van
• Advanced reporting tools
ys($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) {

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

• Visibility and control


$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 */
• Management of travel and • Consolidated reporting
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[$va
>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->i
\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
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 impl
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(); /**
@ • Increase efficiency @ fiscal documents • Exporting and importing to
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 ca
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
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’: $expireT
@
• Automated invoicing and from accounting software
$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 [‘fileLo

@ • 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.

During the last 13 years, dcs plus has chosen to be not a


witness, but an extremely active player - a visionary,
creative, intelligent one. dcs plus has chosen to be not only
a technology provider, but a strong and reliable partner,
managing to build successful collaborations.

Now, through dcs plus BPO - a dcs plus company, we


dare to explore the travel market beyond technology and
to bring our experience and expertise in order to improve
the business processes of travel agencies and tour-
operators.

dcs plus BPO aims to create a synergy between the core


components of a travel agency/ tour-operator (Technology
- Content - Marketing), through smart and advanced tools,
developed under three main business lines:

• 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.

Our Online Marketing services are specially designed for


the travel market and include the most important digital dcs plus BPO was created to allow dcs plus’s customers
promotion tools, such as: and partners to:

• 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

agency points-of-sale that rely on Amadeus for their


everyday business. dcs plus is a Select Partner member of
dcs plus is a part of Amadeus Partner Network, Amadeus Partner Network in the following markets:
as Select Partner
• Bosnia and Herzegovina • Malta
• Bulgaria • Poland
As strategic partners, Amadeus and dcs plus aim to enter • Croatia • Romania
new markets with combined offers and strategies, helping • Czech Republic • Russia
the travel industry players improve their overall business • Estonia • Serbia and Montenegro
by providing solutions suitable for their needs. • Greece • Slovakia
• Hungary • Turkey
dcs plus and Amadeus have joined together in a • Latvia • Ukraine
partnership back in 2006. Initially, our common goal was • Lithuania
to bring innovation inside the Romanian travel market,
Select Partner members are recognized for the value they
providing reliable technological tools. After successfully
bring to the business of many Amadeus customers. Our
reaching our first purpose, we decided to extend the
registered solutions TBS, TINA, AIDA and TRIP enhance
partnership to all our products and to the entire CESE
Amadeus’ offer to travel agencies and provide superior
region (Central, Eastern and Southern Europe). dcs plus’
functionality and/or preferred commercial conditions when
intelligent software applications, together with the
used on Amadeus’ distribution system.
experience and powerful brand of the leading GDS inside
the region, led to deploying TINA in the following markets:
Bulgaria, Estonia, Hungary, Latvia, Lithuania, Romania,
Russia, Serbia, Turkey and Ukraine.

This partnership proved to be a winning strategy for both


parties, as well as for the travel industry.

Amadeus Partner Network connects the most innovative


travel software providers with more than 90.000 travel

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.

Telephone numbers: Telephone numbers:


+40 (0) 21 302 31 32 +40 (0) 21 302 31 32
+40 (0) 21 302 31 33
+40 (0) 21 302 31 34
(Monday to Friday, 9:00 - 18:00 GMT+2)

Email: office@dcsplus.net Email: office@bpo.dcsplus.net


Website: www.dcsplus.net Website: www.bpo.dcsplus.net

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

Das könnte Ihnen auch gefallen