commit 5fd2896407f049317f7b9e54385bc79b088cc003 Author: franzz Date: Wed Aug 7 14:11:17 2013 +0200 Initial commit diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..58dbe4f --- /dev/null +++ b/.htaccess @@ -0,0 +1,64 @@ +Options +FollowSymlinks +RewriteEngine on + +#ErrorDocument 400 /databap/index.php?p=error +#ErrorDocument 401 /databap/index.php?p=error +#ErrorDocument 403 /databap/index.php?p=error +#ErrorDocument 404 /databap/index.php?p=error&test=%{REQUEST_URI} +#ErrorDocument 500 /databap/index.php?p=error + +#reading procedure bytes (by id/phrase) +RewriteRule ^(.*)(p|proc|procedure)\-([0-9]+)$ index.php?p=procedure&proc_id=$3 [L] + +#Article (by id) +RewriteRule ^(.*)(a|art|article)\-(.+)$ index.php?a=art_redirect&id=$3 [L] + +#Doc (by id) +RewriteRule ^(.*)(d|doc|documentation)\-(.+)$ index.php?p=doc&doc_id=$3 [L] + +#reading code bytes (by id/phrase) +RewriteRule ^(.*)(c|code)\-(.+)$ index.php?p=read_code&code=$3 [L] + +#search (by phrase) +RewriteRule ^(.*)(recherche|search)\-(.+)$ index.php?p=search&search_words=$3 [L] + +#Add Procedure +RewriteRule ^(.*)(add_proc|add_procedure|p\-|proc|proc\-|procedure|procedure\-)$ index.php?p=procedure [L] + +#Add Code +RewriteRule ^(.*)(add_code|c\-|code|code\-)$ index.php?p=add_code [L] + +#Add Doc +RewriteRule ^(.*)doc$ index.php?p=doc [L] + +#Liste +RewriteRule ^(.*)liste$ index.php?p=list [L] + +#Profile (by id) +RewriteRule ^(.*)(p|profil)\-(.+)$ index.php?p=profile&profile_user=$3 [L] + +#Profile (current user) +RewriteRule ^(.*)(p\-|profil|profil\-)$ index.php?p=profile&profile_user= [L] + +#Chat (by message id) +RewriteRule ^(.*)chat\-(.+)$ index.php?p=chat&id=$2 [L] + +#Chat (current chat) +RewriteRule ^(.*)(chat|chat\-)$ index.php?p=chat [L] + +#Options +RewriteRule ^(.*)options$ index.php?p=options [L] + +#RSS Feed (by category) +RewriteRule ^(.*)rss\-(.+)$ index.php?p=rss&cat=$2 [L] + +#RSS Feed (all) +RewriteRule ^(.*)rss$ index.php?p=rss [L] + +#Eg +#prevent www. +#RewriteCond %{HTTP_HOST} ^www\.(.*) [NC] +#RewriteRule ^(.*)$ http://%1/$1 [R=301,NC,L] +#E.g RewriteRule ^(.*)\.html $1.php +#reading code bytes (by id) +#RewriteRule ^(.*)code\-([0-9]+)$ index.php?p=read_code&id_code=$2 [L] diff --git a/config.php b/config.php new file mode 100644 index 0000000..ab08237 --- /dev/null +++ b/config.php @@ -0,0 +1,4453 @@ + 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 = 10000; + 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(); + $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 + $this->addUser('francois', 'lutran', 'cgi', 'francois@lutran.fr', self::CLEARANCE_ADMIN); + $this->addUser('test', 'test', 'test', 'test@test.com'); + } + + 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'; + + $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) {if($oImage->getAttribute('class') == 'badge-item-img') break;} + $asResult = $this->downloadToTmp($oImage->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')); + } + + 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); + + $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, 'file_name'=>$sFileName, 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 + if($bCreation) + { + $this->addMessage($iDbDocId, self::MESSAGE_ADD_DOC, self::ALL_CHAN_ID); + } + else + { + $this->addMessage($iDbDocId, 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']); + } + + 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['led'] = self::getDateFormat($asCode['led']); + return $asCode; + } + + private function getProcInfo($iProcId) + { + $asProc = $this->oMySql->selectRow(MySqlManager::PROC_TABLE, $iProcId); + $asProc['description'] = self::getDescriptionFormat($asProc['description']); + $asProc['led'] = self::getDateFormat($asProc['led']); + return $asProc; + } + + private function getArticleInfo($iArtId) + { + $asArt = $this->oMySql->selectRow(MySqlManager::ART_TABLE, $iArtId); + $asTransferredInfo = array('link_art'=>$asArt['link'], 'title'=>$asArt['title'], 'link_auth'=>$asArt['email']); + $asTransferredInfo['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['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)] = '='; + } + //$this->oMySql->bTrace = true; + 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['description']; + } + elseif($sMessageType==self::MESSAGE_ARTICLE) + { + $asTransferredInfo = $this->getArticleInfo($asMessages['messages'][$iMessageId]['message']); + $asMessages['messages'][$iMessageId]['message'] = $this->getJsonMessage($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);
+		$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"),
+						'from'=>MySqlManager::CODE_TABLE,
+						'groupBy'=>array('refer_id'),
+						'orderBy'=>array('id_item'=>'desc'));
+		$asCodeIds = $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"),
+						'orderBy'=>array('id_item'=>'desc'));
+		$asProcIds = $this->oMySql->selectRows($asInfo);
+
+		//get Article Ids
+		$asInfo = array('select'=>array($sIdArtCol." AS id_item"),
+						'from'=>MySqlManager::ART_TABLE,
+						'orderBy'=>array('id_item'=>'desc'));
+		$asArtIds = $this->oMySql->selectRows($asInfo);
+		
+		$asItemIds = array(Databap::CODE_TYPE=>$asCodeIds, Databap::PROC_TYPE=>$asProcIds, Databap::ART_TYPE=>$asArtIds);
+		
+		//Build code info structure
+		$asUsers = $asCode = $asItemList = array();
+		foreach($asItemIds as $sType=>$asTypedItemIds)
+		{
+			switch($sType)
+			{
+				case Databap::CODE_TYPE:
+					$sFuncType = 'Code';
+					break;
+				case Databap::PROC_TYPE:
+					$sFuncType = 'Proc';
+					break;
+				case Databap::ART_TYPE:
+					$sFuncType = 'Article';
+					break;	
+			}
+			foreach($asTypedItemIds as $iItemId)
+			{
+				//null value returned from query (MAX(...))
+				if($iItemId>0)
+				{
+					$asItem = call_user_func(array($this, 'get'.$sFuncType.'Info'), $iItemId);
+					if(array_key_exists($sIdUserCol, $asItem))
+					{
+						$iUserId = $asItem[$sIdUserCol];
+						if(!array_key_exists($iUserId, $asUsers))
+						{
+							$asUsers[$iUserId] = $this->getUserInfo($iUserId);
+						}
+						$asItemList[$sType][$iItemId] = array_merge($asUsers[$iUserId], $asItem);
+					}
+					else $asItemList[$sType][$iItemId] = $asItem;
+					
+					//$asCodeList[$iItemId]['phrase'] = $this->getPhraseFromIdCode($iCodeId);
+					
+				}
+			}
+		}
+		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)?$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()
+	{
+		//$this->getConnectedChans();
+		
+		$sTime = $this->oMySql->selectRows(array('select'=>'DATE_SUB(NOW(), INTERVAL '.self::KEEP_ALIVE.' SECOND)'));
+		$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 = MySqlManager::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
+ */
+class SearchEngine extends PhpObject
+{
+	//Objects
+	private $oMySql;
+
+	//variables
+	private $asWords;
+	private $iLevelMax;
+	private $asItemRanks;
+	private $asItemInfos;
+	private $asCodeInfos;
+	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->asCodeInfos = array();
+		$this->asUserInfos = array();
+	}
+	
+	public function buildIndex($iItemId, $sType)
+	{
+		//Build keywords
+		$sWords = '';
+		switch($sType)
+		{
+			case Databap::CODE_TYPE:
+				$asItemData = $this->oMySql->selectRow(MySqlManager::CODE_TABLE, $iItemId);
+				$sWords = $asItemData[MySqlManager::getText(MySqlManager::CODE_TABLE)].self::KEYWORDS_SEPARATOR.$asItemData['description'];
+				break;
+			case Databap::PROC_TYPE:
+				$asItemData = $this->oMySql->selectRow(MySqlManager::PROC_TABLE, $iItemId);
+				$asItemStepsData = $this->oMySql->selectRows(array('select'=>'description', 'from'=>MySqlManager::STEP_TABLE, 'constraint'=>array('id_procedure'=>$iItemId)));	
+				$sWords =	$asItemData['description'].self::KEYWORDS_SEPARATOR.
+							$asItemData['title'].self::KEYWORDS_SEPARATOR.
+							implode(self::KEYWORDS_SEPARATOR, $asItemStepsData);
+				break;
+			case Databap::ART_TYPE:
+				$asItemData = $this->oMySql->selectRow(MySqlManager::ART_TABLE, $iItemId);
+				$sWords = 	$asItemData['first_name'].self::KEYWORDS_SEPARATOR.
+							$asItemData['last_name'].self::KEYWORDS_SEPARATOR.
+							$asItemData['title'];
+				break;
+			default:
+				$this->addError('function'.__FUNCTION__.'(): Incorrect type "'.$sType.'"');
+				break;
+		}
+		$sWords = strtolower(str_replace("\n", self::KEYWORDS_SEPARATOR, $sWords));
+		$sWords = preg_replace('/(\W+)/', self::KEYWORDS_SEPARATOR, $sWords);
+		
+		//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();
+	}
+
+	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(' ?', $asSequence);
+					
+					//TODO replace with selectRow()
+					$sQuery = "SELECT id_search, id_item, type FROM searchs WHERE keywords REGEXP '{$sRegExp}'";
+					
+					//search sequence
+					$asRows = $this->oMySql->getArrayQuery($sQuery, true);
+					foreach($asRows as $asRow)
+					{
+						$iItemId = $asRow['id_item'];
+						unset($asRow['id_item']);
+						
+						//TODO switch case type
+						switch($asRow['type'])
+						{
+							case Databap::CODE_TYPE:
+								$this->setCodeInfo($iItemId);
+								$this->setUserInfo($this->getCodeInfo($iItemId, MySqlManager::getId(MySqlManager::USER_TABLE)));
+								break;
+						}
+						
+						$this->asItemInfos[$iItemId] = $asRow;
+						$this->incItemRank($iItemId, $iLevel);
+					}
+					$iIndex++;
+				}
+			}
+		}
+	}
+	
+	public function getResults()
+	{
+		$asResult = array();
+		
+		//Mixing info
+		arsort($this->asItemRanks);
+		foreach($this->asItemRanks as $iItemId=>$iRank)
+		{
+			switch($this->asItemInfos[$iItemId]['type'])
+			{
+				case Databap::CODE_TYPE:
+					//$asRow['code'] = $this->formatCode($asRow['code'], $sRegExp);
+					break;
+				case Databap::PROC_TYPE:
+					
+					break;
+				default:
+					continue;
+			}
+			
+			//code
+			$iUserId = $this->getCodeInfo($iItemId, MySqlManager::getId(MySqlManager::USER_TABLE));
+			/* TODO
+			$sCodeLines = implode("\n...\n", $this->getCodeInfo($iItemId, 'code')); 
+			$oCode = new Reader($sCodeLines);
+			$sCodeLines = $oCode->getColoredCode();
+		 	*/
+			$sCodeLines = '';
+			$asResult[$iItemId] = array('id_code'=>$iItemId,
+										'code'=>$sCodeLines,
+										'description'=>$this->getCodeInfo($iItemId, 'description'),
+										'name'=>Databap::getNameFormat($this->getUserInfo($iUserId, 'first_name'), $this->getUserInfo($iUserId, 'last_name')),
+										'company'=>Databap::getCompanyFormat($this->getUserInfo($iUserId, 'company')),
+										'led'=>Databap::getDateFormat($this->getCodeInfo($iItemId, 'led')));
+		}
+		
+		return $asResult;
+	}
+
+	private function getCodeInfo($iCodeId, $sInfoName)
+	{
+		return $this->asCodeInfos[$iCodeId][$sInfoName];
+	}
+	
+	private function getUserInfo($iUserId, $sInfoName)
+	{
+		return $this->asUserInfos[$iUserId][$sInfoName];
+	}
+
+	private function setCodeInfo($iCodeId)
+	{
+		if(!array_key_exists($iCodeId, $this->asCodeInfos))
+		{
+			$this->asCodeInfos[$iCodeId] = $this->oMySql->selectRow
+			(
+				MySqlManager::CODE_TABLE,
+				$iCodeId,
+				array(MySqlManager::getId(MySqlManager::USER_TABLE), 'description', 'led')
+			);
+		}
+		/*
+		else
+		{
+			$this->asCodeInfos[$iCodeId]['code'] = array_unique(array_merge($this->asCodeInfos[$iCodeId]['code'], $asInfos['code']));
+		}
+		*/
+	}
+
+	private function setUserInfo($iUserId)
+	{
+		if(!array_key_exists($iUserId, $this->asUserInfos))
+		{
+			$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 incItemRank($iItemId, $iRank)
+	{
+		if(array_key_exists($iItemId, $this->asItemRanks))
+		{
+			$this->asItemRanks[$iItemId] += $iRank;
+		}
+		else
+		{
+			$this->asItemRanks[$iItemId] = $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->setTrace(false); + if(!$this->oConnection) + { + $this->addError('bug connection'); + $this->sDbState = self::DB_NO_CONN; + } + else + { + if(!mysql_select_db(self::DB_NAME, $this->oConnection)) + { + $this->addError('bug selecting database. Installing...'); + $this->sDbState = self::DB_NO_DATA; + } + } + } + + public function __destruct() + { + parent::__destruct(); + mysql_close($this->oConnection); + } + + 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); + + //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); + } + + 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->addError("\nErreur SQL : \n".str_replace("\t", "", $sQuery)."\n\n".str_replace(array("\t", "\n"), "", mysql_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)) + { + 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 mysql_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 static function cleanSql(&$oData) + { + self::cleanData($oData, 'mysql_real_escape_string'); + $oData = self::cleanData($oData, 'mysql_real_escape_string'); + } + + 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 static function cleanData($oData, $sCleaningFunc) + { + if(!is_array($oData)) + { + $test = call_user_func($sCleaningFunc, $oData); + return $test; + } + 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); + } + } +} + +/** + * 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 = MySqlManager::cleanData($asData, 'stripslashes'); + } + } + + 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 + $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) + { + if($iWidth > $iHeight) + { + $iResizedWidth = $iMaxWidth; + $iResizedHeight = ($iResizedWidth / $iWidth) * $iHeight; + } + else + { + $iResizedHeight = $iMaxHeight; + $iResizedWidth = ($iResizedHeight / $iHeight) * $iWidth; + } + } + else + { + $iResizedWidth = $iWidth; + $iResizedHeight = $iHeight; + } + $asResult['width'] = $iResizedWidth; + $asResult['height'] = $iResizedHeight; + + //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; + } + $asResult['out'] = $sOutPath; + } + else $asResult['error'] = 'Wrong file type'; + + if($bDeleteIn && $asResult['error']=='') unlink($sInPath); + + return $asResult; + } +} + +/* 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, date('d/m/Y H:m:i').' - '.$sTitle, false, 'log'); +} + +?> \ No newline at end of file diff --git a/docs/tmp/.svn/all-wcprops b/docs/tmp/.svn/all-wcprops new file mode 100644 index 0000000..89dfb6e --- /dev/null +++ b/docs/tmp/.svn/all-wcprops @@ -0,0 +1,5 @@ +K 25 +svn:wc:ra_dav:version-url +V 32 +/svn/!svn/ver/132/trunk/docs/tmp +END diff --git a/docs/tmp/.svn/entries b/docs/tmp/.svn/entries new file mode 100644 index 0000000..6508101 --- /dev/null +++ b/docs/tmp/.svn/entries @@ -0,0 +1,28 @@ +10 + +dir +135 +https://databap.googlecode.com/svn/trunk/docs/tmp +https://databap.googlecode.com/svn + + + +2012-01-09T17:32:47.381675Z +132 +francois.lutran + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +4b25baf8-fae0-fdd7-8fdf-ced755d0da00 + diff --git a/docs/tmp/51200e9d10eef.jpg b/docs/tmp/51200e9d10eef.jpg new file mode 100644 index 0000000..0718adc Binary files /dev/null and b/docs/tmp/51200e9d10eef.jpg differ diff --git a/docs/tmp/5122179e53918.jpg b/docs/tmp/5122179e53918.jpg new file mode 100644 index 0000000..8f7f90f Binary files /dev/null and b/docs/tmp/5122179e53918.jpg differ diff --git a/docs/tmp/51222ba9523c6.jpg b/docs/tmp/51222ba9523c6.jpg new file mode 100644 index 0000000..87d8587 Binary files /dev/null and b/docs/tmp/51222ba9523c6.jpg differ diff --git a/docs/tmp/51222bb4255ef.jpg b/docs/tmp/51222bb4255ef.jpg new file mode 100644 index 0000000..1216ef1 Binary files /dev/null and b/docs/tmp/51222bb4255ef.jpg differ diff --git a/docs/tmp/51222bca72a32.gif b/docs/tmp/51222bca72a32.gif new file mode 100644 index 0000000..9966960 Binary files /dev/null and b/docs/tmp/51222bca72a32.gif differ diff --git a/docs/tmp/51222d166cb41.jpg b/docs/tmp/51222d166cb41.jpg new file mode 100644 index 0000000..880b936 Binary files /dev/null and b/docs/tmp/51222d166cb41.jpg differ diff --git a/docs/tmp/51222d2aa6041.jpg b/docs/tmp/51222d2aa6041.jpg new file mode 100644 index 0000000..d6279bd Binary files /dev/null and b/docs/tmp/51222d2aa6041.jpg differ diff --git a/docs/tmp/51222e3399145.jpg b/docs/tmp/51222e3399145.jpg new file mode 100644 index 0000000..1d49f82 Binary files /dev/null and b/docs/tmp/51222e3399145.jpg differ diff --git a/docs/tmp/51222f3a1f9a5.jpg b/docs/tmp/51222f3a1f9a5.jpg new file mode 100644 index 0000000..df153c5 Binary files /dev/null and b/docs/tmp/51222f3a1f9a5.jpg differ diff --git a/docs/tmp/51222f82511fb.jpg b/docs/tmp/51222f82511fb.jpg new file mode 100644 index 0000000..b7eb48e Binary files /dev/null and b/docs/tmp/51222f82511fb.jpg differ diff --git a/docs/tmp/512230be8daf0.jpg b/docs/tmp/512230be8daf0.jpg new file mode 100644 index 0000000..8a68aec Binary files /dev/null and b/docs/tmp/512230be8daf0.jpg differ diff --git a/docs/tmp/5122441cd9981.jpg b/docs/tmp/5122441cd9981.jpg new file mode 100644 index 0000000..bf325d3 Binary files /dev/null and b/docs/tmp/5122441cd9981.jpg differ diff --git a/docs/tmp/5122443197aa0.jpg b/docs/tmp/5122443197aa0.jpg new file mode 100644 index 0000000..643a612 Binary files /dev/null and b/docs/tmp/5122443197aa0.jpg differ diff --git a/docs/tmp/512250295b50a.jpg b/docs/tmp/512250295b50a.jpg new file mode 100644 index 0000000..a940500 Binary files /dev/null and b/docs/tmp/512250295b50a.jpg differ diff --git a/docs/tmp/51225731a0a16.jpg b/docs/tmp/51225731a0a16.jpg new file mode 100644 index 0000000..6e06fef Binary files /dev/null and b/docs/tmp/51225731a0a16.jpg differ diff --git a/docs/tmp/51226168d8e80.gif b/docs/tmp/51226168d8e80.gif new file mode 100644 index 0000000..a4c4008 Binary files /dev/null and b/docs/tmp/51226168d8e80.gif differ diff --git a/docs/tmp/5123411bee8da.jpg b/docs/tmp/5123411bee8da.jpg new file mode 100644 index 0000000..0914c3e Binary files /dev/null and b/docs/tmp/5123411bee8da.jpg differ diff --git a/docs/tmp/512348c3c39b9.jpg b/docs/tmp/512348c3c39b9.jpg new file mode 100644 index 0000000..6cd65c4 Binary files /dev/null and b/docs/tmp/512348c3c39b9.jpg differ diff --git a/docs/tmp/512348e7ed6f8.jpg b/docs/tmp/512348e7ed6f8.jpg new file mode 100644 index 0000000..69e9402 Binary files /dev/null and b/docs/tmp/512348e7ed6f8.jpg differ diff --git a/docs/tmp/512349257e8df.jpg b/docs/tmp/512349257e8df.jpg new file mode 100644 index 0000000..3a24ce5 Binary files /dev/null and b/docs/tmp/512349257e8df.jpg differ diff --git a/docs/tmp/51235842c34f3.jpg b/docs/tmp/51235842c34f3.jpg new file mode 100644 index 0000000..19a31b4 Binary files /dev/null and b/docs/tmp/51235842c34f3.jpg differ diff --git a/docs/tmp/5123b09fa9851.jpg b/docs/tmp/5123b09fa9851.jpg new file mode 100644 index 0000000..3d5bf4e Binary files /dev/null and b/docs/tmp/5123b09fa9851.jpg differ diff --git a/docs/tmp/5123b154796e8.jpg b/docs/tmp/5123b154796e8.jpg new file mode 100644 index 0000000..2673706 Binary files /dev/null and b/docs/tmp/5123b154796e8.jpg differ diff --git a/docs/tmp/5124949dd92db.jpg b/docs/tmp/5124949dd92db.jpg new file mode 100644 index 0000000..fc9c30b Binary files /dev/null and b/docs/tmp/5124949dd92db.jpg differ diff --git a/docs/tmp/512494b65dee0.png b/docs/tmp/512494b65dee0.png new file mode 100644 index 0000000..960bb69 Binary files /dev/null and b/docs/tmp/512494b65dee0.png differ diff --git a/docs/tmp/5124a51198406.jpg b/docs/tmp/5124a51198406.jpg new file mode 100644 index 0000000..4610db2 Binary files /dev/null and b/docs/tmp/5124a51198406.jpg differ diff --git a/docs/tmp/5124a6a382410.jpg b/docs/tmp/5124a6a382410.jpg new file mode 100644 index 0000000..9f203e4 Binary files /dev/null and b/docs/tmp/5124a6a382410.jpg differ diff --git a/docs/tmp/5124afbd77dcb.jpg b/docs/tmp/5124afbd77dcb.jpg new file mode 100644 index 0000000..6a1edfa Binary files /dev/null and b/docs/tmp/5124afbd77dcb.jpg differ diff --git a/docs/tmp/5124b01e8b4db.jpg b/docs/tmp/5124b01e8b4db.jpg new file mode 100644 index 0000000..ce07f77 Binary files /dev/null and b/docs/tmp/5124b01e8b4db.jpg differ diff --git a/docs/tmp/5124b034ba3d7.jpg b/docs/tmp/5124b034ba3d7.jpg new file mode 100644 index 0000000..0e64eda Binary files /dev/null and b/docs/tmp/5124b034ba3d7.jpg differ diff --git a/docs/tmp/5124b051dcc0c.jpg b/docs/tmp/5124b051dcc0c.jpg new file mode 100644 index 0000000..877c054 Binary files /dev/null and b/docs/tmp/5124b051dcc0c.jpg differ diff --git a/docs/tmp/5124b1256ea54.jpg b/docs/tmp/5124b1256ea54.jpg new file mode 100644 index 0000000..3501164 Binary files /dev/null and b/docs/tmp/5124b1256ea54.jpg differ diff --git a/docs/tmp/5124b4c2666a9.jpg b/docs/tmp/5124b4c2666a9.jpg new file mode 100644 index 0000000..514da0c Binary files /dev/null and b/docs/tmp/5124b4c2666a9.jpg differ diff --git a/docs/tmp/5124c58f040e1.jpg b/docs/tmp/5124c58f040e1.jpg new file mode 100644 index 0000000..1838959 Binary files /dev/null and b/docs/tmp/5124c58f040e1.jpg differ diff --git a/docs/tmp/5124cc837cb99.jpg b/docs/tmp/5124cc837cb99.jpg new file mode 100644 index 0000000..e9f839e Binary files /dev/null and b/docs/tmp/5124cc837cb99.jpg differ diff --git a/docs/tmp/5124d18935745.jpg b/docs/tmp/5124d18935745.jpg new file mode 100644 index 0000000..6d44ed1 Binary files /dev/null and b/docs/tmp/5124d18935745.jpg differ diff --git a/docs/tmp/5124de3a12731.jpg b/docs/tmp/5124de3a12731.jpg new file mode 100644 index 0000000..acb9845 Binary files /dev/null and b/docs/tmp/5124de3a12731.jpg differ diff --git a/docs/tmp/5124e12607b72.jpg b/docs/tmp/5124e12607b72.jpg new file mode 100644 index 0000000..ceeb5e2 Binary files /dev/null and b/docs/tmp/5124e12607b72.jpg differ diff --git a/docs/tmp/5124e19e6c8a3.jpg b/docs/tmp/5124e19e6c8a3.jpg new file mode 100644 index 0000000..9ced711 Binary files /dev/null and b/docs/tmp/5124e19e6c8a3.jpg differ diff --git a/docs/tmp/5124e4e81bcd7.jpg b/docs/tmp/5124e4e81bcd7.jpg new file mode 100644 index 0000000..ebf71a0 Binary files /dev/null and b/docs/tmp/5124e4e81bcd7.jpg differ diff --git a/docs/tmp/5124e54d50d37.jpg b/docs/tmp/5124e54d50d37.jpg new file mode 100644 index 0000000..b79e790 Binary files /dev/null and b/docs/tmp/5124e54d50d37.jpg differ diff --git a/docs/tmp/5124e5f9d4f12.jpg b/docs/tmp/5124e5f9d4f12.jpg new file mode 100644 index 0000000..7a47898 Binary files /dev/null and b/docs/tmp/5124e5f9d4f12.jpg differ diff --git a/docs/tmp/5124ef4a496c5.jpg b/docs/tmp/5124ef4a496c5.jpg new file mode 100644 index 0000000..394064e Binary files /dev/null and b/docs/tmp/5124ef4a496c5.jpg differ diff --git a/docs/tmp/5124f69d4e12c.jpg b/docs/tmp/5124f69d4e12c.jpg new file mode 100644 index 0000000..193d7a3 Binary files /dev/null and b/docs/tmp/5124f69d4e12c.jpg differ diff --git a/docs/tmp/5124f7b56d295.jpg b/docs/tmp/5124f7b56d295.jpg new file mode 100644 index 0000000..cc04f11 Binary files /dev/null and b/docs/tmp/5124f7b56d295.jpg differ diff --git a/docs/tmp/5124f8f6c5dbf.jpg b/docs/tmp/5124f8f6c5dbf.jpg new file mode 100644 index 0000000..b87af87 Binary files /dev/null and b/docs/tmp/5124f8f6c5dbf.jpg differ diff --git a/docs/tmp/51250174a15cc.jpg b/docs/tmp/51250174a15cc.jpg new file mode 100644 index 0000000..01850d7 Binary files /dev/null and b/docs/tmp/51250174a15cc.jpg differ diff --git a/docs/tmp/5125ecc872fd3.jpg b/docs/tmp/5125ecc872fd3.jpg new file mode 100644 index 0000000..8a9523f Binary files /dev/null and b/docs/tmp/5125ecc872fd3.jpg differ diff --git a/docs/tmp/5125ecfb0b852.jpg b/docs/tmp/5125ecfb0b852.jpg new file mode 100644 index 0000000..538be94 Binary files /dev/null and b/docs/tmp/5125ecfb0b852.jpg differ diff --git a/docs/tmp/5125eff12b177.jpg b/docs/tmp/5125eff12b177.jpg new file mode 100644 index 0000000..f3180b5 Binary files /dev/null and b/docs/tmp/5125eff12b177.jpg differ diff --git a/docs/tmp/512623643e681.jpg b/docs/tmp/512623643e681.jpg new file mode 100644 index 0000000..54c6915 Binary files /dev/null and b/docs/tmp/512623643e681.jpg differ diff --git a/docs/tmp/51262424a84fd.jpg b/docs/tmp/51262424a84fd.jpg new file mode 100644 index 0000000..313d69e Binary files /dev/null and b/docs/tmp/51262424a84fd.jpg differ diff --git a/docs/tmp/512736a045953.jpg b/docs/tmp/512736a045953.jpg new file mode 100644 index 0000000..1dbf689 Binary files /dev/null and b/docs/tmp/512736a045953.jpg differ diff --git a/docs/tmp/5127371da9fef.jpg b/docs/tmp/5127371da9fef.jpg new file mode 100644 index 0000000..6175800 Binary files /dev/null and b/docs/tmp/5127371da9fef.jpg differ diff --git a/docs/tmp/512745700b26d.jpg b/docs/tmp/512745700b26d.jpg new file mode 100644 index 0000000..dbfa101 Binary files /dev/null and b/docs/tmp/512745700b26d.jpg differ diff --git a/docs/tmp/51274645dae5a.jpg b/docs/tmp/51274645dae5a.jpg new file mode 100644 index 0000000..b544469 Binary files /dev/null and b/docs/tmp/51274645dae5a.jpg differ diff --git a/docs/tmp/512758ff4fd58.jpg b/docs/tmp/512758ff4fd58.jpg new file mode 100644 index 0000000..f9b23f2 Binary files /dev/null and b/docs/tmp/512758ff4fd58.jpg differ diff --git a/docs/tmp/512774a9b8f57.jpg b/docs/tmp/512774a9b8f57.jpg new file mode 100644 index 0000000..31057fc Binary files /dev/null and b/docs/tmp/512774a9b8f57.jpg differ diff --git a/docs/tmp/512776129fa36.jpg b/docs/tmp/512776129fa36.jpg new file mode 100644 index 0000000..d72ded0 Binary files /dev/null and b/docs/tmp/512776129fa36.jpg differ diff --git a/docs/tmp/5127762fcb059.jpg b/docs/tmp/5127762fcb059.jpg new file mode 100644 index 0000000..3be024a Binary files /dev/null and b/docs/tmp/5127762fcb059.jpg differ diff --git a/docs/tmp/512781fce613f.gif b/docs/tmp/512781fce613f.gif new file mode 100644 index 0000000..f64958f Binary files /dev/null and b/docs/tmp/512781fce613f.gif differ diff --git a/docs/tmp/512782a342a36.jpg b/docs/tmp/512782a342a36.jpg new file mode 100644 index 0000000..a8ad60c Binary files /dev/null and b/docs/tmp/512782a342a36.jpg differ diff --git a/docs/tmp/5127833fef5c2.jpg b/docs/tmp/5127833fef5c2.jpg new file mode 100644 index 0000000..84545c1 Binary files /dev/null and b/docs/tmp/5127833fef5c2.jpg differ diff --git a/docs/tmp/5127844faa7ce.jpg b/docs/tmp/5127844faa7ce.jpg new file mode 100644 index 0000000..82d5b4b Binary files /dev/null and b/docs/tmp/5127844faa7ce.jpg differ diff --git a/docs/tmp/51279939d3639.jpg b/docs/tmp/51279939d3639.jpg new file mode 100644 index 0000000..b2c812d Binary files /dev/null and b/docs/tmp/51279939d3639.jpg differ diff --git a/docs/tmp/51279b0674d7f.jpg b/docs/tmp/51279b0674d7f.jpg new file mode 100644 index 0000000..e21dcd7 Binary files /dev/null and b/docs/tmp/51279b0674d7f.jpg differ diff --git a/docs/tmp/5127a2c4dcf7c.jpg b/docs/tmp/5127a2c4dcf7c.jpg new file mode 100644 index 0000000..e6a0e39 Binary files /dev/null and b/docs/tmp/5127a2c4dcf7c.jpg differ diff --git a/docs/tmp/5127a2f6ac9de.jpg b/docs/tmp/5127a2f6ac9de.jpg new file mode 100644 index 0000000..f2db043 Binary files /dev/null and b/docs/tmp/5127a2f6ac9de.jpg differ diff --git a/docs/tmp/51291ddb2a478.jpg b/docs/tmp/51291ddb2a478.jpg new file mode 100644 index 0000000..2150d56 Binary files /dev/null and b/docs/tmp/51291ddb2a478.jpg differ diff --git a/docs/tmp/512b2ee02a481.jpg b/docs/tmp/512b2ee02a481.jpg new file mode 100644 index 0000000..dfb64c0 Binary files /dev/null and b/docs/tmp/512b2ee02a481.jpg differ diff --git a/docs/tmp/512b2fa9e742d.jpg b/docs/tmp/512b2fa9e742d.jpg new file mode 100644 index 0000000..e403b56 Binary files /dev/null and b/docs/tmp/512b2fa9e742d.jpg differ diff --git a/docs/tmp/512b30438a4e1.jpg b/docs/tmp/512b30438a4e1.jpg new file mode 100644 index 0000000..c26e6fb Binary files /dev/null and b/docs/tmp/512b30438a4e1.jpg differ diff --git a/docs/tmp/512b3e820e431.jpg b/docs/tmp/512b3e820e431.jpg new file mode 100644 index 0000000..cb65e8c Binary files /dev/null and b/docs/tmp/512b3e820e431.jpg differ diff --git a/docs/tmp/512b40846206a.jpg b/docs/tmp/512b40846206a.jpg new file mode 100644 index 0000000..f67bb85 Binary files /dev/null and b/docs/tmp/512b40846206a.jpg differ diff --git a/docs/tmp/512b43507bfda.jpg b/docs/tmp/512b43507bfda.jpg new file mode 100644 index 0000000..df57195 Binary files /dev/null and b/docs/tmp/512b43507bfda.jpg differ diff --git a/docs/tmp/512b5c64b73e6.jpg b/docs/tmp/512b5c64b73e6.jpg new file mode 100644 index 0000000..f3331d6 Binary files /dev/null and b/docs/tmp/512b5c64b73e6.jpg differ diff --git a/docs/tmp/512b682bb57ac.jpg b/docs/tmp/512b682bb57ac.jpg new file mode 100644 index 0000000..5b63413 Binary files /dev/null and b/docs/tmp/512b682bb57ac.jpg differ diff --git a/docs/tmp/512b77e01a090.jpg b/docs/tmp/512b77e01a090.jpg new file mode 100644 index 0000000..06145c2 Binary files /dev/null and b/docs/tmp/512b77e01a090.jpg differ diff --git a/docs/tmp/512b7883c9f1c.jpg b/docs/tmp/512b7883c9f1c.jpg new file mode 100644 index 0000000..0e717a8 Binary files /dev/null and b/docs/tmp/512b7883c9f1c.jpg differ diff --git a/docs/tmp/512b79d2b804e.jpg b/docs/tmp/512b79d2b804e.jpg new file mode 100644 index 0000000..e5db44d Binary files /dev/null and b/docs/tmp/512b79d2b804e.jpg differ diff --git a/docs/tmp/512b82c9cb9d0.jpg b/docs/tmp/512b82c9cb9d0.jpg new file mode 100644 index 0000000..03df24c Binary files /dev/null and b/docs/tmp/512b82c9cb9d0.jpg differ diff --git a/docs/tmp/512b8a44ee924.jpg b/docs/tmp/512b8a44ee924.jpg new file mode 100644 index 0000000..bfc87db Binary files /dev/null and b/docs/tmp/512b8a44ee924.jpg differ diff --git a/docs/tmp/512b8b76ed507.jpg b/docs/tmp/512b8b76ed507.jpg new file mode 100644 index 0000000..5bcd1bb Binary files /dev/null and b/docs/tmp/512b8b76ed507.jpg differ diff --git a/docs/tmp/512b8d1599f4d.jpg b/docs/tmp/512b8d1599f4d.jpg new file mode 100644 index 0000000..e76f37c Binary files /dev/null and b/docs/tmp/512b8d1599f4d.jpg differ diff --git a/docs/tmp/512b8fdb5b354.jpg b/docs/tmp/512b8fdb5b354.jpg new file mode 100644 index 0000000..d0d53dd Binary files /dev/null and b/docs/tmp/512b8fdb5b354.jpg differ diff --git a/docs/tmp/512b93b7c83f2.png b/docs/tmp/512b93b7c83f2.png new file mode 100644 index 0000000..49efeb6 Binary files /dev/null and b/docs/tmp/512b93b7c83f2.png differ diff --git a/docs/tmp/512cc3ada084f.jpg b/docs/tmp/512cc3ada084f.jpg new file mode 100644 index 0000000..10eaedf Binary files /dev/null and b/docs/tmp/512cc3ada084f.jpg differ diff --git a/docs/tmp/512cc865628a7.jpg b/docs/tmp/512cc865628a7.jpg new file mode 100644 index 0000000..c66a289 Binary files /dev/null and b/docs/tmp/512cc865628a7.jpg differ diff --git a/docs/tmp/512cc86ed13c9.jpg b/docs/tmp/512cc86ed13c9.jpg new file mode 100644 index 0000000..e8306a6 Binary files /dev/null and b/docs/tmp/512cc86ed13c9.jpg differ diff --git a/docs/tmp/512cd2d1d1ed2.jpg b/docs/tmp/512cd2d1d1ed2.jpg new file mode 100644 index 0000000..572ed37 Binary files /dev/null and b/docs/tmp/512cd2d1d1ed2.jpg differ diff --git a/docs/tmp/512cdaf04bf5f.jpg b/docs/tmp/512cdaf04bf5f.jpg new file mode 100644 index 0000000..871fb07 Binary files /dev/null and b/docs/tmp/512cdaf04bf5f.jpg differ diff --git a/docs/tmp/512cee18a5ed2.jpg b/docs/tmp/512cee18a5ed2.jpg new file mode 100644 index 0000000..feb2a2d Binary files /dev/null and b/docs/tmp/512cee18a5ed2.jpg differ diff --git a/docs/tmp/512dcf60d655f.jpg b/docs/tmp/512dcf60d655f.jpg new file mode 100644 index 0000000..a5f605c Binary files /dev/null and b/docs/tmp/512dcf60d655f.jpg differ diff --git a/docs/tmp/512dda665215e.jpg b/docs/tmp/512dda665215e.jpg new file mode 100644 index 0000000..6d95c71 Binary files /dev/null and b/docs/tmp/512dda665215e.jpg differ diff --git a/docs/tmp/512ddc1c08b6e.jpg b/docs/tmp/512ddc1c08b6e.jpg new file mode 100644 index 0000000..c18ad0c Binary files /dev/null and b/docs/tmp/512ddc1c08b6e.jpg differ diff --git a/docs/tmp/512ddc70d9ee7.jpg b/docs/tmp/512ddc70d9ee7.jpg new file mode 100644 index 0000000..900c620 Binary files /dev/null and b/docs/tmp/512ddc70d9ee7.jpg differ diff --git a/docs/tmp/512de5526da85.jpg b/docs/tmp/512de5526da85.jpg new file mode 100644 index 0000000..92a3a98 Binary files /dev/null and b/docs/tmp/512de5526da85.jpg differ diff --git a/docs/tmp/512e0a8297954.jpg b/docs/tmp/512e0a8297954.jpg new file mode 100644 index 0000000..ce6b7d4 Binary files /dev/null and b/docs/tmp/512e0a8297954.jpg differ diff --git a/docs/tmp/512e0acf891ee.jpg b/docs/tmp/512e0acf891ee.jpg new file mode 100644 index 0000000..fff1e47 Binary files /dev/null and b/docs/tmp/512e0acf891ee.jpg differ diff --git a/docs/tmp/512e0b57d295d.jpg b/docs/tmp/512e0b57d295d.jpg new file mode 100644 index 0000000..949aa64 Binary files /dev/null and b/docs/tmp/512e0b57d295d.jpg differ diff --git a/docs/tmp/512f12e90416f.png b/docs/tmp/512f12e90416f.png new file mode 100644 index 0000000..ffda1bb Binary files /dev/null and b/docs/tmp/512f12e90416f.png differ diff --git a/docs/tmp/512f13712b14f.png b/docs/tmp/512f13712b14f.png new file mode 100644 index 0000000..33d240b Binary files /dev/null and b/docs/tmp/512f13712b14f.png differ diff --git a/docs/tmp/512f34337c553.jpg b/docs/tmp/512f34337c553.jpg new file mode 100644 index 0000000..3224078 Binary files /dev/null and b/docs/tmp/512f34337c553.jpg differ diff --git a/docs/tmp/512f3444efcc0.jpg b/docs/tmp/512f3444efcc0.jpg new file mode 100644 index 0000000..0575424 Binary files /dev/null and b/docs/tmp/512f3444efcc0.jpg differ diff --git a/docs/tmp/512f35070bd37.jpg b/docs/tmp/512f35070bd37.jpg new file mode 100644 index 0000000..494a1fb Binary files /dev/null and b/docs/tmp/512f35070bd37.jpg differ diff --git a/docs/tmp/512f3912a898a.jpg b/docs/tmp/512f3912a898a.jpg new file mode 100644 index 0000000..793c32b Binary files /dev/null and b/docs/tmp/512f3912a898a.jpg differ diff --git a/docs/tmp/512f3a6943136.jpg b/docs/tmp/512f3a6943136.jpg new file mode 100644 index 0000000..045c093 Binary files /dev/null and b/docs/tmp/512f3a6943136.jpg differ diff --git a/docs/tmp/512f3be4a57d6.jpg b/docs/tmp/512f3be4a57d6.jpg new file mode 100644 index 0000000..68a8555 Binary files /dev/null and b/docs/tmp/512f3be4a57d6.jpg differ diff --git a/docs/tmp/512f67763d11b.jpg b/docs/tmp/512f67763d11b.jpg new file mode 100644 index 0000000..29f223c Binary files /dev/null and b/docs/tmp/512f67763d11b.jpg differ diff --git a/docs/tmp/512f67d7dd97b.jpg b/docs/tmp/512f67d7dd97b.jpg new file mode 100644 index 0000000..8268c55 Binary files /dev/null and b/docs/tmp/512f67d7dd97b.jpg differ diff --git a/docs/tmp/512f6ab228f84.png b/docs/tmp/512f6ab228f84.png new file mode 100644 index 0000000..4362ede Binary files /dev/null and b/docs/tmp/512f6ab228f84.png differ diff --git a/docs/tmp/512f6cad9dc0c.jpg b/docs/tmp/512f6cad9dc0c.jpg new file mode 100644 index 0000000..fd8f40e Binary files /dev/null and b/docs/tmp/512f6cad9dc0c.jpg differ diff --git a/docs/tmp/512f847778fc2.jpg b/docs/tmp/512f847778fc2.jpg new file mode 100644 index 0000000..7bbfc2c Binary files /dev/null and b/docs/tmp/512f847778fc2.jpg differ diff --git a/docs/tmp/512f8b53602f3.jpg b/docs/tmp/512f8b53602f3.jpg new file mode 100644 index 0000000..73b7210 Binary files /dev/null and b/docs/tmp/512f8b53602f3.jpg differ diff --git a/docs/tmp/51306cfc66955.jpg b/docs/tmp/51306cfc66955.jpg new file mode 100644 index 0000000..86abea1 Binary files /dev/null and b/docs/tmp/51306cfc66955.jpg differ diff --git a/docs/tmp/51306d91f4211.jpg b/docs/tmp/51306d91f4211.jpg new file mode 100644 index 0000000..882c129 Binary files /dev/null and b/docs/tmp/51306d91f4211.jpg differ diff --git a/docs/tmp/513081dceffa8.jpg b/docs/tmp/513081dceffa8.jpg new file mode 100644 index 0000000..656dcd6 Binary files /dev/null and b/docs/tmp/513081dceffa8.jpg differ diff --git a/docs/tmp/513086b7b224c.jpg b/docs/tmp/513086b7b224c.jpg new file mode 100644 index 0000000..9861420 Binary files /dev/null and b/docs/tmp/513086b7b224c.jpg differ diff --git a/docs/tmp/5130a1e2bca23.jpg b/docs/tmp/5130a1e2bca23.jpg new file mode 100644 index 0000000..616bfbd Binary files /dev/null and b/docs/tmp/5130a1e2bca23.jpg differ diff --git a/docs/tmp/5130b3bc7e0d0.jpg b/docs/tmp/5130b3bc7e0d0.jpg new file mode 100644 index 0000000..dbe19ca Binary files /dev/null and b/docs/tmp/5130b3bc7e0d0.jpg differ diff --git a/docs/tmp/5130ba5b248b4.jpg b/docs/tmp/5130ba5b248b4.jpg new file mode 100644 index 0000000..ca34116 Binary files /dev/null and b/docs/tmp/5130ba5b248b4.jpg differ diff --git a/docs/tmp/5130ba89c5fb6.jpg b/docs/tmp/5130ba89c5fb6.jpg new file mode 100644 index 0000000..4f5c551 Binary files /dev/null and b/docs/tmp/5130ba89c5fb6.jpg differ diff --git a/docs/tmp/5130bab5798ff.jpg b/docs/tmp/5130bab5798ff.jpg new file mode 100644 index 0000000..7626891 Binary files /dev/null and b/docs/tmp/5130bab5798ff.jpg differ diff --git a/docs/tmp/5130cc412d48c.jpeg b/docs/tmp/5130cc412d48c.jpeg new file mode 100644 index 0000000..dd6d37f Binary files /dev/null and b/docs/tmp/5130cc412d48c.jpeg differ diff --git a/docs/tmp/5130cd6d46994.jpg b/docs/tmp/5130cd6d46994.jpg new file mode 100644 index 0000000..d79a518 Binary files /dev/null and b/docs/tmp/5130cd6d46994.jpg differ diff --git a/docs/tmp/5130cd8a826c1.jpg b/docs/tmp/5130cd8a826c1.jpg new file mode 100644 index 0000000..fc8d0b2 Binary files /dev/null and b/docs/tmp/5130cd8a826c1.jpg differ diff --git a/docs/tmp/5130ce8a752b6.jpeg b/docs/tmp/5130ce8a752b6.jpeg new file mode 100644 index 0000000..1179c90 Binary files /dev/null and b/docs/tmp/5130ce8a752b6.jpeg differ diff --git a/docs/tmp/5130cebec707b.jpg b/docs/tmp/5130cebec707b.jpg new file mode 100644 index 0000000..df738ee Binary files /dev/null and b/docs/tmp/5130cebec707b.jpg differ diff --git a/docs/tmp/5130cf18e3ab6.jpeg b/docs/tmp/5130cf18e3ab6.jpeg new file mode 100644 index 0000000..c607e72 Binary files /dev/null and b/docs/tmp/5130cf18e3ab6.jpeg differ diff --git a/docs/tmp/5130d29ca2cc1.jpg b/docs/tmp/5130d29ca2cc1.jpg new file mode 100644 index 0000000..d98802c Binary files /dev/null and b/docs/tmp/5130d29ca2cc1.jpg differ diff --git a/docs/tmp/5130d2a181929.jpg b/docs/tmp/5130d2a181929.jpg new file mode 100644 index 0000000..c555c69 Binary files /dev/null and b/docs/tmp/5130d2a181929.jpg differ diff --git a/docs/tmp/5130d305234a4.jpg b/docs/tmp/5130d305234a4.jpg new file mode 100644 index 0000000..0b0396f Binary files /dev/null and b/docs/tmp/5130d305234a4.jpg differ diff --git a/docs/tmp/5130d3c39fef2.jpg b/docs/tmp/5130d3c39fef2.jpg new file mode 100644 index 0000000..ee3b058 Binary files /dev/null and b/docs/tmp/5130d3c39fef2.jpg differ diff --git a/docs/tmp/5130d3f92c1e5.jpg b/docs/tmp/5130d3f92c1e5.jpg new file mode 100644 index 0000000..475c668 Binary files /dev/null and b/docs/tmp/5130d3f92c1e5.jpg differ diff --git a/docs/tmp/5134594265937.jpg b/docs/tmp/5134594265937.jpg new file mode 100644 index 0000000..3c483c4 Binary files /dev/null and b/docs/tmp/5134594265937.jpg differ diff --git a/docs/tmp/51345b1c05b08.jpg b/docs/tmp/51345b1c05b08.jpg new file mode 100644 index 0000000..4d5f092 Binary files /dev/null and b/docs/tmp/51345b1c05b08.jpg differ diff --git a/docs/tmp/51345b5feb1b3.jpg b/docs/tmp/51345b5feb1b3.jpg new file mode 100644 index 0000000..62825e7 Binary files /dev/null and b/docs/tmp/51345b5feb1b3.jpg differ diff --git a/docs/tmp/51346c6ebf5a5.jpg b/docs/tmp/51346c6ebf5a5.jpg new file mode 100644 index 0000000..01d74d5 Binary files /dev/null and b/docs/tmp/51346c6ebf5a5.jpg differ diff --git a/docs/tmp/51349f73f24ab.jpg b/docs/tmp/51349f73f24ab.jpg new file mode 100644 index 0000000..79cf43e Binary files /dev/null and b/docs/tmp/51349f73f24ab.jpg differ diff --git a/docs/tmp/51349f86539ec.jpg b/docs/tmp/51349f86539ec.jpg new file mode 100644 index 0000000..ef84f3a Binary files /dev/null and b/docs/tmp/51349f86539ec.jpg differ diff --git a/docs/tmp/5134a0add3bdc.jpg b/docs/tmp/5134a0add3bdc.jpg new file mode 100644 index 0000000..af47f24 Binary files /dev/null and b/docs/tmp/5134a0add3bdc.jpg differ diff --git a/docs/tmp/5134a0e81c8c0.jpg b/docs/tmp/5134a0e81c8c0.jpg new file mode 100644 index 0000000..fd897ed Binary files /dev/null and b/docs/tmp/5134a0e81c8c0.jpg differ diff --git a/docs/tmp/5134a31f967bc.jpg b/docs/tmp/5134a31f967bc.jpg new file mode 100644 index 0000000..8cac249 Binary files /dev/null and b/docs/tmp/5134a31f967bc.jpg differ diff --git a/docs/tmp/5134a684aefdf.jpg b/docs/tmp/5134a684aefdf.jpg new file mode 100644 index 0000000..c0d9e04 Binary files /dev/null and b/docs/tmp/5134a684aefdf.jpg differ diff --git a/docs/tmp/5134a8040b7ee.jpg b/docs/tmp/5134a8040b7ee.jpg new file mode 100644 index 0000000..f6fd200 Binary files /dev/null and b/docs/tmp/5134a8040b7ee.jpg differ diff --git a/docs/tmp/5134b2af0b528.jpg b/docs/tmp/5134b2af0b528.jpg new file mode 100644 index 0000000..259607c Binary files /dev/null and b/docs/tmp/5134b2af0b528.jpg differ diff --git a/docs/tmp/5134c5a009353.jpg b/docs/tmp/5134c5a009353.jpg new file mode 100644 index 0000000..d5124a9 Binary files /dev/null and b/docs/tmp/5134c5a009353.jpg differ diff --git a/docs/tmp/5134c6105a5f1.jpg b/docs/tmp/5134c6105a5f1.jpg new file mode 100644 index 0000000..4baa0eb Binary files /dev/null and b/docs/tmp/5134c6105a5f1.jpg differ diff --git a/docs/tmp/5134d6a1abbee.jpg b/docs/tmp/5134d6a1abbee.jpg new file mode 100644 index 0000000..e89949c Binary files /dev/null and b/docs/tmp/5134d6a1abbee.jpg differ diff --git a/docs/tmp/5135ab85bd0a0.jpg b/docs/tmp/5135ab85bd0a0.jpg new file mode 100644 index 0000000..b5706f9 Binary files /dev/null and b/docs/tmp/5135ab85bd0a0.jpg differ diff --git a/docs/tmp/5135ba7187ee0.jpg b/docs/tmp/5135ba7187ee0.jpg new file mode 100644 index 0000000..67c5a86 Binary files /dev/null and b/docs/tmp/5135ba7187ee0.jpg differ diff --git a/docs/tmp/5135bb68cf905.jpg b/docs/tmp/5135bb68cf905.jpg new file mode 100644 index 0000000..e78489a Binary files /dev/null and b/docs/tmp/5135bb68cf905.jpg differ diff --git a/docs/tmp/5135f56685e9f.jpg b/docs/tmp/5135f56685e9f.jpg new file mode 100644 index 0000000..e4b6456 Binary files /dev/null and b/docs/tmp/5135f56685e9f.jpg differ diff --git a/docs/tmp/5135f5c2aded1.jpg b/docs/tmp/5135f5c2aded1.jpg new file mode 100644 index 0000000..2a90946 Binary files /dev/null and b/docs/tmp/5135f5c2aded1.jpg differ diff --git a/docs/tmp/5135f5d2a4313.jpg b/docs/tmp/5135f5d2a4313.jpg new file mode 100644 index 0000000..3a991a4 Binary files /dev/null and b/docs/tmp/5135f5d2a4313.jpg differ diff --git a/docs/tmp/5135fc901775d.jpg b/docs/tmp/5135fc901775d.jpg new file mode 100644 index 0000000..1883087 Binary files /dev/null and b/docs/tmp/5135fc901775d.jpg differ diff --git a/docs/tmp/513603274d372.jpg b/docs/tmp/513603274d372.jpg new file mode 100644 index 0000000..34fc30a Binary files /dev/null and b/docs/tmp/513603274d372.jpg differ diff --git a/docs/tmp/5136041943c10.jpg b/docs/tmp/5136041943c10.jpg new file mode 100644 index 0000000..c67fe3c Binary files /dev/null and b/docs/tmp/5136041943c10.jpg differ diff --git a/docs/tmp/51371f4d66b8f.jpg b/docs/tmp/51371f4d66b8f.jpg new file mode 100644 index 0000000..72d3a67 Binary files /dev/null and b/docs/tmp/51371f4d66b8f.jpg differ diff --git a/docs/tmp/513737c9a28a6.jpg b/docs/tmp/513737c9a28a6.jpg new file mode 100644 index 0000000..d32cc1b Binary files /dev/null and b/docs/tmp/513737c9a28a6.jpg differ diff --git a/docs/tmp/51373a6337b75.jpg b/docs/tmp/51373a6337b75.jpg new file mode 100644 index 0000000..a5d406d Binary files /dev/null and b/docs/tmp/51373a6337b75.jpg differ diff --git a/docs/tmp/51374d5af04bc.jpg b/docs/tmp/51374d5af04bc.jpg new file mode 100644 index 0000000..9a7cbcd Binary files /dev/null and b/docs/tmp/51374d5af04bc.jpg differ diff --git a/docs/tmp/51374ec918656.jpg b/docs/tmp/51374ec918656.jpg new file mode 100644 index 0000000..2db3cb5 Binary files /dev/null and b/docs/tmp/51374ec918656.jpg differ diff --git a/docs/tmp/513751b2907ea.jpg b/docs/tmp/513751b2907ea.jpg new file mode 100644 index 0000000..b8f72ed Binary files /dev/null and b/docs/tmp/513751b2907ea.jpg differ diff --git a/docs/tmp/51375428c1216.jpg b/docs/tmp/51375428c1216.jpg new file mode 100644 index 0000000..032b9a6 Binary files /dev/null and b/docs/tmp/51375428c1216.jpg differ diff --git a/docs/tmp/513756730fb1a.jpg b/docs/tmp/513756730fb1a.jpg new file mode 100644 index 0000000..032b9a6 Binary files /dev/null and b/docs/tmp/513756730fb1a.jpg differ diff --git a/docs/tmp/5137594f4b046.jpg b/docs/tmp/5137594f4b046.jpg new file mode 100644 index 0000000..1f90059 Binary files /dev/null and b/docs/tmp/5137594f4b046.jpg differ diff --git a/docs/tmp/51375a0b29873.jpg b/docs/tmp/51375a0b29873.jpg new file mode 100644 index 0000000..4716363 Binary files /dev/null and b/docs/tmp/51375a0b29873.jpg differ diff --git a/docs/tmp/51375a130c9fc.jpg b/docs/tmp/51375a130c9fc.jpg new file mode 100644 index 0000000..4716363 Binary files /dev/null and b/docs/tmp/51375a130c9fc.jpg differ diff --git a/docs/tmp/51375a5cbb155.jpg b/docs/tmp/51375a5cbb155.jpg new file mode 100644 index 0000000..3860af2 Binary files /dev/null and b/docs/tmp/51375a5cbb155.jpg differ diff --git a/docs/tmp/51375c3ea963e.jpg b/docs/tmp/51375c3ea963e.jpg new file mode 100644 index 0000000..032b9a6 Binary files /dev/null and b/docs/tmp/51375c3ea963e.jpg differ diff --git a/docs/tmp/513768d47c35c.jpg b/docs/tmp/513768d47c35c.jpg new file mode 100644 index 0000000..c67fe3c Binary files /dev/null and b/docs/tmp/513768d47c35c.jpg differ diff --git a/docs/tmp/513768f8de0e7.jpg b/docs/tmp/513768f8de0e7.jpg new file mode 100644 index 0000000..045b167 Binary files /dev/null and b/docs/tmp/513768f8de0e7.jpg differ diff --git a/docs/tmp/5137692803c86.jpg b/docs/tmp/5137692803c86.jpg new file mode 100644 index 0000000..1068259 Binary files /dev/null and b/docs/tmp/5137692803c86.jpg differ diff --git a/docs/tmp/5137696120fc9.jpg b/docs/tmp/5137696120fc9.jpg new file mode 100644 index 0000000..1713b7e Binary files /dev/null and b/docs/tmp/5137696120fc9.jpg differ diff --git a/docs/tmp/51376a8c58e52.jpg b/docs/tmp/51376a8c58e52.jpg new file mode 100644 index 0000000..4de061e Binary files /dev/null and b/docs/tmp/51376a8c58e52.jpg differ diff --git a/docs/tmp/513770db9f4af.jpg b/docs/tmp/513770db9f4af.jpg new file mode 100644 index 0000000..faf343f Binary files /dev/null and b/docs/tmp/513770db9f4af.jpg differ diff --git a/docs/tmp/51387d5574861.jpg b/docs/tmp/51387d5574861.jpg new file mode 100644 index 0000000..f52f38c Binary files /dev/null and b/docs/tmp/51387d5574861.jpg differ diff --git a/docs/tmp/51387dc715a31.jpg b/docs/tmp/51387dc715a31.jpg new file mode 100644 index 0000000..9a85a41 Binary files /dev/null and b/docs/tmp/51387dc715a31.jpg differ diff --git a/docs/tmp/51388085e08bf.jpg b/docs/tmp/51388085e08bf.jpg new file mode 100644 index 0000000..e953527 Binary files /dev/null and b/docs/tmp/51388085e08bf.jpg differ diff --git a/docs/tmp/5138a23abf549.jpg b/docs/tmp/5138a23abf549.jpg new file mode 100644 index 0000000..a20cd27 Binary files /dev/null and b/docs/tmp/5138a23abf549.jpg differ diff --git a/docs/tmp/5138c7e405864.jpg b/docs/tmp/5138c7e405864.jpg new file mode 100644 index 0000000..291509e Binary files /dev/null and b/docs/tmp/5138c7e405864.jpg differ diff --git a/docs/tmp/5139bff61c976.jpg b/docs/tmp/5139bff61c976.jpg new file mode 100644 index 0000000..63df217 Binary files /dev/null and b/docs/tmp/5139bff61c976.jpg differ diff --git a/docs/tmp/5139e3164487d.jpg b/docs/tmp/5139e3164487d.jpg new file mode 100644 index 0000000..63df217 Binary files /dev/null and b/docs/tmp/5139e3164487d.jpg differ diff --git a/docs/tmp/5139e41a860c0.jpg b/docs/tmp/5139e41a860c0.jpg new file mode 100644 index 0000000..6ba58a6 Binary files /dev/null and b/docs/tmp/5139e41a860c0.jpg differ diff --git a/docs/tmp/5139e51839022.jpg b/docs/tmp/5139e51839022.jpg new file mode 100644 index 0000000..87fbfd0 Binary files /dev/null and b/docs/tmp/5139e51839022.jpg differ diff --git a/docs/tmp/5139e67e06975.jpg b/docs/tmp/5139e67e06975.jpg new file mode 100644 index 0000000..95960f1 Binary files /dev/null and b/docs/tmp/5139e67e06975.jpg differ diff --git a/docs/tmp/5139f13852d6b.jpg b/docs/tmp/5139f13852d6b.jpg new file mode 100644 index 0000000..50152d5 Binary files /dev/null and b/docs/tmp/5139f13852d6b.jpg differ diff --git a/docs/tmp/513a1094f1748.jpg b/docs/tmp/513a1094f1748.jpg new file mode 100644 index 0000000..167c1b2 Binary files /dev/null and b/docs/tmp/513a1094f1748.jpg differ diff --git a/docs/tmp/513a14e67b763.jpg b/docs/tmp/513a14e67b763.jpg new file mode 100644 index 0000000..60b02f7 Binary files /dev/null and b/docs/tmp/513a14e67b763.jpg differ diff --git a/docs/tmp/513a1647ee814.jpg b/docs/tmp/513a1647ee814.jpg new file mode 100644 index 0000000..efeb46e Binary files /dev/null and b/docs/tmp/513a1647ee814.jpg differ diff --git a/docs/tmp/513dbba02b819.jpg b/docs/tmp/513dbba02b819.jpg new file mode 100644 index 0000000..ed256e7 Binary files /dev/null and b/docs/tmp/513dbba02b819.jpg differ diff --git a/docs/tmp/513dcd413da57.jpg b/docs/tmp/513dcd413da57.jpg new file mode 100644 index 0000000..80e7420 Binary files /dev/null and b/docs/tmp/513dcd413da57.jpg differ diff --git a/docs/tmp/513dd96c7ba89.jpg b/docs/tmp/513dd96c7ba89.jpg new file mode 100644 index 0000000..23d4844 Binary files /dev/null and b/docs/tmp/513dd96c7ba89.jpg differ diff --git a/docs/tmp/513de2bebfa0d.jpg b/docs/tmp/513de2bebfa0d.jpg new file mode 100644 index 0000000..f3e92c9 Binary files /dev/null and b/docs/tmp/513de2bebfa0d.jpg differ diff --git a/docs/tmp/513e1273beaa9.jpg b/docs/tmp/513e1273beaa9.jpg new file mode 100644 index 0000000..16b923e Binary files /dev/null and b/docs/tmp/513e1273beaa9.jpg differ diff --git a/docs/tmp/513ef30510f8f.jpg b/docs/tmp/513ef30510f8f.jpg new file mode 100644 index 0000000..3af2763 Binary files /dev/null and b/docs/tmp/513ef30510f8f.jpg differ diff --git a/docs/tmp/513f091f38065.jpg b/docs/tmp/513f091f38065.jpg new file mode 100644 index 0000000..ca0d31c Binary files /dev/null and b/docs/tmp/513f091f38065.jpg differ diff --git a/docs/tmp/513f0f8a7c09f.jpg b/docs/tmp/513f0f8a7c09f.jpg new file mode 100644 index 0000000..d1c967b Binary files /dev/null and b/docs/tmp/513f0f8a7c09f.jpg differ diff --git a/docs/tmp/513f1197e35d9.jpg b/docs/tmp/513f1197e35d9.jpg new file mode 100644 index 0000000..cfe5349 Binary files /dev/null and b/docs/tmp/513f1197e35d9.jpg differ diff --git a/docs/tmp/513f2c57e8e63.jpg b/docs/tmp/513f2c57e8e63.jpg new file mode 100644 index 0000000..6891ab5 Binary files /dev/null and b/docs/tmp/513f2c57e8e63.jpg differ diff --git a/docs/tmp/513f2d86c3fc8.jpg b/docs/tmp/513f2d86c3fc8.jpg new file mode 100644 index 0000000..50b7953 Binary files /dev/null and b/docs/tmp/513f2d86c3fc8.jpg differ diff --git a/docs/tmp/513f2fa2d1361.jpg b/docs/tmp/513f2fa2d1361.jpg new file mode 100644 index 0000000..ac8bd57 Binary files /dev/null and b/docs/tmp/513f2fa2d1361.jpg differ diff --git a/docs/tmp/513f34d6b2617.jpg b/docs/tmp/513f34d6b2617.jpg new file mode 100644 index 0000000..284de50 Binary files /dev/null and b/docs/tmp/513f34d6b2617.jpg differ diff --git a/docs/tmp/513f37883c1b3.jpg b/docs/tmp/513f37883c1b3.jpg new file mode 100644 index 0000000..6dec80d Binary files /dev/null and b/docs/tmp/513f37883c1b3.jpg differ diff --git a/docs/tmp/51404cf9aae9b.jpg b/docs/tmp/51404cf9aae9b.jpg new file mode 100644 index 0000000..e96e246 Binary files /dev/null and b/docs/tmp/51404cf9aae9b.jpg differ diff --git a/docs/tmp/514054954fba7.jpg b/docs/tmp/514054954fba7.jpg new file mode 100644 index 0000000..9c86fae Binary files /dev/null and b/docs/tmp/514054954fba7.jpg differ diff --git a/docs/tmp/514068b55606f.jpg b/docs/tmp/514068b55606f.jpg new file mode 100644 index 0000000..2fe78ed Binary files /dev/null and b/docs/tmp/514068b55606f.jpg differ diff --git a/docs/tmp/51408e855efdd.jpg b/docs/tmp/51408e855efdd.jpg new file mode 100644 index 0000000..7064eec Binary files /dev/null and b/docs/tmp/51408e855efdd.jpg differ diff --git a/docs/tmp/51409a2247e4c.jpg b/docs/tmp/51409a2247e4c.jpg new file mode 100644 index 0000000..7cbf891 Binary files /dev/null and b/docs/tmp/51409a2247e4c.jpg differ diff --git a/docs/tmp/51409ab0c31fc.jpg b/docs/tmp/51409ab0c31fc.jpg new file mode 100644 index 0000000..8df27d7 Binary files /dev/null and b/docs/tmp/51409ab0c31fc.jpg differ diff --git a/docs/tmp/51419a5a8d740.jpg b/docs/tmp/51419a5a8d740.jpg new file mode 100644 index 0000000..87ab327 Binary files /dev/null and b/docs/tmp/51419a5a8d740.jpg differ diff --git a/docs/tmp/5141c3cf76f72.jpg b/docs/tmp/5141c3cf76f72.jpg new file mode 100644 index 0000000..4719224 Binary files /dev/null and b/docs/tmp/5141c3cf76f72.jpg differ diff --git a/docs/tmp/5141c6f2340ad.jpg b/docs/tmp/5141c6f2340ad.jpg new file mode 100644 index 0000000..dbb9a10 Binary files /dev/null and b/docs/tmp/5141c6f2340ad.jpg differ diff --git a/docs/tmp/5141cccef1946.jpg b/docs/tmp/5141cccef1946.jpg new file mode 100644 index 0000000..3cdbaad Binary files /dev/null and b/docs/tmp/5141cccef1946.jpg differ diff --git a/docs/tmp/5141cd7449a5c.jpg b/docs/tmp/5141cd7449a5c.jpg new file mode 100644 index 0000000..3cdbaad Binary files /dev/null and b/docs/tmp/5141cd7449a5c.jpg differ diff --git a/docs/tmp/514308089b164.jpg b/docs/tmp/514308089b164.jpg new file mode 100644 index 0000000..f009b98 Binary files /dev/null and b/docs/tmp/514308089b164.jpg differ diff --git a/docs/tmp/5143235883048.jpg b/docs/tmp/5143235883048.jpg new file mode 100644 index 0000000..c5a46d9 Binary files /dev/null and b/docs/tmp/5143235883048.jpg differ diff --git a/docs/tmp/514323cf3c76c.jpg b/docs/tmp/514323cf3c76c.jpg new file mode 100644 index 0000000..136406b Binary files /dev/null and b/docs/tmp/514323cf3c76c.jpg differ diff --git a/docs/tmp/51432c167553d.jpg b/docs/tmp/51432c167553d.jpg new file mode 100644 index 0000000..a29034c Binary files /dev/null and b/docs/tmp/51432c167553d.jpg differ diff --git a/docs/tmp/51432d26ba2ee.jpg b/docs/tmp/51432d26ba2ee.jpg new file mode 100644 index 0000000..2090ca4 Binary files /dev/null and b/docs/tmp/51432d26ba2ee.jpg differ diff --git a/docs/tmp/514335ad71bf4.jpg b/docs/tmp/514335ad71bf4.jpg new file mode 100644 index 0000000..7804773 Binary files /dev/null and b/docs/tmp/514335ad71bf4.jpg differ diff --git a/docs/tmp/5146f3783558b.jpg b/docs/tmp/5146f3783558b.jpg new file mode 100644 index 0000000..daf9fef Binary files /dev/null and b/docs/tmp/5146f3783558b.jpg differ diff --git a/docs/tmp/514863d330eab.jpg b/docs/tmp/514863d330eab.jpg new file mode 100644 index 0000000..c230a23 Binary files /dev/null and b/docs/tmp/514863d330eab.jpg differ diff --git a/docs/tmp/514865611ff22.jpg b/docs/tmp/514865611ff22.jpg new file mode 100644 index 0000000..6a6851a Binary files /dev/null and b/docs/tmp/514865611ff22.jpg differ diff --git a/docs/tmp/514868ab7d98e.jpg b/docs/tmp/514868ab7d98e.jpg new file mode 100644 index 0000000..494d7f0 Binary files /dev/null and b/docs/tmp/514868ab7d98e.jpg differ diff --git a/docs/tmp/5148704854405.jpg b/docs/tmp/5148704854405.jpg new file mode 100644 index 0000000..1b76b4a Binary files /dev/null and b/docs/tmp/5148704854405.jpg differ diff --git a/docs/tmp/51488612c0207.jpg b/docs/tmp/51488612c0207.jpg new file mode 100644 index 0000000..ee5fea1 Binary files /dev/null and b/docs/tmp/51488612c0207.jpg differ diff --git a/docs/tmp/514991f5cbde1.jpg b/docs/tmp/514991f5cbde1.jpg new file mode 100644 index 0000000..1fa9564 Binary files /dev/null and b/docs/tmp/514991f5cbde1.jpg differ diff --git a/docs/tmp/5149cf6f4638e.jpg b/docs/tmp/5149cf6f4638e.jpg new file mode 100644 index 0000000..910f228 Binary files /dev/null and b/docs/tmp/5149cf6f4638e.jpg differ diff --git a/docs/tmp/5149e1bf684de.jpg b/docs/tmp/5149e1bf684de.jpg new file mode 100644 index 0000000..5fda526 Binary files /dev/null and b/docs/tmp/5149e1bf684de.jpg differ diff --git a/docs/tmp/5149e5395c2b5.jpg b/docs/tmp/5149e5395c2b5.jpg new file mode 100644 index 0000000..5b96269 Binary files /dev/null and b/docs/tmp/5149e5395c2b5.jpg differ diff --git a/docs/tmp/514ad57ccd99c.jpg b/docs/tmp/514ad57ccd99c.jpg new file mode 100644 index 0000000..a76e644 Binary files /dev/null and b/docs/tmp/514ad57ccd99c.jpg differ diff --git a/docs/tmp/514adca8ef6ea.jpg b/docs/tmp/514adca8ef6ea.jpg new file mode 100644 index 0000000..7fafe75 Binary files /dev/null and b/docs/tmp/514adca8ef6ea.jpg differ diff --git a/docs/tmp/514ae0ea344e7.jpg b/docs/tmp/514ae0ea344e7.jpg new file mode 100644 index 0000000..cd7fd2f Binary files /dev/null and b/docs/tmp/514ae0ea344e7.jpg differ diff --git a/docs/tmp/514b35d68c3c9.jpg b/docs/tmp/514b35d68c3c9.jpg new file mode 100644 index 0000000..8beff31 Binary files /dev/null and b/docs/tmp/514b35d68c3c9.jpg differ diff --git a/docs/tmp/51502da4947e8.jpg b/docs/tmp/51502da4947e8.jpg new file mode 100644 index 0000000..4c77b42 Binary files /dev/null and b/docs/tmp/51502da4947e8.jpg differ diff --git a/docs/tmp/51504aefb22e8.jpg b/docs/tmp/51504aefb22e8.jpg new file mode 100644 index 0000000..6eeb14d Binary files /dev/null and b/docs/tmp/51504aefb22e8.jpg differ diff --git a/docs/tmp/51529451d89e0.jpg b/docs/tmp/51529451d89e0.jpg new file mode 100644 index 0000000..ca97c8a Binary files /dev/null and b/docs/tmp/51529451d89e0.jpg differ diff --git a/docs/tmp/51543b300f79b.jpg b/docs/tmp/51543b300f79b.jpg new file mode 100644 index 0000000..f3ffdc6 Binary files /dev/null and b/docs/tmp/51543b300f79b.jpg differ diff --git a/docs/tmp/515454ec86c32.jpg b/docs/tmp/515454ec86c32.jpg new file mode 100644 index 0000000..b41140f Binary files /dev/null and b/docs/tmp/515454ec86c32.jpg differ diff --git a/docs/tmp/51557a5e45865.jpg b/docs/tmp/51557a5e45865.jpg new file mode 100644 index 0000000..b3cddc2 Binary files /dev/null and b/docs/tmp/51557a5e45865.jpg differ diff --git a/docs/tmp/5155980b73eef.jpg b/docs/tmp/5155980b73eef.jpg new file mode 100644 index 0000000..08d0276 Binary files /dev/null and b/docs/tmp/5155980b73eef.jpg differ diff --git a/docs/tmp/5155ba56d244f.jpg b/docs/tmp/5155ba56d244f.jpg new file mode 100644 index 0000000..d0a7464 Binary files /dev/null and b/docs/tmp/5155ba56d244f.jpg differ diff --git a/docs/tmp/515ac33be0988.jpg b/docs/tmp/515ac33be0988.jpg new file mode 100644 index 0000000..3cd6460 Binary files /dev/null and b/docs/tmp/515ac33be0988.jpg differ diff --git a/docs/tmp/515acaa455e79.jpg b/docs/tmp/515acaa455e79.jpg new file mode 100644 index 0000000..9f37ea7 Binary files /dev/null and b/docs/tmp/515acaa455e79.jpg differ diff --git a/docs/tmp/515acb82d5ab8.jpg b/docs/tmp/515acb82d5ab8.jpg new file mode 100644 index 0000000..514969e Binary files /dev/null and b/docs/tmp/515acb82d5ab8.jpg differ diff --git a/docs/tmp/515d33a3e4606.jpg b/docs/tmp/515d33a3e4606.jpg new file mode 100644 index 0000000..476e916 Binary files /dev/null and b/docs/tmp/515d33a3e4606.jpg differ diff --git a/docs/tmp/515d45a7c4972.jpg b/docs/tmp/515d45a7c4972.jpg new file mode 100644 index 0000000..b6309de Binary files /dev/null and b/docs/tmp/515d45a7c4972.jpg differ diff --git a/docs/tmp/515e8e693c1ae.gif b/docs/tmp/515e8e693c1ae.gif new file mode 100644 index 0000000..fa23429 Binary files /dev/null and b/docs/tmp/515e8e693c1ae.gif differ diff --git a/docs/tmp/515ea0f6f37de.jpg b/docs/tmp/515ea0f6f37de.jpg new file mode 100644 index 0000000..baf4bcd Binary files /dev/null and b/docs/tmp/515ea0f6f37de.jpg differ diff --git a/docs/tmp/515eb10d0e057.jpg b/docs/tmp/515eb10d0e057.jpg new file mode 100644 index 0000000..aed6269 Binary files /dev/null and b/docs/tmp/515eb10d0e057.jpg differ diff --git a/docs/tmp/515ebfa768ce3.jpg b/docs/tmp/515ebfa768ce3.jpg new file mode 100644 index 0000000..0dcd7ec Binary files /dev/null and b/docs/tmp/515ebfa768ce3.jpg differ diff --git a/docs/tmp/515ec7e3a21f2.jpg b/docs/tmp/515ec7e3a21f2.jpg new file mode 100644 index 0000000..ea037bf Binary files /dev/null and b/docs/tmp/515ec7e3a21f2.jpg differ diff --git a/docs/tmp/5162b6e1a40a8.jpg b/docs/tmp/5162b6e1a40a8.jpg new file mode 100644 index 0000000..31e98d6 Binary files /dev/null and b/docs/tmp/5162b6e1a40a8.jpg differ diff --git a/docs/tmp/5163e6045a7ba.jpg b/docs/tmp/5163e6045a7ba.jpg new file mode 100644 index 0000000..4c05e68 Binary files /dev/null and b/docs/tmp/5163e6045a7ba.jpg differ diff --git a/docs/tmp/5163e7386fab5.jpg b/docs/tmp/5163e7386fab5.jpg new file mode 100644 index 0000000..6e23382 Binary files /dev/null and b/docs/tmp/5163e7386fab5.jpg differ diff --git a/docs/tmp/5164283015ec4.jpg b/docs/tmp/5164283015ec4.jpg new file mode 100644 index 0000000..3536d4b Binary files /dev/null and b/docs/tmp/5164283015ec4.jpg differ diff --git a/docs/tmp/5167dfba7ce31.jpg b/docs/tmp/5167dfba7ce31.jpg new file mode 100644 index 0000000..e858ca5 Binary files /dev/null and b/docs/tmp/5167dfba7ce31.jpg differ diff --git a/docs/tmp/516bef314be93.jpg b/docs/tmp/516bef314be93.jpg new file mode 100644 index 0000000..5f2d74b Binary files /dev/null and b/docs/tmp/516bef314be93.jpg differ diff --git a/docs/tmp/516fc4a297ffe.jpg b/docs/tmp/516fc4a297ffe.jpg new file mode 100644 index 0000000..6685fc7 Binary files /dev/null and b/docs/tmp/516fc4a297ffe.jpg differ diff --git a/docs/tmp/516ff2353f7cc.jpg b/docs/tmp/516ff2353f7cc.jpg new file mode 100644 index 0000000..04aba24 Binary files /dev/null and b/docs/tmp/516ff2353f7cc.jpg differ diff --git a/docs/tmp/516ff6a37243a.jpg b/docs/tmp/516ff6a37243a.jpg new file mode 100644 index 0000000..c4f3281 Binary files /dev/null and b/docs/tmp/516ff6a37243a.jpg differ diff --git a/docs/tmp/517109139edf5.jpg b/docs/tmp/517109139edf5.jpg new file mode 100644 index 0000000..caf8803 Binary files /dev/null and b/docs/tmp/517109139edf5.jpg differ diff --git a/docs/tmp/51711336ccf13.jpg b/docs/tmp/51711336ccf13.jpg new file mode 100644 index 0000000..5531b00 Binary files /dev/null and b/docs/tmp/51711336ccf13.jpg differ diff --git a/docs/tmp/517113c080359.jpg b/docs/tmp/517113c080359.jpg new file mode 100644 index 0000000..0a41811 Binary files /dev/null and b/docs/tmp/517113c080359.jpg differ diff --git a/docs/tmp/517113f987903.jpg b/docs/tmp/517113f987903.jpg new file mode 100644 index 0000000..ac90359 Binary files /dev/null and b/docs/tmp/517113f987903.jpg differ diff --git a/docs/tmp/5174fd52df44e.jpg b/docs/tmp/5174fd52df44e.jpg new file mode 100644 index 0000000..a996847 Binary files /dev/null and b/docs/tmp/5174fd52df44e.jpg differ diff --git a/docs/tmp/5177d0626e3f3.jpg b/docs/tmp/5177d0626e3f3.jpg new file mode 100644 index 0000000..90433ab Binary files /dev/null and b/docs/tmp/5177d0626e3f3.jpg differ diff --git a/docs/tmp/5177d0d3b841a.jpg b/docs/tmp/5177d0d3b841a.jpg new file mode 100644 index 0000000..e0d95f3 Binary files /dev/null and b/docs/tmp/5177d0d3b841a.jpg differ diff --git a/docs/tmp/5177d132d09d4.jpg b/docs/tmp/5177d132d09d4.jpg new file mode 100644 index 0000000..3769c3c Binary files /dev/null and b/docs/tmp/5177d132d09d4.jpg differ diff --git a/docs/tmp/51793252e00c5.jpg b/docs/tmp/51793252e00c5.jpg new file mode 100644 index 0000000..3e3a194 Binary files /dev/null and b/docs/tmp/51793252e00c5.jpg differ diff --git a/docs/tmp/517a788e6b453.jpg b/docs/tmp/517a788e6b453.jpg new file mode 100644 index 0000000..f3cdcc7 Binary files /dev/null and b/docs/tmp/517a788e6b453.jpg differ diff --git a/docs/tmp/517fc1a4d6a84.jpg b/docs/tmp/517fc1a4d6a84.jpg new file mode 100644 index 0000000..2255993 Binary files /dev/null and b/docs/tmp/517fc1a4d6a84.jpg differ diff --git a/docs/tmp/517fe181976f8.jpg b/docs/tmp/517fe181976f8.jpg new file mode 100644 index 0000000..7c6695f Binary files /dev/null and b/docs/tmp/517fe181976f8.jpg differ diff --git a/docs/tmp/517fe3d89a6b6.jpg b/docs/tmp/517fe3d89a6b6.jpg new file mode 100644 index 0000000..7c6695f Binary files /dev/null and b/docs/tmp/517fe3d89a6b6.jpg differ diff --git a/docs/tmp/517fe41057e19.jpg b/docs/tmp/517fe41057e19.jpg new file mode 100644 index 0000000..2255993 Binary files /dev/null and b/docs/tmp/517fe41057e19.jpg differ diff --git a/docs/tmp/517fe81f8489c.jpg b/docs/tmp/517fe81f8489c.jpg new file mode 100644 index 0000000..d5e4748 Binary files /dev/null and b/docs/tmp/517fe81f8489c.jpg differ diff --git a/docs/tmp/517feaeb79a1a.png b/docs/tmp/517feaeb79a1a.png new file mode 100644 index 0000000..f552c72 Binary files /dev/null and b/docs/tmp/517feaeb79a1a.png differ diff --git a/docs/tmp/518238a5dd290.jpg b/docs/tmp/518238a5dd290.jpg new file mode 100644 index 0000000..d3c4700 Binary files /dev/null and b/docs/tmp/518238a5dd290.jpg differ diff --git a/docs/tmp/51825ef95270a.jpg b/docs/tmp/51825ef95270a.jpg new file mode 100644 index 0000000..d22de3c Binary files /dev/null and b/docs/tmp/51825ef95270a.jpg differ diff --git a/docs/tmp/51826c236cd45.jpg b/docs/tmp/51826c236cd45.jpg new file mode 100644 index 0000000..2d16110 Binary files /dev/null and b/docs/tmp/51826c236cd45.jpg differ diff --git a/docs/tmp/51826c3e6589d.jpg b/docs/tmp/51826c3e6589d.jpg new file mode 100644 index 0000000..80001de Binary files /dev/null and b/docs/tmp/51826c3e6589d.jpg differ diff --git a/docs/tmp/5182769c83e93.jpg b/docs/tmp/5182769c83e93.jpg new file mode 100644 index 0000000..15652d9 Binary files /dev/null and b/docs/tmp/5182769c83e93.jpg differ diff --git a/docs/tmp/5190a77477b1c.jpg b/docs/tmp/5190a77477b1c.jpg new file mode 100644 index 0000000..5858406 Binary files /dev/null and b/docs/tmp/5190a77477b1c.jpg differ diff --git a/docs/tmp/5192302372c9d.jpg b/docs/tmp/5192302372c9d.jpg new file mode 100644 index 0000000..700fd66 Binary files /dev/null and b/docs/tmp/5192302372c9d.jpg differ diff --git a/docs/tmp/5192304b4fefe.jpg b/docs/tmp/5192304b4fefe.jpg new file mode 100644 index 0000000..de92eb4 Binary files /dev/null and b/docs/tmp/5192304b4fefe.jpg differ diff --git a/docs/tmp/51933cf9b9061.jpg b/docs/tmp/51933cf9b9061.jpg new file mode 100644 index 0000000..6c0cb3b Binary files /dev/null and b/docs/tmp/51933cf9b9061.jpg differ diff --git a/docs/tmp/51937d99ba70a.jpg b/docs/tmp/51937d99ba70a.jpg new file mode 100644 index 0000000..dab7452 Binary files /dev/null and b/docs/tmp/51937d99ba70a.jpg differ diff --git a/docs/tmp/51937e24a4781.jpg b/docs/tmp/51937e24a4781.jpg new file mode 100644 index 0000000..bbd2aa4 Binary files /dev/null and b/docs/tmp/51937e24a4781.jpg differ diff --git a/docs/tmp/51937fe84f790.jpg b/docs/tmp/51937fe84f790.jpg new file mode 100644 index 0000000..565a817 Binary files /dev/null and b/docs/tmp/51937fe84f790.jpg differ diff --git a/docs/tmp/5193864355e79.jpg b/docs/tmp/5193864355e79.jpg new file mode 100644 index 0000000..3a929eb Binary files /dev/null and b/docs/tmp/5193864355e79.jpg differ diff --git a/docs/tmp/51939fe2dddcd.jpg b/docs/tmp/51939fe2dddcd.jpg new file mode 100644 index 0000000..d52881c Binary files /dev/null and b/docs/tmp/51939fe2dddcd.jpg differ diff --git a/docs/tmp/5193df3c5be40.png b/docs/tmp/5193df3c5be40.png new file mode 100644 index 0000000..2af13ad Binary files /dev/null and b/docs/tmp/5193df3c5be40.png differ diff --git a/docs/tmp/519489b4cd0e8.jpg b/docs/tmp/519489b4cd0e8.jpg new file mode 100644 index 0000000..77654ef Binary files /dev/null and b/docs/tmp/519489b4cd0e8.jpg differ diff --git a/docs/tmp/5194e25117cc4.jpg b/docs/tmp/5194e25117cc4.jpg new file mode 100644 index 0000000..15e232d Binary files /dev/null and b/docs/tmp/5194e25117cc4.jpg differ diff --git a/docs/tmp/51962f3c0a6fb.gif b/docs/tmp/51962f3c0a6fb.gif new file mode 100644 index 0000000..a42e158 Binary files /dev/null and b/docs/tmp/51962f3c0a6fb.gif differ diff --git a/docs/tmp/51962f4536b8b.gif b/docs/tmp/51962f4536b8b.gif new file mode 100644 index 0000000..b027f94 Binary files /dev/null and b/docs/tmp/51962f4536b8b.gif differ diff --git a/docs/tmp/519b8cd58f4e6.jpg b/docs/tmp/519b8cd58f4e6.jpg new file mode 100644 index 0000000..d5a9794 Binary files /dev/null and b/docs/tmp/519b8cd58f4e6.jpg differ diff --git a/docs/tmp/519ca8fa10dcb.jpg b/docs/tmp/519ca8fa10dcb.jpg new file mode 100644 index 0000000..e69d83d Binary files /dev/null and b/docs/tmp/519ca8fa10dcb.jpg differ diff --git a/docs/tmp/519cab0279789.jpg b/docs/tmp/519cab0279789.jpg new file mode 100644 index 0000000..d45ed43 Binary files /dev/null and b/docs/tmp/519cab0279789.jpg differ diff --git a/docs/tmp/519cacac20cc2.jpg b/docs/tmp/519cacac20cc2.jpg new file mode 100644 index 0000000..c2f8f8d Binary files /dev/null and b/docs/tmp/519cacac20cc2.jpg differ diff --git a/docs/tmp/519cc6983d55c.jpg b/docs/tmp/519cc6983d55c.jpg new file mode 100644 index 0000000..4f46c21 Binary files /dev/null and b/docs/tmp/519cc6983d55c.jpg differ diff --git a/docs/tmp/519f24d8d0406.jpg b/docs/tmp/519f24d8d0406.jpg new file mode 100644 index 0000000..34e3f19 Binary files /dev/null and b/docs/tmp/519f24d8d0406.jpg differ diff --git a/docs/tmp/519f24e0e7f4b.jpg b/docs/tmp/519f24e0e7f4b.jpg new file mode 100644 index 0000000..8daa316 Binary files /dev/null and b/docs/tmp/519f24e0e7f4b.jpg differ diff --git a/docs/tmp/519f721436747.jpg b/docs/tmp/519f721436747.jpg new file mode 100644 index 0000000..84a0c78 Binary files /dev/null and b/docs/tmp/519f721436747.jpg differ diff --git a/docs/tmp/51a363417c5da.jpg b/docs/tmp/51a363417c5da.jpg new file mode 100644 index 0000000..57c4129 Binary files /dev/null and b/docs/tmp/51a363417c5da.jpg differ diff --git a/docs/tmp/51a49fb744d48.jpg b/docs/tmp/51a49fb744d48.jpg new file mode 100644 index 0000000..e077ffa Binary files /dev/null and b/docs/tmp/51a49fb744d48.jpg differ diff --git a/docs/tmp/51a4b3104a0c6.jpg b/docs/tmp/51a4b3104a0c6.jpg new file mode 100644 index 0000000..750e77a Binary files /dev/null and b/docs/tmp/51a4b3104a0c6.jpg differ diff --git a/docs/tmp/51a4d15eb306f.jpg b/docs/tmp/51a4d15eb306f.jpg new file mode 100644 index 0000000..b9e4cc7 Binary files /dev/null and b/docs/tmp/51a4d15eb306f.jpg differ diff --git a/docs/tmp/51a4d4291e0c0.jpg b/docs/tmp/51a4d4291e0c0.jpg new file mode 100644 index 0000000..b696ad2 Binary files /dev/null and b/docs/tmp/51a4d4291e0c0.jpg differ diff --git a/docs/tmp/51a5d5c4e688d.jpg b/docs/tmp/51a5d5c4e688d.jpg new file mode 100644 index 0000000..fbcf2ec Binary files /dev/null and b/docs/tmp/51a5d5c4e688d.jpg differ diff --git a/docs/tmp/51a5d64f1cbaa.jpg b/docs/tmp/51a5d64f1cbaa.jpg new file mode 100644 index 0000000..172712e Binary files /dev/null and b/docs/tmp/51a5d64f1cbaa.jpg differ diff --git a/docs/tmp/51a5e82c07bdb.jpg b/docs/tmp/51a5e82c07bdb.jpg new file mode 100644 index 0000000..87e8b88 Binary files /dev/null and b/docs/tmp/51a5e82c07bdb.jpg differ diff --git a/docs/tmp/51a5e8386e9b8.jpg b/docs/tmp/51a5e8386e9b8.jpg new file mode 100644 index 0000000..172712e Binary files /dev/null and b/docs/tmp/51a5e8386e9b8.jpg differ diff --git a/docs/tmp/51a6096990177.gif b/docs/tmp/51a6096990177.gif new file mode 100644 index 0000000..8829739 Binary files /dev/null and b/docs/tmp/51a6096990177.gif differ diff --git a/docs/tmp/51a6279c874ec.jpg b/docs/tmp/51a6279c874ec.jpg new file mode 100644 index 0000000..6602e99 Binary files /dev/null and b/docs/tmp/51a6279c874ec.jpg differ diff --git a/docs/tmp/51a66b7b3c32c.jpg b/docs/tmp/51a66b7b3c32c.jpg new file mode 100644 index 0000000..2241bb7 Binary files /dev/null and b/docs/tmp/51a66b7b3c32c.jpg differ diff --git a/docs/tmp/51a70285405ce.jpg b/docs/tmp/51a70285405ce.jpg new file mode 100644 index 0000000..8012a2d Binary files /dev/null and b/docs/tmp/51a70285405ce.jpg differ diff --git a/docs/tmp/51a73cde7d7e3.jpg b/docs/tmp/51a73cde7d7e3.jpg new file mode 100644 index 0000000..40402c7 Binary files /dev/null and b/docs/tmp/51a73cde7d7e3.jpg differ diff --git a/docs/tmp/51a74dfc11429.jpg b/docs/tmp/51a74dfc11429.jpg new file mode 100644 index 0000000..4d6b11d Binary files /dev/null and b/docs/tmp/51a74dfc11429.jpg differ diff --git a/docs/tmp/51a869d843621.jpg b/docs/tmp/51a869d843621.jpg new file mode 100644 index 0000000..092fb89 Binary files /dev/null and b/docs/tmp/51a869d843621.jpg differ diff --git a/docs/tmp/51a86a895621e.gif b/docs/tmp/51a86a895621e.gif new file mode 100644 index 0000000..95a6728 Binary files /dev/null and b/docs/tmp/51a86a895621e.gif differ diff --git a/docs/tmp/51a86b039ff58.jpg b/docs/tmp/51a86b039ff58.jpg new file mode 100644 index 0000000..96ac9ec Binary files /dev/null and b/docs/tmp/51a86b039ff58.jpg differ diff --git a/docs/tmp/51a86b162f7b4.jpg b/docs/tmp/51a86b162f7b4.jpg new file mode 100644 index 0000000..b2555bd Binary files /dev/null and b/docs/tmp/51a86b162f7b4.jpg differ diff --git a/docs/tmp/51a86c3c7bfc2.jpg b/docs/tmp/51a86c3c7bfc2.jpg new file mode 100644 index 0000000..074d8af Binary files /dev/null and b/docs/tmp/51a86c3c7bfc2.jpg differ diff --git a/docs/tmp/51a871a6cd813.jpg b/docs/tmp/51a871a6cd813.jpg new file mode 100644 index 0000000..074d8af Binary files /dev/null and b/docs/tmp/51a871a6cd813.jpg differ diff --git a/docs/tmp/51a8b1982c327.jpg b/docs/tmp/51a8b1982c327.jpg new file mode 100644 index 0000000..290cd25 Binary files /dev/null and b/docs/tmp/51a8b1982c327.jpg differ diff --git a/docs/tmp/51a8b43c2a7bb.jpg b/docs/tmp/51a8b43c2a7bb.jpg new file mode 100644 index 0000000..290cd25 Binary files /dev/null and b/docs/tmp/51a8b43c2a7bb.jpg differ diff --git a/docs/tmp/51ac6277a72da.jpg b/docs/tmp/51ac6277a72da.jpg new file mode 100644 index 0000000..72606b2 Binary files /dev/null and b/docs/tmp/51ac6277a72da.jpg differ diff --git a/docs/tmp/51ac651dca917.jpg b/docs/tmp/51ac651dca917.jpg new file mode 100644 index 0000000..d333132 Binary files /dev/null and b/docs/tmp/51ac651dca917.jpg differ diff --git a/docs/tmp/51ac6ac3836bb.jpg b/docs/tmp/51ac6ac3836bb.jpg new file mode 100644 index 0000000..d333132 Binary files /dev/null and b/docs/tmp/51ac6ac3836bb.jpg differ diff --git a/docs/tmp/51ac97d24946f.jpg b/docs/tmp/51ac97d24946f.jpg new file mode 100644 index 0000000..a0b38e7 Binary files /dev/null and b/docs/tmp/51ac97d24946f.jpg differ diff --git a/docs/tmp/51acaa42741cf.jpg b/docs/tmp/51acaa42741cf.jpg new file mode 100644 index 0000000..c8cfd71 Binary files /dev/null and b/docs/tmp/51acaa42741cf.jpg differ diff --git a/docs/tmp/51acacfaefe36.jpg b/docs/tmp/51acacfaefe36.jpg new file mode 100644 index 0000000..1cd803e Binary files /dev/null and b/docs/tmp/51acacfaefe36.jpg differ diff --git a/docs/tmp/51af35f4b2ddf.jpg b/docs/tmp/51af35f4b2ddf.jpg new file mode 100644 index 0000000..0854208 Binary files /dev/null and b/docs/tmp/51af35f4b2ddf.jpg differ diff --git a/docs/tmp/51af3c3f283f5.jpg b/docs/tmp/51af3c3f283f5.jpg new file mode 100644 index 0000000..10dc04e Binary files /dev/null and b/docs/tmp/51af3c3f283f5.jpg differ diff --git a/docs/tmp/51af4be498f6e.jpg b/docs/tmp/51af4be498f6e.jpg new file mode 100644 index 0000000..f97e7d1 Binary files /dev/null and b/docs/tmp/51af4be498f6e.jpg differ diff --git a/docs/tmp/51af5c9a94979.jpg b/docs/tmp/51af5c9a94979.jpg new file mode 100644 index 0000000..23e6f1f Binary files /dev/null and b/docs/tmp/51af5c9a94979.jpg differ diff --git a/docs/tmp/51b06248a83a0.jpg b/docs/tmp/51b06248a83a0.jpg new file mode 100644 index 0000000..3af0791 Binary files /dev/null and b/docs/tmp/51b06248a83a0.jpg differ diff --git a/docs/tmp/51b0add86f11b.jpg b/docs/tmp/51b0add86f11b.jpg new file mode 100644 index 0000000..2f99fc1 Binary files /dev/null and b/docs/tmp/51b0add86f11b.jpg differ diff --git a/docs/tmp/51b1cc10ea4bd.png b/docs/tmp/51b1cc10ea4bd.png new file mode 100644 index 0000000..720fa80 Binary files /dev/null and b/docs/tmp/51b1cc10ea4bd.png differ diff --git a/docs/tmp/51b585b9427fa.jpg b/docs/tmp/51b585b9427fa.jpg new file mode 100644 index 0000000..f9341d5 Binary files /dev/null and b/docs/tmp/51b585b9427fa.jpg differ diff --git a/docs/tmp/51b58d9166d29.jpg b/docs/tmp/51b58d9166d29.jpg new file mode 100644 index 0000000..1f249be Binary files /dev/null and b/docs/tmp/51b58d9166d29.jpg differ diff --git a/docs/tmp/51b59103bfd34.jpg b/docs/tmp/51b59103bfd34.jpg new file mode 100644 index 0000000..0fbabb8 Binary files /dev/null and b/docs/tmp/51b59103bfd34.jpg differ diff --git a/docs/tmp/51b5b8afe8355.jpg b/docs/tmp/51b5b8afe8355.jpg new file mode 100644 index 0000000..d660ed6 Binary files /dev/null and b/docs/tmp/51b5b8afe8355.jpg differ diff --git a/docs/tmp/51b5d37f8425a.jpg b/docs/tmp/51b5d37f8425a.jpg new file mode 100644 index 0000000..cbbc8c5 Binary files /dev/null and b/docs/tmp/51b5d37f8425a.jpg differ diff --git a/docs/tmp/51b5da8e5ba7e.jpg b/docs/tmp/51b5da8e5ba7e.jpg new file mode 100644 index 0000000..7517dbf Binary files /dev/null and b/docs/tmp/51b5da8e5ba7e.jpg differ diff --git a/docs/tmp/51b5debc81277.jpg b/docs/tmp/51b5debc81277.jpg new file mode 100644 index 0000000..82369a0 Binary files /dev/null and b/docs/tmp/51b5debc81277.jpg differ diff --git a/docs/tmp/51b6dd2a13b04.jpg b/docs/tmp/51b6dd2a13b04.jpg new file mode 100644 index 0000000..45175f6 Binary files /dev/null and b/docs/tmp/51b6dd2a13b04.jpg differ diff --git a/docs/tmp/51b6e2112d44a.jpg b/docs/tmp/51b6e2112d44a.jpg new file mode 100644 index 0000000..dfb3921 Binary files /dev/null and b/docs/tmp/51b6e2112d44a.jpg differ diff --git a/docs/tmp/51b72006577c4.jpeg b/docs/tmp/51b72006577c4.jpeg new file mode 100644 index 0000000..0b7d925 Binary files /dev/null and b/docs/tmp/51b72006577c4.jpeg differ diff --git a/docs/tmp/51b72564aee6e.jpg b/docs/tmp/51b72564aee6e.jpg new file mode 100644 index 0000000..bd09f8a Binary files /dev/null and b/docs/tmp/51b72564aee6e.jpg differ diff --git a/docs/tmp/51b736663c0e7.jpg b/docs/tmp/51b736663c0e7.jpg new file mode 100644 index 0000000..693ec89 Binary files /dev/null and b/docs/tmp/51b736663c0e7.jpg differ diff --git a/docs/tmp/51b7367a00e82.gif b/docs/tmp/51b7367a00e82.gif new file mode 100644 index 0000000..5e3508a Binary files /dev/null and b/docs/tmp/51b7367a00e82.gif differ diff --git a/docs/tmp/51b7367c3a52f.png b/docs/tmp/51b7367c3a52f.png new file mode 100644 index 0000000..25efdc8 Binary files /dev/null and b/docs/tmp/51b7367c3a52f.png differ diff --git a/docs/tmp/51b82c999cd2a.jpg b/docs/tmp/51b82c999cd2a.jpg new file mode 100644 index 0000000..fcc4548 Binary files /dev/null and b/docs/tmp/51b82c999cd2a.jpg differ diff --git a/docs/tmp/51b838ba0828e.jpg b/docs/tmp/51b838ba0828e.jpg new file mode 100644 index 0000000..87d03a5 Binary files /dev/null and b/docs/tmp/51b838ba0828e.jpg differ diff --git a/docs/tmp/51b83bdab59fe.jpg b/docs/tmp/51b83bdab59fe.jpg new file mode 100644 index 0000000..cdb0e55 Binary files /dev/null and b/docs/tmp/51b83bdab59fe.jpg differ diff --git a/docs/tmp/51b83be511640.png b/docs/tmp/51b83be511640.png new file mode 100644 index 0000000..b1b68a1 Binary files /dev/null and b/docs/tmp/51b83be511640.png differ diff --git a/docs/tmp/51b83bf95054b.png b/docs/tmp/51b83bf95054b.png new file mode 100644 index 0000000..b1b68a1 Binary files /dev/null and b/docs/tmp/51b83bf95054b.png differ diff --git a/docs/tmp/51b8493ba5b98.jpg b/docs/tmp/51b8493ba5b98.jpg new file mode 100644 index 0000000..ec63e80 Binary files /dev/null and b/docs/tmp/51b8493ba5b98.jpg differ diff --git a/docs/tmp/51b8669acd0a9.gif b/docs/tmp/51b8669acd0a9.gif new file mode 100644 index 0000000..2161f70 Binary files /dev/null and b/docs/tmp/51b8669acd0a9.gif differ diff --git a/docs/tmp/51b867df8d6f0.jpg b/docs/tmp/51b867df8d6f0.jpg new file mode 100644 index 0000000..1438f9b Binary files /dev/null and b/docs/tmp/51b867df8d6f0.jpg differ diff --git a/docs/tmp/51b86caf25b78.gif b/docs/tmp/51b86caf25b78.gif new file mode 100644 index 0000000..c20be76 Binary files /dev/null and b/docs/tmp/51b86caf25b78.gif differ diff --git a/docs/tmp/51b86cf8027a7.gif b/docs/tmp/51b86cf8027a7.gif new file mode 100644 index 0000000..a20b7e4 Binary files /dev/null and b/docs/tmp/51b86cf8027a7.gif differ diff --git a/docs/tmp/51b86f14db652.jpg b/docs/tmp/51b86f14db652.jpg new file mode 100644 index 0000000..f5c8a84 Binary files /dev/null and b/docs/tmp/51b86f14db652.jpg differ diff --git a/docs/tmp/51b9813974e06.gif b/docs/tmp/51b9813974e06.gif new file mode 100644 index 0000000..a351daf Binary files /dev/null and b/docs/tmp/51b9813974e06.gif differ diff --git a/docs/tmp/51b9cc5935f92.jpg b/docs/tmp/51b9cc5935f92.jpg new file mode 100644 index 0000000..5cfa93f Binary files /dev/null and b/docs/tmp/51b9cc5935f92.jpg differ diff --git a/docs/tmp/51bad11099e46.gif b/docs/tmp/51bad11099e46.gif new file mode 100644 index 0000000..f411c68 Binary files /dev/null and b/docs/tmp/51bad11099e46.gif differ diff --git a/docs/tmp/51baf7a23cb2a.jpg b/docs/tmp/51baf7a23cb2a.jpg new file mode 100644 index 0000000..3e220c2 Binary files /dev/null and b/docs/tmp/51baf7a23cb2a.jpg differ diff --git a/docs/tmp/51bb13f3b1bd7.jpg b/docs/tmp/51bb13f3b1bd7.jpg new file mode 100644 index 0000000..1bec3da Binary files /dev/null and b/docs/tmp/51bb13f3b1bd7.jpg differ diff --git a/docs/tmp/51bb1418a675b.jpg b/docs/tmp/51bb1418a675b.jpg new file mode 100644 index 0000000..3e220c2 Binary files /dev/null and b/docs/tmp/51bb1418a675b.jpg differ diff --git a/docs/tmp/51bb29998830b.jpg b/docs/tmp/51bb29998830b.jpg new file mode 100644 index 0000000..5714988 Binary files /dev/null and b/docs/tmp/51bb29998830b.jpg differ diff --git a/docs/tmp/51bb29b0094d7.jpg b/docs/tmp/51bb29b0094d7.jpg new file mode 100644 index 0000000..712dbd1 Binary files /dev/null and b/docs/tmp/51bb29b0094d7.jpg differ diff --git a/docs/tmp/51bb29b4a1749.png b/docs/tmp/51bb29b4a1749.png new file mode 100644 index 0000000..74a7e4f Binary files /dev/null and b/docs/tmp/51bb29b4a1749.png differ diff --git a/docs/tmp/51bee51060c33.jpg b/docs/tmp/51bee51060c33.jpg new file mode 100644 index 0000000..0c43978 Binary files /dev/null and b/docs/tmp/51bee51060c33.jpg differ diff --git a/docs/tmp/51bef8cece750.jpg b/docs/tmp/51bef8cece750.jpg new file mode 100644 index 0000000..1e16458 Binary files /dev/null and b/docs/tmp/51bef8cece750.jpg differ diff --git a/docs/tmp/51bef8dd1db4f.jpg b/docs/tmp/51bef8dd1db4f.jpg new file mode 100644 index 0000000..191cdda Binary files /dev/null and b/docs/tmp/51bef8dd1db4f.jpg differ diff --git a/docs/tmp/51c027b1d06c3.jpg b/docs/tmp/51c027b1d06c3.jpg new file mode 100644 index 0000000..7dff2d5 Binary files /dev/null and b/docs/tmp/51c027b1d06c3.jpg differ diff --git a/docs/tmp/51c04fcf5d820.jpg b/docs/tmp/51c04fcf5d820.jpg new file mode 100644 index 0000000..74d7d9f Binary files /dev/null and b/docs/tmp/51c04fcf5d820.jpg differ diff --git a/docs/tmp/51c0511e90ffc.png b/docs/tmp/51c0511e90ffc.png new file mode 100644 index 0000000..a097f16 Binary files /dev/null and b/docs/tmp/51c0511e90ffc.png differ diff --git a/docs/tmp/51c0dcdf343cb.jpg b/docs/tmp/51c0dcdf343cb.jpg new file mode 100644 index 0000000..a66c809 Binary files /dev/null and b/docs/tmp/51c0dcdf343cb.jpg differ diff --git a/docs/tmp/51c17907ac28b.jpg b/docs/tmp/51c17907ac28b.jpg new file mode 100644 index 0000000..a171442 Binary files /dev/null and b/docs/tmp/51c17907ac28b.jpg differ diff --git a/docs/tmp/51c18a0d48111.jpg b/docs/tmp/51c18a0d48111.jpg new file mode 100644 index 0000000..e643c4d Binary files /dev/null and b/docs/tmp/51c18a0d48111.jpg differ diff --git a/docs/tmp/51c18abdd4507.jpg b/docs/tmp/51c18abdd4507.jpg new file mode 100644 index 0000000..3c37e8d Binary files /dev/null and b/docs/tmp/51c18abdd4507.jpg differ diff --git a/docs/tmp/51c1a0c881f6e.jpg b/docs/tmp/51c1a0c881f6e.jpg new file mode 100644 index 0000000..1f687af Binary files /dev/null and b/docs/tmp/51c1a0c881f6e.jpg differ diff --git a/docs/tmp/51c1a0d94e901.jpg b/docs/tmp/51c1a0d94e901.jpg new file mode 100644 index 0000000..0379bd1 Binary files /dev/null and b/docs/tmp/51c1a0d94e901.jpg differ diff --git a/docs/tmp/51c2c6811eb7b.jpg b/docs/tmp/51c2c6811eb7b.jpg new file mode 100644 index 0000000..c5fec8f Binary files /dev/null and b/docs/tmp/51c2c6811eb7b.jpg differ diff --git a/docs/tmp/51c2d51fa3104.jpg b/docs/tmp/51c2d51fa3104.jpg new file mode 100644 index 0000000..40f94fa Binary files /dev/null and b/docs/tmp/51c2d51fa3104.jpg differ diff --git a/docs/tmp/51c311f0156d4.jpg b/docs/tmp/51c311f0156d4.jpg new file mode 100644 index 0000000..1c6a215 Binary files /dev/null and b/docs/tmp/51c311f0156d4.jpg differ diff --git a/docs/tmp/51c406146e1ed.jpg b/docs/tmp/51c406146e1ed.jpg new file mode 100644 index 0000000..de2858e Binary files /dev/null and b/docs/tmp/51c406146e1ed.jpg differ diff --git a/docs/tmp/51c40cacc3d8a.jpg b/docs/tmp/51c40cacc3d8a.jpg new file mode 100644 index 0000000..5b63c86 Binary files /dev/null and b/docs/tmp/51c40cacc3d8a.jpg differ diff --git a/docs/tmp/51c419139854e.jpg b/docs/tmp/51c419139854e.jpg new file mode 100644 index 0000000..5b63c86 Binary files /dev/null and b/docs/tmp/51c419139854e.jpg differ diff --git a/docs/tmp/51c4192089677.jpg b/docs/tmp/51c4192089677.jpg new file mode 100644 index 0000000..5b63c86 Binary files /dev/null and b/docs/tmp/51c4192089677.jpg differ diff --git a/docs/tmp/51c4293500e04.jpg b/docs/tmp/51c4293500e04.jpg new file mode 100644 index 0000000..503f01d Binary files /dev/null and b/docs/tmp/51c4293500e04.jpg differ diff --git a/docs/tmp/51c430d975998.gif b/docs/tmp/51c430d975998.gif new file mode 100644 index 0000000..7cad1ed Binary files /dev/null and b/docs/tmp/51c430d975998.gif differ diff --git a/docs/tmp/51c443795708b.gif b/docs/tmp/51c443795708b.gif new file mode 100644 index 0000000..7cad1ed Binary files /dev/null and b/docs/tmp/51c443795708b.gif differ diff --git a/docs/tmp/51c451ad489db.jpg b/docs/tmp/51c451ad489db.jpg new file mode 100644 index 0000000..a3e04ab Binary files /dev/null and b/docs/tmp/51c451ad489db.jpg differ diff --git a/docs/tmp/51c4645a65877.jpg b/docs/tmp/51c4645a65877.jpg new file mode 100644 index 0000000..c700ad4 Binary files /dev/null and b/docs/tmp/51c4645a65877.jpg differ diff --git a/docs/tmp/51c811e713dc2.jpg b/docs/tmp/51c811e713dc2.jpg new file mode 100644 index 0000000..1b212bd Binary files /dev/null and b/docs/tmp/51c811e713dc2.jpg differ diff --git a/docs/tmp/51cab976ee38d.jpg b/docs/tmp/51cab976ee38d.jpg new file mode 100644 index 0000000..b02be7e Binary files /dev/null and b/docs/tmp/51cab976ee38d.jpg differ diff --git a/docs/tmp/51cabafda5096.jpg b/docs/tmp/51cabafda5096.jpg new file mode 100644 index 0000000..2ee6077 Binary files /dev/null and b/docs/tmp/51cabafda5096.jpg differ diff --git a/docs/tmp/51cafd55430a1.jpg b/docs/tmp/51cafd55430a1.jpg new file mode 100644 index 0000000..a09a042 Binary files /dev/null and b/docs/tmp/51cafd55430a1.jpg differ diff --git a/docs/tmp/51cafd6cd7cb0.jpg b/docs/tmp/51cafd6cd7cb0.jpg new file mode 100644 index 0000000..a77dccc Binary files /dev/null and b/docs/tmp/51cafd6cd7cb0.jpg differ diff --git a/docs/tmp/51cd968bf2294.jpg b/docs/tmp/51cd968bf2294.jpg new file mode 100644 index 0000000..e5dd52f Binary files /dev/null and b/docs/tmp/51cd968bf2294.jpg differ diff --git a/docs/tmp/51d158c863c61.jpg b/docs/tmp/51d158c863c61.jpg new file mode 100644 index 0000000..26196a9 Binary files /dev/null and b/docs/tmp/51d158c863c61.jpg differ diff --git a/docs/tmp/51d2a5f5676a4.jpg b/docs/tmp/51d2a5f5676a4.jpg new file mode 100644 index 0000000..b1fd80d Binary files /dev/null and b/docs/tmp/51d2a5f5676a4.jpg differ diff --git a/docs/tmp/51d2a8f580ec9.jpg b/docs/tmp/51d2a8f580ec9.jpg new file mode 100644 index 0000000..4107dcd Binary files /dev/null and b/docs/tmp/51d2a8f580ec9.jpg differ diff --git a/docs/tmp/51d2b949b6454.jpg b/docs/tmp/51d2b949b6454.jpg new file mode 100644 index 0000000..9c2f4d3 Binary files /dev/null and b/docs/tmp/51d2b949b6454.jpg differ diff --git a/docs/tmp/51d2c84f87428.jpg b/docs/tmp/51d2c84f87428.jpg new file mode 100644 index 0000000..a3c7956 Binary files /dev/null and b/docs/tmp/51d2c84f87428.jpg differ diff --git a/docs/tmp/51d400169962a.jpg b/docs/tmp/51d400169962a.jpg new file mode 100644 index 0000000..6afa263 Binary files /dev/null and b/docs/tmp/51d400169962a.jpg differ diff --git a/docs/tmp/51d4019f88d73.jpg b/docs/tmp/51d4019f88d73.jpg new file mode 100644 index 0000000..edcc86b Binary files /dev/null and b/docs/tmp/51d4019f88d73.jpg differ diff --git a/docs/tmp/51d54364c3f28.jpg b/docs/tmp/51d54364c3f28.jpg new file mode 100644 index 0000000..340e701 Binary files /dev/null and b/docs/tmp/51d54364c3f28.jpg differ diff --git a/docs/tmp/51d55bc7cfe95.jpg b/docs/tmp/51d55bc7cfe95.jpg new file mode 100644 index 0000000..5671306 Binary files /dev/null and b/docs/tmp/51d55bc7cfe95.jpg differ diff --git a/docs/tmp/51d69650c3708.jpg b/docs/tmp/51d69650c3708.jpg new file mode 100644 index 0000000..8583e1e Binary files /dev/null and b/docs/tmp/51d69650c3708.jpg differ diff --git a/docs/tmp/51dbdd313e166.jpg b/docs/tmp/51dbdd313e166.jpg new file mode 100644 index 0000000..e36cf5d Binary files /dev/null and b/docs/tmp/51dbdd313e166.jpg differ diff --git a/docs/tmp/51dd21df3ddab.jpg b/docs/tmp/51dd21df3ddab.jpg new file mode 100644 index 0000000..ba2033e Binary files /dev/null and b/docs/tmp/51dd21df3ddab.jpg differ diff --git a/docs/tmp/51dd61399a733.gif b/docs/tmp/51dd61399a733.gif new file mode 100644 index 0000000..3bc04d8 Binary files /dev/null and b/docs/tmp/51dd61399a733.gif differ diff --git a/docs/tmp/51e3e4a05c464.jpg b/docs/tmp/51e3e4a05c464.jpg new file mode 100644 index 0000000..62d250f Binary files /dev/null and b/docs/tmp/51e3e4a05c464.jpg differ diff --git a/docs/tmp/51e7f8e61b072.jpg b/docs/tmp/51e7f8e61b072.jpg new file mode 100644 index 0000000..5be74a7 Binary files /dev/null and b/docs/tmp/51e7f8e61b072.jpg differ diff --git a/docs/tmp/51ecf00ca0281.jpg b/docs/tmp/51ecf00ca0281.jpg new file mode 100644 index 0000000..8e1ffe5 Binary files /dev/null and b/docs/tmp/51ecf00ca0281.jpg differ diff --git a/docs/tmp/51ed5371e0de5.jpg b/docs/tmp/51ed5371e0de5.jpg new file mode 100644 index 0000000..b74862c Binary files /dev/null and b/docs/tmp/51ed5371e0de5.jpg differ diff --git a/docs/tmp/51ed5375ec3b6.jpg b/docs/tmp/51ed5375ec3b6.jpg new file mode 100644 index 0000000..b74862c Binary files /dev/null and b/docs/tmp/51ed5375ec3b6.jpg differ diff --git a/docs/tmp/51ee6a467f395.jpg b/docs/tmp/51ee6a467f395.jpg new file mode 100644 index 0000000..b8d9615 Binary files /dev/null and b/docs/tmp/51ee6a467f395.jpg differ diff --git a/docs/tmp/51ee72828aa1f.jpg b/docs/tmp/51ee72828aa1f.jpg new file mode 100644 index 0000000..b4afbdf Binary files /dev/null and b/docs/tmp/51ee72828aa1f.jpg differ diff --git a/docs/tmp/51efa623deae4.jpg b/docs/tmp/51efa623deae4.jpg new file mode 100644 index 0000000..d304c45 Binary files /dev/null and b/docs/tmp/51efa623deae4.jpg differ diff --git a/docs/tmp/51efa632b4f83.jpg b/docs/tmp/51efa632b4f83.jpg new file mode 100644 index 0000000..1f5319a Binary files /dev/null and b/docs/tmp/51efa632b4f83.jpg differ diff --git a/docs/tmp/51efdc1484363.jpg b/docs/tmp/51efdc1484363.jpg new file mode 100644 index 0000000..b50bcd3 Binary files /dev/null and b/docs/tmp/51efdc1484363.jpg differ diff --git a/docs/tmp/51f246fb362a6.jpg b/docs/tmp/51f246fb362a6.jpg new file mode 100644 index 0000000..d263332 Binary files /dev/null and b/docs/tmp/51f246fb362a6.jpg differ diff --git a/docs/tmp/51f63448a55d7.jpg b/docs/tmp/51f63448a55d7.jpg new file mode 100644 index 0000000..015e771 Binary files /dev/null and b/docs/tmp/51f63448a55d7.jpg differ diff --git a/docs/tmp/51f6379d556c6.png b/docs/tmp/51f6379d556c6.png new file mode 100644 index 0000000..8688f13 Binary files /dev/null and b/docs/tmp/51f6379d556c6.png differ diff --git a/docs/tmp/51f63b030b549.png b/docs/tmp/51f63b030b549.png new file mode 100644 index 0000000..8688f13 Binary files /dev/null and b/docs/tmp/51f63b030b549.png differ diff --git a/docs/tmp/51f6657c58fe7.jpg b/docs/tmp/51f6657c58fe7.jpg new file mode 100644 index 0000000..fc2fe6f Binary files /dev/null and b/docs/tmp/51f6657c58fe7.jpg differ diff --git a/docs/tmp/51f6658f05342.jpg b/docs/tmp/51f6658f05342.jpg new file mode 100644 index 0000000..eb971cb Binary files /dev/null and b/docs/tmp/51f6658f05342.jpg differ diff --git a/docs/tmp/51f665af20578.jpg b/docs/tmp/51f665af20578.jpg new file mode 100644 index 0000000..1b7560e Binary files /dev/null and b/docs/tmp/51f665af20578.jpg differ diff --git a/docs/tmp/51f67530ebf4e.jpg b/docs/tmp/51f67530ebf4e.jpg new file mode 100644 index 0000000..e261376 Binary files /dev/null and b/docs/tmp/51f67530ebf4e.jpg differ diff --git a/docs/tmp/51f68a5b61302.jpg b/docs/tmp/51f68a5b61302.jpg new file mode 100644 index 0000000..e2f5659 Binary files /dev/null and b/docs/tmp/51f68a5b61302.jpg differ diff --git a/docs/tmp/51fa24f94dfe9.jpg b/docs/tmp/51fa24f94dfe9.jpg new file mode 100644 index 0000000..e389b81 Binary files /dev/null and b/docs/tmp/51fa24f94dfe9.jpg differ diff --git a/docs/tmp/51ffa8d33e9f1.jpg b/docs/tmp/51ffa8d33e9f1.jpg new file mode 100644 index 0000000..7be3002 Binary files /dev/null and b/docs/tmp/51ffa8d33e9f1.jpg differ diff --git a/docs/tmp/5200ec4f54bec.jpg b/docs/tmp/5200ec4f54bec.jpg new file mode 100644 index 0000000..c08cc61 Binary files /dev/null and b/docs/tmp/5200ec4f54bec.jpg differ diff --git a/docs/tmp/520235a70d846.jpg b/docs/tmp/520235a70d846.jpg new file mode 100644 index 0000000..e139eb9 Binary files /dev/null and b/docs/tmp/520235a70d846.jpg differ diff --git a/docs/tmp/520235e041d56.jpg b/docs/tmp/520235e041d56.jpg new file mode 100644 index 0000000..d567415 Binary files /dev/null and b/docs/tmp/520235e041d56.jpg differ diff --git a/get_vpn.php b/get_vpn.php new file mode 100644 index 0000000..ce2e02d --- /dev/null +++ b/get_vpn.php @@ -0,0 +1,51 @@ +Free\ VPN\ (NL|UK\/US)\ PPTP\ Connection\<\/strong\>\ \\((.*)\ Available\)\<\/em\>/i', $sVpnPage, $asMatches); + +$bAvailableAccount = false; +foreach($asMatches[2] as $iAccountNb) +{ + if($iAccountNb>0) + { + $bAvailableAccount = true; + } +} + +if($bAvailableAccount) +{ + $sResult = 'VPN available. '; + if(false && file_exists($sSentMailFile)) + { + $sResult .= 'Mail already sent'; + } + else + { + $sHeaders = 'MIME-Version: 1.0'."\r\n". + 'Content-type: text/plain; charset=UTF-8'."\r\n". + 'From: webmaster@lutran.fr'."\r\n". + 'Reply-To: webmaster@lutran.fr'."\r\n". + 'X-Mailer: PHP/'.phpversion(); + if(mail('julien.lutran@gmail.com', 'VPN disponible', 'go get one ! https://billing.hideipvpn.com/cart.php?gid=1', $sHeaders)) + { + $sResult .= 'Mail sent'; + file_put_contents($sSentMailFile, 'mail sent at '.date('d/m/Y H:i:s')); + } + else + { + $sResult .= 'Failed attempt to send mail'; + } + } +} +else +{ + $sResult = 'No VPN available'; +} +file_put_contents($sLogFile, $sResult.'.', FILE_APPEND); + +?> diff --git a/images/add_24.png b/images/add_24.png new file mode 100644 index 0000000..85b2587 Binary files /dev/null and b/images/add_24.png differ diff --git a/images/afk.png b/images/afk.png new file mode 100644 index 0000000..d98ad58 Binary files /dev/null and b/images/afk.png differ diff --git a/images/avatar_96.png b/images/avatar_96.png new file mode 100644 index 0000000..08e0507 Binary files /dev/null and b/images/avatar_96.png differ diff --git a/images/chat_24.png b/images/chat_24.png new file mode 100644 index 0000000..52cc1b5 Binary files /dev/null and b/images/chat_24.png differ diff --git a/images/chat_48.png b/images/chat_48.png new file mode 100644 index 0000000..3d67d25 Binary files /dev/null and b/images/chat_48.png differ diff --git a/images/doc_24.png b/images/doc_24.png new file mode 100644 index 0000000..6807539 Binary files /dev/null and b/images/doc_24.png differ diff --git a/images/edit_24.png b/images/edit_24.png new file mode 100644 index 0000000..ad93f10 Binary files /dev/null and b/images/edit_24.png differ diff --git a/images/error_48.png b/images/error_48.png new file mode 100644 index 0000000..445702c Binary files /dev/null and b/images/error_48.png differ diff --git a/images/exit_24.png b/images/exit_24.png new file mode 100644 index 0000000..5e07040 Binary files /dev/null and b/images/exit_24.png differ diff --git a/images/exit_32.png b/images/exit_32.png new file mode 100644 index 0000000..8a7a53a Binary files /dev/null and b/images/exit_32.png differ diff --git a/images/expand_left_16.png b/images/expand_left_16.png new file mode 100644 index 0000000..1c94dc8 Binary files /dev/null and b/images/expand_left_16.png differ diff --git a/images/expand_minus.png b/images/expand_minus.png new file mode 100644 index 0000000..5c45ffe Binary files /dev/null and b/images/expand_minus.png differ diff --git a/images/expand_plus.png b/images/expand_plus.png new file mode 100644 index 0000000..7db42ca Binary files /dev/null and b/images/expand_plus.png differ diff --git a/images/expand_right_16.png b/images/expand_right_16.png new file mode 100644 index 0000000..5b63bb1 Binary files /dev/null and b/images/expand_right_16.png differ diff --git a/images/favicon_gc3.ico b/images/favicon_gc3.ico new file mode 100644 index 0000000..db32fba Binary files /dev/null and b/images/favicon_gc3.ico differ diff --git a/images/grad_white_transparent_50.png b/images/grad_white_transparent_50.png new file mode 100644 index 0000000..f517bfe Binary files /dev/null and b/images/grad_white_transparent_50.png differ diff --git a/images/grad_white_transparent_h_50.png b/images/grad_white_transparent_h_50.png new file mode 100644 index 0000000..859c9f3 Binary files /dev/null and b/images/grad_white_transparent_h_50.png differ diff --git a/images/grad_white_transparent_v_50.png b/images/grad_white_transparent_v_50.png new file mode 100644 index 0000000..f517bfe Binary files /dev/null and b/images/grad_white_transparent_v_50.png differ diff --git a/images/help_24.png b/images/help_24.png new file mode 100644 index 0000000..312d0f7 Binary files /dev/null and b/images/help_24.png differ diff --git a/images/key_24.png b/images/key_24.png new file mode 100644 index 0000000..ecd4565 Binary files /dev/null and b/images/key_24.png differ diff --git a/images/logo_25.png b/images/logo_25.png new file mode 100644 index 0000000..e5a9bcf Binary files /dev/null and b/images/logo_25.png differ diff --git a/images/logo_35.png b/images/logo_35.png new file mode 100644 index 0000000..39dfa91 Binary files /dev/null and b/images/logo_35.png differ diff --git a/images/logo_43.png b/images/logo_43.png new file mode 100644 index 0000000..e12c3d3 Binary files /dev/null and b/images/logo_43.png differ diff --git a/images/logo_ar_24.png b/images/logo_ar_24.png new file mode 100644 index 0000000..43913d2 Binary files /dev/null and b/images/logo_ar_24.png differ diff --git a/images/logo_biosa_24.png b/images/logo_biosa_24.png new file mode 100644 index 0000000..6c4db98 Binary files /dev/null and b/images/logo_biosa_24.png differ diff --git a/images/logo_cgi_24.png b/images/logo_cgi_24.png new file mode 100644 index 0000000..44f94ac Binary files /dev/null and b/images/logo_cgi_24.png differ diff --git a/images/logo_homsys_24.png b/images/logo_homsys_24.png new file mode 100644 index 0000000..1b22dfb Binary files /dev/null and b/images/logo_homsys_24.png differ diff --git a/images/logo_logica_24.png b/images/logo_logica_24.png new file mode 100644 index 0000000..7e2d3b2 Binary files /dev/null and b/images/logo_logica_24.png differ diff --git a/images/logo_mic_24.png b/images/logo_mic_24.png new file mode 100644 index 0000000..d9b150b Binary files /dev/null and b/images/logo_mic_24.png differ diff --git a/images/logo_mu_25.png b/images/logo_mu_25.png new file mode 100644 index 0000000..df4c10b Binary files /dev/null and b/images/logo_mu_25.png differ diff --git a/images/logo_mu_e_50.png b/images/logo_mu_e_50.png new file mode 100644 index 0000000..22f7cb4 Binary files /dev/null and b/images/logo_mu_e_50.png differ diff --git a/images/logo_obs_24.jpg b/images/logo_obs_24.jpg new file mode 100644 index 0000000..6b9fc53 Binary files /dev/null and b/images/logo_obs_24.jpg differ diff --git a/images/logo_obs_24.jpg.old b/images/logo_obs_24.jpg.old new file mode 100644 index 0000000..379a7ca Binary files /dev/null and b/images/logo_obs_24.jpg.old differ diff --git a/images/mail_24.png b/images/mail_24.png new file mode 100644 index 0000000..a4bf2e3 Binary files /dev/null and b/images/mail_24.png differ diff --git a/images/ms_paperclip_112.jpg b/images/ms_paperclip_112.jpg new file mode 100644 index 0000000..7b1b0b4 Binary files /dev/null and b/images/ms_paperclip_112.jpg differ diff --git a/images/no_16.gif b/images/no_16.gif new file mode 100644 index 0000000..130b7e2 Binary files /dev/null and b/images/no_16.gif differ diff --git a/images/no_24.png b/images/no_24.png new file mode 100644 index 0000000..217dad2 Binary files /dev/null and b/images/no_24.png differ diff --git a/images/no_48.png b/images/no_48.png new file mode 100644 index 0000000..445702c Binary files /dev/null and b/images/no_48.png differ diff --git a/images/ok_48.png b/images/ok_48.png new file mode 100644 index 0000000..31e0b7d Binary files /dev/null and b/images/ok_48.png differ diff --git a/images/options_24.png b/images/options_24.png new file mode 100644 index 0000000..993454d Binary files /dev/null and b/images/options_24.png differ diff --git a/images/options_48.png b/images/options_48.png new file mode 100644 index 0000000..5bc6bf0 Binary files /dev/null and b/images/options_48.png differ diff --git a/images/paste_32.png b/images/paste_32.png new file mode 100644 index 0000000..4bd5444 Binary files /dev/null and b/images/paste_32.png differ diff --git a/images/paste_48.png b/images/paste_48.png new file mode 100644 index 0000000..41227c6 Binary files /dev/null and b/images/paste_48.png differ diff --git a/images/pdf_24.png b/images/pdf_24.png new file mode 100644 index 0000000..a06937d Binary files /dev/null and b/images/pdf_24.png differ diff --git a/images/ppt_24.png b/images/ppt_24.png new file mode 100644 index 0000000..42e0d83 Binary files /dev/null and b/images/ppt_24.png differ diff --git a/images/preview_24.png b/images/preview_24.png new file mode 100644 index 0000000..d89d734 Binary files /dev/null and b/images/preview_24.png differ diff --git a/images/preview_48.png b/images/preview_48.png new file mode 100644 index 0000000..87bd645 Binary files /dev/null and b/images/preview_48.png differ diff --git a/images/print_24.png b/images/print_24.png new file mode 100644 index 0000000..a9adbe1 Binary files /dev/null and b/images/print_24.png differ diff --git a/images/processing_16.gif b/images/processing_16.gif new file mode 100644 index 0000000..c160783 Binary files /dev/null and b/images/processing_16.gif differ diff --git a/images/processing_48.gif b/images/processing_48.gif new file mode 100644 index 0000000..f8bc3ea Binary files /dev/null and b/images/processing_48.gif differ diff --git a/images/raw_24.png b/images/raw_24.png new file mode 100644 index 0000000..57c335e Binary files /dev/null and b/images/raw_24.png differ diff --git a/images/raw_48.png b/images/raw_48.png new file mode 100644 index 0000000..b183541 Binary files /dev/null and b/images/raw_48.png differ diff --git a/images/rss_24.png b/images/rss_24.png new file mode 100644 index 0000000..5475f29 Binary files /dev/null and b/images/rss_24.png differ diff --git a/images/santa_128.png b/images/santa_128.png new file mode 100644 index 0000000..9ea5a3d Binary files /dev/null and b/images/santa_128.png differ diff --git a/images/sap_gold_332.jpg b/images/sap_gold_332.jpg new file mode 100644 index 0000000..942bf5f Binary files /dev/null and b/images/sap_gold_332.jpg differ diff --git a/images/save_24.png b/images/save_24.png new file mode 100644 index 0000000..a1c5169 Binary files /dev/null and b/images/save_24.png differ diff --git a/images/screenshot_16.png b/images/screenshot_16.png new file mode 100644 index 0000000..225370b Binary files /dev/null and b/images/screenshot_16.png differ diff --git a/images/screenshot_24.png b/images/screenshot_24.png new file mode 100644 index 0000000..476af87 Binary files /dev/null and b/images/screenshot_24.png differ diff --git a/images/search_48.png b/images/search_48.png new file mode 100644 index 0000000..aa3f087 Binary files /dev/null and b/images/search_48.png differ diff --git a/images/share_24.png b/images/share_24.png new file mode 100644 index 0000000..359ae9b Binary files /dev/null and b/images/share_24.png differ diff --git a/images/share_48.png b/images/share_48.png new file mode 100644 index 0000000..355bd11 Binary files /dev/null and b/images/share_48.png differ diff --git a/images/smile_100.gif b/images/smile_100.gif new file mode 100644 index 0000000..d0b4284 Binary files /dev/null and b/images/smile_100.gif differ diff --git a/images/tux_24.png b/images/tux_24.png new file mode 100644 index 0000000..9a9633f Binary files /dev/null and b/images/tux_24.png differ diff --git a/images/tux_48.png b/images/tux_48.png new file mode 100644 index 0000000..dc46e73 Binary files /dev/null and b/images/tux_48.png differ diff --git a/images/xls_24.png b/images/xls_24.png new file mode 100644 index 0000000..d99cd8a Binary files /dev/null and b/images/xls_24.png differ diff --git a/images/yes_24.png b/images/yes_24.png new file mode 100644 index 0000000..16fa275 Binary files /dev/null and b/images/yes_24.png differ diff --git a/images/yes_48.png b/images/yes_48.png new file mode 100644 index 0000000..31e0b7d Binary files /dev/null and b/images/yes_48.png differ diff --git a/images/zip_24.png b/images/zip_24.png new file mode 100644 index 0000000..0f34d7e Binary files /dev/null and b/images/zip_24.png differ diff --git a/inc/rss.php b/inc/rss.php new file mode 100644 index 0000000..86a36cb --- /dev/null +++ b/inc/rss.php @@ -0,0 +1,169 @@ +asDesc = $asDesc; + array_walk($asItems, array($this, 'addItem')); + } + + public function addItem($asItem) + { + $this->asItems[] = $asItem; + return count($this->asItems) - 1; + } + + public function removeItem($iItemId) + { + $bExist = array_key_exists($iItemId, $this->asItems); + if($bExist) unset($this->asItems[$iItemId]); + return $bExist; + } + + private function getGlobalPubDate() + { + $iGlobalPubDate = 0; + foreach($this->asItems as $asItem) + { + $iItemPubDate = strtotime($asItem['pub_date']); + if($iItemPubDate>$iGlobalPubDate) + { + $iGlobalPubDate = $iItemPubDate; + } + } + return self::cleanRss(self::getDate($iGlobalPubDate)); + } + + public function getFeed() + { + + //feed header + $sRssFeedHeader = self::getHtml($this->asDesc['title'], 'title'); + $sRssFeedHeader .= self::getHtml($this->asDesc['link'], 'link'); + $sRssFeedHeader .= self::getHtml($this->asDesc['copyright'], 'copyright'); + $sRssFeedHeader .= self::getHtml($this->asDesc['description'], 'description'); + $sRssFeedHeader .= self::getHtml('', 'atom:link', '', '', array('href'=>$this->asDesc['link'], 'rel'=>'self', 'type'=>'application/atom+xml'), true); + $sRssFeedHeader .= self::getHtml($this->asDesc['language'], 'language'); + $sRssFeedHeader .= self::getHtml($this->getGlobalPubDate(), 'pubDate'); + $sRssFeedHeader .= self::getHtml('Lutran.fr RSS Feed Generator', 'generator'); + $sRssFeedHeader .= self::getHtml($this->asDesc['webmaster_mail'].' (Webmaster)', 'webMaster'); + + //feed items + $asSortedItems = $this->rSortTimeMatrix($this->asItems, 'pub_date'); + $sItems = implode("\n", array_map(array($this, 'buildItem'), $asSortedItems)); + + //Global Feed + $sFeed = ''; + $sFeed .= self::getHtml(self::getHtml($sRssFeedHeader.$sItems, 'channel'), 'rss', '', '', array('version'=>'2.0', 'xmlns:atom'=>'http://www.w3.org/2005/Atom')); + + return $sFeed; + } + + private static function getDate($sDate) + { + if(!is_numeric($sDate)) + { + $sDate = strtotime($sDate); + } + return date('r', $sDate); + } + + private function buildItem($asItem) + { + $sRssItem = self::getHtml(self::cleanRss($asItem['title']), 'title'); + $sRssItem .= self::getHtml(self::cleanRss($asItem['author']), 'author'); + $sRssItem .= self::getHtml($asItem['link'], 'link'); + $sRssItem .= self::getHtml($asItem['category'], 'category'); + $sRssItem .= self::getHtml(self::cleanRss($asItem['description'].'.'), 'description'); + $sRssItem .= self::getHtml(self::getDate($asItem['pub_date']), 'pubDate'); + $sRssItem .= self::getHtml($asItem['guid'], 'guid', '', '', array('isPermaLink'=>'true')); + return self::getHtml($sRssItem, 'item'); + } + + private static function getHtml($oText, $sTag, $sClass='', $sStyle='', $asExtraAttr=array(), $bAutoClose=false, $sInter='') + { + $sHtmlAttr = ''; + if($sClass!='') + { + $asExtraAttr['class'] = $sClass; + } + if($sStyle!='') + { + $asExtraAttr['style'] = $sStyle; + } + foreach($asExtraAttr as $sAttrName=>$sAttrValue) + { + $sHtmlAttr .= ' '.$sAttrName.'="'.$sAttrValue.'"'; + } + if($bAutoClose) + { + $sHtml = self::encapsulate('', "\n".'<'.$sTag.$sHtmlAttr, ' />'); + } + else + { + $sHtml = self::encapsulate($oText, "\n".'<'.$sTag.$sHtmlAttr.'>', '', $sInter); + } + return $sHtml; + } + + private static function cleanRss($oText) + { + $asForbiddenChars = array('&', '<', '>', '"', "'"); + $asReplacementCode = array('&', '<', '>', '"', '''); + if(!is_array($oText)) + { + return str_replace($asForbiddenChars, $asReplacementCode, $oText); + } + elseif(count($oText)>0) + { + $oTextKeys = array_map(array($this, 'cleanRss'), array_keys($oText)); + $oTextValues = array_map(array($this, 'cleanRss'), array_values($oText)); + return array_combine($oTextKeys, $oTextValues); + } + else + { + return $oText; + } + } + + private static function encapsulate($oText, $sPre='', $sPost=false, $sInter='') + { + if($sPost===false) + { + $sPost = $sPre; + } + if(is_array($oText)) + { + $oText = implode($sPost.$sInter.$sPre, $oText); + } + return $sPre.$oText.$sPost; + } + + private static function rSortTimeMatrix($asMatrix, $sTimeCol) + { + $asResult = array(); + foreach($asMatrix as $iRowId=>$asLine) $asKeys[$iRowId] = strtotime($asLine[$sTimeCol]); + arsort($asKeys); + foreach($asKeys as $iRowId=>$iTimeStamp) $asResult[$iRowId] = $asMatrix[$iRowId]; + return $asResult; + } +} +?> \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..27c5cfb --- /dev/null +++ b/index.php @@ -0,0 +1,222 @@ +logMeIn($sAuthName, $sAuthCompany, $sToken, $sAction); + +//if connected +if($bUserOk && $sAction!=Databap::EXT_ACCESS) +{ + //if expected page, relocate + //$oDatabap->redirectExpectedPage(); + + //loading data + if($sAction!='') + { + //Public actions + switch($sAction) + { + case 'log_me_out': + $oDatabap->logMeOut(); + break; + case 'add_code': + $sResult = $oDatabap->addCode(array('description'=>$sDescription, 'content'=>$sContent, 'link'=>$sLink)); + break; + case 'edit_code': + $sResult = $oDatabap->editCode($oCode, $sContent); + break; + case 'read_code': + if($oCode!==false) $sResult = $oDatabap->getColoredCode($oCode); + break; + case 'nude_code': + if($oCode!==false) $sResult = $oDatabap->getNudeCode($oCode); + break; + case 'raw_code': + if($oCode!==false) $sResult = $oDatabap->getRawCode($oCode); + break; + case 'dl_code': + if($oCode!==false) $sResult = $oDatabap->getSavedCode($oCode); + break; + case 'dl_file': + $sResult = $oDatabap->getFile($iItemId); + break; + case 'print_code': + if($oCode!==false) $sResult = $oDatabap->getPrintCode($oCode); + break; + case 'add_procedure': + $sResult = $oDatabap->addProcedure($_POST); + break; + case 'get_procedure': + $sResult = $oDatabap->getProcedure($iProcId); + break; + case 'add_doc': + $sResult = $oDatabap->addDoc($_POST); + break; + case 'get_doc': + $sResult = $oDatabap->getDoc($iItemId); + break; + case 'art_redirect': + $sResult = $oDatabap->redirectArticle($iItemId); + break; + case 'upload_image': + $sResult = $oDatabap->uploadImage(); + break; + case 'upload_file': + $sResult = $oDatabap->uploadDoc(); + break; + case 'url': + $sResult = $oDatabap->checkValue(MySqlManager::URL_TABLE, array('phrase'=>$sLink)); + break; + case 'user_info': + $sResult = $oDatabap->getUserInfo($oDatabap->getUserId(), true); + break; + case 'profile': + $sResult = $oDatabap->getProfile($oUser); + break; + case 'search': + $sResult = $oDatabap->getResults($sSearchWords); + break; + case 'code_block': + $sResult = $oDatabap->getCodeBlock(); + break; + case 'join_chan': + $sResult = $oDatabap->joinChan($sChan, $bFirstConn, $asAttendees); + break; + case 'quit_chan': + $sResult = $oDatabap->quitChan($sChan); + break; + case 'disconnect_chat': + $sResult = $oDatabap->disconnectChat(); + break; + case 'add_message': + $sResult = $oDatabap->addChatMessage($sMessage, $sChan); + break; + case 'messages': + $sResult = $oDatabap->getMessages($iMessageId); + break; + case 'connected_users': + $sResult = $oDatabap->getConnectedUsers(true); + break; + case 'get_options': + $sResult = $oDatabap->getOptions(); + break; + case 'set_options': + $sResult = $oDatabap->setOptions($_POST, false); + break; + case 'list': + $sResult = $oDatabap->getItemList(); + break; + case 'css': + $sResult = $oDatabap->getStyleSheet(); + break; + } + + //Admin actions + if($oDatabap->getUserClearance()==Databap::CLEARANCE_ADMIN) + { + switch($sAction) + { + case 'add_user': + $asInfo = explode('-', strtolower($oUser)); + $oDatabap->addUser($asInfo[0], $asInfo[1], $asInfo[2]); + break; + case 'reset_token': + $sResult = print_r($oDatabap->resetToken(), true); + break; + case 'build_index': + $oDatabap->buildCompleteIndex(); + $sResult = 'Index ok'; + break; + case 'install': + $oMySqlInstall = new MySqlManager(); + $sResult = $oMySqlInstall->getFullInstallQuery(); + break; + } + } + } + //Loading a page + else + { + $sResult = $oDatabap->getPage($sPage, $_GET); + } +} +//External Access with token +elseif($bUserOk && $sAction==Databap::EXT_ACCESS) +{ + //Public actions + switch($sPage) + { + case 'rss': //RSS Feed + $sResult = $oDatabap->getRss($sCategory); + break; + } + + //Restricted actions + if($oDatabap->getUserClearance()==Databap::CLEARANCE_ADMIN) + { + switch($sPage) + { + case 'sap_blog': //Syncing SAP BW Blog with database & spreading the news on chat + $sResult = $oDatabap->syncSapBlog(); + break; + } + } +} +elseif($sAction!='') +{ + $sResult = Databap::DISCONNECTED; +} +//loading logon +else +{ + //$oDatabap->setExpectedPage($_SERVER['REQUEST_URI']); + $oPage = new Mask('logon'); + $oPage->setTag('name', $sAuthName); + $oPage->setTag('company', $sAuthCompany); + $sResult = $oPage->getMask(); +} + +//clean unwanted error log +ob_end_clean(); +//$sDebug = ob_get_clean(); + +echo $sResult; +//echo json_encode($sDebug); +?> \ No newline at end of file diff --git a/jquery/fileuploader.js b/jquery/fileuploader.js new file mode 100644 index 0000000..2badbf5 --- /dev/null +++ b/jquery/fileuploader.js @@ -0,0 +1,1304 @@ +/** + * http://github.com/valums/file-uploader + * + * Multiple file upload component with progress-bar, drag-and-drop. + * 2010 Andrew Valums ( andrew(at)valums.com ) + * + * Licensed under GNU GPL 2 or later, see license.txt. + */ + +// +// Helper functions +// + +var qq = qq || {}; + +/** + * Adds all missing properties from second obj to first obj + */ +qq.extend = function(first, second){ + for (var prop in second){ + first[prop] = second[prop]; + } +}; + +/** + * Searches for a given element in the array, returns -1 if it is not present. + * @param {Number} [from] The index at which to begin the search + */ +qq.indexOf = function(arr, elt, from){ + if (arr.indexOf) return arr.indexOf(elt, from); + + from = from || 0; + var len = arr.length; + + if (from < 0) from += len; + + for (; from < len; from++){ + if (from in arr && arr[from] === elt){ + return from; + } + } + return -1; +}; + +qq.getUniqueId = (function(){ + var id = 0; + return function(){ return id++; }; +})(); + +// +// Events + +qq.attach = function(element, type, fn){ + if (element.addEventListener){ + element.addEventListener(type, fn, false); + } else if (element.attachEvent){ + element.attachEvent('on' + type, fn); + } +}; +qq.detach = function(element, type, fn){ + if (element.removeEventListener){ + element.removeEventListener(type, fn, false); + } else if (element.attachEvent){ + element.detachEvent('on' + type, fn); + } +}; + +qq.preventDefault = function(e){ + if (e.preventDefault){ + e.preventDefault(); + } else{ + e.returnValue = false; + } +}; + +// +// Node manipulations + +/** + * Insert node a before node b. + */ +qq.insertBefore = function(a, b){ + b.parentNode.insertBefore(a, b); +}; +qq.remove = function(element){ + element.parentNode.removeChild(element); +}; + +qq.contains = function(parent, descendant){ + // compareposition returns false in this case + if (parent == descendant) return true; + + if (parent.contains){ + return parent.contains(descendant); + } else { + return !!(descendant.compareDocumentPosition(parent) & 8); + } +}; + +/** + * Creates and returns element from html string + * Uses innerHTML to create an element + */ +qq.toElement = (function(){ + var div = document.createElement('div'); + return function(html){ + div.innerHTML = html; + var element = div.firstChild; + div.removeChild(element); + return element; + }; +})(); + +// +// Node properties and attributes + +/** + * Sets styles for an element. + * Fixes opacity in IE6-8. + */ +qq.css = function(element, styles){ + if (styles.opacity != null){ + if (typeof element.style.opacity != 'string' && typeof(element.filters) != 'undefined'){ + styles.filter = 'alpha(opacity=' + Math.round(100 * styles.opacity) + ')'; + } + } + qq.extend(element.style, styles); +}; +qq.hasClass = function(element, name){ + var re = new RegExp('(^| )' + name + '( |$)'); + return re.test(element.className); +}; +qq.addClass = function(element, name){ + if (!qq.hasClass(element, name)){ + element.className += ' ' + name; + } +}; +qq.removeClass = function(element, name){ + var re = new RegExp('(^| )' + name + '( |$)'); + element.className = element.className.replace(re, ' ').replace(/^\s+|\s+$/g, ""); +}; +qq.setText = function(element, text){ + element.innerText = text; + element.textContent = text; +}; + +// +// Selecting elements + +qq.children = function(element){ + var children = [], + child = element.firstChild; + + while (child){ + if (child.nodeType == 1){ + children.push(child); + } + child = child.nextSibling; + } + + return children; +}; + +qq.getByClass = function(element, className){ + if (element.querySelectorAll){ + return element.querySelectorAll('.' + className); + } + + var result = []; + var candidates = element.getElementsByTagName("*"); + var len = candidates.length; + + for (var i = 0; i < len; i++){ + if (qq.hasClass(candidates[i], className)){ + result.push(candidates[i]); + } + } + return result; +}; + +/** + * obj2url() takes a json-object as argument and generates + * a querystring. pretty much like jQuery.param() + * + * how to use: + * + * `qq.obj2url({a:'b',c:'d'},'http://any.url/upload?otherParam=value');` + * + * will result in: + * + * `http://any.url/upload?otherParam=value&a=b&c=d` + * + * @param Object JSON-Object + * @param String current querystring-part + * @return String encoded querystring + */ +qq.obj2url = function(obj, temp, prefixDone){ + var uristrings = [], + prefix = '&', + add = function(nextObj, i){ + var nextTemp = temp + ? (/\[\]$/.test(temp)) // prevent double-encoding + ? temp + : temp+'['+i+']' + : i; + if ((nextTemp != 'undefined') && (i != 'undefined')) { + uristrings.push( + (typeof nextObj === 'object') + ? qq.obj2url(nextObj, nextTemp, true) + : (Object.prototype.toString.call(nextObj) === '[object Function]') + ? encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj()) + : encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj) + ); + } + }; + + if (!prefixDone && temp) { + prefix = (/\?/.test(temp)) ? (/\?$/.test(temp)) ? '' : '&' : '?'; + uristrings.push(temp); + uristrings.push(qq.obj2url(obj)); + } else if ((Object.prototype.toString.call(obj) === '[object Array]') && (typeof obj != 'undefined') ) { + // we wont use a for-in-loop on an array (performance) + for (var i = 0, len = obj.length; i < len; ++i){ + add(obj[i], i); + } + } else if ((typeof obj != 'undefined') && (obj !== null) && (typeof obj === "object")){ + // for anything else but a scalar, we will use for-in-loop + for (var i in obj){ + add(obj[i], i); + } + } else { + uristrings.push(encodeURIComponent(temp) + '=' + encodeURIComponent(obj)); + } + + return uristrings.join(prefix) + .replace(/^&/, '') + .replace(/%20/g, '+'); +}; + +// +// +// Uploader Classes +// +// + +var qq = qq || {}; + +/** + * Creates upload button, validates upload, but doesn't create file list or dd. + */ +qq.FileUploaderBasic = function(o){ + this._options = { + // set to true to see the server response + debug: false, + action: '/server/upload', + params: {}, + button: null, + multiple: true, + maxConnections: 3, + // validation + allowedExtensions: [], + sizeLimit: 0, + minSizeLimit: 0, + // events + // return false to cancel submit + onSubmit: function(id, fileName){}, + onProgress: function(id, fileName, loaded, total){}, + onComplete: function(id, fileName, responseJSON){}, + onCancel: function(id, fileName){}, + // messages + messages: { + typeError: "{file} a une extension invalide. Seules les extensions {extensions} sont autorisées", + sizeError: "{file} est trop lourd, la taille maximum est {sizeLimit}", + minSizeError: "{file} est trop petit, la taille minimum est {minSizeLimit}", + emptyError: "{file} est vide, sélectionner un fichier différent", + onLeave: "Les fichiers sont en cours de transfert, si vous quittez la page maintenant, le transfert sera annulé" + }, + showMessage: function(message){ + //alert(message); + databap.addErrorBefore(message, $(this.element).find('.uploader_list')); + } + }; + qq.extend(this._options, o); + + // number of files being uploaded + this._filesInProgress = 0; + this._handler = this._createUploadHandler(); + + if (this._options.button){ + this._button = this._createUploadButton(this._options.button, this._options.stepId); + } + + this._preventLeaveInProgress(); +}; + +qq.FileUploaderBasic.prototype = { + setParams: function(params){ + this._options.params = params; + }, + getInProgress: function(){ + return this._filesInProgress; + }, + _createUploadButton: function(element, iStepId){ + var self = this; + + return new qq.UploadButton({ + element: element, + stepId:iStepId, + multiple: this._options.multiple && qq.UploadHandlerXhr.isSupported(), + onChange: function(input){ + self._onInputChange(input); + } + }); + }, + _createUploadHandler: function(){ + var self = this, + handlerClass; + + if(qq.UploadHandlerXhr.isSupported()){ + handlerClass = 'UploadHandlerXhr'; + } else { + handlerClass = 'UploadHandlerForm'; + } + + var handler = new qq[handlerClass]({ + debug: this._options.debug, + action: this._options.action, + maxConnections: this._options.maxConnections, + onProgress: function(id, fileName, loaded, total){ + self._onProgress(id, fileName, loaded, total); + self._options.onProgress(id, fileName, loaded, total); + }, + onComplete: function(id, fileName, result){ + //alert('_complete2'); + self._onComplete(id, fileName, result); + self._options.onComplete(id, fileName, result); + }, + onCancel: function(id, fileName){ + self._onCancel(id, fileName); + self._options.onCancel(id, fileName); + } + }); + + return handler; + }, + _preventLeaveInProgress: function(){ + var self = this; + + qq.attach(window, 'beforeunload', function(e){ + if (!self._filesInProgress){return;} + + var e = e || window.event; + // for ie, ff + e.returnValue = self._options.messages.onLeave; + // for webkit + return self._options.messages.onLeave; + }); + }, + _onSubmit: function(id, fileName){ + this._filesInProgress++; + }, + _onProgress: function(id, fileName, loaded, total){ + }, + _onComplete: function(id, fileName, result){ + //alert('_complete4'); + this._filesInProgress--; + if (result.error){ + this._options.showMessage(result.error); + } + }, + _onCancel: function(id, fileName){ + this._filesInProgress--; + }, + _onInputChange: function(input){ + if (this._handler instanceof qq.UploadHandlerXhr){ + this._uploadFileList(input.files); + } else { + if (this._validateFile(input)){ + this._uploadFile(input); + } + } + this._button.reset(); + }, + _uploadFileList: function(files){ + for (var i=0; i this._options.sizeLimit){ + this._error('sizeError', name); + return false; + + } else if (size && size < this._options.minSizeLimit){ + this._error('minSizeError', name); + return false; + } + + return true; + }, + _error: function(code, fileName){ + var message = this._options.messages[code]; + function r(name, replacement){ message = message.replace(name, replacement); } + + r('{file}', this._formatFileName(fileName)); + r('{extensions}', this._options.allowedExtensions.join(', ')); + r('{sizeLimit}', this._formatSize(this._options.sizeLimit)); + r('{minSizeLimit}', this._formatSize(this._options.minSizeLimit)); + + this._options.showMessage(message); + }, + _formatFileName: function(name){ + if (name.length > 33){ + name = name.slice(0, 19) + '...' + name.slice(-13); + } + return name; + }, + _isAllowedExtension: function(fileName){ + var ext = (-1 !== fileName.indexOf('.')) ? fileName.replace(/.*[.]/, '').toLowerCase() : ''; + var allowed = this._options.allowedExtensions; + + if (!allowed.length){return true;} + + for (var i=0; i 99); + + return Math.max(bytes, 0.1).toFixed(1) + ['Ko', 'Mo', 'Go', 'To', 'Po', 'Eo'][i]; + } +}; + + +/** + * Class that creates upload widget with drag-and-drop and file list + * @inherits qq.FileUploaderBasic + */ +qq.FileUploader = function(o){ + // call parent constructor + qq.FileUploaderBasic.apply(this, arguments); + + // additional options + qq.extend(this._options, { + element: null, + // if set, will be used instead of uploader_list in template + listElement: null, + + template:'
    ' + + '
    Glisser les images ici
    ' + + 'Ajouter une image' + + '
    ' + + '
      ' + + '
      ', + + // template for one item in file list + fileTemplate:'
    • ' + + '' + + '' + + '' + + 'Cancel' + + 'Echoué' + + '
    • ', + + classes: { + // used to get elements from templates + button: 'uploader_button', + drop: 'uploader_droparea', + dropActive: 'uploader_droparea-active', + list: 'uploader_list', + + file: 'uploader_item_file', + spinner: 'uploader_item_loading', + size: 'uploader_item_size', + cancel: 'uploader_item_cancel', + + // added to list item when upload completes + // used in css to hide progress spinner + success: 'uploader_item_success', + fail: 'uploader_item_failed' + } + }); + // overwrite options with user supplied + qq.extend(this._options, o); + + this._element = this._options.element; + this._options.template = this._options.template.replace('id="uploader_list"', 'id="'+databap.getElemTag('uploader_list', this._options.stepId)+'"'); + this._element.innerHTML = this._options.template; + this._listElement = this._options.listElement || this._find(this._element, 'list'); + + this._classes = this._options.classes; + + this._button = this._createUploadButton(this._find(this._element, 'button'), this._options.stepId); + + this._bindCancelEvent(); + this._setupDragDrop(); +}; + +// inherit from Basic Uploader +qq.extend(qq.FileUploader.prototype, qq.FileUploaderBasic.prototype); + +qq.extend(qq.FileUploader.prototype, { + /** + * Gets one of the elements listed in this._options.classes + **/ + _find: function(parent, type){ + var element = qq.getByClass(parent, this._options.classes[type])[0]; + if (!element){ + throw new Error('element not found ' + type); + } + + return element; + }, + _setupDragDrop: function(){ + var self = this, + dropArea = this._find(this._element, 'drop'); + + var dz = new qq.UploadDropZone({ + element: dropArea, + onEnter: function(e){ + qq.addClass(dropArea, self._classes.dropActive); + e.stopPropagation(); + }, + onLeave: function(e){ + e.stopPropagation(); + }, + onLeaveNotDescendants: function(e){ + qq.removeClass(dropArea, self._classes.dropActive); + }, + onDrop: function(e){ + dropArea.style.display = 'none'; + qq.removeClass(dropArea, self._classes.dropActive); + self._uploadFileList(e.dataTransfer.files); + } + }); + + dropArea.style.display = 'none'; + + qq.attach(document, 'dragenter', function(e){ + if (!dz._isValidFileDrag(e)) return; + + dropArea.style.display = 'block'; + }); + qq.attach(document, 'dragleave', function(e){ + if (!dz._isValidFileDrag(e)) return; + + var relatedTarget = document.elementFromPoint(e.clientX, e.clientY); + // only fire when leaving document out + if ( ! relatedTarget || relatedTarget.nodeName == "HTML"){ + dropArea.style.display = 'none'; + } + }); + }, + _onSubmit: function(id, fileName){ + //alert('_onSubmit0'); + qq.FileUploaderBasic.prototype._onSubmit.apply(this, arguments); + this._addToList(id, fileName); + }, + _onProgress: function(id, fileName, loaded, total){ + qq.FileUploaderBasic.prototype._onProgress.apply(this, arguments); + + var item = this._getItemByFileId(id); + var size = this._find(item, 'size'); + size.style.display = 'inline'; + + var text; + if (loaded != total){ + text = Math.round(loaded / total * 100) + '% de ' + this._formatSize(total); + } else { + text = this._formatSize(total); + } + + qq.setText(size, text); + }, + _onComplete: function(id, fileName, result){ + //alert('_complete3'); + qq.FileUploaderBasic.prototype._onComplete.apply(this, arguments); + + // mark completed + var item = this._getItemByFileId(id); + qq.remove(this._find(item, 'cancel')); + qq.remove(this._find(item, 'spinner')); + + if (result.success) + { + qq.addClass(item, this._classes.success); + this._find(item, 'size').style.display = 'none'; + this._find(item, 'file').style.display = 'none'; + + //Get image Id + var itemIds = databap.getElemIds(item.id); + + //Get Image description from file name + if(result.fileDesc) + { + fileName = result.fileDesc; + } + else + { + fileName = this._find(item, 'file').innerHTML; + fileName = fileName.substring(0, fileName.lastIndexOf('.')).replace(/_/g, ' '); + } + + //Add Action buttons + addImage(this._options.stepId, itemIds[1], result.file_name, fileName); + } + else + { + qq.addClass(item, this._classes.fail); + } + }, + _addToList: function(id, fileName){ + var item = qq.toElement(this._options.fileTemplate); + item.qqFileId = id; + + //Add Ids into the tag + item.id = databap.getElemTag('uploader_item', [this._options.stepId, id+1]); + + var fileElement = this._find(item, 'file'); + qq.setText(fileElement, this._formatFileName(fileName)); + this._find(item, 'size').style.display = 'none'; + + this._listElement.appendChild(item); + }, + _getItemByFileId: function(id){ + var item = this._listElement.firstChild; + + // there can't be txt nodes in dynamically created list + // and we can use nextSibling + while (item){ + if (item.qqFileId == id) return item; + item = item.nextSibling; + } + }, + /** + * delegate click event for cancel link + **/ + _bindCancelEvent: function(){ + var self = this, + list = this._listElement; + + qq.attach(list, 'click', function(e){ + e = e || window.event; + var target = e.target || e.srcElement; + + if (qq.hasClass(target, self._classes.cancel)){ + qq.preventDefault(e); + + var item = target.parentNode; + self._handler.cancel(item.qqFileId); + qq.remove(item); + } + }); + } +}); + +qq.UploadDropZone = function(o){ + this._options = { + element: null, + onEnter: function(e){}, + onLeave: function(e){}, + // is not fired when leaving element by hovering descendants + onLeaveNotDescendants: function(e){}, + onDrop: function(e){} + }; + qq.extend(this._options, o); + + this._element = this._options.element; + + this._disableDropOutside(); + this._attachEvents(); +}; + +qq.UploadDropZone.prototype = { + _disableDropOutside: function(e){ + // run only once for all instances + if (!qq.UploadDropZone.dropOutsideDisabled ){ + + qq.attach(document, 'dragover', function(e){ + if (e.dataTransfer){ + e.dataTransfer.dropEffect = 'none'; + e.preventDefault(); + } + }); + + qq.UploadDropZone.dropOutsideDisabled = true; + } + }, + _attachEvents: function(){ + var self = this; + + qq.attach(self._element, 'dragover', function(e){ + if (!self._isValidFileDrag(e)) return; + + var effect = e.dataTransfer.effectAllowed; + if (effect == 'move' || effect == 'linkMove'){ + e.dataTransfer.dropEffect = 'move'; // for FF (only move allowed) + } else { + e.dataTransfer.dropEffect = 'copy'; // for Chrome + } + + e.stopPropagation(); + e.preventDefault(); + }); + + qq.attach(self._element, 'dragenter', function(e){ + if (!self._isValidFileDrag(e)) return; + + self._options.onEnter(e); + }); + + qq.attach(self._element, 'dragleave', function(e){ + if (!self._isValidFileDrag(e)) return; + + self._options.onLeave(e); + + var relatedTarget = document.elementFromPoint(e.clientX, e.clientY); + // do not fire when moving a mouse over a descendant + if (qq.contains(this, relatedTarget)) return; + + self._options.onLeaveNotDescendants(e); + }); + + qq.attach(self._element, 'drop', function(e){ + if (!self._isValidFileDrag(e)) return; + + e.preventDefault(); + self._options.onDrop(e); + }); + }, + _isValidFileDrag: function(e){ + var dt = e.dataTransfer, + // do not check dt.types.contains in webkit, because it crashes safari 4 + isWebkit = navigator.userAgent.indexOf("AppleWebKit") > -1; + + // dt.effectAllowed is none in Safari 5 + // dt.types.contains check is for firefox + return dt && dt.effectAllowed != 'none' && + (dt.files || (!isWebkit && dt.types.contains && dt.types.contains('Files'))); + + } +}; + +qq.UploadButton = function(o){ + this._options = { + element: null, + // if set to true adds multiple attribute to file input + multiple: false, + // name attribute of file input + name: 'file', + onChange: function(input){}, + hoverClass: 'uploader_button-hover', + focusClass: 'uploader_button-focus' + }; + + qq.extend(this._options, o); + + this._element = this._options.element; + + // make button suitable container for input + qq.css(this._element, { + position: 'relative', + overflow: 'hidden', + // Make sure browse button is in the right side + // in Internet Explorer + direction: 'ltr' + }); + + this._input = this._createInput(); +}; + +qq.UploadButton.prototype = { + /* returns file input element */ + getInput: function(){ + return this._input; + }, + /* cleans/recreates the file input */ + reset: function(){ + if (this._input.parentNode){ + qq.remove(this._input); + } + + qq.removeClass(this._element, this._options.focusClass); + this._input = this._createInput(); + }, + _createInput: function(){ + var input = document.createElement("input"); + + if (this._options.multiple){ + input.setAttribute("multiple", "multiple"); + } + + //Add Ids into Tag + var sIdTag = databap.getElemTag(this._options.name, this._options.stepId); + input.setAttribute("type", "file"); + input.setAttribute("name", sIdTag); + input.setAttribute("id", sIdTag); + + qq.css(input, { + position: 'absolute', + // in Opera only 'browse' button + // is clickable and it is located at + // the right side of the input + left: '-297px', + top: '-2px', + width:'auto', + fontFamily: 'Arial', + // 4 persons reported this, the max values that worked for them were 243, 236, 236, 118 + fontSize: '28px', + margin: 0, + padding: 0, + cursor: 'pointer', + opacity: 0 + }); + + this._element.appendChild(input); + + var self = this; + qq.attach(input, 'change', function(){ + self._options.onChange(input); + }); + + /* + qq.attach(input, 'mouseover', function(){ + qq.addClass(self._element, self._options.hoverClass); + }); + qq.attach(input, 'mouseout', function(){ + qq.removeClass(self._element, self._options.hoverClass); + }); + qq.attach(input, 'focus', function(){ + qq.addClass(self._element, self._options.focusClass); + }); + qq.attach(input, 'blur', function(){ + qq.removeClass(self._element, self._options.focusClass); + }); + */ + // IE and Opera, unfortunately have 2 tab stops on file input + // which is unacceptable in our case, disable keyboard access + if (window.attachEvent){ + // it is IE or Opera + input.setAttribute('tabIndex', "-1"); + } + + return input; + } +}; + +/** + * Class for uploading files, uploading itself is handled by child classes + */ +qq.UploadHandlerAbstract = function(o){ + this._options = { + debug: false, + action: '/upload.php', + // maximum number of concurrent uploads + maxConnections: 999, + onProgress: function(id, fileName, loaded, total){}, + onComplete: function(id, fileName, response){}, + onCancel: function(id, fileName){} + }; + qq.extend(this._options, o); + + this._queue = []; + // params for files in queue + this._params = []; +}; +qq.UploadHandlerAbstract.prototype = { + log: function(str){ + if (this._options.debug && window.console) console.log('[uploader] ' + str); + }, + /** + * Adds file or file input to the queue + * @returns id + **/ + add: function(file){}, + /** + * Sends the file identified by id and additional query params to the server + */ + upload: function(id, params){ + var len = this._queue.push(id); + + var copy = {}; + qq.extend(copy, params); + this._params[id] = copy; + + // if too many active uploads, wait... + if (len <= this._options.maxConnections){ + //alert('_upload0'); + this._upload(id, this._params[id]); + } + }, + /** + * Cancels file upload by id + */ + cancel: function(id){ + this._cancel(id); + this._dequeue(id); + }, + /** + * Cancells all uploads + */ + cancelAll: function(){ + for (var i=0; i= max && i < max){ + var nextId = this._queue[max-1]; + this._upload(nextId, this._params[nextId]); + } + } +}; + +/** + * Class for uploading files using form and iframe + * @inherits qq.UploadHandlerAbstract + */ +qq.UploadHandlerForm = function(o){ + qq.UploadHandlerAbstract.apply(this, arguments); + + this._inputs = {}; +}; +// @inherits qq.UploadHandlerAbstract +qq.extend(qq.UploadHandlerForm.prototype, qq.UploadHandlerAbstract.prototype); + +qq.extend(qq.UploadHandlerForm.prototype, { + add: function(fileInput){ + //alert('add0b'); + fileInput.setAttribute('name', 'qqfile'); + var id = 'qq-upload-handler-iframe' + qq.getUniqueId(); + + this._inputs[id] = fileInput; + + // remove file input from DOM + if (fileInput.parentNode){ + qq.remove(fileInput); + } + + return id; + }, + getName: function(id){ + // get input value and remove path to normalie + return this._inputs[id].value.replace(/.*(\/|\\)/, ""); + }, + _cancel: function(id){ + this._options.onCancel(id, this.getName(id)); + + delete this._inputs[id]; + + var iframe = document.getElementById(id); + if (iframe){ + // to cancel request set src to something else + // we use src="javascript:false;" because it doesn't + // trigger ie6 prompt on https + iframe.setAttribute('src', 'javascript:false;'); + + qq.remove(iframe); + } + }, + _upload: function(id, params){ + var input = this._inputs[id]; + + if (!input){ + throw new Error('file with passed id was not added, or already uploaded or cancelled'); + } + + var fileName = this.getName(id); + + var iframe = this._createIframe(id); + var form = this._createForm(iframe, params); + form.appendChild(input); + + var self = this; + this._attachLoadEvent(iframe, function(){ + self.log('iframe loaded'); + + var response = self._getIframeContentJSON(iframe); + + self._options.onComplete(id, fileName, response); + self._dequeue(id); + + delete self._inputs[id]; + // timeout added to fix busy state in FF3.6 + setTimeout(function(){ + qq.remove(iframe); + }, 1); + }); + + form.submit(); + qq.remove(form); + + return id; + }, + _attachLoadEvent: function(iframe, callback){ + qq.attach(iframe, 'load', function(){ + // when we remove iframe from dom + // the request stops, but in IE load + // event fires + if (!iframe.parentNode){ + return; + } + + // fixing Opera 10.53 + if (iframe.contentDocument && + iframe.contentDocument.body && + iframe.contentDocument.body.innerHTML == "false"){ + // In Opera event is fired second time + // when body.innerHTML changed from false + // to server response approx. after 1 sec + // when we upload file with iframe + return; + } + + callback(); + }); + }, + /** + * Returns json object received by iframe from server. + */ + _getIframeContentJSON: function(iframe){ + // iframe.contentWindow.document - for IE<7 + var doc = iframe.contentDocument ? iframe.contentDocument: iframe.contentWindow.document, + response; + + this.log("converting iframe's innerHTML to JSON"); + this.log("innerHTML = " + doc.body.innerHTML); + + try { + response = eval("(" + doc.body.innerHTML + ")"); + } catch(err){ + response = {}; + } + + return response; + }, + /** + * Creates iframe with unique name + */ + _createIframe: function(id){ + // We can't use following code as the name attribute + // won't be properly registered in IE6, and new window + // on form submit will open + // var iframe = document.createElement('iframe'); + // iframe.setAttribute('name', id); + + var iframe = qq.toElement('