wait 1sec -> if(focus) -> expand {-5 lines, + 5 lines} * Translations in EN / ES */ /** * Manage includes * @author franzz */ class ClassManagement extends PhpObject { const INC_FOLDER = 'inc/'; const INC_EXT = '.php'; const SETTINGS_FILE = 'settings.php'; private $asIncFiles; function __construct() { parent::__construct(); $this->asIncFiles = array(); $this->incFile(self::SETTINGS_FILE); } function __destruct() { parent::__destruct(); } public function incClass($sClassName) { return $this->incFile(self::INC_FOLDER.$sClassName.self::INC_EXT); } public function incFile($sFilePath, $bMandatory=true) { $bIncluded = false; if(!in_array($sFilePath, $this->asIncFiles)) { $sMissingFile = 'File "'.$sFilePath.'" missing.'; if(file_exists($sFilePath)) { $bIncluded = require_once($sFilePath); $this->asIncFiles[] = $sFilePath; } elseif($bMandatory) { die($sMissingFile.' Stopping process.'); } else { $this->addError($sMissingFile); } } return $bIncluded; } } /** * Common class for all classes. * Handles the error log process * @author franzz */ class PhpObject { //Log file name const LOG_FILENAME = 'log.html'; //Message types const NOTICE_TAB = 'Notice'; const WARNING_TAB = 'Warning'; const ERROR_TAB = 'Error'; const ALL_TAB = 'All'; //Extraction mode const MODE_ARRAY = 0; const MODE_TEXT = 1; const MODE_HTML = 2; const MODE_FILE = 3; //Class variables private $asMessageStack; private $iExtractMode; private $bDebug; function __construct($iExtractMode=self::MODE_FILE) { $this->resetMessageStack(); $this->iExtractMode = $iExtractMode; $this->setDebug(false); } protected function setDebug($bSwitch) { $this->bDebug = $bSwitch; } private static function getLogPath() { return dirname(__FILE__).'/'.self::LOG_FILENAME; } private function resetMessageStack($sType=self::ALL_TAB) { if($sType==self::ALL_TAB) { $this->resetMessageStack(self::NOTICE_TAB); $this->resetMessageStack(self::WARNING_TAB); $this->resetMessageStack(self::ERROR_TAB); } else { $this->asMessageStack[$sType] = array(); } } protected function addNotice($sNotice) { $this->addTrace(self::NOTICE_TAB, $sNotice); } protected function addWarning($sWarning) { $this->addTrace(self::WARNING_TAB, $sWarning); } protected function addError($sError) { $this->addTrace(self::ERROR_TAB, $sError); } private function addTrace($sType, $sMessage) { $this->asMessageStack[$sType][] = $sMessage; } protected function getCleanMessageStack($sType=self::ALL_TAB) { $asMessages = ($sType==self::ALL_TAB)?$this->asMessageStack:$this->asMessageStack[$sType]; $this->resetMessageStack($sType); return $this->glueMessages($asMessages); } protected function getCleanMessageStacks($aoExtsources, $sType=self::ALL_TAB) { $aoExtsources[] = $this; $aoMessages = array(); foreach($aoExtsources as $oExtSource) { $oMessages = $oExtSource->getCleanMessageStack($sType); if($oMessages!='') { $aoMessages[get_class($oExtSource)] = $oMessages; } } return $this->glueMessages($aoMessages); } private function glueMessages($asMessages) { switch($this->iExtractMode) { case self::MODE_TEXT: $oMessageStack = self::recursiveImplode("\n", $asMessages); break; case self::MODE_HTML: $oMessageStack = self::recursiveImplode('
', $asMessages); break; case self::MODE_ARRAY: $oMessageStack = $asMessages; break; case self::MODE_FILE: $oMessageStack = self::recursiveImplode("\n", $asMessages); break; } return $oMessageStack; } private static function flattenMessageStack($asTab, $sGlobalKey='') { $asFlatTab = array(); foreach($asTab as $oKey=>$oRow) { $sKey = is_numeric($oKey)?$sGlobalKey:$oKey.' - '; if(is_array($oRow)) { $asFlatTab = array_merge($asFlatTab, self::flattenMessageStack($oRow, $sKey)); } else { $asFlatTab[] = $sKey.$oRow; } } return $asFlatTab; } private static function recursiveImplode($sGlue, $asTab) { $asTab = self::flattenMessageStack($asTab); return implode($sGlue, $asTab); } function __destruct() { $sErrorStack = $this->getCleanMessageStack($this->bDebug?self::ALL_TAB:self::ERROR_TAB); if($sErrorStack!='') { switch($this->iExtractMode) { case self::MODE_TEXT: echo $sErrorStack; break; case self::MODE_HTML: echo $sErrorStack; break; case self::MODE_ARRAY: break; case self::MODE_FILE: @file_put_contents(self::getLogPath(), "\n\n".date('r')."\n".$sErrorStack, FILE_APPEND); break; } } } } /** * Application Core * @author franzz */ class Databap extends PhpObject { //Common Constants const EXPECTED_PAGE_COOKIE = 'exp_page'; const MAIN_SEPARATOR = ' '; const DATE_FORMAT = 'd/m/Y'; const TIME_FORMAT = 'H:i:s'; const DATE_TIME_FORMAT = 'd/m/Y H:i:s'; const DATE_COMPACT_FORMAT = 'YmdHis'; const DATE_TIME_SQL_FORMAT = 'Y-m-d H:i:s'; const DATE_SQL_FORMAT = 'Y-m-d'; const HISTORY_LENGTH = 10; const STYLE_PATH = 'style.css'; public static $UPLOAD_IMG_EXTS = array('jpg', 'jpeg', 'gif', 'png'); public static $UPLOAD_DOC_EXTS = array('jpg', 'jpeg', 'gif', 'png', 'doc', 'ppt', 'pdf', 'xls', 'docx', 'pptx', 'xlsx'); const ID_SEPARATOR = '_'; //Code const MAX_LIST_LENGTH = 5; //Authorizations const CLEARANCE_MEMBER = 0; const CLEARANCE_ADMIN = 9; const EXT_ACCESS = 'external_access'; const USER_COOKIE_ID = 'id_user'; const USER_COOKIE_PASS = 'checksum'; const COOKIE_LIFETIME = 7; const TOKEN_LENGTH = 32; //HTTP Requests response const DISCONNECTED = '__DISCONNECTED__'; const ERROR = '__ERROR__'; const SUCCESS = '__SUCCESS__'; //Chat Constants //TODO Transfer these constants to chat page const MESSAGE_USER = 'U'; const MESSAGE_ADD_CODE = 'A'; const MESSAGE_EDIT_CODE = 'E'; const MESSAGE_ADD_PROC = 'PA'; const MESSAGE_EDIT_PROC = 'PE'; const MESSAGE_ADD_DOC = 'DA'; const MESSAGE_EDIT_DOC = 'DE'; const MESSAGE_ACTION = 'M'; const MESSAGE_PRIVATE = 'P'; const MESSAGE_IMG = 'I'; const MESSAGE_9GAG = '9'; const MESSAGE_NICK = 'N'; const MESSAGE_STATUS = 'S'; const MESSAGE_CONN = 'C'; const MESSAGE_INVITE = 'V'; const MESSAGE_REBOOT = 'R'; const MESSAGE_ARTICLE = 'B'; const DEFAULT_COMPANY_LOGO = 'tux_24.png'; const DEFAULT_CHAN = 'Principal'; const ALL_CHAN_ID = 1; const ALL_CHAN_TEXT = 'A.l.l_C.h.a.n_I.n.c.l.u.d.e.d'; const KEEP_ALIVE = 600; //seconds const REBOOT_DELAY = 15; //seconds const PM_SEP = '___'; const CHAT_IMG_MAX_WIDTH = 700; const CHAT_IMG_MAX_HEIGHT = 2000; const JSON_PREFIX = '[\-JSON-/]'; //Options Constants const LANG_FR = 'FR'; const OPT_TEXT = 'T'; const OPT_SELECT = 'S'; const OPT_NICKNAME = 1; const OPT_BG = 2; const OPT_BRIGHT_BG = 3; const OPT_HOVER = 4; const OPT_TOKEN = 5; const OPT_IMAGE_CHAT = 6; const OPT_STATUS = 7; //Search Constants const PROC_TYPE = 'p'; const CODE_TYPE = 'c'; const DOC_TYPE = 'd'; const ART_TYPE = 'a'; //Doc constants const DOC_FOLDER = 'docs/'; const DOC_TMP_FOLDER = 'docs/tmp/'; //Objects private $oMySql; private $oProcedure; private $oSearchEngine; private $oClassManagement; //Variables private $iUserId; private $asUserInfo; private $sLanguage; function __construct($oClassManagement) { parent::__construct(); //Browser <> PHP <> MySql synchronization date_default_timezone_set(Settings::TIMEZONE); //header('Content-Type: text/html; charset=utf-8'); $this->oMySql = new MySqlManager(); if($this->oMySql->sDbState == MySqlManager::DB_NO_DATA) $this->install(); $this->oProcedure = new Procedure($this->oMySql); $this->setUserId(0); $this->sLanguage = self::LANG_FR; $this->oSearchEngine = new SearchEngine($this->oMySql); $this->oClassManagement = $oClassManagement; } private function install() { //Install DB $this->oMySql->install(); //Options $sOptionNameCol = MySqlManager::getText(MySqlManager::OPTNAME_TABLE); $sOptionNameIdCol = MySqlManager::getId(MySqlManager::OPTNAME_TABLE); $iNicknameOptId = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_NICKNAME, $sOptionNameCol=>'pseudo du chat', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); $iBgColorOptId = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_BG, $sOptionNameCol=>'couleur de fond', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); $iBgColorOpt2Id = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_BRIGHT_BG, $sOptionNameCol=>'couleur de fond 2 (claire)', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); $iHoverColorOptId = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_HOVER, $sOptionNameCol=>'couleur de survol', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); $iHoverColorOptId = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_TOKEN, $sOptionNameCol=>'token', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); $iChatImageOptId = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_IMAGE_CHAT, $sOptionNameCol=>'image du chat', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); $iChatImageOptId = $this->oMySql->insertRow(MySqlManager::OPTNAME_TABLE, array($sOptionNameIdCol=>self::OPT_STATUS, $sOptionNameCol=>'Mission en cours', 'type'=>self::OPT_TEXT, 'language'=>self::LANG_FR)); //Insert default and all channels $this->oMySql->insertRow(MySqlManager::CHAN_TABLE, array('safe_name'=>self::getChanSafeName(self::ALL_CHAN_TEXT), MySqlManager::getText(MySqlManager::CHAN_TABLE)=>self::ALL_CHAN_TEXT)); $this->oMySql->insertRow(MySqlManager::CHAN_TABLE, array('safe_name'=>self::getChanSafeName(self::DEFAULT_CHAN), MySqlManager::getText(MySqlManager::CHAN_TABLE)=>self::DEFAULT_CHAN)); //Install default users : admin and test $iAdminId = $this->addUser('francois', 'lutran', 'cgi', 'francois@lutran.fr', self::CLEARANCE_ADMIN); $this->addUser('test', 'test', 'test', 'test@test.com'); //Write the SAP blog parser bash script to main folder @file_put_contents('sap_website_parser.sh', "#!/bin/bash\n\n/usr/bin/php -f index.php a=external_access p=sap_blog auth_token=".$iAdminId.'_'.$this->generateToken($iAdminId)); } private function setUserId($iUserId) { $this->iUserId = $iUserId; $this->asUserInfo = ($iUserId>0)?$this->getUserInfo($iUserId):array(); } public function getUserId() { return $this->iUserId; } public function getPage($sPage, $asVars) { $oPage = new Mask('index'); $oPage->setTag('first_page', $sPage); $oPage->setTag('index_link', $asVars['serv_name']); $oPage->setTag('line_break', "Global databap constants\n"); //Rss Link $oPage->setTag('rss_link', $this->getUserOptionValue(self::OPT_TOKEN)); //Pass GET variables down to jQuery $asVars['user_id'] = $this->getUserId(); $sLastKey = end(array_keys($asVars)); foreach($asVars as $sName => $sValue) { $oPage->addInstance('VARS', array('var_name'=>$sName, 'var_value'=>$sValue, 'coma'=>$sName==$sLastKey?'':', ')); } //Pages $asMaskPaths = glob(Mask::MASK_FOLDER.'*.html'); $asMaskNames = array_map('basename', $asMaskPaths, array_fill(1, count($asMaskPaths), '.html')); $sLastKey = end(array_keys($asMaskNames)); foreach($asMaskNames as $sKey=>$sMaskName) { $oPage->addInstance('PAGES', array('page_name'=>$sMaskName, 'page_value'=>$sMaskName, 'coma'=>$sKey==$sLastKey?'':', ')); } //Constants $asConstants = array( 'disconnected'=>self::DISCONNECTED, 'error'=>self::ERROR, 'keep_alive'=>self::KEEP_ALIVE, 'opt_type_text'=>self::OPT_TEXT, 'opt_type_select'=>self::OPT_SELECT, 'max_size'=>self::getMaxSize(), 'authorized_img_exts'=>self::$UPLOAD_IMG_EXTS, 'authorized_file_exts'=>self::$UPLOAD_DOC_EXTS, 'id_sep'=>self::ID_SEPARATOR, 'mask_folder'=>Mask::MASK_FOLDER, 'image_folder'=>Procedure::IMAGE_FOLDER, 'image_folder_tmp'=>Procedure::IMAGE_FOLDER_TMP, 'image_folder_thumb'=>Procedure::IMAGE_FOLDER_THUMB, 'default_chan'=>self::DEFAULT_CHAN, 'all_chan_id'=>self::ALL_CHAN_ID, 'all_chan_text'=>self::ALL_CHAN_TEXT, 'pm_separator'=>self::PM_SEP, 'reboot_delay'=>self::REBOOT_DELAY); $oPage->setTag('constants', $this->jsonConvert($asConstants)); return $oPage->getMask(); } public function getRss($sCat='') { //Class Feed $this->oClassManagement->incClass('rss'); //Building header $asDesc = array ( 'title'=>'Flux RSS Databap'.($sCat==''?'':' - '.$sCat), 'link'=>$_GET['serv_name'].'rss'.($sCat==''?'':'-'.$sCat), 'copyright'=>'Powered by Francois Lutran. RSS Feed Generator under GPLv3 License', 'description'=>'Flux RSS du chat'.($sCat==''?'':', section '.$sCat), 'language'=>'fr', 'webmaster_mail'=>'francois@lutran.fr' ); $oFeed = new Feed($asDesc); //Building items switch(strtolower($sCat)) { case '9gag': $sRegEx = '^(https?://|www\\.)(.*)9gag\\.com'; break; case 'youtube': $sRegEx = '^(https?://|www\\.)(.*)youtube\\.com'; break; case '': $sRegEx = '^(https?://|www\\.)[\.A-Za-z0-9\-]+\\.[a-zA-Z]{2,4}'; break; default: $sRegEx = '^(https?://|www\\.)(.*)'.$sCat.'(.*)\\.[a-zA-Z]{2,4}'; break; } $asResult = $this->oMySql->selectRows(array('select'=>array('id_message', 'nickname', 'message', 'led'), 'from'=>'messages', 'constraint'=>array('message'=>$sRegEx), 'constOpe'=>array('message'=>' REGEXP '))); $sPattern = '/(https?\:\/\/|www\.)[\S]+\.[a-zA-Z]{2,4}([\S]*)/i'; foreach($asResult as $iLinkId=>$asRow) { //get link out of message preg_match($sPattern, $asRow['message'], $asMatches); $asRow['link'] = $asMatches[0]; //add item $asItem = array ( 'title'=>'Lien'.($sCat==''?'':' - '.$sCat).' #'.($iLinkId+1), 'category'=>$sCat, 'description'=>'Lien posté par '.self::getNickNameFormat($asRow['nickname']).' à '.self::getDateFormat($asRow['led']).' : '.$asRow['message'].'', 'author'=>$asRow['nickname'], 'link'=>$_GET['serv_name'].'chat-'.$asRow['id_message'], 'pub_date'=>$asRow['led'], 'guid'=>$_GET['serv_name'].'chat-'.$asRow['id_message'] ); $oFeed->addItem($asItem); } return $oFeed->getFeed(); } public function syncSapBlog() { //Get articles list $asArticles = $this->getSAPBlogRss(); //Update and spread the news $asResult = array(); if(date('N')>5) $asResult[] = "Pas de mise-à-jour pendant le week end !"; else { foreach($asArticles as $asArticle) { $iArticleId = $this->oMySql->selectValue(MySqlManager::ART_TABLE, MySqlManager::getId(MySqlManager::ART_TABLE), array('title'=>$asArticle['title'])); if($iArticleId==0) { $iArticleId = $this->oMySql->insertRow(MySqlManager::ART_TABLE, $asArticle); $this->addMessage($iArticleId, self::MESSAGE_ARTICLE, self::ALL_CHAN_ID); $this->oSearchEngine->buildIndex($iArticleId, self::ART_TYPE); $sResult = 'ADDED'; } else $sResult = 'OK'; $asResult[$iArticleId] = $sResult.' | '.implode(' | ', $asArticle); } } return MySqlManager::implodeAll($asResult, '->', "\n"); } private function getSAPBlogRss() { $sSAPDomain = 'http://scn.sap.com'; $sSAPBlogUrl = $sSAPDomain.'/community/data-warehousing/netweaver-bw/blog'; //TODO add http://www.biportal.org/sap_bi_blog $oDom = $this->getRemotePageDom($sSAPBlogUrl); $aoArticles = $oDom->getElementsByTagName('header'); $asArticles = array(); foreach($aoArticles as $oArticle) { if($oArticle->getAttribute('class')=='jive-blog-post-subject') { $asArticleInfo = array(); $aoLinks = $oArticle->getElementsByTagName('a'); foreach($aoLinks as $oLink) { switch($oLink->getAttribute('class')) { //Title & link case 'font-color-normal': $asArticleInfo['title'] = ucfirst(trim($oLink->nodeValue)); $asArticleInfo['title'] = substr($asArticleInfo['title'], -1)=='.'?substr($asArticleInfo['title'], 0, -1):$asArticleInfo['title']; $asArticleInfo['link'] = $oLink->getAttribute('href'); $asArticleInfo['date'] = substr(str_replace(array($sSAPBlogUrl.'/', '/'), array('', '-'), $asArticleInfo['link']), 0, 10); break; //Author case 'jiveTT-hover-user jive-username-link': $asNames = array_filter(explode(' ', ucwords(trim($oLink->nodeValue)))); $asArticleInfo['first_name'] = array_shift($asNames); $asArticleInfo['last_name'] = implode('-', $asNames); $asArticleInfo['email'] = $sSAPDomain.$oLink->getAttribute('href'); break; } } $asArticles[] = $asArticleInfo; } } return $asArticles; } private function get9gagPost($sUrl) { $asPost = array('url'=>$sUrl); $oDom = $this->getRemotePageDom($sUrl); $oDivs = $oDom->getElementsByTagName('section'); foreach($oDivs as $oPost) {if($oPost->getAttribute('id')=='individual-post') break;} //Title $asPost['title'] = $oPost->getElementsByTagName('h2')->item(0)->nodeValue; //Image $oImages = $oPost->getElementsByTagName('img'); foreach($oImages as $oImage) { switch($oImage->getAttribute('class')) { case 'badge-item-animated-img': $o9gagImage = $oImage; break 2; case 'badge-item-img': $o9gagImage = $oImage; break; } } $asResult = $this->downloadToTmp($o9gagImage->getAttribute('src')); if($asResult['error']=='') { $asPost['url_img'] = $asResult['out']; $asPost['width'] = $asResult['width']; $asPost['height'] = $asResult['height']; } return $asPost; } private function getRemotePageDom($sUrl) { $oDom = new DOMDocument(); @$oDom->loadHTML(file_get_contents($sUrl)); return $oDom->getElementsByTagName('body')->item(0); } public function addUser($sFirstName, $sLastName, $sCompany, $sEmail='', $iClearance=self::CLEARANCE_MEMBER) { $sFirstName = strtolower($sFirstName); $sLastName = strtolower($sLastName); $sCompany = strtolower($sCompany); $sEmail = strtolower($sEmail); //Checking company existency in company table $sCompanyTextCol = MySqlManager::getText(MySqlManager::COMP_TABLE); $iCompanyId = $this->oMySql->selectInsert(MySqlManager::COMP_TABLE, array($sCompanyTextCol=>strtolower($sCompany), 'logo'=>self::DEFAULT_COMPANY_LOGO), array($sCompanyTextCol)); $asInfo = array('first_name'=>$sFirstName, 'last_name'=>$sLastName, 'email'=>$sEmail, MySqlManager::getId(MySqlManager::COMP_TABLE)=>$iCompanyId, 'clearance'=>$iClearance, 'led'=>'0000-00-00 00:00:00'); $iUserId = $this->oMySql->insertRow(MySqlManager::USER_TABLE, $asInfo); //Admin Options $sOptionTable = MySqlManager::OPT_TABLE; $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); $sOptNameIdCol = MySqlManager::getId(MySqlManager::OPTNAME_TABLE); $sOptionTextCol = MySqlManager::getText(MySqlManager::OPT_TABLE); $this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_NICKNAME, $sOptionTextCol=>$sFirstName)); //$this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_BG, $sOptionTextCol=>'#04357B')); //$this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_BRIGHT_BG, $sOptionTextCol=>'#D9E5F2')); //$this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_HOVER, $sOptionTextCol=>'#EFAB00')); $this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_TOKEN, $sOptionTextCol=>$this->generateRssLink($iUserId))); //$this->oMySql->insertRow($sOptionTable, array($sUserIdCol=>$iUserId, $sOptNameIdCol=>self::OPT_IMAGE_CHAT, $sOptionTextCol=>'images/sap_gold_332.jpg')); return $iUserId; } public function resetToken() { $asRs = $this->oMySql->selectRows(array('from'=>'`options`', 'constraint'=>array('id_option_name'=>self::OPT_TOKEN))); foreach($asRs as $asR) { $sTokenLink = $this->generateRssLink($asR['id_user']); $this->oMySql->updateRow('`options`', array('id_option'=>$asR['id_option']), array('`option`'=>$sTokenLink)); } return $asRs; } private function generateRssLink($iUserId=0) { if($iUserId==0) $iUserId = $this->getUserId(); return $_GET['serv_name'].'?a='.self::EXT_ACCESS.'&p=rss&auth_token='.$iUserId.'_'.$this->generateToken($iUserId); } private function generateToken($iUserId) { $sEncryptedToken = ''; if($iUserId>0) { $asUser = $this->oMySql->selectRow(MySqlManager::USER_TABLE, $iUserId, array('first_name', 'last_name', 'email', MySqlManager::getId(MySqlManager::COMP_TABLE))); $sEncryptedToken = self::encryptPassword($asUser['first_name'].$asUser['last_name'].$asUser['email'].$asUser[MySqlManager::getId(MySqlManager::COMP_TABLE)]); } else { $this->addError('generating token : invalid user id "'.$iUserId.'"'); } return $sEncryptedToken; } public function buildCompleteIndex() { $asSearchTypes = array( self::CODE_TYPE=>MySqlManager::CODE_TABLE, self::PROC_TYPE=>MySqlManager::PROC_TABLE, self::ART_TYPE=>MySqlManager::ART_TABLE, self::DOC_TYPE=>MySqlManager::DOC_TABLE); $this->oMySql->getArrayQuery("TRUNCATE ".MySqlManager::SEARCH_TABLE); foreach($asSearchTypes as $sSearchType=>$sSearchTable) { $asItemIds = $this->oMySql->selectRows(array('select'=>MySqlManager::getId($sSearchTable), 'from'=>$sSearchTable)); foreach($asItemIds as $iItemId) $this->oSearchEngine->buildIndex($iItemId, $sSearchType); } } //insert new code / version public function addCode($asData) { $sContent = $asData['content']; $sDescription = $asData['description']; $sLink = str_replace(' ', '_', $asData['link']); //insert value in db $asInsert = array( MySqlManager::getText(MySqlManager::CODE_TABLE)=>$sContent, 'description'=>$sDescription, 'id_user'=>$this->getUserId()); $iCodeId = $this->oMySql->insertRow(MySqlManager::CODE_TABLE, $asInsert); //insert link if exists if($sLink!='') { $asInsert = array(MySqlManager::getId(MySqlManager::CODE_TABLE)=>$iCodeId, 'phrase'=>$sLink); $this->oMySql->insertRow(MySqlManager::URL_TABLE, $asInsert); } //refer id update $this->oMySql->updateRow(MySqlManager::CODE_TABLE, $iCodeId, array('refer_id'=>$iCodeId)); //Add message $this->addMessage($iCodeId, self::MESSAGE_ADD_CODE, self::ALL_CHAN_ID); //Add record in Search Table $this->oSearchEngine->buildIndex($iCodeId, self::CODE_TYPE); return $iCodeId; } public function editCode($oCode, $sContent) { $asRef = $this->getCodeInfo($oCode, 'first'); $iVersion = $this->oMySql->selectValue(MySqlManager::CODE_TABLE, 'COUNT(id_code)', array('refer_id'=>$asRef['id_code']))+1; $asEdit = array('code'=>$sContent, 'description'=>$asRef['description'].' (v'.$iVersion.')', 'id_user'=>$this->iUserId, 'refer_id'=>$asRef['id_code']); $iCodeId = $this->oMySql->insertRow(MySqlManager::CODE_TABLE, $asEdit); //Add message $this->addMessage($iCodeId, self::MESSAGE_EDIT_CODE, self::ALL_CHAN_ID); //Add record in Search Table $this->oSearchEngine->buildIndex($iCodeId, self::CODE_TYPE); return $iCodeId; } public function addProcedure($asPost) { $asPost['id_user'] = $this->getUserId(); $this->oProcedure->inputForm($asPost); $asErrors = $this->oProcedure->checkIntegrity(); if(count($asErrors)==0) { //Previous procedure Id $iPrevProcId = isset($asPost['procedure_id'])?$asPost['procedure_id']:0; //Load procedure into database $bCreation = $this->oProcedure->saveProcedure($iPrevProcId); //Extract extra data $asProc = $this->oProcedure->getProcedure(); $iNewProcId = $asProc['proc_id']; $asUser = $this->getUserInfo($this->getUserId()); $oResult = array('result'=>'success', 'proc_id'=>$iNewProcId, 'led'=>$asProc['led'], 'name'=>$asUser['name'], 'company'=>$asUser['company']); //Add Message in chat if($bCreation) { $this->addMessage($iNewProcId, self::MESSAGE_ADD_PROC, self::ALL_CHAN_ID); } else { $this->addMessage($iNewProcId, self::MESSAGE_EDIT_PROC, self::ALL_CHAN_ID); } //Add record in Search Table $this->oSearchEngine->buildIndex($iNewProcId, self::PROC_TYPE); } else { $oResult = array('result'=>'fail', 'errors'=>$asErrors); } return self::jsonExport($oResult); } public function getProcedure($iProcId) { $this->oProcedure->loadProcedure($iProcId); $asProc = $this->oProcedure->getProcedure(); //Adding user info $asUser = $this->getUserInfo($asProc['id_user']); $asProc['name'] = $asUser['name']; $asProc['company'] = $asUser['company']; return self::jsonExport($asProc); } public function addDoc($asPost) { //Previous Doc Id $iPrevDocId = $asPost['doc_id']; $bCreation = ($iPrevDocId==0); //User $iUserId = $this->getUserId(); //Titles $sTitle = isset($asPost['title'])?$asPost['title']:''; $sDescription = isset($asPost['description'])?$asPost['description']:''; //Get docs $sImagePattern = '/c1_(?P\d+)_image_(?P\w+)/'; foreach($asPost as $sFormId=>$sValue) { preg_match($sImagePattern, $sFormId, $asMatches); if($asMatches['doc_info'] == 'name' || $asMatches['doc_info'] == 'desc') { $asDocs[$asMatches['doc_id']][$asMatches['doc_info']] = $sValue; } } //Load doc into database $asData = array('title'=>$sTitle, 'description'=>$sDescription, MySqlManager::getId(MySqlManager::USER_TABLE)=>$iUserId); $iDbDocId = $this->oMySql->insertRow(MySqlManager::DOC_TABLE, $asData); //Load doc files into database $asDocData[MySqlManager::getId(MySqlManager::DOC_TABLE)] = $iDbDocId; foreach($asDocs as $asDocInfo) { //insert into database $sFileName = $asDocInfo['name']; $asDocData['description'] = $asDocInfo['desc']; $asDocData['file_name'] = $sFileName; $iDbImageId = $this->oMySql->insertRow(MySqlManager::FILE_TABLE, $asDocData); //Move file $sTempFilePath = self::DOC_TMP_FOLDER.$sFileName; $sFilePath = self::DOC_FOLDER.$sFileName; if(!rename($sTempFilePath, $sFilePath)) { $this->addError('Unmoveable file : '.$sTempFilePath); } } //Add this doc to the group $iReferId = $bCreation?$iDbDocId:$this->oMySql->selectValue(MySqlManager::DOC_TABLE, 'refer_id', $iPrevDocId); $this->oMySql->updateRow(MySqlManager::DOC_TABLE, $iDbDocId, array('refer_id'=>$iReferId)); //Add Message in chat $this->addMessage($iDbDocId, $bCreation?self::MESSAGE_ADD_DOC:self::MESSAGE_EDIT_DOC, self::ALL_CHAN_ID); //Add record in Search Table $this->oSearchEngine->buildIndex($iDbDocId, self::DOC_TYPE); return self::jsonExport(array('result'=>'success','doc_id'=>$iDbDocId)); } public function getDoc($iDocId) { //Extract doc data $asDoc = $this->oMySql->selectRow(MySqlManager::DOC_TABLE, $iDocId); //Extract extra data $asUser = $this->getUserInfo($asDoc['id_user']); $asDoc['name'] = $asUser['name']; $asDoc['company'] = $asUser['company']; //Extract doc files $asFiles = $this->oMySql->selectRows(array('from'=>MySqlManager::FILE_TABLE, 'constraint'=>array('id_doc'=>$iDocId))); foreach($asFiles as $asFile) { $asDoc['files'][$asFile[MySqlManager::getId(MySqlManager::FILE_TABLE)]] = array('description'=>$asFile['description'], 'ext'=>pathinfo($asFile['file_name'], PATHINFO_EXTENSION)); } return self::jsonExport($asDoc); } public function getFile($iFileId) { $sFileName = $this->oMySql->selectValue(MySqlManager::FILE_TABLE, 'file_name', $iFileId); $sFilePath = self::DOC_FOLDER.$sFileName; $sFileFullPath = dirname($_SERVER['SCRIPT_FILENAME'])."/".$sFilePath; if(!file_exists($sFilePath)) { die(); } else { //get mime type if(class_exists('finfo')) { $oFileInfo = new finfo(FILEINFO_MIME); $sMimetype = $oFileInfo->file($sFileFullPath); } else { $sMimetype = 'application/force-download'; } //set headers header('Pragma: public'); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Cache-Control: public'); header('Content-Description: File Transfer'); header('Content-Type: '.$sMimetype); header('Content-Disposition: attachment; filename='.$sFileName); header('Content-Transfer-Encoding: binary'); header('Content-Length: '.@filesize($sFilePath)); // download if ($oFile = @fopen($sFilePath, 'rb')) { while(!feof($oFile)) { print(fread($oFile, 1024*8)); flush(); if(connection_status() != 0) { @fclose($oFile); } } @fclose($oFile); } } } public function uploadImage() { $oFileUploader = new fileUploader(Procedure::IMAGE_FOLDER_TMP, self::$UPLOAD_IMG_EXTS); $asResult = $oFileUploader->handleUpload(); return $this->jsonConvert($asResult); } public function uploadDoc() { $oFileUploader = new fileUploader(self::DOC_TMP_FOLDER, self::$UPLOAD_DOC_EXTS); $asResult = $oFileUploader->handleUpload(); return $this->jsonConvert($asResult); } private function getCodeInfo($oCode, $sMode='', $bCode=false) { $iCodeId = is_numeric($oCode)?$oCode:$this->getIdCodeFromPhrase($oCode); //Mode (get last or first id_code) if($sMode!='') { $this->switchCodeId($iCodeId, $sMode); } //Efficient request : don't request for the entire code if(!$bCode) { $asSelect = MySqlManager::getTablecolumns(MySqlManager::CODE_TABLE); unset($asSelect[MySqlManager::getText(MySqlManager::CODE_TABLE)]); $asSelect = array_keys($asSelect); } else { $asSelect = "*"; } $asCode = $this->oMySql->selectRow(MySqlManager::CODE_TABLE, $iCodeId, $asSelect); $asCode['description'] = self::getDescriptionFormat($asCode['description']); $asCode['timestamp'] = strtotime($asCode['led']); $asCode['led'] = self::getDateFormat($asCode['led']); return $asCode; } private function getProcInfo($iProcId) { $asProc = $this->oMySql->selectRow(MySqlManager::PROC_TABLE, $iProcId); $asProc['title'] = self::getDescriptionFormat($asProc['title']); $asProc['description'] = self::getDescriptionFormat($asProc['description']); $asProc['timestamp'] = strtotime($asProc['led']); $asProc['led'] = self::getDateFormat($asProc['led']); return $asProc; } private function getDocInfo($iDocId) { $asDoc = $this->oMySql->selectRow(MySqlManager::DOC_TABLE, $iDocId); $asDoc['title'] = self::getDescriptionFormat($asDoc['title']); $asDoc['description'] = self::getDescriptionFormat($asDoc['description']); $asDoc['timestamp'] = strtotime($asDoc['led']); $asDoc['led'] = self::getDateFormat($asDoc['led']); return $asDoc; } private function getArticleInfo($iArtId) { $asArt = $this->oMySql->selectRow(MySqlManager::ART_TABLE, $iArtId); $asTransferredInfo = array('link_art'=>$asArt['link'], 'art_title'=>$asArt['title'], 'link_auth'=>$asArt['email']); $asTransferredInfo['art_date'] = self::getDateFormat($asArt['date'], self::DATE_FORMAT); $asTransferredInfo['name'] = self::getNameFormat($asArt['first_name'], $asArt['last_name']); $asTransferredInfo['description'] = self::getDescriptionFormat($asArt['title']); $asTransferredInfo['timestamp'] = strtotime($asArt['led']); $asTransferredInfo['led'] = self::getDateFormat($asArt['led']); $asTransferredInfo['company'] = 'SAP'; return $asTransferredInfo; } public function getUserInfo($iUserId, $bJson=false) { if($iUserId==$this->getUserId() && !empty($this->asUserInfo)) { $asUserInfo = $this->asUserInfo; } else { $asRow = $this->oMySql->selectRow(MySqlManager::USER_TABLE, $iUserId); $asCompany = $this->oMySql->selectRow(MySqlManager::COMP_TABLE, $asRow[MySqlManager::getId(MySqlManager::COMP_TABLE)]); $asUserInfo = array( 'name'=>self::getNameFormat($asRow['first_name'], $asRow['last_name']), 'nickname'=>self::getNickNameFormat($this->getChatNickNames($iUserId)), 'email'=>$asRow['email'], 'company'=>self::getCompanyFormat($asCompany[MySqlManager::getText(MySqlManager::COMP_TABLE)]), 'status'=>$this->getDescriptionFormat($this->getUserOptionValue(self::OPT_STATUS, $iUserId)), 'logo'=>$asCompany['logo'], 'clearance'=>$asRow['clearance'], 'user_led'=>self::getDateFormat($asRow['led'], self::DATE_FORMAT)); } return $bJson?$this->jsonExport($asUserInfo):$asUserInfo; } public function getUserClearance() { $asUserInfo = $this->getUserInfo($this->getUserId()); return $asUserInfo['clearance']; } public function getOptions() { $sOptValueIdCol = MySqlManager::getId(MySqlManager::OPTVAL_TABLE); $sOptionTextCol = MySqlManager::getText(MySqlManager::OPT_TABLE); $sOptNameTextCol = MySqlManager::getText(MySqlManager::OPTNAME_TABLE); $sOptValueTextCol = MySqlManager::getText(MySqlManager::OPTVAL_TABLE); //Available Options $asAvailableOptions = $this->getAvailableOptions(); //User options $asUserOptions = $this->getUserOptions(); //Build Options panel $asSelectedOptions = array(); foreach($asAvailableOptions as $sOptNameId=>$asOption) { $asSelectedOptions[$sOptNameId]['option_name'] = self::getDescriptionFormat($asOption[$sOptNameTextCol]); $asSelectedOptions[$sOptNameId]['user_value_id'] = array_key_exists($sOptNameId, $asUserOptions)?$asUserOptions[$sOptNameId][$sOptValueIdCol]:0; $asSelectedOptions[$sOptNameId]['user_value'] = array_key_exists($sOptNameId, $asUserOptions)?$asUserOptions[$sOptNameId][$sOptionTextCol]:''; $asSelectedOptions[$sOptNameId]['type'] = $asOption['type']; if($asOption['type']==self::OPT_SELECT) { $asOptionValuesInfo = array('select'=>array($sOptValueIdCol, $sOptValueTextCol), 'from'=>MySqlManager::OPTVAL_TABLE, 'constraint'=>array('language'=>$this->sLanguage, MySqlManager::getId(MySqlManager::OPTNAME_TABLE)=>$sOptNameId), 'orderBy'=>array('led'=>'ASC')); $asSelectedOptions[$sOptNameId]['select'] = $this->oMySql->selectRows($asOptionValuesInfo, true, $sOptValueIdCol); } } return $this->jsonExport($asSelectedOptions); } public function setOptions($asUserOptions, $bSilentUpdate=true) { foreach($this->getAvailableOptions() as $sOptNameId=>$asOption) { if(array_key_exists($sOptNameId, $asUserOptions)) { switch($asOption['type']) { case self::OPT_SELECT: //insert id of option value table $sUpdateCol = MySqlManager::getId(MySqlManager::OPTVAL_TABLE); break; case self::OPT_TEXT: //insert value $sUpdateCol = MySqlManager::getText(MySqlManager::OPT_TABLE); break; default: continue 2; } $sNewValue = $asUserOptions[$sOptNameId]; //Check identical data $asKeys = array(MySqlManager::getId(MySqlManager::USER_TABLE)=>$this->getUserId(), MySqlManager::getId(MySqlManager::OPTNAME_TABLE)=>$sOptNameId); $asData = array($sUpdateCol=>$sNewValue) + $asKeys; $sOldValue = $this->oMySql->selectValue(MySqlManager::OPT_TABLE, $sUpdateCol, $asKeys); //Update value if($sOldValue!=$sNewValue) { //Update value $this->oMySql->insertUpdateRow(MySqlManager::OPT_TABLE, $asData, array_keys($asKeys)); //Spread the word if(!$bSilentUpdate) { //TODO rassembler les messages similaires dans une fonction de template switch($sOptNameId) { case self::OPT_NICKNAME: $sType = self::MESSAGE_NICK; $sChanName = self::ALL_CHAN_TEXT; $sMessage = $sOldValue.' a changé son pseudo en '.$sNewValue; break; case self::OPT_STATUS: $sType = self::MESSAGE_STATUS; $sChanName = self::ALL_CHAN_TEXT; $sMessage = 'est sur une nouvelle mission : '.$this->getDescriptionFormat($sNewValue); break; default: continue 2; } $sChanId = $this->getChanId($sChanName); $this->addMessage($sMessage, $sType, $sChanId); } } } } } private function getAvailableOptions() { $sOptNameIdCol = MySqlManager::getId(MySqlManager::OPTNAME_TABLE); $sOptNameTextCol = MySqlManager::getText(MySqlManager::OPTNAME_TABLE); $asInfo = array('select'=>array($sOptNameIdCol, $sOptNameTextCol, 'type'), 'from'=>MySqlManager::OPTNAME_TABLE, 'constraint'=>array('language'=>$this->sLanguage)); return $this->oMySql->selectRows($asInfo, true, $sOptNameIdCol); } private function getUserOptionValue($sOptionNameId, $iUserId=0) { $asUserOptions = $this->getUserOptions($sOptionNameId, $iUserId); $asOptionInfo = array_shift($asUserOptions); return $asOptionInfo['option']; } private function getUserOptions($oOptionNameIds=array(), $iUserId=0) { $iUserId = $iUserId>0?$iUserId:$this->getUserId(); $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); $sOptNameIdCol = MySqlManager::getId(MySqlManager::OPTNAME_TABLE); $sOptValueIdCol = MySqlManager::getId(MySqlManager::OPTVAL_TABLE); $sOptionTextCol = MySqlManager::getText(MySqlManager::OPT_TABLE); if(!is_array($oOptionNameIds)) { $oOptionNameIds = array($oOptionNameIds); } $asUserinfo = array('select'=>array($sOptNameIdCol, $sOptValueIdCol, $sOptionTextCol), 'from'=>MySqlManager::OPT_TABLE, 'constraint'=>array($sUserIdCol=>$iUserId)); if(count($oOptionNameIds)>0) { $asUserinfo['constraint'][$sOptNameIdCol] = "(".implode(", ", $oOptionNameIds).")"; $asUserinfo['constOpe'] = array($sUserIdCol=>"=", $sOptNameIdCol=>" IN "); $asUserinfo['constVar'] = true; } return $this->oMySql->selectRows($asUserinfo, true, $sOptNameIdCol); } public function getProfile($oUser) { switch($oUser) { case '': $iUserId = $this->getUserId(); break; case is_numeric($oUser): $iUserId = $oUser; break; case is_string($oUser): $oRes = $this->oMySql->selectValue(MySqlManager::USER_TABLE, MySqlManager::getId(MySqlManager::USER_TABLE), array('user'=>$oUser)); $iUserId = !$oRes?$this->getUserId():$oRes; break; default: $iUserId = $this->getUserId(); break; } //User Info $asProfile = $this->getUserInfo($iUserId); //History Info $iFullCodeId = MySqlManager::getId(MySqlManager::CODE_TABLE, true); $iFullLedName = MySqlManager::getFullColumnName(MySqlManager::CODE_TABLE, 'led'); $asInfo = array('select'=>array($iFullCodeId, 'description', 'refer_id', $iFullLedName, 'phrase'), 'from'=>MySqlManager::CODE_TABLE, 'join'=>array(MySqlManager::URL_TABLE=>MySqlManager::getId(MySqlManager::CODE_TABLE)), 'constraint'=>array('id_user'=>$iUserId), 'orderBy'=>array('led'=>'DESC')); $asHistory = $this->oMySql->selectRows($asInfo); foreach($asHistory as $asCode) { $asProfile['history'][$asCode['id_code']]['action'] = (($asCode['refer_id']==$asCode['id_code'])?'Création':'Modification').' de code'; $asProfile['history'][$asCode['id_code']]['date'] = self::getDateFormat($asCode['led']); $asProfile['history'][$asCode['id_code']]['description'] = $asCode['description']; $asProfile['history'][$asCode['id_code']]['phrase'] = ($asCode['phrase']=='')?$asCode['id_code']:$asCode['phrase']; } return $this->jsonExport($asProfile); } private static function isPmChan($sChanSafeName) { $asResult = array('is_pm'=>false, 'chan_name'=>$sChanSafeName); preg_match('/(?P\d+)'.self::PM_SEP.'(?P\d+)/', $sChanSafeName, $asMatch); if(!empty($asMatch)) { $asResult['is_pm'] = true; $asResult['from'] = $asMatch['from']; $asResult['to'] = $asMatch['to']; if($asMatch['from'] > $asMatch['to']) $asResult['chan_name'] = $asMatch['to'].self::PM_SEP.$asMatch['from']; else $asResult['chan_name'] = $asMatch['from'].self::PM_SEP.$asMatch['to']; } return $asResult; } private function checkChanAuth($sChanSafeName, $iUserId=0) { $asUserInfo = ($iUserId > 0)?$this->getUserInfo($iUserId):array('company'=>''); $asCompanies = $this->oMySql->selectRows(array('select'=>MySqlManager::getText(MySqlManager::COMP_TABLE), 'from'=>MySqlManager::COMP_TABLE)); $iCompChan = array_search($sChanSafeName, array_map(array('self', 'getChanSafeName'), $asCompanies)); $asPm = $this->isPmChan($sChanSafeName); return $sChanSafeName!='' && //Empty channel name ($iCompChan===false || $asUserInfo['company']==self::getCompanyFormat($asCompanies[$iCompChan])) && //Test Company Channel (!$asPm['is_pm'] || $iUserId==$asPm['from'] || $iUserId==$asPm['to']); //Test PM } public function joinChan($sChanName, $bFirstConn=false, $asAttendees=array()) { $asResult = array('success'=>self::ERROR); $sSafeChanName = self::getChanSafeName($sChanName); //Authorization to join channel $asMessages = array(); if($this->checkChanAuth($sSafeChanName, $this->getUserId())) { //On first connection, display on-join message for all channels if($bFirstConn) { $asConnChans = $this->getChannels($this->getUserId()); $bFirstChan = true; foreach($asConnChans as $iConnChanId=>$sConnChanName) { //Checking once for all channels if(!$bFirstChan || !$this->isUserConnected($iConnChanId)) { $asMessages[$iConnChanId] = $sConnChanName; } else break; $bFirstChan = false; } } //Add connection link in DB $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); $sChanIdCol = MySqlManager::getId(MySqlManager::CHAN_TABLE); $asData = array('safe_name'=>$sSafeChanName, MySqlManager::getText(MySqlManager::CHAN_TABLE)=>$sChanName); $iChanId = $this->oMySql->insertUpdateRow(MySqlManager::CHAN_TABLE, $asData, array('safe_name'), false); //Is user already connected to this chan $bConnectedUser = $this->isUserConnected($iChanId); $iConnId = $this->oMySql->insertUpdateRow(MySqlManager::CONN_TABLE, array($sUserIdCol=>$this->getUserId(), $sChanIdCol=>$iChanId), array($sUserIdCol, $sChanIdCol), false); if($iConnId>0) $asResult['success'] = self::SUCCESS; if($asResult['success']==self::SUCCESS) { //Return connected channels $asResult['channels'] = $this->getChannels($this->getUserId()); //Add tab title (customized) //TODO delete this shit and insert the channel type into channels DB table foreach($asResult['channels'] as $iConnectedChanId=>$sConnectedChanName) { $asPm = $this->isPmChan($sConnectedChanName); $asResult['channel_tab_names'][$iConnectedChanId] = $asPm['is_pm']?$this->getChatNickNames($asPm['from']==$this->getUserId()?$asPm['to']:$asPm['from']):$sConnectedChanName; } $asResult['current_chan_id'] = $iChanId; //Communicate on user's connection if(!$bConnectedUser) { $asMessages[$iChanId] = $this->oMySql->selectValue(MySqlManager::CHAN_TABLE, MySqlManager::getText(MySqlManager::CHAN_TABLE), $iChanId); } $sStatus = $this->getUserOptionValue(self::OPT_STATUS); $asUserInfo = $this->getUserInfo($this->getUserId()); foreach($asMessages as $iChanId=>$sMsgChanName) { $asPm = $this->isPmChan($sMsgChanName); $this->addMessage(' de '.$asUserInfo['company'].' ('.($sStatus==''?'aucune mission en cours':$this->getDescriptionFormat($sStatus)).') rejoint '.($asPm['is_pm']?'le chan privé':'#'.$sMsgChanName), self::MESSAGE_CONN, $iChanId); } //Send invites to attendees if(!empty($asAttendees)) { $asUserInfo = $this->getUserInfo($this->getUserId()); foreach($asAttendees as $sAttendee) { if($this->checkChanAuth($sSafeChanName, $sAttendee)) { $this->addMessage($sAttendee, self::MESSAGE_INVITE, $iChanId); } } } //Update chan leds $this->pingChans(); } } return self::jsonExport($asResult); } private function isUserConnected($iChanId=0, $iUserId=0) { $iUserId = $iUserId>0?$iUserId:$this->getUserId(); $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); $asInfo = array('select' => MySqlManager::getId(MySqlManager::CONN_TABLE), 'from' => MySqlManager::CONN_TABLE, 'constraint'=> array($sUserIdCol=>$iUserId, 'led'=>"DATE_SUB(NOW(), INTERVAL ".self::KEEP_ALIVE." SECOND)"), 'constOpe' => array($sUserIdCol=>'=', 'led'=>'>'), 'constVar' => true, 'groupBy' => $sUserIdCol); //Add chan constraint if($iChanId>0) { $asInfo['constraint'][MySqlManager::getId(MySqlManager::CHAN_TABLE)] = $iChanId; $asInfo['constOpe'][MySqlManager::getId(MySqlManager::CHAN_TABLE)] = '='; } return (count($this->oMySql->selectRows($asInfo))>0); } public function pingChans() { $aiConstraints = array(MySqlManager::getId(MySqlManager::USER_TABLE)=>$this->getUserId()); $asUpdates = array('led'=>date(self::DATE_TIME_SQL_FORMAT)); $this->oMySql->updateRows(MySqlManager::CONN_TABLE, $aiConstraints, $asUpdates); } public function quitChan($sChanName) { $iChanId = $this->getChanId($sChanName); $iConnId = $this->getConnId($iChanId); if($iConnId>0) { $this->oMySql->deleteRow(MySqlManager::CONN_TABLE, $iConnId); $asPm = $this->isPmChan($sChanName); $this->addMessage('quitte '.($asPm['is_pm']?'le chan privé':'#'.$sChanName), self::MESSAGE_CONN, $iChanId); } } private function getActiveChannels() { //Get connected users $asActiveChanUsers = $this->getConnectedUsers(false, false); $asActiveChans = array_keys($asActiveChanUsers); //Get Channel names $sChanIdCol = MySqlManager::getId(MySqlManager::CHAN_TABLE); $asChanNames = $this->oMySql->selectRows(array('select'=>array($sChanIdCol, MySqlManager::getText(MySqlManager::CHAN_TABLE)), 'from'=>MySqlManager::CHAN_TABLE), true, $sChanIdCol); foreach($asActiveChans as $iChanId) { $asChannels[$asChanNames[$iChanId]] = count($asActiveChanUsers[$iChanId]); } //remove restricted channels $asPublicChannels = array(); foreach($asChannels as $sChanName=>$iChanCount) { if($this->checkChanAuth($this->getChanSafeName($sChanName))) $asPublicChannels[$sChanName] = $iChanCount; } return $asPublicChannels; } private function getChannels($iUserId=0) { $sChanIdCol = MySqlManager::getId(MySqlManager::CHAN_TABLE, true); $sChanNameCol = MySqlManager::getText(MySqlManager::CHAN_TABLE); $asInfo = array('select'=>array($sChanIdCol, $sChanNameCol), 'from'=> MySqlManager::CONN_TABLE, 'join'=> array(MySqlManager::CHAN_TABLE=>MySqlManager::getId(MySqlManager::CHAN_TABLE)), 'orderBy'=> array(MySqlManager::getId(MySqlManager::CONN_TABLE)=>'ASC')); if($iUserId > 0) $asInfo['constraint'] = array(MySqlManager::getId(MySqlManager::USER_TABLE)=>$iUserId); $asChannels = $this->oMySql->selectRows($asInfo, true, MySqlManager::getId(MySqlManager::CHAN_TABLE)); //remove restricted channels $asPublicChannels = array(); foreach($asChannels as $iChanId=>$sChanName) { if($this->checkChanAuth($this->getChanSafeName($sChanName), $iUserId)) $asPublicChannels[$iChanId] = $sChanName; } return $asPublicChannels; } private function getChanId($sChanName) { $sChanIdCol = MySqlManager::getId(MySqlManager::CHAN_TABLE); $sSafeChanName = self::getChanSafeName($sChanName); $iChanId = $this->oMySql->selectValue(MySqlManager::CHAN_TABLE, $sChanIdCol, array('safe_name'=>$sSafeChanName)); if($iChanId==0) { $this->addError('No channel id found with channel name (safe): '.$sSafeChanName.', unsafe: '.$sChanName); } return $iChanId; } private function getConnId($iChanId, $iUserId=0) { $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); $sConnIdCol = MySqlManager::getId(MySqlManager::CONN_TABLE); $sChanIdCol = MySqlManager::getId(MySqlManager::CHAN_TABLE); if($iUserId==0) $iUserId = $this->getUserId(); $iConnId = $this->oMySql->selectValue(MySqlManager::CONN_TABLE, $sConnIdCol, array($sUserIdCol=>$iUserId, $sChanIdCol=>$iChanId)); if($iConnId==0) { $this->addError('No connection found for user: '.$iUserId.' and channel id: '.$iChanId); } return $iConnId; } public function addChatMessage($sMessage, $sChanName) { $sMessage = htmlspecialchars($sMessage); $sType = self::MESSAGE_USER; if(substr($sMessage, 0, 1) == '/') { if(substr($sMessage, 0, 4) == '/me ') { $sType = self::MESSAGE_ACTION; $sMessage = substr($sMessage, 3); } elseif(substr($sMessage, 0, 6) == '/slap ') { $sType = self::MESSAGE_ACTION; $sMessage = ' fout une grosse tarte à '.substr($sMessage, 5); } elseif(substr($sMessage, 0, 4) == '/bs ') { $sType = self::MESSAGE_ACTION; $sMessage = ' bitch-slaps '.substr($sMessage, 3); } elseif(substr($sMessage, 0, 6) == '/kick ') { $sType = self::MESSAGE_ACTION; $sMessage = ' met un coup de pied au cul de '.substr($sMessage, 5); } elseif(substr($sMessage, 0, 6) == '/nick ' && strlen($sMessage)>6) { $sNewNick = $this->getNickNameFormat(substr($sMessage, 5)); $sOldNick = $this->getNickNameFormat($this->getChatNickNames($this->getUserId())); //changing Nickname $this->setOptions(array(self::OPT_NICKNAME=>$sNewNick)); //Message $sType = self::MESSAGE_NICK; $sChanName = self::ALL_CHAN_TEXT; $sMessage = $sOldNick.' a changé son pseudo en '.$sNewNick; } elseif(substr($sMessage, 0, 9) == '/mission ' && strlen($sMessage)>9) { $sNewStatus = substr($sMessage, 9); $sNewFormatStatus = $this->getDescriptionFormat($sNewStatus); //changing Nickname $this->setOptions(array(self::OPT_STATUS=>$sNewStatus)); //Message $sType = self::MESSAGE_STATUS; $sChanName = self::ALL_CHAN_TEXT; $sMessage = 'est sur une nouvelle mission : '.$sNewFormatStatus; } elseif(substr($sMessage, 0, 6) == '/mail ' && strlen($sMessage)>6) { $sImagePattern = '/\/mail (?P\w+) (?P.*)/'; preg_match($sImagePattern, $sMessage, $asMatches); //Looking for user Id $asContraints = array( 'LOWER(`'.MySqlManager::getText(MySqlManager::OPT_TABLE).'`)'=>strtolower($asMatches['nickname']), MySqlManager::getId(MySqlManager::OPTNAME_TABLE)=>self::OPT_NICKNAME); $iUserIdTo = $this->oMySql->selectValue(MySqlManager::OPT_TABLE, MySqlManager::getId(MySqlManager::USER_TABLE), $asContraints); //Handling mail if($iUserIdTo>0) { if(trim($asMatches['message'])!='') { if($this->sendPM($iUserIdTo, $asMatches['message'])) { $sMessage = 'a envoyé un mail à '.$asMatches['nickname']; } else { $sMessage = 'n\'a pas envoyé de mail à '.$asMatches['nickname'].' (raison inconnue, voir log)'; } } else { $sMessage = 'n\'a pas envoyé de mail à '.$asMatches['nickname'].' (message vide)'; } } else { $sMessage = 'n\'a pas envoyé de mail à '.$asMatches['nickname'].' (pseudo inconnu)'; } $sType = self::MESSAGE_ACTION; } elseif(substr($sMessage, 0, 5) == '/mean') { $sPageContent = file_get_contents('http://www.randominsults.net/'); $sStartText = ''; $sEndText = ''; $iStartPos = strpos($sPageContent, $sStartText); $iEndPos = strpos($sPageContent, $sEndText); $sMessage = substr($sPageContent, $iStartPos + strlen($sStartText), $iEndPos - $iStartPos); } elseif(substr($sMessage, 0, 5) == '/like') { $sType = self::MESSAGE_ACTION; $sMessage = ' plussoie'; } elseif(substr($sMessage, 0, 4) == '/now') { $sType = self::MESSAGE_ACTION; $asWeekDays = array('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'); $asMonths = array('janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'); $sMessage = ' a demandé l\'heure. Pour sa gouverne, il est exactement '.date(self::TIME_FORMAT). ', le '.$asWeekDays[date('N')-1].' '.date('j').' '.$asMonths[date('n')-1].' '.date('Y').' (semaine '.date('W').')'; } elseif($sMessage == '/channels' || substr($sMessage, 0, 5) == '/list' || $sMessage == '/chans') { //Always at least one channel open (the one the message is sent from) $sMessage = ' a demandé les chans disponibles. Pour sa gouverne, les chans ayant des membres connectés sont #'.MySqlManager::implodeAll($this->getActiveChannels(), ' (', ', #', '', ')'); $sType = self::MESSAGE_ACTION; } elseif(substr($sMessage, 0, 5) == '/img ' && strlen($sMessage)>5) { $sUrl = trim(substr($sMessage, 4)); $asResult = $this->downloadToTmp($sUrl); if($asResult['error']=='') { $sMessage = $this->getJsonMessage(array($asResult['out'], $asResult['width'], $asResult['height'], $sUrl)); $sType = self::MESSAGE_IMG; } } elseif(substr($sMessage, 0, 6) == '/9gag ' && strlen($sMessage)>6) { $sMessage = $this->getJsonMessage($this->get9gagPost(trim(substr($sMessage, 6)))); $sType = self::MESSAGE_9GAG; } elseif(substr($sMessage, 0, 7) == '/reboot' && $this->getUserClearance()==self::CLEARANCE_ADMIN) { $sMessage = 'L\'administrateur a demandé un reboot. Votre page va se rafraichir automatiquement dans '.self::REBOOT_DELAY.' secondes.'; $sType = self::MESSAGE_REBOOT; } } elseif(substr($sMessage, 0, 1) == '@' && strpos($sMessage, ' ')>1) { $sType = self::MESSAGE_PRIVATE; } $sChanId = $this->getChanId($sChanName); return $this->addMessage($sMessage, $sType, $sChanId); } private function getJsonMessage($asData) { return self::JSON_PREFIX.$this->jsonConvert($asData); } private function downloadToTmp($sUrl) { $sFileInfo = pathinfo($sUrl); $sImageExt = strtolower($sFileInfo['extension']); $sFilePath = self::DOC_TMP_FOLDER.uniqid().'.'.$sImageExt; return ToolBox::createThumbnail($sUrl, self::CHAT_IMG_MAX_WIDTH, self::CHAT_IMG_MAX_HEIGHT, $sFilePath); } private function sendPM($iUserIdTo, $sMessage) { $bSuccess = false; $asUserFrom = $this->getUserInfo($this->getUserId()); $asUserTo = $this->getUserInfo($iUserIdTo); $sFrom = $asUserFrom['name'].' '; $sTo = $asUserTo['name'].' <'.$asUserTo['email'].'>'; $sMessage .= "\n\n\n".'Ne répondez pas à ce mail. Connectez-vous sur Databap.'; $sResult = ToolBox::sendMail($sFrom, $sTo, 'Databap ', 'Databap PM', $sMessage); $bSuccess = ($sResult==''); if(!$bSuccess) $this->addError($sResult); return $bSuccess; } private function addMessage($sMessage, $sType, $iChanId) { $bResult = false; if($iChanId>0) { $asInsert = array( MySqlManager::getId(MySqlManager::USER_TABLE) => $this->getUserId(), 'nickname' => $this->getChatNickNames($this->getUserId()), MySqlManager::getId(MySqlManager::CHAN_TABLE) => $iChanId, MySqlManager::getText(MySqlManager::MSG_TABLE) => $sMessage, 'type' => $sType, 'date' => 'CURDATE()'); $bResult = $this->oMySql->insertRow(MySqlManager::MSG_TABLE, $asInsert); } else { $this->addError('Message deleted: No channel found'); } return $bResult; } public function getMessages($iFirstMsgId="0") { //Update chan ping $this->pingChans(); //Get messages and users' infos $sChanIdCol = MySqlManager::getId(MySqlManager::CHAN_TABLE, true); $sChanTextCol = MySqlManager::getText(MySqlManager::CHAN_TABLE); $sMsgIdCol = MySqlManager::getId(MySqlManager::MSG_TABLE, true); $sMsgTextCol = MySqlManager::getText(MySqlManager::MSG_TABLE); $sMsgTableLed = MySqlManager::getFullColumnName(MySqlManager::MSG_TABLE, 'led'); $sMsgTableChanIdCol = MySqlManager::getFullColumnName(MySqlManager::MSG_TABLE, MySqlManager::getId(MySqlManager::CHAN_TABLE)); $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE, true); $sMsgTableUserIdCol = MySqlManager::getFullColumnName(MySqlManager::MSG_TABLE, MySqlManager::getId(MySqlManager::USER_TABLE)); $sUserTableUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE, true); $sConnTableUserIdCol = MySqlManager::getFullColumnName(MySqlManager::CONN_TABLE, MySqlManager::getId(MySqlManager::USER_TABLE)); $sConnTableChanIdCol = MySqlManager::getFullColumnName(MySqlManager::CONN_TABLE, MySqlManager::getId(MySqlManager::CHAN_TABLE)); //channel related messages $sCurDate = date(Databap::DATE_SQL_FORMAT); $asInfo = array('select' => array($sMsgTableChanIdCol, $sMsgIdCol, $sMsgTextCol, 'type', $sMsgTableLed, 'first_name', 'last_name', $sUserIdCol, 'nickname'), 'from' => MySqlManager::CONN_TABLE, 'joinOn' => array( MySqlManager::MSG_TABLE =>array($sMsgTableChanIdCol, '=', $sConnTableChanIdCol), MySqlManager::USER_TABLE=>array($sUserTableUserIdCol, '=', $sMsgTableUserIdCol)), 'constraint'=> array($sConnTableUserIdCol=>$this->getUserId(), $sMsgIdCol=>$iFirstMsgId, 'date'=>$sCurDate), 'constOpe' => array($sConnTableUserIdCol=>'=', $sMsgIdCol=>'>', 'date'=>'=')); $asSqlMessages = $this->oMySql->selectRows($asInfo, true, 'id_message'); //Global messages $asInfo['from'] = MySqlManager::MSG_TABLE; $asInfo['joinOn'] = array(MySqlManager::USER_TABLE=>array($sUserTableUserIdCol, '=', $sMsgTableUserIdCol)); $asInfo['constraint'] = array($sMsgTableChanIdCol=>self::ALL_CHAN_ID, $sMsgIdCol=>$iFirstMsgId, 'date'=>$sCurDate); $asInfo['constOpe'] = array($sMsgTableChanIdCol=>'=', $sMsgIdCol=>'>', 'date'=>'='); $asSqlGlobalMessages = $this->oMySql->selectRows($asInfo, true, 'id_message'); //Invites messages $asInfo['constraint'] = array($sMsgIdCol=>$iFirstMsgId, 'date'=>$sCurDate, 'type'=>self::MESSAGE_INVITE, 'message'=>$this->getUserId()); $asInfo['constOpe'] = array($sMsgIdCol=>'>', 'date'=>'=', 'type'=>'=', 'message'=>'='); $asSqlInviteMessages = $this->oMySql->selectRows($asInfo, true, 'id_message'); //Merge flows if(!empty($asSqlGlobalMessages)) $asSqlMessages = array_diff_key($asSqlMessages, $asSqlGlobalMessages) + $asSqlGlobalMessages; if(!empty($asSqlInviteMessages)) $asSqlMessages = array_diff_key($asSqlMessages, $asSqlInviteMessages) + $asSqlInviteMessages; //Sort messages ksort($asSqlMessages); //Sort out messages for Json Export $iPrefixLen = strlen(self::JSON_PREFIX); $asMessages = array('messages'=>array(), 'last_message_id'=>0); foreach($asSqlMessages as $iMessageId=>$asMessageInfo) { $iChanId = $asMessageInfo[MySqlManager::getId(MySqlManager::CHAN_TABLE)]; $iUserId = $asMessageInfo[MySqlManager::getId(MySqlManager::USER_TABLE)]; $sMessageType = $asMessageInfo['type']; $asMessages['messages'][$iMessageId]['id_chan'] = $iChanId; $asMessages['messages'][$iMessageId]['message'] = $asMessageInfo[$sMsgTextCol]; $asMessages['messages'][$iMessageId]['msg_class'] = $sMessageType; $asMessages['messages'][$iMessageId]['time'] = self::getDateFormat($asMessageInfo['led'], self::TIME_FORMAT); $asMessages['messages'][$iMessageId]['name'] = self::getNameFormat($asMessageInfo['first_name'], $asMessageInfo['last_name']); $asMessages['messages'][$iMessageId]['nickname'] = self::getNickNameFormat($asMessageInfo['nickname']); //generated messages if($sMessageType==self::MESSAGE_ADD_CODE || $sMessageType==self::MESSAGE_EDIT_CODE) { $asCode = $this->getCodeInfo($asMessages['messages'][$iMessageId]['message']); $asMessages['messages'][$iMessageId]['description'] = $asCode['description']; } elseif($sMessageType==self::MESSAGE_ADD_PROC || $sMessageType==self::MESSAGE_EDIT_PROC) { $asProc = $this->getProcInfo($asMessages['messages'][$iMessageId]['message']); $asMessages['messages'][$iMessageId]['description'] = $asProc['title']; } elseif($sMessageType==self::MESSAGE_ADD_DOC || $sMessageType==self::MESSAGE_EDIT_DOC) { $asDoc = $this->getDocInfo($asMessages['messages'][$iMessageId]['message']); $asMessages['messages'][$iMessageId]['description'] = $asDoc['title']; } elseif($sMessageType==self::MESSAGE_ARTICLE) { $asTransferredInfo = $this->getArticleInfo($asMessages['messages'][$iMessageId]['message']); $asMessages['messages'][$iMessageId] = array_merge($asMessages['messages'][$iMessageId], $asTransferredInfo); } //Switch Chan ID with name for the user to join if($sMessageType==self::MESSAGE_INVITE) { $asMessages['messages'][$iMessageId]['id_chan'] = $this->oMySql->selectValue(MySqlManager::CHAN_TABLE, 'safe_name', $iChanId); } //Json message if(substr($asMessages['messages'][$iMessageId]['message'], 0, $iPrefixLen) == self::JSON_PREFIX) { $asMessages['messages'][$iMessageId]['message'] = json_decode(substr($asMessages['messages'][$iMessageId]['message'], $iPrefixLen)); } else //Normal message { //Dynamic web link $asPatterns = array('`((?:https?|ftp)://\S+[[:alnum:]]/?)`si', '`((?$1 ', '$1'); $asMessages['messages'][$iMessageId]['message'] = preg_replace($asPatterns, $asLinks, $asMessages['messages'][$iMessageId]['message']); //Dynamic chan link $asPatterns = '/(^|\s)#(\w*[^\s]+\w*)/'; $asLinks = '\1#\2'; $asMessages['messages'][$iMessageId]['message'] = preg_replace($asPatterns, $asLinks, $asMessages['messages'][$iMessageId]['message']); } } //Set last message Id (if new messages since $iFirstMsgId) if(!empty($asMessages['messages'])) { $asMessages['last_message_id'] = max(array_keys($asMessages['messages'])); } return $this->jsonExport($asMessages); } private function getConnectedChans($iuserId=0) { $iuserId = $iuserId>0?$iuserId:$this->getUserId(); $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); $asInfo = array('select' => array(MySqlManager::getId(MySqlManager::CHAN_TABLE, true), MySqlManager::getText(MySqlManager::CHAN_TABLE)), 'from' => MySqlManager::CONN_TABLE, 'join' => array(MySqlManager::CHAN_TABLE=>MySqlManager::getId(MySqlManager::CHAN_TABLE)), 'constraint'=> array($sUserIdCol=>$iUserId, 'led'=>"DATE_SUB(NOW(), INTERVAL ".self::KEEP_ALIVE." SECOND)"), 'constOpe' => array($sUserIdCol=>'=', 'led'=>'>'), 'constVar' => true); return $this->oMySql->selectRows($asInfo, true, MySqlManager::getId(MySqlManager::CONN_TABLE)); } public function getConnectedUsers($bUserRestricted=false, $bJson=true) { $sQuery = " SELECT /* config.php 1313 */ conn2.id_channel, conn2.id_user, conn2.led, IF(DATE_SUB(NOW(), INTERVAL 1 MINUTE) < conn2.led, 0, 1) AS afk, users.first_name, users.last_name, companies.company, companies.logo, option_nickname.option AS nickname, option_status.option AS status FROM connections AS conn1 LEFT JOIN connections AS conn2 ON conn2.id_channel = conn1.id_channel LEFT JOIN users ON users.id_user = conn2.id_user LEFT JOIN companies ON companies.id_company = users.id_company LEFT JOIN `options` AS option_nickname ON option_nickname.id_user = users.id_user AND option_nickname.id_option_name = ".self::OPT_NICKNAME." LEFT JOIN `options` AS option_status ON option_status.id_user = users.id_user AND option_status.id_option_name = ".self::OPT_STATUS." WHERE conn2.led > DATE_SUB(NOW(), INTERVAL ".self::KEEP_ALIVE." SECOND)". ($bUserRestricted?"AND conn1.id_user = ".$this->getUserId():"")." GROUP BY conn2.id_channel, conn2.id_user ORDER BY option_nickname.option ASC"; $asUserChannels = $this->oMySql->getArrayQuery($sQuery, true); //Last messages $iUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); $asLastMsg = $this->oMySql->selectRows(array('select'=>array($iUserIdCol, 'MAX(led)'), 'from'=>MySqlManager::MSG_TABLE), true, $iUserIdCol); $asConnectedUsers = array(); foreach($asUserChannels as $asUser) { $sChanId = $asUser[MySqlManager::getId(MySqlManager::CHAN_TABLE)]; $iUserId = $asUser[$iUserIdCol]; $asConnectedUsers[$sChanId][$iUserId]['id_user'] = $iUserId; $asConnectedUsers[$sChanId][$iUserId]['name'] = self::getNameFormat($asUser['first_name'], $asUser['last_name']); $asConnectedUsers[$sChanId][$iUserId]['company'] = self::getCompanyFormat($asUser['company']); $asConnectedUsers[$sChanId][$iUserId]['status'] = $asUser['status']; $asConnectedUsers[$sChanId][$iUserId]['logo'] = $asUser['logo']; $asConnectedUsers[$sChanId][$iUserId]['nickname'] = self::getNickNameFormat($asUser['nickname']==''?$asUser['first_name']:$asUser['nickname']); $asConnectedUsers[$sChanId][$iUserId]['last_activity'] = array_key_exists($iUserId, $asLastMsg)?self::getDateFormat($asLastMsg[$iUserId], self::TIME_FORMAT):''; $asConnectedUsers[$sChanId][$iUserId]['ping'] = self::getDateFormat($asUser['led'], self::TIME_FORMAT); $asConnectedUsers[$sChanId][$iUserId]['afk'] = $asUser['afk']; } return $bJson?$this->jsonExport($asConnectedUsers):$asConnectedUsers; } private function getChatNickNames($iUserId=0) { $sUserIdCol = MySqlManager::getId(MySqlManager::USER_TABLE); $sNicknameCol = MySqlManager::getText(MySqlManager::OPT_TABLE); $asConstraints = array(MySqlManager::getId(MySqlManager::OPTNAME_TABLE)=>self::OPT_NICKNAME); if($iUserId>0) { $asConstraints[MySqlManager::getId(MySqlManager::USER_TABLE)] = $iUserId; $oResult = $this->oMySql->selectValue(MySqlManager::OPT_TABLE, $sNicknameCol, $asConstraints); } else { $asInfo = array('select'=>array($sUserIdCol, $sNicknameCol), 'from'=>MySqlManager::OPT_TABLE, 'constraint'=>$asConstraints); $oResult = $this->oMySql->selectRows($asInfo, true, $sUserIdCol); } return $oResult; } private function switchCodeId(&$iCodeId, $sMode) { switch($sMode) { case 'first': $iCodeId = $this->oMySql->selectValue(MySqlManager::CODE_TABLE, 'refer_id', $iCodeId); break; case 'last': $iRefId = $this->oMySql->selectValue(MySqlManager::CODE_TABLE, 'refer_id', $iCodeId); $iCodeId = $this->oMySql->selectValue(MySqlManager::CODE_TABLE, 'MAX(id_code)', array('refer_id'=>$iRefId)); break; } } private function getIdCodeFromPhrase($sPhrase) { $iCodeId = $this->oMySql->selectValue(MySqlManager::URL_TABLE, MySqlManager::getId(MySqlManager::CODE_TABLE), array('phrase'=>$sPhrase)); $this->switchCodeId($iCodeId, 'last'); return $iCodeId; } private function getPhraseFromIdCode($iCodeId) { $this->switchCodeId($iCodeId, 'first'); return $this->oMySql->selectValue(MySqlManager::URL_TABLE, 'phrase', array('id_code'=>$iCodeId)); } private function getCodeVersions($iRefCodeId) { $asInfo = array('select'=>array(MySqlManager::getId(MySqlManager::CODE_TABLE)), 'from'=>MySqlManager::CODE_TABLE, 'constraint'=>array('refer_id'=>$iRefCodeId), 'orderBy'=>array('id_code'=>'asc')); $asCodeIds = $this->oMySql->selectRows($asInfo); $asCodeVersions = array(); foreach($asCodeIds as $iCodeId) { $asCodeVersions[] = $this->getCodeInfo($iCodeId); } return $asCodeVersions; } public function getColoredCode($oCode) { $asCode = $this->getCodeInfo($oCode, '', true); //code $oReader = new Reader($asCode['code']); $asCode['code'] = $oReader->getColoredCode(); //phrase $sPhrase = $this->getPhraseFromIdCode($asCode['id_code']); if($sPhrase !== false) { $asCode['phrase'] = $sPhrase; } //user $asUsers[$asCode['id_user']] = $this->getUserInfo($asCode['id_user']); $asCode = array_merge($asCode, $asUsers[$asCode['id_user']]); //versions $asCodeVersions = $this->getCodeVersions($asCode['refer_id']); $iCodeRowId = 0; foreach($asCodeVersions as $iRowId=>$asCodeVersion) { if($asCodeVersion['id_code']==$asCode['id_code']) $iCodeRowId = $iRowId; } $asCode['truncated'] = ($iCodeRowId > self::MAX_LIST_LENGTH)?$asCodeVersions[$iCodeRowId-1-self::MAX_LIST_LENGTH]['id_code']:0; foreach($asCodeVersions as $iRowId=>$asCodeVersion) { if($iCodeRowId - $iRowId <= self::MAX_LIST_LENGTH) { if(!array_key_exists($asCodeVersion['id_user'], $asUsers)) { $asUsers[$asCodeVersion['id_user']] = $this->getUserInfo($asCodeVersion['id_user']); } if($asCodeVersion['id_code']!=$asCode['id_code']) { $asVersionInfo = array_merge($asCodeVersion, $asUsers[$asCodeVersion['id_user']]); $asCode['other_versions'][] = $asVersionInfo; } } } return $this->jsonExport($asCode); } public function getNudeCode($oCode) { $asCode = $this->getCodeInfo($oCode, '', true); //$sCode = Reader::convText2Html($asCode['code']); return $asCode['code']; } public function getRawCode($oCode) { $asCode = $this->getCodeInfo($oCode, '', true); $asUser = $this->getUserInfo($asCode['id_user']); $sEncodedCode = $this->jsonConvert($asCode['code']); $sEncodedDesc = $this->jsonConvert($asCode['description']); return ' Databap •

					
				
				';
	}

	public function getSavedCode($oCode)
	{
		$asCode = $this->getCodeInfo($oCode, '', true);
		$sPhrase = $this->getPhraseFromIdCode($asCode['id_code']);
		if(!$sPhrase)
		{
			$sPhrase = 'code_numero_'.$asCode['id_code'];
		}
		
		//set headers
		header('Pragma: public');
		header('Expires: 0');
		header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
		header('Cache-Control: public');
		header('Content-Description: File Transfer');
		header('Content-Type: application/force-download');
		header('Content-Disposition: attachment; filename='.$sPhrase.'.abap');
		header('Content-Transfer-Encoding: binary');
		//header('Content-Length: '.filesize($sFilePath));

		return $asCode['code'];
	}

	public function getPrintCode($oCode)
	{
		return $this->getRawCode($oCode).'';
	}
	
	public function getItemList()
	{
		$sIdCodeCol = MySqlManager::getId(MySqlManager::CODE_TABLE);
		$sIdProcCol = MySqlManager::getId(MySqlManager::PROC_TABLE);
		$sIdArtCol = MySqlManager::getId(MySqlManager::ART_TABLE);
		$sIdDocCol = MySqlManager::getId(MySqlManager::DOC_TABLE);
		$sIdCodeColFull = MySqlManager::getId(MySqlManager::CODE_TABLE, true);
		$sIdUserCol = MySqlManager::getId(MySqlManager::USER_TABLE);
		$sIdUserColFull = MySqlManager::getId(MySqlManager::USER_TABLE, true);
		$sLedColFull = MySqlManager::getFullColumnName(MySqlManager::CODE_TABLE, 'led') ;
		
		//Get code Ids
		$asInfo = array('select'=>array("MAX($sIdCodeCol) AS id_item"), //selecting last version among codes having the same refer_id
						'from'=>MySqlManager::CODE_TABLE,
						'groupBy'=>array('refer_id'),
						'orderBy'=>array('id_item'=>'desc'));
		$asItemIds[Databap::CODE_TYPE] = $this->oMySql->selectRows($asInfo);
		
		//get Proc Ids
		$asInfo = array('select'=>array("MAX($sIdProcCol) AS id_item"),
						'from'=>MySqlManager::PROC_TABLE,
						'constraint'=>array('refer_id'=>"0"), //meaning that there is no newer version 
						'orderBy'=>array('id_item'=>'desc'));
		$asItemIds[Databap::PROC_TYPE] = $this->oMySql->selectRows($asInfo);

		//get Article Ids
		$asInfo = array('select'=>array($sIdArtCol." AS id_item"),
						'from'=>MySqlManager::ART_TABLE,
						'orderBy'=>array('id_item'=>'desc'));
		$asItemIds[Databap::ART_TYPE] = $this->oMySql->selectRows($asInfo);
		
		//Get documentation
		$asInfo = array('select'=>array("MAX($sIdDocCol) AS id_item"), //selecting last version among docs having the same refer_id
						'from'=>MySqlManager::DOC_TABLE,
						'groupBy'=>array('refer_id'),
						'orderBy'=>array('id_item'=>'desc'));
		$asItemIds[Databap::DOC_TYPE] = $this->oMySql->selectRows($asInfo);
		
		$asTypeFunc = array(Databap::CODE_TYPE=>'Code', Databap::PROC_TYPE=>'Proc', Databap::ART_TYPE=>'Article', Databap::DOC_TYPE=>'Doc');
		
		//Build code info structure
		//TODO phrases for all item types?
		$asUsers = $asCode = $asItemList = array();
		foreach($asItemIds as $sType=>$asTypedItemIds)
		{
			$asTypedItemIds = array_filter($asTypedItemIds); //null value returned from query (MAX(...))
			foreach($asTypedItemIds as $iItemId)
			{
				$asItem = call_user_func(array($this, 'get'.$asTypeFunc[$sType].'Info'), $iItemId);
				$asItem['type'] = $sType;
				$asItem['id_item'] = $iItemId;
				if(array_key_exists($sIdUserCol, $asItem)) //replacing user's id with user's name & company (if not done already)
				{
					$iUserId = $asItem[$sIdUserCol];
					if(!array_key_exists($iUserId, $asUsers)) $asUsers[$iUserId] = $this->getUserInfo($iUserId);
					$asItem['name'] = $asUsers[$iUserId]['name'];
					$asItem['company'] = $asUsers[$iUserId]['company'];
				}
				$asItemList[$asItem['timestamp'].$asItem['type'].$asItem['id_item']] = $asItem;
			}
		}
		krsort($asItemList);
		return $this->jsonExport($asItemList);
	}

	public function getCodeBlock()
	{
		$oMask = new Mask();
		$oMask->initFile('code_block');
		return $oMask->getMask();
	}

	public static function checkNoPassResetAction($sAction)
	{
		return in_array($sAction, array('messages', 'upload_image', 'user_info', 'rss'));
	}
	
	public function logMeIn($sName, $sCompany, $sToken, $sAction)
	{
		$iUserId = 0;
		$bResetPass = true;
		$sUserTableId = MySqlManager::getId(MySqlManager::USER_TABLE);
		$sCompanyTableId = MySqlManager::getId(MySqlManager::COMP_TABLE);
		$sCompanyTableText = MySqlManager::getText(MySqlManager::COMP_TABLE);

		//login using form
		if($sName!='' && $sCompany!='')
		{
			$asNames = explode(' ', strtolower($sName));
			$sCompany = strtolower($sCompany);
			
			//Get Company's Id
			$iCompanyId = $this->oMySql->selectValue(MySqlManager::COMP_TABLE, $sCompanyTableId, array($sCompanyTableText=>$sCompany)); 
			
			$asConsts = array('first_name'=>$asNames[0], 'last_name'=>$asNames[1], $sCompanyTableId=>$iCompanyId);
			$iUserId = $this->oMySql->selectValue(MySqlManager::USER_TABLE, $sUserTableId, $asConsts);
			if(!$iUserId)
			{
				$asInvConsts = array('first_name'=>$asNames[1], 'last_name'=>$asNames[0], $sCompanyTableId=>$iCompanyId);
				$iUserId = $this->oMySql->selectValue(MySqlManager::USER_TABLE, $sUserTableId, $asInvConsts);
			}
		}
		//auto login by cookie
		elseif(isset($_COOKIE[self::USER_COOKIE_ID]))
		{
			$asConstraints = array(	$sUserTableId=>$_COOKIE[self::USER_COOKIE_ID],
									'pass'=>$_COOKIE[self::USER_COOKIE_PASS]);
			
			$asUserInfo = $this->oMySql->selectRow(MySqlManager::USER_TABLE, $asConstraints, array($sUserTableId, 'led'));
			$iUserId = $asUserInfo[$sUserTableId];
			
			//Check pass reset necessity
			$bResetPass = (substr($asUserInfo['led'], 0, 10) != date(Databap::DATE_SQL_FORMAT));
		}
		//Login using Token (limited access)
		elseif($sAction==self::EXT_ACCESS && $sToken!='')
		{
			$iUserId = $this->checkToken($sToken);
			$bResetPass = false;
		}
		
		if($iUserId>0)
		{
			$this->setUserId($iUserId);
			if(!self::checkNoPassResetAction($sAction) && $bResetPass)
			{
				$this->resetPassword();
			}
		}

		return ($this->getUserId()>0);
	}
	
	public function checkToken($sKey)
	{
		$iUserId = strstr($sKey, '_', true);
		$sToken = substr($sKey, strlen($iUserId)+1);
		return (strlen($sToken)==self::TOKEN_LENGTH && $this->generateToken($iUserId)==$sToken && $this->checkValue(MySqlManager::USER_TABLE, $iUserId))?$iUserId:0;
	}
	
	public function resetNecessary($iUserId)
	{
		$sLed = substr($this->oMySql->selectValue(MySqlManager::USER_TABLE, 'led', $iUserId), 0, 10);
		return $sLed != date(Databap::DATE_SQL_FORMAT);
	}
	
	public function resetPassword()
	{
		$iUserId = $this->getUserId();
		$sNewPass = self::getCookiePass();
		$iTimeLimit = time()+60*60*24*self::COOKIE_LIFETIME;
		$this->oMySql->updateRow(MySqlManager::USER_TABLE, $iUserId, array('pass'=>$sNewPass));
		setcookie(self::USER_COOKIE_ID, $iUserId, $iTimeLimit);
		setcookie(self::USER_COOKIE_PASS, $sNewPass, $iTimeLimit);
	}

	public function logMeOut()
	{
		$this->disconnectChat();
		$this->setUserId(0);
		setcookie(self::USER_COOKIE_ID, '', time()-60*60);
		setcookie(self::USER_COOKIE_PASS, '', time()-60*60);
	}

	/* Not needed so far
	public function setExpectedPage($sExpectedPage)
	{
		setcookie(self::EXPECTED_PAGE_COOKIE, $sExpectedPage, time()+60*60);
	}

	public function redirectExpectedPage()
	{
		if(array_key_exists(self::EXPECTED_PAGE_COOKIE, $_COOKIE))
		{
			$sLocation = $_COOKIE[self::EXPECTED_PAGE_COOKIE];
			setcookie(self::EXPECTED_PAGE_COOKIE, '', time()-60*60);
			header('Location:'.$sLocation);
		}
	}
	*/
	
	public function redirectArticle($iArtId)
	{
		$asArtInfo = $this->getArticleInfo($iArtId);
		header('Location:'.$asArtInfo['link_art']);
	}
	
	public function disconnectChat()
	{
		$sTime = $this->oMySql->selectRows(array('select'=>'DATE_SUB(NOW(), INTERVAL '.self::KEEP_ALIVE.' SECOND)'));

		//Is the user connected?
		$sUserIdColName = MySqlManager::getId(MySqlManager::USER_TABLE);
		$bConnected = $this->oMySql->selectRows(array('select'=>array('COUNT(1)'), 'from'=>MySqlManager::CONN_TABLE, 'constraint'=>array($sUserIdColName=>$this->getUserId(), 'led'=>$sTime), 'constOpe'=>array($sUserIdColName=>'=', 'led'=>'>')));
		if($bConnected)
		{
			$this->oMySql->updateRows(MySqlManager::CONN_TABLE, array(MySqlManager::getId(MySqlManager::USER_TABLE)=>$this->getUserId()), array('led'=>$sTime));
			$this->addMessage('se déconnecte', self::MESSAGE_CONN, self::ALL_CHAN_ID);
		}
	}

	public function checkValue($sTableName, $asConstraints)
	{
		return $this->oMySql->selectValue($sTableName, 'COUNT(1)', $asConstraints);
	}

	private static function getCookiePass()
	{
		return self::encryptPassword(	$_SERVER['HTTP_USER_AGENT'].
		$_SERVER['REMOTE_ADDR'].
		$_SERVER['REQUEST_TIME'].
		strstr(microtime(), ' ', true).
		$_SERVER['SERVER_SIGNATURE'].
		$_SERVER['SERVER_ADMIN']);
	}

	private static function encryptPassword($sPass)
	{
		$sRandomText = 'F_RA-1H"2{bvj)5f?0sd3r#fP,K]U|w}hGiN@(sZ.sDe!7*x/:Mq+&';
		for($iIndex=0; $iIndex < strlen($sPass); $iIndex++)
		{
			$sPass[$iIndex] = $sRandomText[$iIndex%strlen($sRandomText)] ^ $sPass[$iIndex];
		}
		return md5($sPass);
	}
	
	public static function getChanSafeName($sChanName)
	{
		$sChanSafeName = preg_replace('/[^a-z0-9]/', '_', strtolower($sChanName));
		
		//Sort PM chans
		$asPm = self::isPmChan($sChanSafeName);
		if(!$asPm['is_pm']) $sChanSafeName = $asPm['chan_name'];
		
		return $sChanSafeName;
	}

	public function getResults($sSearchWords)
	{
		$this->oSearchEngine->setWords($sSearchWords);
		$asResults = $this->oSearchEngine->getResults();
		
		//complementary infos
		$asCompleteResults = array();
		foreach($asResults as $iCodeId=>$asCodeInfo)
		{
			//phrase
			$sPhrase = $this->getPhraseFromIdCode($asCodeInfo['id_code']);
			if($sPhrase !== false)
			{
				$asCodeInfo['phrase'] = $sPhrase; 
			}
			$asCompleteResults[$iCodeId] = $asCodeInfo;
		}
		
		return $this->jsonExport($asCompleteResults);
	}
	
	public function getStyleSheet()
	{
		$sStyle = file_get_contents(self::STYLE_PATH);
		$asDefaultColors = array(	self::OPT_BG=>'#04357B',
									self::OPT_BRIGHT_BG=>'#D9E5F2',
									self::OPT_HOVER=>'#EFAB00',
									self::OPT_IMAGE_CHAT=>'images/sap_gold_332.jpg');
		
		//Inserting Color Ids
		foreach($asDefaultColors as $iOptionNameId=>$sDefaultColor)							
		{							
			$asColorAnchors[$iOptionNameId] = '[OPT#'.$iOptionNameId.']';
		}
		$sStyle = str_replace($asDefaultColors, $asColorAnchors, $sStyle);
		
		//Switching color Ids with user colors
		$asOptionvalues = $this->getUserOptions(array_keys($asDefaultColors));
		foreach($asColorAnchors as $iOptionNameId=>$sColorAnchor)
		{
			$sOptionValue = (array_key_exists($iOptionNameId, $asOptionvalues) && $asOptionvalues[$iOptionNameId]['option']!='')?$asOptionvalues[$iOptionNameId]['option']:$asDefaultColors[$iOptionNameId];
			$sStyle = str_replace($sColorAnchor, $sOptionValue, $sStyle);
		}
		
		//setting header content type
		header("Content-Type: text/css");
		return $sStyle;
	}

	public static function getDateFormat($oTime, $sFormat=Databap::DATE_TIME_FORMAT)
	{
		$iTimeStamp = is_numeric($oTime)?$oTime:strtotime($oTime);
		return date($sFormat, $iTimeStamp);
	}
	
	public static function getNameFormat($sFirstName, $sLastName)
	{
		return ToolBox::capitalizeWords(trim($sFirstName.' '.$sLastName), ' -');;
	}
	
	public static function getNickNameFormat($sNickName)
	{
		$sNickName = ToolBox::capitalizeWords(trim($sNickName), ' -');
		return str_replace(' ', '_', $sNickName);
	}

	public static function getCompanyFormat($sCompany)
	{
		return ucwords($sCompany);
	}
	
	public static function getDescriptionFormat($sDescription)
	{
		return ucfirst($sDescription);
	}
	
	public static function jsonExport($asData, $bConvert=false)
	{
		header('Content-type: application/json');
		if($bConvert)
		{
			$asData = ToolBox::cleanData($asData, 'utf8_encode');
		}
		return self::jsonConvert($asData);
	}
	
	public static function jsonConvert($asData)
	{
		//return htmlspecialchars(json_encode($asData), ENT_NOQUOTES);
		return json_encode($asData);
	}
	
	public static function getMaxSize()
	{
		$iPostSize = self::toBytes(ini_get('post_max_size'));
		$iUploadSize = self::toBytes(ini_get('upload_max_filesize'));
		
		return min($iPostSize, $iUploadSize);
	}
	
	public static function toBytes($str)
	{
		$val = trim($str);
		$last = strtolower($str[strlen($str)-1]);
		switch($last) {
			case 'g': $val *= 1024;
			case 'm': $val *= 1024;
			case 'k': $val *= 1024;		
		}
		return $val;
	}
}

