> ------------------------------------------------------------------------------ See main.inc.php for release information EStat_root : common classe for admin and public classes --------------------------------------------------------------------------- */ include_once(PHPWG_PLUGINS_PATH.'GrumPluginClasses/classes/CommonPlugin.class.inc.php'); include_once(ESTAT_LIB.'statDB.class.inc.php'); include_once(ESTAT_LIB.'statDBGlobal.class.inc.php'); include_once(ESTAT_LIB.'statDBMonth.class.inc.php'); include_once(ESTAT_LIB.'statDBCountry.class.inc.php'); class EStat_root extends CommonPlugin { const DATA_DIRECTORY='plugins/EStat/data/'; const EXPORT_DIRECTORY='plugins/EStat/export/'; const FILE_MONTH='ES-M'; const FILE_GLOBAL='ES-G'; const FILE_COUNTRY='ES-IPCountry'; public $countryCodes=array( 'AD','AE','AF','AG','AI','AL','AM','AO','AQ','AR','AS','AT','AU','AW','AX','AZ', 'BA','BB','BD','BE','BF','BG','BH','BI','BJ','BL','BM','BN','BO','BQ','BR','BS','BT','BV','BW','BY','BZ', 'CA','CC','CD','CF','CG','CH','CI','CK','CL','CM','CN','CO','CR','CU','CV','CW','CX','CY','CZ', 'DE','DJ','DK','DM','DO','DZ', 'EC','EE','EG','EH','ER','ES','ET', 'FI','FJ','FK','FM','FO','FR', 'GA','GB','GD','GE','GF','GG','GH','GI','GL','GM','GN','GP','GQ','GR','GS','GT','GU','GW','GY', 'HK','HM','HN','HR','HT','HU', 'ID','IE','IL','IM','IN','IO','IQ','IR','IS','IT', 'JE','JM','JO','JP', 'KE','KG','KH','KI','KM','KN','KP','KR','KW','KY','KZ', 'LA','LB','LC','LI','LK','LR','LS','LT','LU','LV','LY', 'MA','MC','MD','ME','MF','MG','MH','MK','ML','MM','MN','MO','MP','MQ','MR','MS','MT','MU','MV','MW','MX','MY','MZ', 'NA','NC','NE','NF','NG','NI','NL','NO','NP','NR','NU','NZ', 'OM', 'PA','PE','PF','PG','PH','PK','PL','PM','PN','PR','PS','PT','PW','PY', 'QA', 'RE','RO','RS','RU','RW', 'SA','SB','SC','SD','SE','SG','SH','SI','SJ','SK','SL','SM','SN','SO','SR','SS','ST','SV','SX','SY','SZ', 'TC','TD','TF','TG','TH','TJ','TK','TL','TM','TN','TO','TR','TT','TV','TW','TZ', 'UA','UG','UM','US','UY','UZ', 'VA','VC','VE','VG','VI','VN','VU', 'WF','WS', 'XA', 'YE','YT', 'ZA','ZM','ZW' ); protected $fileStatDir=''; public function __construct($prefixeTable, $filelocation) { $this->setPluginName('EStat'); $this->setPluginNameFiles("estat"); parent::__construct($prefixeTable, $filelocation); $this->section_name=$this->getPluginNameFiles(); $this->fileStatDir=GPCCore::getPiwigoSystemPath().'/'.PWG_LOCAL_DIR.self::DATA_DIRECTORY; } /** * initialize default values */ public function initConfig() { //global $user; $this->config=array( 'plugin.newInstall' => 'y', 'plugin.active' => 'n', 'plugin.historyImported' => 'n', 'plugin.ipCountryBuild' => 'n', 'build.delay' => '+24hours', // delay between 2 consolidation 'build.start' => '2:30:00', // consolidation can be done from 'build.end' => '6:30:00', // build.start hour to build.end hour 'compress.public' => 'n', // compress file in public session 'compress.method' => 'none', // none, gz 'global.itemPerPage' => 250, 'logs.reverseDNS' => 'n', 'logs.ipCountry' => 'y' ); } /** * return admin link * @param String $mode: if equal 'ajax' return url for ajax call * @return String: url */ public function getAdminLink($mode='') { switch($mode) { case 'ajax': return('plugins/'.basename(dirname($this->getFileLocation())).'/estat_ajax.php'); break; case 'ajaxExport': return('plugins/'.basename(dirname($this->getFileLocation())).'/estat_ajax_export.php'); break; default: return(parent::getAdminLink()); break; } } /** * checks files that need to be consolidated * pack them if needed * * @param Boolean $force: force consolidation if true, otherwise let system to determinate if needed * @param Boolean $pack: pack files if needed * @return Boolean: true if at least one consolidation was applied, otherwise false */ protected function checkBuildStatPeriod($force=false, $pack=true) { $build=false; $currentTime=date('H:i:s'); $currentDateTime=strtotime($this->config['build.delay']); $currentYearMonth=date('Ym'); $dbStatG=new StatDBGlobal($this->fileStatDir, self::FILE_GLOBAL); $dbStatG->open(ASDF_OPEN_WRITE); // check if current period exist in global file $dbStatG->checkFilePeriod(substr($currentYearMonth, 0,4), substr($currentYearMonth,4,2)); // get list of files to build $fileList=$dbStatG->getFilesList(ASDF_EXIST_PACKED|ASDF_EXIST_UNPACKED, ASDF_BUILD_MISSING); foreach($fileList as $file) { $file['month']=sprintf('%02d', $file['month']); if($force or ($currentDateTime>=$file['lastBuilt'] and $currentTime>=$this->config['build.start'] and $currentTime<=$this->config['build.end'])) { if($dbStatG->buildStatPeriod($this->fileStatDir, self::FILE_MONTH, $file['year'], $file['month'])) $build=true; if($pack and //pack only if asked $file['packed']==0 and //packed size=0 -> not packed $file['year'].$file['month']!=$currentYearMonth) //don't pack current file! { $dbStatM=new StatDBMonth($this->fileStatDir, self::FILE_MONTH, $file['year'], $file['month']); if($dbStatM->pack()) { $packedFileName=$dbStatM->getFileName(ASDF_EXIST_PACKED, true); if($packedFileName!='') $dbStatG->updatePackedSize($file['year'], $file['month'], filesize($packedFileName)); $dbStatM->delete(ASDF_DELETE_UNPACKED); } } } } $dbStatG->close(); return($build); } /** * Returns the needed informations to process the data migration * * Returned information is an array: * array( * 'nbLogs' => number of logs * 'idMax' => maximum id in HISTORY table * 'idMin' => minimum id in HISTORY table * 'dateMax' => maximum date in HISTORY table * 'dateMin' => minimum date in HISTORY table * 'periods' => array of periods to process * key=period (yyyy-mm) * value=number of events for the period * * @return Array */ protected function getPiwigoHistoryInfo() { $data=array( 'nbLogs' => 0, 'idMax' => 0, 'idMin' => 0, 'dateMax' => '', 'dateMin' => '', 'periods' => array() ); $sql="SELECT COUNT(id), MIN(id), MAX(id), MIN(`date`), MAX(`date`) FROM ".HISTORY_TABLE; $result=pwg_query($sql); if($result) { if($tmp=pwg_db_fetch_row($result)) { $data['nbLogs']=$tmp[0]; $data['idMin']=$tmp[1]; $data['idMax']=$tmp[2]; $data['dateMin']=$tmp[3]; $data['dateMax']=$tmp[4]; $sql="SELECT COUNT(id) AS nbEvents, YEAR(`date`) AS periodYear, MONTH(`date`) AS periodMonth FROM ".HISTORY_TABLE." GROUP BY periodYear, periodMonth ORDER BY periodYear, periodMonth;"; $result=pwg_query($sql); if($result) { while($row=pwg_db_fetch_assoc($result)) { $data['periods'][sprintf('%04d-%02d', $row['periodYear'], $row['periodMonth'])]=$row['nbEvents']; } } } } return($data); } /* * ------------------------------------------------------------------------- * -- functions used by the ajax classes * ------------------------------------------------------------------------- */ /** * generic function to consolidate a list of Id in a list with distinct Id * * the &$table parameter is an array with predefined keys * * * @param Array &$table : the table were to put result * @param String $key : the key (of the table) where result have to be put * @param String $sql : SQL request to produce the id list */ protected function prepareIdList(&$table, $key, $sql) { $result=pwg_query($sql); if($result) { while($row=pwg_db_fetch_assoc($result)) { $table[$key][$row['id']]=$row; } } } /** * generic function to prepare a list of unique IP adress with the following * informations: * - reverse DNS (if asked) * - country * returned keys are IP adress, associated value is an array * ip => array('rdns' => reverseDNS, 'country' => country_code) * * @param Array &$table : the table were to put result * @param Array $items : array of IP adress * @param Boolean $force : if true, force to produce the reversed DNS address */ protected function getIpInfos(&$table, $items, $force=false) { $dbCountry=new StatDBCountry($this->fileStatDir, self::FILE_COUNTRY); $dbCountry->open(ASDF_OPEN_READ); foreach($items as $IP) { $table['IPadress'][$IP]=array( 'rdns' => '', 'country' => $dbCountry->getIpCountry($IP, false) ); if($force or $this->config['logs.reverseDNS']=='y') $table['IPadress'][$IP]=gethostbyaddr($IP); } $dbCountry->close(); } /** * return the label associated to the id, or a default value if nothing is * found * * @param Array $table: array of label * @param string $key: type of id * @param string $id: id for which the label have to be retrieved * @param string $default: default value to return if no label found * @return string: label */ protected function getId($table, $key, $id, $default='', $field='name') { if(is_array($id)) { $tmp=array(); foreach($id as $val) { $tmp[]=$this->getId($table, $key, $val, $default, $field); } return(implode(',', $tmp)); } elseif(isset($table[$key]) and isset($table[$key][$id]) and isset($table[$key][$id][$field])) { return($table[$key][$id][$field]); } else return($default); } /** * build a filter of category id; for each given id, the childrens are selected * too * * @param Array $catId: category id list * @return Array */ protected function buildCatIdFilter($catId) { $returned=array(); if(!is_array($catId)) return($returned); $where=array(); foreach($catId as $id) { $where[]="FIND_IN_SET('$id', uppercats)"; } $sql="SELECT DISTINCT id FROM ".CATEGORIES_TABLE." WHERE ".implode(' OR ', $where); $tmpResult=pwg_query($sql); if($tmpResult) { $tmp=array(); while($row=pwg_db_fetch_row($tmpResult)) { $tmp[]=$row[0]; } $returned=array('operator' => 'in', 'value' => $tmp); } return($returned); } } //class /* * ------------------------------------------------------------------------------------------------ * -- classes used for ajax -- * ------------------------------------------------------------------------------------------------ */ /** * this class is used to build list of unique Id */ class EStat_IdList { protected $listItems=array(); public function __construct($items) { if(is_array($items)) $this->init($items); } public function __destruct() { } /** * initialize the names of identifiers pool * for example, init(array('album', 'image')) to declare a pool for 'album' id * and another pool for 'image' id * * @param Array $items: array of names */ public function init($items) { $this->listItems=array(); foreach($items as $item) { $this->listItems[$item]=array(); } } /** * add items in the pool * * $items is an array of (key => value) * for example: * addItems( * 'album' => 1, * 'album' => 2, * 'album' => 1, * 'image' => 236 * ); * * will be (internaly) stored as * listItems( * 'album' => array(1,2), * 'image' => array(236) * ); * * @param Array $items */ public function addItems($items) { if(is_array($items)) { foreach($items as $key => $item) { $this->add($key, $item); } } } /** * return the associated list of item for an identifier * for example: * getItems('album') * will return: * array(1,2) * * @param Array $item: identifier * @return Array */ public function getItems($item) { if(isset($this->listItems[$item])) { return(array_keys($this->listItems[$item])); } else { return(array()); } } /** * add an item for an identifier * * @param string $key: identifier * @param string $item: value to add */ private function add($key, $item) { if(is_array($item)) { foreach($item as $val) { $this->add($key, $val); } return(true); } if($item!='' and isset($this->listItems[$key]) and !isset($this->listItems[$key][$item])) { $this->listItems[$key][$item]=''; } } } // class EStat_IdList class ReverseIP { protected $ipList=array(); protected $ipFile=''; /** * add an IP adress in the list, by default there's no browser type set * for the IP adress * @param String $IP: IP adress * @param Integer $type: if not set, value is defined automatically */ public function addIP($IP, $type=null) { if(!isset($this->ipList[$IP])) $this->ipList[$IP]=UA_BROWSER_TYPE_UNKNOWN; //$this->fromReverseDNS(gethostbyaddr($IP)); } /** * return IP browser type * @param String $IP: IP adress * @return Integer: -1 if IP adress not exists */ public function getIP($IP) { if(!isset($this->ipList[$IP])) return(-1); return($this->ipList[$IP]); } /** * set IP type * * @param String $IP: IP adress * @param Integer $type: type of browser * @return Integer: type set, -1 if an error occurs */ public function setIP($IP, $type) { if(!isset($this->ipList[$IP])) return(-1); if($this->ipList[$IP]==-1) $this->ipList[$IP]=$type; return($this->ipList[$IP]); } /** * delete the temporary ip file */ public function deleteIPFile() { if(file_exists($this->ipFile)) unlink($this->ipFile); } /** * load an IP file in memory * * @param String $file: file to load * @return Boolean: true or false */ public function loadIPFile($file) { $this->ipFile=$file; if(!file_exists($file)) return(false); $this->ipList=array(); $fHandle=fopen($this->ipFile, 'r'); if($fHandle) { $tmp=fread($fHandle, filesize($this->ipFile)); fclose($fHandle); $this->ipList=unserialize($tmp); return(true); } return(false); } /** * save IP list on a file * * @param String $file: file to load * @return Boolean: true or false */ public function saveIPFile() { if($this->ipFile=='') return(false); $fHandle=fopen($this->ipFile, 'w'); if($fHandle) { fwrite($fHandle, serialize($this->ipList)); fclose($fHandle); return(true); } return(false); } /** * try to know from a reversed DNS adress if visitor is a crawler or not * * @param String $revDNS: a reversed DNS IP address * @return Integer: UA_BROWSER_TYPE_UNKNOWN or UA_BROWSER_TYPE_CRAWLER */ public function fromReverseDNS($revDNS) { $returned=UA_BROWSER_TYPE_UNKNOWN; if(preg_match('/\s*([a-z]*(?:bot|spyder|crawl|crawler|spider)[a-z]*)/i', $revDNS)) $returned=UA_BROWSER_TYPE_CRAWLER; return($returned); } } // class ReverseIP ?>