/**
 * TODO
 * Code Manager
 * @author franzz
 */
class Code extends PhpObject
{
	//Objects
	private $oMySql;
	
	//Database tables and columns
	private $sProcTable;
	
	
	function __construct($oMySql)
	{
		parent::__construct();
		$this->oMySql = $oMySql;
	}
}

/**
 * Procedure Manager
 * @author franzz
 */
class Procedure extends PhpObject
{
	//Constants
	const THUMB_MAX_SIZE = 100;
	const IMAGE_FOLDER = 'screenshot/';
	const IMAGE_FOLDER_TMP = 'screenshot/tmp/';
	const IMAGE_FOLDER_THUMB = 'screenshot/thumbnails/';
	
	//Objects
	private $oMySql;

	//Procedure Elements
	private $iProcId;
	private $iUserId;
	private $sTitle;
	private $sDescription;
	private $dLed;
	private $asSteps; // [id_step]=>text
	private $asImages; // [id_step][id_image]=>array('name'=>file name, 'desc'=> description)
	
	function __construct($oMySql)
	{
		parent::__construct();
		$this->oMySql = $oMySql;
		$this->iPrevProcId = 0;
		$this->setProcedureId(0);
		$this->sTitle = '';
		$this->sDescription = '';
		$this->dLed = '';
		$this->asSteps = array();
		$this->asImages = array();
	}
	
	public function getProcedureId()
	{
		return $this->iProcId;
	}
	
	public function getProcedureLed()
	{
		return $this->dLed;
	}
	
	public function setProcedureId($iProcId)
	{
		$this->iProcId = $iProcId;
	}
	
	public function inputForm($asPost)
	{
		//User
		$this->iUserId = $asPost['id_user'];
		
		//Titles
		$this->sTitle = isset($asPost['title'])?$asPost['title']:'';
		$this->sDescription = isset($asPost['description'])?$asPost['description']:'';
		
		//Steps
		$sStepPattern = '/c(?P\d+)_step_text/';
		$sImagePattern = '/c(?P\d+)_(?P\d+)_image_(?P\w+)/';
		foreach($asPost as $sFormId=>$sValue)
		{
			//Step Text
			preg_match($sStepPattern, $sFormId, $asMatches);
			if(isset($asMatches['step_id']))
			{
				$this->asSteps[$asMatches['step_id']] = $sValue;
			}
			else
			{
				preg_match($sImagePattern, $sFormId, $asMatches);
				if
				(
					isset($asMatches['step_id'], $asMatches['image_id'], $asMatches['image_info']) &&
					($asMatches['image_info'] == 'name' || $asMatches['image_info']== 'desc')
				)
				{
					$this->asImages[$asMatches['step_id']][$asMatches['image_id']][$asMatches['image_info']] = $sValue;					
				}
			}
		}
	}
	
	private function isImageSaved($sFileName, $bInTemp=false)
	{
		$sPath = $bInTemp?self::IMAGE_FOLDER_TMP:self::IMAGE_FOLDER;
		return file_exists($sPath.$sFileName);
	}
	
	public function checkIntegrity()
	{
		$asErrors = array();
		
		//Check title
		if($this->sTitle=='') $asErrors['title'] = 'Un titre est nécessaire';
		
		//Check steps
		if(count($this->asSteps)==0) 
		{
			$asErrors['step'] = 'Au moins une étape est nécessaire';
		}
		else
		{
			foreach($this->asSteps as $iStepId=>$sText)
			{
				if($sText=='')
				{
					$asErrors['step_'.$iStepId] = 'Une étape ne possède pas de texte';
				}
			}
		}
		
		//Check images
		foreach($this->asImages as $iStepId=>$asImages)
		{
			foreach($asImages as $iImageId=>$asFileInfo)
			{
				//file exists in temp or already stored
				if(!$this->isImageSaved($asFileInfo['name']) && !$this->isImageSaved($asFileInfo['name'], true))
				{
					$asErrors['image_'.$iStepId.'_'.$iImageId] = 'Une image n\'a pas été téléchargée';
				}
				if($asFileInfo['desc']=='')
				{
					$asErrors['image_'.$iStepId.'_'.$iImageId] = 'Une image n\'a pas de nom (description)';
				}
			}
		}
		return $asErrors;
	}
	
	public function saveProcedure($iPrevProcId)
	{
		//Add new procedure
		$asData = array('title'=>$this->sTitle, 'description'=>$this->sDescription, MySqlManager::getId(MySqlManager::USER_TABLE)=>$this->iUserId);
		$iDbProcId = $this->oMySql->insertRow(MySqlManager::PROC_TABLE, $asData);
		
		//Add new steps
		$asStepData = $asImgData = array(MySqlManager::getId(MySqlManager::PROC_TABLE)=>$iDbProcId);
		foreach($this->asSteps as $iStepId=>$sStepDesc)
		{
			$asStepData['description'] = $sStepDesc;
			$iDbStepId = $this->oMySql->insertRow(MySqlManager::STEP_TABLE, $asStepData);
			
			//Add new images
			$asImgData[MySqlManager::getId(MySqlManager::STEP_TABLE)] = $iDbStepId;
			foreach($this->asImages[$iStepId] as $asImageInfo)
			{
				$asImgData['description'] = $asImageInfo['desc'];
				$asImgData['file_name'] = $asImageInfo['name'];
				$iDbImageId = $this->oMySql->insertRow(MySqlManager::IMG_TABLE, $asImgData);
				$this->saveImage($asImgData['file_name']);
			}
		}
		
		//Add this procedure to the group
		$bCreation = ($iPrevProcId==0);
		//$this->oMySql->deleteRow(MySqlManager::PROC_TABLE, $this->iPrevProcId);
		//$this->oMySql->updateRow(MySqlManager::PROC_TABLE, $iPrevProcId, array('refer_id'=>$iDbProcId));
		$iReferId = $bCreation?$iDbProcId:$this->oMySql->selectValue(MySqlManager::PROC_TABLE, 'refer_id', $iPrevProcId);
		$this->oMySql->updateRow(MySqlManager::PROC_TABLE, $iDbProcId, array('refer_id'=>$iReferId));
		
		//Update new Procedure Id
		//$this->setProcedureId($iDbProcId);
		
		//Reload Procedure
		$this->loadProcedure($iDbProcId);
		
		//return creation or edition
		return $bCreation;
	}
	
	private function saveImage($sFileName)
	{
		if(!$this->isImageSaved($sFileName))
		{
			$sTempFilePath = self::IMAGE_FOLDER_TMP.$sFileName;
			$sFilePath = self::IMAGE_FOLDER.$sFileName;
			
			//Real Size Image
			if(!rename($sTempFilePath, $sFilePath))
			{
				$this->addError('Unmoveable file : '.$sTempFilePath);
			}
			
			//Thumbnail picture
			$asResult = ToolBox::createThumbnail($sFilePath, self::THUMB_MAX_SIZE, self::THUMB_MAX_SIZE, self::IMAGE_FOLDER_THUMB);
			if($asResult['error']!='') $this->addError('Unable to create thumbnail : '.$asResult['out']. '('.$asResult['error'].')');
		}
	}
	
	public function loadProcedure($iProcId)
	{
		//Column names
		$sProcIdCol = MySqlManager::getId(MySqlManager::PROC_TABLE);
		$sStepIdCol = MySqlManager::getId(MySqlManager::STEP_TABLE);
		$sImageIdCol = MySqlManager::getId(MySqlManager::IMG_TABLE);
		
		//Get last id available
		$this->setProcedureId($iProcId);
		$this->refreshProcId();
		$asInfo = array('from'=>MySqlManager::PROC_TABLE, 'constraint'=>array($sProcIdCol=>$this->getProcedureId()));
		
		//Load procedure info
		$asResult = $this->oMySql->selectRow(MySqlManager::PROC_TABLE, $asInfo['constraint']);
		$this->sTitle = $asResult['title'];
		$this->sDescription = $asResult['description'];
		$this->iUserId = $asResult[MySqlManager::getId(MySqlManager::USER_TABLE)];
		$this->dLed = $asResult['led'];
		
		//Load steps info
		$asInfo['from'] = MySqlManager::STEP_TABLE;
		$this->asSteps = $this->oMySql->selectRows($asInfo, true, $sStepIdCol);
		
		//Load images info
		$asInfo['from'] = MySqlManager::IMG_TABLE;
		foreach($this->oMySql->selectRows($asInfo) as $asRow)
		{
			$this->asImages[$asRow[$sStepIdCol]][$asRow[$sImageIdCol]]['name'] = $asRow['file_name'];
			$this->asImages[$asRow[$sStepIdCol]][$asRow[$sImageIdCol]]['desc'] = $asRow['description'];
		}
	}
	
	public function getProcedure()
	{
		// [id_step]=>text
		// [id_step][id_image]=>array('name'=>file name, 'desc'=> description)
		/*
		$asSteps = $asImages = array();
		$iLocalStepId = 1;
		foreach($this->asSteps as $iStepId=>$sStepDesc)
		{
			$asSteps[$iLocalStepId] = $sStepDesc;
			if(array_key_exists($iStepId, $this->asImages))
			{
				$asImages[] = 
			}
			$iLocalStepId++;
		}*/
		return array(	'proc_id'=>$this->iProcId,
						'id_user'=>$this->iUserId,
						'title'=>Databap::getDescriptionFormat($this->sTitle), 
						'description'=>Databap::getDescriptionFormat($this->sDescription),
						'led'=>Databap::getDateFormat($this->dLed),
						'steps'=>$this->asSteps,
						'images'=>$this->asImages);
	}
	
	private function refreshProcId()
	{
		/*
		$iReferId = $this->getProcedureId();
		do
		{
			$iProcId = $iReferId;
			$iReferId = $this->oMySql->selectValue(MySqlManager::PROC_TABLE, 'refer_id', $iProcId);
		}
		while($iReferId!=0);
		$this->setProcedureId($iProcId);
		*/
		$iReferId = $this->oMySql->selectValue(MySqlManager::PROC_TABLE, 'refer_id', $this->getProcedureId());
		$sSelect = "MAX(".MySqlManager::getId(MySqlManager::PROC_TABLE).")";
		$iProcId = $this->oMySql->selectValue(MySqlManager::PROC_TABLE, $sSelect, array('refer_id'=>$iReferId));
		$this->setProcedureId($iProcId);
	}
}

/* if feeling the need of creating a tables for code lines :
 codes :
 ALTER TABLE codes ADD line_low int(10) AFTER code;
 ALTER TABLE codes ADD line_high int(10) AFTER line_low;

 lines :
 id_line
 id_code
 line
 */

/**
 * Search Engine : Search through all types of databap documents
 * 
 * Procedure to add a new type in the search
 * - Add type in the index builder : SearchEngine->buildIndex()
 * - Add type in the rebuild index function : Databap->buildCompleteIndex()
 * - Add type in item info : SearchEngine->setItemInfo()
 * - Add type in .htaccess
 * - Add type in getItemList() and create a getinfo()
 * - consider the message types : add + edit in Databap->getMessages() and in chat.html
 */
class SearchEngine extends PhpObject
{
	//Objects
	private $oMySql;

	//variables
	private $asWords;
	private $iLevelMax;
	private $asItemRanks;
	private $asItemInfos;
	private $asUserInfos;

	//Constants
	const RESULT_A_PAGE = 20;
	const KEYWORDS_SEPARATOR = ' ';

	function __construct($oMySql)
	{
		parent::__construct();
		$this->oMySql = $oMySql;
		$this->asWords = $this->setWords('');
		$this->asItemRanks = array();
		$this->asItemInfos = array();
		$this->asUserInfos = array();
	}
	
	public function buildIndex($iItemId, $sType)
	{
		//Build keywords
		switch($sType)
		{
			case Databap::CODE_TYPE:
				$asItemData = $this->oMySql->selectRow(MySqlManager::CODE_TABLE, $iItemId);
				$asWords = array($asItemData[MySqlManager::getText(MySqlManager::CODE_TABLE)], $asItemData['description']);
				break;
			case Databap::PROC_TYPE:
				$sItemIdCol = MySqlManager::getId(MySqlManager::PROC_TABLE);
				$asItemData = $this->oMySql->selectRow(MySqlManager::PROC_TABLE, $iItemId);
				$asItemStepsData = $this->oMySql->selectRows(array('select'=>'description', 'from'=>MySqlManager::STEP_TABLE, 'constraint'=>array($sItemIdCol=>$iItemId)));	
				$asWords = array('desc'=>$asItemData['description'], 'title'=>$asItemData['title']) + $asItemStepsData;
				break;
			case Databap::ART_TYPE:
				$asItemData = $this->oMySql->selectRow(MySqlManager::ART_TABLE, $iItemId);
				$asWords = array($asItemData['first_name'], $asItemData['last_name'], $asItemData['title']);
				break;
			case Databap::DOC_TYPE:
				$sItemIdCol = MySqlManager::getId(MySqlManager::DOC_TABLE);
				$asItemData = $this->oMySql->selectRow(MySqlManager::DOC_TABLE, $iItemId);
				$asItemFilesData = $this->oMySql->selectRows(array('select'=>'description', 'from'=>MySqlManager::FILE_TABLE, 'constraint'=>array($sItemIdCol=>$iItemId)));	
				$asWords = array('desc'=>$asItemData['description'], 'title'=>$asItemData['title']) + $asItemFilesData;
				break;
			default:
				$this->addError('function '.__FUNCTION__.'(): Incorrect type "'.$sType.'"');
				break;
		}
		$sWords = implode(self::KEYWORDS_SEPARATOR, $asWords);
		$sWords = strtolower(str_replace("\n", self::KEYWORDS_SEPARATOR, $sWords));
		//TODO Fix char encoding
		$sWords = preg_replace('/(\W+)/', self::KEYWORDS_SEPARATOR, $sWords); //remove all non-word characters
		
		//Add / Modify search database
		$asData = array('id_item'=>$iItemId, 'type'=>$sType, 'refer_id'=>$asItemData['refer_id'], 'keywords'=>$sWords);
		$this->oMySql->insertUpdateRow(MySqlManager::SEARCH_TABLE, $asData, array('id_item', 'type'));
	}

	public function setWords($sSearchWords)
	{
		$this->asWords = $this->getParsedWords($sSearchWords);
		$this->iLevelMax = count($this->asWords);
		$this->setResults();
	}
	
	/**
	 * TODO Customized item preview
		$sCodeLines = implode("\n...\n", $this->getCodeInfo($iItemId, 'code'));
		$oCode = new Reader($sCodeLines);
		$sCodeLines = $oCode->getColoredCode();
	 */
	private function setItemInfo($iSearchId, $iItemType, $iItemId)
	{
		if(!array_key_exists($iSearchId, $this->asItemInfos))
		{
			$this->asItemInfos[$iSearchId] = array('type'=>$iItemType, 'id_item'=>$iItemId);
			switch($iItemType)
			{
				case Databap::CODE_TYPE:
					$sItemTable = MySqlManager::CODE_TABLE;
					$asItemFields = array(MySqlManager::getId(MySqlManager::USER_TABLE), 'description', 'led');
					break;
				case Databap::PROC_TYPE:
					$sItemTable = MySqlManager::PROC_TABLE;
					$asItemFields = array(MySqlManager::getId(MySqlManager::USER_TABLE), 'title AS description', 'led');
					break;
				case Databap::ART_TYPE:
					$sItemTable = MySqlManager::ART_TABLE;
					$asItemFields = array('first_name', 'last_name', 'title AS description', 'led');
					break;
				case Databap::DOC_TYPE:
					$sItemTable = MySqlManager::DOC_TABLE;
					$asItemFields = array(MySqlManager::getId(MySqlManager::USER_TABLE), 'title AS description', 'led');
					break;
			}
			$this->asItemInfos[$iSearchId] += $this->oMySql->selectRow($sItemTable, $iItemId, $asItemFields);
		}
	}
	
	private function getItemInfo($iSearchId, $sInfoName)
	{
		if(array_key_exists($iSearchId, $this->asItemInfos) && array_key_exists($sInfoName, $this->asItemInfos[$iSearchId]))
			return $this->asItemInfos[$iSearchId][$sInfoName];
		else return false;		
	}
	
	private function setUserInfo($iUserId)
	{
		if(!array_key_exists($iUserId, $this->asUserInfos) && $iUserId > 0)
		{
			$sCompanyIdCol = MySqlManager::getId(MySqlManager::COMP_TABLE);
			$this->asUserInfos[$iUserId] = $this->oMySql->selectRow
			(
					MySqlManager::USER_TABLE,
					$iUserId,
					array('first_name', 'last_name', $sCompanyIdCol)
			);
				
			$this->asUserInfos[$iUserId]['company'] = $this->oMySql->selectValue(MySqlManager::COMP_TABLE, MySqlManager::getText(MySqlManager::COMP_TABLE), $this->asUserInfos[$iUserId][$sCompanyIdCol]);
			unset($this->asUserInfos[$iUserId][$sCompanyIdCol]);
		}
	}
	
	private function getUserInfo($iUserId, $sInfoName)
	{
		if(array_key_exists($iUserId, $this->asUserInfos) && array_key_exists($sInfoName, $this->asUserInfos[$iUserId]))
			return $this->asUserInfos[$iUserId][$sInfoName];
		else return false;
	}

	private function setResults()
	{
		if($this->iLevelMax > 0)
		{
			//set Results and Ranking
			$aiLevels = range(1, $this->iLevelMax);
			arsort($aiLevels);
			foreach($aiLevels as $iLevel)
			{
				//all possibilies at level $iLevel
				$iIndex = 0;
				while(($iIndex + $iLevel) <= $this->iLevelMax)
				{
					//building query
					$asSequence = array_slice($this->asWords, $iIndex, $iLevel);
					$this->oMySql->cleanSql($asSequence);
					
					//$sRegExp = implode('(.{0,2})', $asSequence);
					$sRegExp = implode(self::KEYWORDS_SEPARATOR.'?', $asSequence);
					$sSequence = implode(self::KEYWORDS_SEPARATOR, $asSequence);
					
					//TODO replace with selectRow()
					$sQuery = "SELECT id_search, id_item, type, keywords FROM searchs WHERE keywords REGEXP '{$sRegExp}'";
					
					//search sequence
					$asItems = $this->oMySql->getArrayQuery($sQuery, true);
					foreach($asItems as $asItem)
					{
						$iSearchId = $asItem['id_search']; 
						$iItemId = $asItem['id_item'];;
						$iItemType = $asItem['type'];
						$sWords = $asItem['keywords'];
						
						//Calculte bonus points
						$sWords = str_replace(self::KEYWORDS_SEPARATOR.$sSequence.self::KEYWORDS_SEPARATOR,	self::KEYWORDS_SEPARATOR, self::KEYWORDS_SEPARATOR.$sWords.self::KEYWORDS_SEPARATOR, $iSeqCount);
						$sWords = str_replace(self::KEYWORDS_SEPARATOR.$sSequence,							self::KEYWORDS_SEPARATOR, self::KEYWORDS_SEPARATOR.$sWords.self::KEYWORDS_SEPARATOR, $iStaCount);
						$sWords = str_replace($sSequence.self::KEYWORDS_SEPARATOR,							self::KEYWORDS_SEPARATOR, self::KEYWORDS_SEPARATOR.$sWords.self::KEYWORDS_SEPARATOR, $iEndCount);
						$iBonus = $iSeqCount*5 + $iStaCount*2 + $iEndCount;
						
						$this->incItemRank($iSearchId, $iLevel*10+$iBonus);
						$this->setItemInfo($iSearchId, $iItemType, $iItemId);
						$this->setUserInfo($this->getItemInfo($iSearchId, MySqlManager::getId(MySqlManager::USER_TABLE)));
					}
					$iIndex++;
				}
			}
		}
	}
	
	public function getResults()
	{
		$asResult = array();
		
		//Mixing info
		arsort($this->asItemRanks);
		foreach($this->asItemRanks as $iSearchId=>$iRank)
		{
			$iUserId = $this->getItemInfo($iSearchId, MySqlManager::getId(MySqlManager::USER_TABLE));
			$sFirstName = $this->getUserInfo($iUserId, 'first_name')?$this->getUserInfo($iUserId, 'first_name'):$this->getItemInfo($iSearchId, 'first_name');
			$sLastName = $this->getUserInfo($iUserId, 'last_name')?$this->getUserInfo($iUserId, 'last_name'):$this->getItemInfo($iSearchId, 'last_name');
			$sCompany = $this->getUserInfo($iUserId, 'company')?$this->getUserInfo($iUserId, 'company'):'SAP';
			$asResult[] = array('id_item'=>$this->getItemInfo($iSearchId, 'id_item'),
								'type'=>$this->getItemInfo($iSearchId, 'type'),
								'description'=>/*'['.$iRank.'] '.*/$this->getItemInfo($iSearchId, 'description'),
								'name'=>Databap::getNameFormat($sFirstName, $sLastName),
								'company'=>Databap::getCompanyFormat($sCompany),
								'led'=>Databap::getDateFormat($this->getItemInfo($iSearchId, 'led')));
		}
		return $asResult;
	}

	private function incItemRank($iSearchId, $iRank)
	{
		if(array_key_exists($iSearchId, $this->asItemRanks))
		{
			$this->asItemRanks[$iSearchId] += $iRank;
		}
		else
		{
			$this->asItemRanks[$iSearchId] = $iRank;
		}
	}

	private function formatCode($sCode, $sPattern)
	{
		preg_match_all('/\n(.*)\n(.*)'.$sPattern.'(.*)\n(.*)\n/i', $sCode, $asMatches);
		if(!array_key_exists(0, $asMatches) || !is_array($asMatches[0]) || count($asMatches[0])==0)
		{
			$asResult = array(implode("\n", array_slice(explode("\n", $sCode), 0, 3))."\n[...]");
		}
		else
		{
			$asResult = $asMatches[0];
		}
		return $asResult;
	}

	private function getParsedWords($sSearchWords)
	{
		return array_unique(array_filter(explode(' ', $sSearchWords), array($this, 'checkSearchedWords')));
	}

	private function checkSearchedWords($sWord)
	{
		return (strlen($sWord) >= 2);
	}
}

/**
 * Abap Reader
 * @author franzz
 */
class Reader
{
	//objects
	//private $oMask;

	//general
	private $sCode;
	private $bImprovedColoring;

	//Special Wors / Chars
	private $asAbap;

	//constants
	const CASE_SENSITIVE = false;
	const NOT_FOUND = '* Code introuvable...';

	function __construct($sCode, $bImprovedColoring = false)
	{
		//settings
		$this->bImprovedColoring = $bImprovedColoring;

		//get code in db
		$this->sCode = $sCode;
		if($this->sCode=='')
		{
			$this->sCode = self::NOT_FOUND;
		}

		//ABAP key words and chars
		$this->asAbap = array
		(
			'word'=>array
			(
				'wCore'=>array(	/*'<icon>','<itab>',*/'ABBREVIATED','ABSTRACT','ABSTRACTFINAL','ACCEPT','ACCEPTING','ACCORDING','ACTUAL','ADD',
								'ADD-CORRESPONDING','ADDITIONS','ADJACENT','ALIASES','ALL','ALLOCATE','ANALYZER', 'AND','APPEND','APPENDING','AS',
								'ASCENDING','ASCENDINGDESCENDING','AT','ATTRIBUTE','AUTHORITY-CHECK','BACKGOUND','BEFORE','BEGIN','BETWEEN',
								'BIGLITTLE','BINARY','BIT','BLANK','BLOCK','BREAK-POINT','BY','BYPASSING','BYTE','BYTECHARACTER','CASE',
								'CASERESPECTING','CASTING','CENTERED','CHAIN','CHANGE','CHANGING','CHARACTER','CHECK','CHECKBOX',
								'CHECKBOXSYMBOLICONLINE','CLASS','CLASS-DATA','CLASS-EVENTS','CLASS-METHODS','CLEANUP','CLEAR','CLIENT','CLOCK',
								'CODE','COL_BACKGROUND','COL_HEADING','COL_NORMAL','COL_TOTAL','COLLECT','COLOR','COLUMN','COMMENT','COMMIT',
								'COMMON','COMMUNICATION','COMPARING','COMPONENTS','COMPUTE','CONCATENATE','CONDENSE','CONSTANTS','CONTEXT',
								'CONTEXTS','CONTINUE','CONTROL','CONTROLS','CONVERSION','CONVERT','COUNTRY','COUNTY','CURRENT','CURSOR',
								'CUSTOMER-FUNCTION','DATA','DATABASE','DATASET','DATE','DEALLOCATE','DECIMALS','DEFAULT','DEFAULTUTF-8NON-UNICODE',
								'DEFERRED','DEFINE','DEFINING','DEFINITION','DELETE','DELETING','DEMAND','DESCENDING','DESCRIBE','DESTINATION',
								'DIALOG','DIRECTORY','DISTANCE','DISTINCT','DIVIDE-CORRESPONDING','DO','DUPLICATE','DUPLICATES','DURING','DYNAMIC',
								'EDIT','EDITOR-CALL','ELSE','ELSEIF','ENCODING','END','ENDAT','ENDCASE','ENDCATCH','ENDCHAIN','ENDCLASS','ENDDO',
								'ENDEXEC','ENDFORM','ENDFUNCTION','ENDIAN','ENDIF','ENDING','ENDINTERFACE','ENDLOOP','ENDMETHOD','ENDMODULE',
								'ENDON','ENDPROVIDE','ENDSELECT','ENDTRY','ENDWHILE','END-OF_FILE','END-OF-DEFINITION','END-OF-PAGE',
								'END-OF-SELECTION','ENTRIES','EQ','ERRORS','EVENT','EVENTS','EXCEPTION','EXCEPTIONS','EXCEPTION-TABLE','EXCLUDE',
								'EXCLUDING','EXEC','EXIT','EXIT-COMMAND','EXPORT','EXPORTING','EXTENDED','EXTRACT','FETCH','FIELD','FIELD-GROUPS',
								'FIELDSNO','FIELD-SYMBOLS','FILTER','FINAL','FIND','FIRST','FOR','FORM','FORMAT','FORWARDBACKWARD','FOUND','FRAME',
								'FREE','FRIENDS','FROM','FUNCTION','FUNCTION-POOL','GE','GET','GIVING','GROUP','GT','HANDLER','HASHED','HAVING',
								'HEADER','HEADING','HELP-ID','HIDE','HIGHLOW','HOLD','HOTSPOT','ID','IF','IGNORING','IMMEDIATELY','IMPLEMENTATION',
								'IMPORT','IMPORTING','IN','INCREMENT','INDEX','INDEX-LINE','INHERITING','INIT','INITIAL','INITIALIZATION','INNER',
								'INNERLEFT','INSERT','INSTANCES','INTENSIFIED','INTERFACE','INTERFACES','INTERVALS','INTO','INTOAPPENDING',
								'INVERTED-DATE','IS','ITAB','JOIN','KEEPING','KEY','KEYS','KIND','LANGUAGE','LAST','LE','LEAVE','LEFT',
								'LEFT-JUSTIFIED','LEFTRIGHT','LEFTRIGHTCIRCULAR','LEGACY','LENGTH','LIKE','LINE','LINE-COUNT','LINES','LINES',
								'LINE-SELECTION','LINE-SIZE','LIST','LIST-PROCESSING','LOAD','LOAD-OF-PROGRAM','LOCAL','LOCALE','LOOP','LT',
								'MARGIN','MARK','MASK','MATCH','MAXIMUM','MEMORY','MESSAGE','MESSAGE-ID','MESSAGES','METHOD','METHODS','MOD','MODE',
								'MODEIN','MODIF','MODIFIER','MODIFY','MODULE','MOVE','MOVE-CORRESPONDING','MULTIPLY-CORRESPONDING','NE','NEW',
								'NEW-LINE','NEW-PAGE','NEXT','NODES','NODETABLE','NO-DISPLAY','NO-EXTENSION','NO-GAP','NO-GAPS',
								'NO-HEADINGWITH-HEADING','NON','NO-SCROLLING','NO-SCROLLINGSCROLLING','NOT','NO-TITLEWITH-TITLE','NO-ZERO','NUMBER',
								'OBJECT','OBLIGATORY','OCCURENCE','OCCURENCES','OCCURS','OF','OFF','OFFSET','ON','ONLY','ONOFF','OPEN','OPTION',
								'OPTIONAL','OR','ORDER','OTHERS','OUTPUT-LENGTH','OVERLAY','PACK','PACKAGE','PAGE','PAGELAST','PAGEOF','PAGEPAGE',
								'PAGES','PARAMETER','PARAMETERS','PARAMETER-TABLE','PART','PERFORM','PERFORMING','PFN','PF-STATUS','PLACES',
								'POS_HIGH','POS_LOW','POSITION','POSITIONS','PRIMARY','PRINT','PRINT-CONTROL','PRIVATE','PROCESS','PROGRAM',
								'PROPERTY','PROTECTED','PROVIDE','PUBLIC','PUBLICPROTECTEDPRIVATE','PUSHBUTTON','PUT','QUICKINFO','RADIOBUTTON',
								'RAISE','RAISING','RANGE','RANGES','READ','RECEIVE','RECEIVING','REDEFINITION','REFERENCE','REFRESH','REJECT',
								'RENAMING','REPLACE','REPLACEMENT','REPORT','RESERVE','RESET','RESOLUTION','RESULTS','RETURN','RETURNING','RIGHT',
								'RIGHT-JUSTIFIED','ROLLBACK','ROWS','RUN','SCAN','SCREEN','SCREEN-GROUP1','SCREEN-GROUP2','SCREEN-GROUP3',
								'SCREEN-GROUP4','SCREEN-GROUP5','SCREEN-INPUT','SCREEN-INTENSIFIED','SCROLL','SCROLL-BOUNDARY','SEARCH','SECTION',
								'SELECT','SELECTION','SELECTIONS','SELECTION-SCREEN','SELECTION-SET','SELECTION-TABLE','SELECT-OPTIONS','SEND',
								'SEPARATED','SET','SHARED','SHIFT','SIGN','SINGLE','SINGLEDISTINCT','SIZE','SKIP','SORT','SORTABLE','SPACE',
								'SPECIFIED','SPLIT','SQL','STABLE','STAMP','STANDARD','START','STARTING','START-OF-SELECTION','STATICS','STEP-LOOP',
								'STOP','STRLEN','STRUCTURE','SUBMIT','SUBTRACT','SUBTRACT-CORRESPONDING','SUFFIX','SUPPLY','SUPPRESS','SYMBOLS',
								'SYSTEM-EXCEPTIONS','TABLE','TABLES','TABLEVIEW','TASK','TEXT','THEN','TIME','TIMES','TITLE','TITLEBAR','TO',
								'TOPIC','TOP-OF-PAGE','TRAILING','TRANSACTION','TRANSFER','TRANSLATE','TRY','TYPE','TYPELIKE','TYPE-POOL',
								'TYPE-POOLS','TYPES','ULINE','UNION','UNIQUE','UNTIL','UP','UPDATE','UPPERLOWER','USER-COMMAND','USING','VALUE',
								'VALUES','VARY','VARYING','VERSION','VIA','WAIT','WHEN','WHERE','WHILE','WINDOW','WITH','WORK','WRITE','ZONE','ADD',
								'ADD-CORRESPONDING','APPEND','BREAK-POINT','CORRESPONDING','FIELDS','ASSIGN','AVG','BACK','CALL','CATCH','CHANGE',
								'CHECK','CLEAR','CLOSE','COMMIT','COMPUTE','COUNT','CREATE','DATASET','DELETE','DESCRIBE','DIVIDE','DO','ELSE',
								'ELSEIF','EXIT','FIND','FOR','FORMAT','GET','IF','INCLUDE','LOOP','MAX','MESSAGE','METHOD','MIN','MODIFY','MOVE',
								'MULTIPLY','NEW-PAGE','OBJECT','OPEN','ORDER','READ','SORT','SUM','TRANSLATE','ULINE','WORK','WRITE','XSTRLEN',
								'ABS','ACOS','AFTER','ALL','AND','AS','ASIN','ASSIGNING','AT','ATAN','BEGIN','BUFFER','BY','CA','CEIL','CENTERED',
								'CN','CO','COLOR','COMPARING','COMPONENT','COS','COSH','CP','CS','DEFAULT','END','EQ','EXP','FLOOR','FRAC','GE',
								'GROUP','GT','ICON','IN','INDEX','INNER','INTENSIFIED','INTO','IS','JOIN','LE','LEADING','LEAVE','LEFT','LIKE',
								'LOG','LOG10','LOWER','LT','M','MOD','NA','NE','NOT','NP','NS','O','OF','OFF','ON','OR','OTHERS','OUTER','REF',
								'RIGHT','SIGN','SIN','SINH','SQRT','TABLE','TABLENAME','TAN','TANH','TITLE','TO','TRAILING','TRUNC','TYPE','UPPER',
								'USING','VALUE','WITH', 'TRANSPORTING', 'TYPE-POOLS'),
				'wBwCore'=>array('SOURCE_PACKAGE', 'RESULT_PACKAGE', '', ''),
				'cOperator'=>array('(', ')', ',', '.', ':', '-', '~', '[', ']', '<', '>'),
				'cCalculation'=>array('+', '-', '*', '/', '=', '(', ')'),
				'cComment'=>array('*'),
				'cPartComment'=>array('"'),
				'cString'=>array('"', '''),
				'iNumber'=>array('1', '2', '3', '4', '5', '6', '7', '8', '9', '0'),
				'wExpand'=>array('loop'=>'endloop', 'if'=>'endif'),
				'wGlobal'=>array('SY'),
				'wCodePart'=>array('main', 'global', 'global2')
			),
			'color'=>array
			(
				'wCore'=>'core',
				'wBwCore'=>'core',
				'cOperator'=>'operator',
				'cCalculation'=>'operator',
				'cComment'=>'comment',
				'cPartComment'=>'comment',
				'cString'=>'string',
				'iNumber'=>'number',
				'wExpand'=>'expand',
				'wGlobal'=>'glogal',
				'wCodePart'=>'code_part'
			)
		);
	}

	private function getWords($sKey)
	{
		return $this->asAbap['word'][$sKey];
	}

	private function getColor($sKey)
	{
		return $this->asAbap['color'][$sKey];
	}

	private function addColoring()
	{
		//Safe characters
		$sCode = $this->convText2Html($this->sCode);
		$sServName = $_GET['serv_name'];
		$sSafeRexExServName = preg_quote(str_replace('http://', '', $sServName), '/');
		
		$asLines = explode("\n", $sCode);
		$asLineClasses = array();
		$iOffSet = 0;
		
		//building lines
		foreach($asLines as $iLineNb=>$sLine)
		{
			/*	Code ref:
			 *	
  • * * - * * #line# *
  • */ $sLiClass = $sExpandSign = $sCodeClass = $sExpandHtml = $sExpandButton = ''; $sFirstWord = $this->getFirstWord($sLine); $sFirstChar = $this->getFirstChar($sLine); //Apply current classes $sLiClass = implode(' ', $asLineClasses); //Expanding starting/ending point if(in_array($sFirstWord, array_keys($this->getWords('wExpand')))) //start { $iLoopId = count($asLineClasses)+$iOffSet; $asLineClasses[] = 'loop'.$iLoopId; $sExpandButton = ''; } elseif(in_array($sFirstWord, $this->getWords('wExpand'))) //end { array_pop($asLineClasses); $iOffSet++; $sExpandButton = ''; } //Enlarge line if(strpos($sLine, $sServName) !== false) { $sLiClass .= ' bigline'; } //comments (entire line) $sCodeClass = (in_array($sFirstChar, $this->getWords('cComment')))?'comment':'code'; $asColoredLines[] = '
  • '.$sExpandButton.''.$sLine.'
  • '; } $sCode = implode('', $asColoredLines); //Strings foreach($this->getWords('cString') as $sStringWord) { $sPattern = '/>([^<]*?)'.$sStringWord.'([^<]*?)'.$sStringWord.'/'; $sCode = preg_replace($sPattern, '>$1'.$sStringWord.'$2'.$sStringWord.'', $sCode); } //Part comment $sPattern = '/>([^<]*)\"\;([^<]*)\<\/span\>\<\/li\>/'; $sCode = preg_replace($sPattern, '>$1"$2', $sCode); //Internal Url //$sPattern = '/>([^<]*)'.preg_quote($_GET['serv_name'], '/').'\/r\-([^<]*)/'; $sPattern = '/>([^<\/]*)(http:\/\/'.$sSafeRexExServName.'|'.$sSafeRexExServName.')(c|code|p|proc|procedure)\-([^<]*)/'; $sCode = preg_replace($sPattern, '>$1', $sCode); //Global / Main $sPattern = '/>\*\ \[('.implode('|', $this->getWords('wCodePart')).')\]/'; $sCode = preg_replace($sPattern, '>> $1', $sCode); //Core Words foreach($this->getWords('wCore') as $sCoreWord) { $sCoreWord = strtolower($sCoreWord); $sPattern = '/>(([^<]*)([^\w&<]{1})|.{0})('.$sCoreWord.')([\W])/'; $sCode = preg_replace($sPattern, '>$1$4$5', $sCode); } //$sCoreWords = str_replace(' ', '\ ', implode('|', array_map('strtolower', $this->getWords('wCore')))); //$sPattern = '/>(([^<]*)([^\w<]{1})|.{0})('.$sCoreWords.')(?=[\W])/U'; //$sCode = preg_replace($sPattern, '>$1$4', $sCode); //Operators //TODO distinguer operator / calculation $asOperators = array_unique(array_merge($this->getWords('cOperator'), $this->getWords('cCalculation'))); foreach($asOperators as $sOpWord) { $sPattern = '/>([^<]*)\\'.$sOpWord.'/'; $sCode = preg_replace($sPattern, '>$1'.$sOpWord.'', $sCode); } //$sPattern = '/>([^<]*)['.implode(array_map('strtolower', ($this->getWords('cOperator')))).']/'; //echo $sPattern; //$sCode = preg_replace($sPattern, '>$1$2$3', $sCode); //Numbers $sPreChar = '\\'.implode('\\', $this->getWords('cCalculation')).'\s'; $sPattern = '/>((([^<]*)['.$sPreChar.']{1})|.{0})([0-9]+)([\W])/'; $sCode = preg_replace($sPattern, '>$1$4$5', $sCode); return $sCode; } private static function getFirstWord($sText) { return strstr(str_replace('.', ' ', trim($sText)), ' ', true); } private static function getFirstChar($sText) { return substr(str_replace('.', ' ', trim($sText)), 0, 1); } private function addColor(&$sText, $sWord, $sClassColor) { $sText = str_replace($sWord, ''.$sWord.'', $sText); } private static function getColoredWord($sWord, $sClass) { return ''.$sWord.''; } public static function convText2Html($sCode) { return htmlspecialchars(strtolower($sCode), ENT_QUOTES); } public function getColoredCode() { return $this->addColoring(); } } /** * Mask Reader * @author franzz * */ class Mask extends PhpObject { public $sMaskName; public $sFilePath; private $sMask; private $asTags; private $asPartsSource; private $aoInstances; const MASK_FOLDER = 'masks/'; const START_TAG = 'START'; const END_TAG = 'END'; const TAG_MARK = '#'; public function __construct($sFileName='') { //init parent::__construct(); $this->sMaskName = ''; $this->sFilePath = ''; $this->sMask = ''; $this->asTags = array(); $this->asPartsSource = array(); $this->aoInstances = array(); $this->sFilePath = ''; //load file if($sFileName!='') { $this->initFile($sFileName); } } public function initFile($sFileName) { $sFilePath = self::MASK_FOLDER.basename($sFileName).'.html'; if(file_exists($sFilePath)) { $this->sFilePath = $sFilePath; $sSource = file_get_contents($this->sFilePath); $this->initMask($sFileName, $sSource); } else { $this->addError('Fichier introuvable à l\'adresse : '.$sFilePath); } } public function initFileFromString($sSource, $sPartName='', $iInstanceNb=0) { $this->initMask($sPartName.' (from row) '.$iInstanceNb, $sSource); } private function initMask($sMaskName, $sSource) { $this->sMaskName = $sMaskName; $this->sMask = $sSource; $this->setParts(); } private function setParts() { while(preg_match('/\<\!-- \[PART\] (?P\S+) \[START\] --\>/', $this->sMask, $asMatch)) { $sPartName = $asMatch['part']; $this->asPartsSource[$sPartName] = $this->getCleanPart($sPartName); $this->aoInstances[$sPartName] = array(); } } private function getCleanPart($sPartName) { $iStartPos = $this->getPartStartPos($sPartName); $iEndPos = $this->getPartEndPos($sPartName); $sPart = substr($this->sMask, $iStartPos, $iEndPos-$iStartPos); $sExtendedPart = $this->getPartPattern($sPartName, self::START_TAG).$sPart. $this->getPartPattern($sPartName, self::END_TAG); $this->sMask = str_replace($sExtendedPart, $this->getPartTagPattern($sPartName), $this->sMask); return $sPart; } private function getPartStartPos($sPartName) { $sPartStartPattern = $this->getPartPattern($sPartName, self::START_TAG); return strpos($this->sMask, $sPartStartPattern) + strlen($sPartStartPattern); } private function getPartEndPos($sPartName) { $sPartEndPattern = $this->getPartPattern($sPartName, self::END_TAG); return strpos($this->sMask, $sPartEndPattern); } private function getPartPattern($sPartName, $sAction) { return ''; } private function getPartTagPattern($sPartName, $bMark=true) { $sPartTag = 'PART '.$sPartName; return $bMark?$this->addTagMark($sPartTag):$sPartTag; } public function addInstance($sPartName, $asTags) { $this->newInstance($sPartName); foreach($asTags as $sTagName=>$sTagValue) { $this->setInstanceTag($sPartName, $sTagName, $sTagValue); } } public function newInstance($sPartName) { //Finding the part $oMask = &$this->findPart($this, $sPartName); //Retrieving source html $sPartSource = $oMask->asPartsSource[$sPartName]; //Creating new instance $oInstance = new Mask(); $oInstance->initFileFromString($sPartSource, $sPartName); $oMask->aoInstances[$sPartName][] = $oInstance; } public function setInstanceTag($sPartName, $sTagName, $sTagValue) { $oMask = &$this->findPart($this, $sPartName); $oMask->getCurrentInstance($sPartName)->setTag($sTagName, $sTagValue); } private function &findPart($oMask, $sPartName) { if(array_key_exists($sPartName, $oMask->aoInstances)) { return $oMask; } else //not tested { foreach($oMask->aoInstances as $sLevelPartName=>$aoInstances) { if(!empty($aoInstances)) { //take last instances return $this->findPart($oMask->getCurrentInstance($sLevelPartName), $sPartName); } } } $this->addError('No part found : '.$sPartName); } private function getCurrentInstance($sPartName) { if(!empty($this->aoInstances[$sPartName])) { return end($this->aoInstances[$sPartName]); } else { return false; } } public function setTag($sTagName, $sTagValue) { $this->asTags[$sTagName] = $sTagValue; } public function getMask() { $sCompletedMask = $this->sMask; //build parts foreach($this->aoInstances as $sPart=>$aoParts) { $sTagValue = ''; foreach($aoParts as $oInstance) { $sTagValue .= $oInstance->getMask(); } $this->setTag($this->getPartTagPattern($sPart, false), $sTagValue); } //replace tags if(!empty($this->asTags)) { $asTags = $this->addTagMark(array_keys($this->asTags)); $sCompletedMask = str_replace($asTags, $this->asTags, $sCompletedMask); } return $sCompletedMask; } private function addTagMark($oData) { return ToolBox::array_map_encapsulate($oData, self::TAG_MARK); } } class fileUploader { const GET_NAME = 'qqfile'; private $asAllowedExtensions; private $iSizeLimit; private $sFolderPath; function __construct($sFolderPath, $asAllowedExtensions = array()/*, $sizeLimit = 10485760*/) { $this->sFolderPath = $sFolderPath; $this->asAllowedExtensions = array_map("strtolower", $asAllowedExtensions); //$this->iSizeLimit = $sizeLimit; //$this->checkServerSettings(); $this->iSizeLimit = Databap::getMaxSize(); /* if(isset($_GET[self::GET_NAME])) { //$this->file = new qqUploadedFileXhr(); } elseif(isset($_FILES[self::GET_NAME])) { $this->file = new qqUploadedFileForm(); } else { $this->file = false; } */ } /* private function checkServerSettings() { $postSize = $this->toBytes(ini_get('post_max_size')); $uploadSize = $this->toBytes(ini_get('upload_max_filesize')); if($postSize < $this->iSizeLimit || $uploadSize < $this->iSizeLimit){ $size = max(1, $this->iSizeLimit / 1024 / 1024) . 'M'; die("{'error':'increase post_max_size and upload_max_filesize to $size'}"); } } private function toBytes($str) { $val = trim($str); $last = strtolower($str[strlen($str)-1]); switch($last) { case 'g': $val *= 1024; case 'm': $val *= 1024; case 'k': $val *= 1024; } return $val; } */ function save($sFileName) { $oInput = fopen("php://input", "r"); $sTemp = tmpfile(); $iRealSize = stream_copy_to_stream($oInput, $sTemp); fclose($oInput); if($iRealSize != $this->getSize()) { return false; } $sPath = $this->sFolderPath.$sFileName; $oTarget = fopen($sPath, "w"); fseek($sTemp, 0, SEEK_SET); stream_copy_to_stream($sTemp, $oTarget); fclose($oTarget); return true; } function getName() { return isset($_GET[self::GET_NAME])?$_GET[self::GET_NAME]:false; } function getSize() { if(isset($_SERVER["CONTENT_LENGTH"])) { return (int)$_SERVER["CONTENT_LENGTH"]; } else { throw new Exception('Getting content length is not supported.'); } } //Returns array('success'=>true) or array('error'=>'error message') function handleUpload(/*$sUploadDir, $replaceOldFile = false*/) { if(!is_writable($this->sFolderPath)) { return array('error' => "Erreur serveur. Le dossier est protégé en écriture"); } if(!$this->getName()) { return array('error' => 'Aucun fichier téléchargé'); } $iSize = $this->getSize(); if($iSize == 0) { return array('error' => 'Fichier vide'); } if($iSize > $this->iSizeLimit) { return array('error' => 'Fichier trop volumineux'); } $asPathInfo = pathinfo($this->getName()); $sExt = strtolower($asPathInfo['extension']); $sExt = ($sExt=='jpg')?'jpeg':$sExt; if($this->asAllowedExtensions && !in_array($sExt, $this->asAllowedExtensions)) { $sAuthorizedExts = implode(', ', $this->asAllowedExtensions); return array('error' => 'Le fichier a une extension invalide, les extensions autorisées sont : '.$sAuthorizedExts); } // //$sFileName = $asPathInfo['filename']; $sFileName = md5(uniqid()); /* if(!$replaceOldFile){ // don't overwrite previous files that were uploaded //while (file_exists($sUploadDir . $sFileName . '.' . $sExt)) { // $sFileName .= rand(10, 99); //} if(file_exists($sUploadDir . $sFileName . '.' . $sExt)) { $sFileName .= date(Databap::DATE_COMPACT_FORMAT); } } */ if($this->save($sFileName.'.'.$sExt)) { return array('success'=>true, 'file_name'=>$sFileName.'.'.$sExt); } else { return array('error'=> 'Erreur server. Impossible de sauvegarder le fichier.'); } } } /** * MySql query manager and generator * @author franzz */ class MySqlManager extends PhpObject { const DB_SERVER = Settings::DB_SERVER; const DB_LOGIN = Settings::DB_LOGIN; const DB_PASS = Settings::DB_PASS; const DB_NAME = Settings::DB_NAME; const DB_NO_CONN = 'ERR_1'; const DB_NO_DATA = 'ERR_2'; const ID_TAG = 'id_'; const USER_TABLE = 'users'; const COMP_TABLE = 'companies'; const CODE_TABLE = 'codes'; const URL_TABLE = 'urls'; const MSG_TABLE = 'messages'; const CHAN_TABLE = 'channels'; const CONN_TABLE = 'connections'; const ART_TABLE = 'articles'; const OPT_TABLE = 'options'; const OPTVAL_TABLE = 'option_values'; const OPTNAME_TABLE = 'option_names'; const PROC_TABLE = 'procedures'; const STEP_TABLE = 'steps'; const IMG_TABLE = 'images'; const DOC_TABLE = 'docs'; const FILE_TABLE = 'files'; const SEARCH_TABLE = 'searchs'; public $sDbState; private $oConnection; private $bTrace; public function __construct() { parent::__construct(); //$this->oConnection = mysql_connect(self::DB_SERVER, self::DB_LOGIN, self::DB_PASS); $this->oConnection = new mysqli(self::DB_SERVER, self::DB_LOGIN, self::DB_PASS); /* $dsn = 'mysql:dbname=databap;host=127.0.0.1'; try {$dbh = new PDO($dsn, self::DB_LOGIN, self::DB_PASS);} catch (PDOException $e) {$this->addError('Connexion échouée : ' . $e->getMessage());} */ $this->setTrace(false); //if(!$this->oConnection) if($this->oConnection->connect_error) { //$this->addError('bug connection'); $this->addError('bug connection : '.$this->oConnection->connect_error); $this->sDbState = self::DB_NO_CONN; } else { //if(!mysql_select_db(self::DB_NAME, $this->oConnection)) if(!$this->oConnection->select_db(self::DB_NAME)) { $this->addError('bug selecting database. Installing...'); $this->sDbState = self::DB_NO_DATA; } } } public function __destruct() { parent::__destruct(); //mysql_close($this->oConnection); $this->oConnection->close(); } public function setTrace($bAction) { $this->bTrace = $bAction; } public static function getTables() { $oReflect = new ReflectionClass(__CLASS__); $asConstants = $oReflect->getConstants(); $sTableTag = '_TABLE'; $asTables = array(); foreach($asConstants as $sConstant=>$sConstantValue) { if(strpos($sConstant, $sTableTag)!==false && strpos($sConstant, $sTableTag)==(strlen($sConstant) - strlen($sTableTag))) { $asTables[] = $sConstantValue; } } return $asTables; } public function install() { //Create Database $this->setQuery("DROP DATABASE IF EXISTS ".self::DB_NAME); $this->setQuery("CREATE DATABASE ".self::DB_NAME); //mysql_select_db(self::DB_NAME, $this->oConnection); $this->oConnection->select_db(self::DB_NAME); //Create tables @array_walk($this->getInstallQueries(), array($this, 'setQuery')); } //For debug purposes public function getFullInstallQuery() { $asInstallQueries = $this->getInstallQueries(); return str_replace("\n", "
    ", implode(";\n\n", $asInstallQueries))."\n\n"; } private function getInstallQueries() { $asTables = $this->getTables(); $asInstallQueries = array_map(array($this, 'getInstallQuery'), $asTables); $asAlterQueries = $this->getForeignKeyQueries($asTables); return array_merge($asInstallQueries, $asAlterQueries); } private function getInstallQuery($sTableName) { $asTableColumns = $this->getTableColumns($sTableName); $sQuery = "\n".$this->implodeAll($asTableColumns, "` ", "\n", "`", ",")."\n".implode(", \n", $this->getTableConstraints($sTableName)); return "CREATE TABLE `{$sTableName}` ({$sQuery})"; } private function getForeignKeyQueries($asTableNames) { $asForeignKeyQueries = array(); foreach($asTableNames as $sTableName) { $asTableColumns = array_keys(self::getTablecolumns($sTableName)); foreach($asTableColumns as $sColumnName) { if(self::isId($sColumnName) && $sColumnName!=self::getId($sTableName)) { $asForeignKeyQueries[] = "ALTER TABLE ".$sTableName." ADD INDEX(`".$sColumnName."`)"; $asForeignKeyQueries[] = "ALTER TABLE ".$sTableName." ADD FOREIGN KEY (`".$sColumnName."`) REFERENCES ".self::getTable($sColumnName)."(`".$sColumnName."`)"; } } } return $asForeignKeyQueries; } private function setQuery($sQuery, $sTypeQuery=__FUNCTION__) { $this->getQuery($sQuery, $sTypeQuery); //return (mysql_affected_rows()!=0); return ($this->oConnection->affected_rows!=0); } private function getQuery($sQuery, $sTypeQuery=__FUNCTION__) { $sQuery = str_replace(array("\n", "\t"), array(" ", ""), $sQuery); //$oResult = mysql_query($sQuery, $this->oConnection); if($this->bTrace) { $this->setDebug(true); $this->addNotice($sQuery); } if(!($oResult = $this->oConnection->query($sQuery))) { $this->addError("\nErreur SQL : \n".str_replace("\t", "", $sQuery)."\n\n".str_replace(array("\t", "\n"), "", $this->oConnection->error)); } return $oResult; } public function getArrayQuery($sQuery, $bStringOnly=false, $sGroupBy='', $sTypeQuery=__FUNCTION__) { $iIndex = 0; $iColumnCount = 0; $asResult = array(); $oResult = $this->getQuery($sQuery, true, $sTypeQuery); if($oResult!==false) { //while($asCurrentRow = mysql_fetch_array($oResult)) while($asCurrentRow = $oResult->fetch_array()) { if($bStringOnly) { $asCurrentRow = $this->arrayKeyFilter($asCurrentRow, 'is_string'); } //Add table reel keys if($sGroupBy!='' && array_key_exists($sGroupBy, $asCurrentRow)) { $iRowKey = $asCurrentRow[$sGroupBy]; unset($asCurrentRow[$sGroupBy]); } else { $iRowKey = $iIndex; } //For first loop, check table width if($iIndex==0) { $iColumnCount = count($asCurrentRow); } //One column case : collapse a level if($iColumnCount==1) { $asCurrentRow = array_shift($asCurrentRow); } $asResult[$iRowKey] = $asCurrentRow; $iIndex++; } } return $asResult; } private function getMaxIncrementedValue($sTable) { return $this->selectValue($sTable, "MAX(".$this->getId($sTable).")"); } public static function getId($sTableName, $bFull=false) { $sColumnName = self::ID_TAG.self::getText($sTableName); return $bFull?self::getFullColumnName($sTableName, $sColumnName):$sColumnName; } public static function getText($sTableName, $bFull=false) { $sColumnName = substr(str_replace('`', '', $sTableName), 0, -1); $sColumnName = substr($sColumnName, -2)=='ie'?substr($sColumnName, 0, -2).'y':$sColumnName; return $bFull?self::getFullColumnName($sTableName, $sColumnName):$sColumnName; } public static function getFullColumnName($sTableName, $sColumnName) { return $sTableName.".".$sColumnName; } private static function isId($sColumnName, $sTableName='') { $asTables = ($sTableName=='')?self::getTables():array($sTableName); $asTableIds = array_map(array('self', 'getId'), $asTables); return in_array($sColumnName, $asTableIds); } private static function getTable($sTableId) { $asTables = self::getTables(); $asTableIds = array_map(array('self', 'getId'), $asTables); if(in_array($sTableId, $asTableIds)) return $asTables[array_search($sTableId, $asTableIds)]; else { $this->addError('Id '.$sTableId.' présent dans aucune table'); return false; } } public static function getTablecolumns($sTableName) { $asTableColumns = array(self::getId($sTableName)); switch($sTableName) { case self::USER_TABLE: $asTableColumns[] = 'first_name'; $asTableColumns[] = 'last_name'; $asTableColumns[] = 'email'; $asTableColumns[] = self::getId(self::COMP_TABLE); $asTableColumns[] = 'pass'; $asTableColumns[] = 'clearance'; break; case self::COMP_TABLE: $asTableColumns[] = self::getText(self::COMP_TABLE); $asTableColumns[] = 'logo'; break; case self::CODE_TABLE: $asTableColumns[] = self::getText(self::CODE_TABLE); $asTableColumns[] = 'description'; $asTableColumns[] = self::getId(self::USER_TABLE); $asTableColumns[] = 'refer_id'; break; case self::URL_TABLE: $asTableColumns[] = self::getId(self::CODE_TABLE);; $asTableColumns[] = 'phrase'; break; case self::MSG_TABLE: $asTableColumns[] = self::getId(self::USER_TABLE); $asTableColumns[] = 'nickname'; $asTableColumns[] = self::getId(self::CHAN_TABLE); $asTableColumns[] = self::getText(self::MSG_TABLE); $asTableColumns[] = 'type'; $asTableColumns[] = 'date'; break; case self::CHAN_TABLE: $asTableColumns[] = 'safe_name'; $asTableColumns[] = self::getText(self::CHAN_TABLE); break; case self::CONN_TABLE: $asTableColumns[] = self::getId(self::USER_TABLE); $asTableColumns[] = self::getId(self::CHAN_TABLE); break; case self::OPT_TABLE: $asTableColumns[] = self::getId(self::USER_TABLE); $asTableColumns[] = self::getId(self::OPTNAME_TABLE); $asTableColumns[] = self::getId(self::OPTVAL_TABLE); $asTableColumns[] = self::getText(self::OPT_TABLE); break; case self::OPTNAME_TABLE: $asTableColumns[] = self::getText(self::OPTNAME_TABLE); $asTableColumns[] = 'type'; $asTableColumns[] = 'language'; break; case self::OPTVAL_TABLE: $asTableColumns[] = self::getId(self::OPTNAME_TABLE); $asTableColumns[] = self::getText(self::OPTVAL_TABLE); $asTableColumns[] = 'language'; break; case self::PROC_TABLE: $asTableColumns[] = self::getId(self::USER_TABLE); $asTableColumns[] = 'title'; $asTableColumns[] = 'description'; $asTableColumns[] = 'refer_id'; break; case self::STEP_TABLE: $asTableColumns[] = self::getId(self::PROC_TABLE); $asTableColumns[] = 'description'; break; case self::IMG_TABLE: $asTableColumns[] = self::getId(self::PROC_TABLE); $asTableColumns[] = self::getId(self::STEP_TABLE); $asTableColumns[] = 'description'; $asTableColumns[] = 'file_name'; break; case self::DOC_TABLE: $asTableColumns[] = self::getId(self::USER_TABLE); $asTableColumns[] = 'title'; $asTableColumns[] = 'description'; $asTableColumns[] = 'refer_id'; break; case self::FILE_TABLE: $asTableColumns[] = self::getId(self::DOC_TABLE); $asTableColumns[] = 'description'; $asTableColumns[] = 'file_name'; break; case self::SEARCH_TABLE: $asTableColumns[] = 'id_item'; $asTableColumns[] = 'refer_id'; $asTableColumns[] = 'type'; $asTableColumns[] = 'keywords'; break; case self::ART_TABLE: $asTableColumns[] = 'title'; $asTableColumns[] = 'link'; $asTableColumns[] = 'date'; $asTableColumns[] = 'first_name'; $asTableColumns[] = 'last_name'; $asTableColumns[] = 'email'; break; default: return false; } $asTableColumns[] = 'led'; $asTableName = array_fill(0, count($asTableColumns), $sTableName); return array_combine($asTableColumns, array_map(array('self', 'getColumnType'), $asTableColumns, $asTableName)); } private static function getColumnType($sColumnName, $sTableName) { $sColumnType = ''; switch($sColumnName) { case self::getText(self::USER_TABLE): //TODO delete use and field $sColumnType = "varchar(50) NOT NULL"; break; case 'first_name': $sColumnType = "varchar(20) NOT NULL"; break; case 'last_name': $sColumnType = "varchar(20) NOT NULL"; break; case 'nickname': $sColumnType = "varchar(50) NOT NULL"; break; case 'email': $sColumnType = "varchar(100) NOT NULL"; break; case 'pass': $sColumnType = "varchar(128) NOT NULL"; break; case 'clearance': $sColumnType = "int(1) NOT NULL"; break; case self::getText(self::CODE_TABLE): $sColumnType = "longtext NOT NULL"; break; case 'title': $sColumnType = "varchar(200) NOT NULL"; break; case 'description': $sColumnType = "varchar(500) NOT NULL"; break; case 'link': $sColumnType = "varchar(200) NOT NULL"; break; case 'refer_id': $sColumnType = "int(10) UNSIGNED NOT NULL"; break; case 'phrase': $sColumnType = "varchar(50) NOT NULL"; break; case self::getText(self::MSG_TABLE): $sColumnType = "varchar(500) NOT NULL"; break; case 'type': $sColumnType = "varchar(2) NOT NULL"; break; case 'safe_name': $sColumnType = "varchar(50) NOT NULL"; break; case self::getText(self::CHAN_TABLE): $sColumnType = "varchar(50) NOT NULL"; break; case self::getText(self::OPT_TABLE): $sColumnType = "varchar(100) NOT NULL"; break; case self::getText(self::OPTNAME_TABLE): $sColumnType = "varchar(100) NOT NULL"; break; case self::getText(self::OPTVAL_TABLE): $sColumnType = "varchar(50) NOT NULL"; break; case 'language': $sColumnType = "varchar(2) NOT NULL"; break; case 'file_name': $sColumnType = "varchar(40) NOT NULL"; break; case 'preview_name': $sColumnType = "varchar(40) NOT NULL"; break; case 'id_item': $sColumnType = "int(10) UNSIGNED NOT NULL"; break; case 'keywords': $sColumnType = "longtext NOT NULL"; break; case self::getText(self::COMP_TABLE): $sColumnType = "varchar(30) NOT NULL"; break; case 'logo': $sColumnType = "varchar(20) NOT NULL"; break; case 'date': $sColumnType = "date NOT NULL"; break; case 'led': $sColumnType = "TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP"; break; case self::isId($sColumnName, $sTableName): $sColumnType = "int(10) UNSIGNED auto_increment"; break; case self::isId($sColumnName): $sColumnType = "int(10) UNSIGNED"; break; } return $sColumnType; } private static function getTableConstraints($sTableName) { //Primary key $asTableConstraints = array('PRIMARY' => "PRIMARY KEY (`".self::getId($sTableName)."`)"); //Foreign keys //Foreign keys applied using ALTER TABLE syntax at the end to prevent scheduling CREATE TABLE queries //Other constraints switch($sTableName) { case self::USER_TABLE : $asTableConstraints[] = "UNIQUE KEY `user_first_and_last_name` (`first_name`, `last_name`)"; break; case self::URL_TABLE : $asTableConstraints[] = "UNIQUE KEY `uni_phrase` (`phrase`)"; break; case self::MSG_TABLE : $asTableConstraints[] = "INDEX(`date`)"; break; case self::ART_TABLE: $asTableConstraints[] = "INDEX(`title`)"; break; } return $asTableConstraints; } private function addQuotes($oData) { //TODO remake $asTrustedFunc = array('CURDATE()', 'NOW()'); $sChar = "'"; if(is_array($oData)) { $asChar = array_fill(1, count($oData), $sChar); return array_combine(array_keys($oData), array_map(array($this, 'addQuotes'), $oData, $asChar)); } else { if(in_array($oData, $asTrustedFunc)) return $oData; else return $sChar.$oData.$sChar; } } private function getLastId() { return $this->oConnection->insert_id; } public function insertRow($sTableName, $asData) { $this->cleanSql($sTableName); $this->cleanSql($asData); $asQueryValues = $this->addQuotes($asData); $sQuery = "INSERT INTO ".$sTableName." (`".implode("`, `", array_keys($asQueryValues))."`) VALUES (".implode(", ", $asQueryValues).")"; return $this->setQuery($sQuery)?$this->getLastId():0; } public function updateRow($sTableName, $asConstraints, $asData) { return $this->updateRows($sTableName, $asConstraints, $asData, 1); } public function updateRows($sTableName, $asConstraints, $asData, $iLimit=0) { if(!is_array($asConstraints)) { $asConstraints = array($this->getId($sTableName)=>$asConstraints); } $this->cleanSql($sTableName); $this->cleanSql($iTableId); $this->cleanSql($asData); $this->cleanSql($asConstraints); $asQueryValues = $this->addQuotes($asData); $asConstraintsValues = $this->addQuotes($asConstraints); $this->addColumnSelectors($asQueryValues); $this->addColumnSelectors($asConstraintsValues); $sLimit = $iLimit>0?" LIMIT $iLimit":""; $sQuery = "UPDATE {$sTableName} ". "SET ".$this->implodeAll($asQueryValues, " = ", ", ")." ". "WHERE ".$this->implodeAll($asConstraintsValues, " = ", " AND ").$sLimit; $iResult = false; if($this->setQuery($sQuery)) { $iResult = ($iLimit==1)?$this->selectValue($sTableName, $this->getId($sTableName), $asConstraints):true; } return $iResult; } public function insertUpdateRow($sTableName, $asData, $asKeys=array(), $bUpdate=true) { $sTableIdName = self::getId($sTableName); //check for data in the db if($asKeys==array()) { $asKeys[] = $sTableIdName; } $asValues = array_intersect_key($asData, array_flip($asKeys)); $iTableId = $this->selectValue($sTableName, $sTableIdName, $asValues); //insert if(!$iTableId) { $iTableId = $this->insertRow($sTableName, $asData); } //Update elseif($bUpdate) { if(array_key_exists($sTableIdName, $asData)) { unset($asData[$sTableIdName]); } $iTableId = $this->updateRow($sTableName, $iTableId, $asData); } return $iTableId; } public function selectInsert($sTableName, $asData, $asKeys=array()) { return $this->insertUpdateRow($sTableName, $asData, $asKeys, false); } public function deleteRow($sTableName, $iTableId) { $this->cleanSql($sTableName); $this->cleanSql($iTableId); //linked tables switch($sTableName) { case self::CODE_TABLE : $asTables = array($sTableName, self::URL_TABLE); break; case self::PROC_TABLE : $asTables = array($sTableName, self::STEP_TABLE, self::IMG_TABLE); break; case is_string($sTableName) : $asTables = array($sTableName); break; case is_array($sTableName): $asTables = $sTableName; break; default: $asTables = array(); } foreach($asTables as $sTable) { $this->setQuery("DELETE FROM ".$sTable." WHERE ".$this->getId($sTableName)." = ".$iTableId); } } public function selectRows($asInfo, $bStringOnly=true, $sGroupBy='') { $asAttributes = array('select'=>"SELECT", 'from'=>"FROM", 'join'=>"LEFT JOIN", 'joinOn'=>"LEFT JOIN", 'constraint'=>"WHERE", 'groupBy'=>"GROUP BY", 'orderBy'=>"ORDER BY", 'limit'=>'LIMIT'); $asRowSeparators = array('select'=>", ", 'from'=>"", 'join'=>" LEFT JOIN ", 'joinOn'=>" LEFT JOIN ", 'constraint'=>" AND ", 'groupBy'=>", ", 'orderBy'=>", ", 'limit'=>""); $asOperators = array('constraint'=>" = ", 'orderBy'=>" ", 'join'=>" USING(", 'joinOn'=>" ON "); $asEndOfStatement = array('constraint'=>"", 'orderBy'=>"", 'join'=>")", 'joinOn'=>""); //$sQuery = "/* ".str_replace(array("\n", "\t"), '', print_r($asInfo, true))." */"; $sQuery = ""; foreach($asAttributes as $sStatement => $sKeyWord) { $asSelection = array_key_exists($sStatement, $asInfo)?$asInfo[$sStatement]:array(); if(!is_array($asSelection)) { $asSelection = array($asSelection); } //if provided values if(!empty($asSelection)) { $this->cleanSql($asSelection); if($sStatement=='constraint' && !array_key_exists('constVar', $asInfo)) { $asSelection = $this->addQuotes($asSelection); } $this->addColumnSelectors($asSelection); $sQuery .= " ".$sKeyWord." "; //in case of double value input if(array_key_exists($sStatement, $asOperators)) { if($sStatement=='constraint' && array_key_exists('constOpe', $asInfo)) { $asOperators[$sStatement] = $asInfo['constOpe']; } $sQuery .= $this->implodeAll($asSelection, $asOperators[$sStatement], $asRowSeparators[$sStatement], "", $asEndOfStatement[$sStatement]); } else { $sQuery .= implode($asRowSeparators[$sStatement], $asSelection); } } //default value for select elseif($sStatement=='select') { $sQuery .= " ".$sKeyWord." * "; } } return $this->getArrayQuery($sQuery, $bStringOnly, $sGroupBy); } private function addColumnSelectors(&$asSelection) { //TODO get rid of this $sSqlWord = 'option'; $sKey = array_search($sSqlWord, $asSelection); if($sKey!==false) { $asSelection[$sKey] = "`".$asSelection[$sKey]."`"; } elseif(array_key_exists($sSqlWord, $asSelection)) { $asSelection["`".$sSqlWord."`"] = $asSelection[$sSqlWord]; unset($asSelection[$sSqlWord]); } } public function selectRow($sTableName, $asConstraints=array(), $sColumnName='*') { if(!is_array($asConstraints)) { $asConstraints = array($this->getId($sTableName)=>$asConstraints); } $asResult = $this->selectRows(array('select'=>$sColumnName, 'from'=>$sTableName, 'constraint'=>$asConstraints)); $iCountNb = count($asResult); switch($iCountNb) { case 0 : return false; case $iCountNb > 1 : $this->addError('Trop de résultats pour un selectRow() : '.$iCountNb.' lignes. Table: '.$sTableName.', contrainte: '.self::implodeAll($asConstraints, '=', ' ').', colonne: '.$sColumnName); break; } return array_shift($asResult); } public function selectValue($sTableName, $sColumnName, $oConstraints=array()) { if(!is_array($oConstraints)) { $oConstraints = array($this->getId($sTableName)=>$oConstraints); } return $this->selectRow($sTableName, $oConstraints, $sColumnName); } public function pingValue($sTableName, $oConstraints) { return $this->selectValue($sTableName, 'COUNT(1)', $oConstraints); } public function cleanSql(&$oData) { //self::cleanData($oData, 'mysql_real_escape_string'); //$oData = self::cleanData($oData, 'mysql_real_escape_string'); $this->cleanData($oData); $oData = $this->cleanData($oData); } public static function implodeAll($asText, $asKeyValueSeparator='', $sRowSeparator='', $sKeyPre='', $sValuePost=false) { if($sValuePost===false) { $sValuePost = $sKeyPre; } $asCombinedText = array(); //if unique value for key value separator if(!is_array($asKeyValueSeparator) && !empty($asText)) { $asKeyValueSeparator = array_combine(array_keys($asText), array_fill(0, count($asText), $asKeyValueSeparator)); } foreach($asText as $sKey=>$sValue) { $asCombinedText[] = $sKeyPre.$sKey.$asKeyValueSeparator[$sKey].(is_array($sValue)?implode($sValue):$sValue).$sValuePost; } return implode($sRowSeparator, $asCombinedText); } public static function arrayKeyFilter($asArray, $sCallBack) { $asValidKeys = array_flip(array_filter(array_keys($asArray), $sCallBack)); return array_intersect_key($asArray, $asValidKeys); } public function cleanData($oData) { if(!is_array($oData)) { return $this->oConnection->real_escape_string($oData); } elseif(count($oData)>0) { $asKeys = array_map(array($this, 'cleanData'), array_keys($oData)); $asValues = array_map(array($this, 'cleanData'), $oData); return array_combine($asKeys, $asValues); } } } /** * ToolBox - Only static functions missing from php librairy * @author franzz */ class ToolBox { function __construct(){} public function toString($sSep, $asData) { if(is_array($asData) && count($asData)>0) { $sData = $sSep; foreach($asData as $oData) { $sData .= self::toString($oData).$sSep; } return $sData; } else return $asData; } public static function cleanPost(&$asData) { //get rid of magic quotes if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) { $asData = self::cleanData($asData, 'stripslashes'); } } public static function cleanData($oData, $sCleaningFunc) { if(!is_array($oData)) { return call_user_func($sCleaningFunc, $oData); } elseif(count($oData)>0) { $asCleaningFunc = array_fill(1, count($oData), $sCleaningFunc); $asKeys = array_map(array('self', 'cleanData'), array_keys($oData), $asCleaningFunc); $asValues = array_map(array('self', 'cleanData'), $oData, $asCleaningFunc); return array_combine($asKeys, $asValues); } } public static function fixGlobalVars($argv) { //Add CLI arguments if(defined('STDIN')) parse_str(implode('&', array_slice($argv, 1)), $_GET); //Add Server Name $sServerName = array_key_exists('SERVER_NAME', $_SERVER)?$_SERVER['SERVER_NAME']:$_SERVER['PWD']; $sAppPath = 'http://'.str_replace('http://', '', $sServerName.dirname($_SERVER['SCRIPT_NAME'])); $_GET['serv_name'] = $sAppPath.(substr($sAppPath, -1)!='/'?'/':''); } public static function array_map_encapsulate($oData, $sChar) { if(is_array($oData)) { $asChar = array_fill(1, count($oData), $sChar); return array_combine(array_keys($oData), array_map(array('self', 'array_map_encapsulate'), $oData, $asChar)); } else { return $sChar.$oData.$sChar; } } public static function capitalizeWords($acText, $sCharList = '') { // Use ucwords if no delimiters are given if($sCharList=='') { return ucwords($acText); } // Go through all characters $capitalizeNext = true; $max = strlen($acText); for ($i = 0; $i < $max; $i++) { if(strpos($sCharList, $acText[$i]) !== false) { $capitalizeNext = true; } elseif($capitalizeNext) { $capitalizeNext = false; $acText[$i] = strtoupper($acText[$i]); } } return $acText; } public static function sendMail($sFrom, $sTo, $sReplyTo, $sSubject, $sMessage) { $sResult = ''; if($sFrom!='' && $sTo!='' && $sReplyTo!='' && $sSubject!='' && $sMessage!='') { $sHtmlMessage = str_replace("\n", '
    ', $sMessage); $sPlainMessage = strip_tags(str_replace('
    ', "\n", $sHtmlMessage)); $iBoundary = uniqid('HTMLEMAIL'); $sHeaders = 'From: '.$sFrom."\r\n". 'Reply-To: '.$sReplyTo."\r\n". 'MIME-Version: 1.0'."\r\n". 'Content-Type: multipart/alternative;'. 'boundary = '.$iBoundary."\r\n\r\n". 'MIME encoded Message'. '--'.$iBoundary."\r\n". 'Content-Type: text/plain; charset=UTF-8'."\r\n". 'Content-Transfer-Encoding: base64'."\r\n\r\n". chunk_split(base64_encode($sPlainMessage)). '--'.$iBoundary."\r\n". 'Content-Type: text/html; charset=UTF-8'."\r\n". 'Content-Transfer-Encoding: base64'."\r\n\r\n". chunk_split(base64_encode($sHtmlMessage)); if(!mail($sTo, $sSubject, '', $sHeaders)) { $sResult = 'Email: An unknown error occured'; } } else { $sResult = 'Email: Some fields were empty'; } } public static function createThumbnail($sInPath, $iMaxWidth, $iMaxHeight, $sOutPath='', $bDeleteIn=false) { $asResult = array('error'=>''); //Look up the extension to choose the image creator //TODO use MIME types $sInInfo = pathinfo($sInPath); $sInName = strtolower($sInInfo['basename']); $sImageExt = strtolower($sInInfo['extension']); $sImageExt = ($sImageExt=='jpg')?'jpeg':$sImageExt; //New Destination folder if($sOutPath=='') $sOutPath = $sInPath; elseif(substr($sOutPath, -1)=='/') $sOutPath .= $sInName; //New sizes if(in_array($sImageExt, Databap::$UPLOAD_IMG_EXTS)) { list($iWidth, $iHeight) = getimagesize($sInPath); if($iWidth > $iMaxWidth || $iHeight > $iMaxHeight) { $dResizeDeltaWidth = $iWidth - $iMaxWidth; $dResizeDeltaHeight = $iHeight - $iMaxHeight; if($dResizeDeltaWidth > $dResizeDeltaHeight) { $iResizedWidth = $iMaxWidth; $iResizedHeight = ($iResizedWidth / $iWidth) * $iHeight; } else { $iResizedHeight = $iMaxHeight; $iResizedWidth = ($iResizedHeight / $iHeight) * $iWidth; } //create image from source $oSource = call_user_func('imagecreatefrom'.$sImageExt, $sInPath); //Resize $oThumb = imagecreatetruecolor($iResizedWidth, $iResizedHeight); imagecopyresized($oThumb, $oSource, 0, 0, 0, 0, $iResizedWidth, $iResizedHeight, $iWidth, $iHeight); //Save if(file_exists($sOutPath)) unlink($sOutPath); if(!call_user_func_array('image'.$sImageExt, array($oThumb, $sOutPath))) { $asResult['error'] = 'Unable to create thumbnail : '.$sOutPath; } } elseif($sInPath != $sOutPath) { $iResizedWidth = $iWidth; $iResizedHeight = $iHeight; if(!copy($sInPath, $sOutPath)) $asResult['error'] = 'Copy failed from '.$sInPath.' to '.$sOutPath; } $asResult['width'] = $iResizedWidth; $asResult['height'] = $iResizedHeight; $asResult['out'] = $sOutPath; } else $asResult['error'] = 'Wrong file type'; if($bDeleteIn && $asResult['error']=='' && $sInPath != $sOutPath) unlink($sInPath); return $asResult; } public static function utf8_compliant($sText) { if(strlen($sText) == 0) return true; return (preg_match('/^.{1}/us', $sText, $ar) == 1); } /** * Only works with ISO-8859-1 and UTF-8 encoding */ public static function strlen($sText) { if(utf8_compliant($sText)) return strlen(utf8_decode($sText)); else return strlen($sText); } } /* Debug */ function pre($sText, $sMode='return', $bDie=false, $sTitle='Test') { $sLog = '
    '.$sTitle.'
    '.print_r($sText, true).'
    '; switch($sMode) { case 'echo': echo $sLog; break; case 'return': if($bDie) echo $sLog; break; case 'log': file_put_contents('log.html', ($sTitle!=''?$sTitle." :\n":'').print_r($sText, true)."\n\n", FILE_APPEND); break; default: break; } if($bDie) { die('[die() called by the test function '.__FUNCTION__.'()]'); } return $sLog; } function dlog($sText, $sTitle='Test') { pre($sText, 'log', false, date('d/m/Y H:m:i').' - '.$sTitle, false); } ?>