Current Path : /var/www/html/clients/kampol.e-nk.ru/administrator/components/com_joomlapack/models/ |
Current File : /var/www/html/clients/kampol.e-nk.ru/administrator/components/com_joomlapack/models/statistics.php |
<?php /** * @package JoomlaPack * @version $id$ * @license GNU General Public License, version 2 or later * @author JoomlaPack Developers * @copyright Copyright 2006-2009 JoomlaPack Developers * @since 1.3 */ defined('_JEXEC') or die('Restricted access'); /** * JoomlaPack statistics model class * used for all requirements of backup statistics in JP * */ class JoomlapackModelStatistics extends JModel { /** @var integer Backup Statistics ID */ var $_id; /** @var stdClass Statistics object */ var $_stats; /** @var TableStatistics The statistics table being updated */ var $_table; /** @var int Total number of backup statistics records */ var $_total; /** @var array A list of backup statistics records */ var $_list; /** * Overides the JModel implementation to provide a Singleton implementation * * @param string The model type to instantiate * @param string Prefix for the model class name. Optional. * @param array Configuration array for model. Optional. * @return JoomlapackModelStatistics A model object, or false on failure */ function &getInstance( $type = 'model', $prefix = '', $config = array() ) { static $instance; if(!is_object($instance)) { $instance = new JoomlapackModelStatistics(); } return $instance; } /** * Constructor. Sets the internal reference to Statistics ID. * */ function __construct($id = 0) { global $mainframe; parent::__construct(); // Get the pagination request variables $limit = $mainframe->getUserStateFromRequest('global.list.limit', 'limit', $mainframe->getCfg('list_limit')); $limitstart = $mainframe->getUserStateFromRequest(JRequest::getCmd('option','com_joomlapack') .'profileslimitstart','limitstart',0); // Set the page pagination variables $this->setState('limit',$limit); $this->setState('limitstart',$limitstart); $this->setId($id); } /** * Sets a Profile ID and resets internal data * * @param int $id Profile ID */ function setId($id=0) { $this->_id = $id; $this->_stats = null; } /** * Returns the entry for the backup statistic record whose ID is loaded in the model * * @return stdClass An object representing the backup statistic record */ function &getStatistic() { if(empty($this->_stats)) { $db =& $this->getDBO(); $query = "SELECT * FROM #__jp_stats WHERE `id`". " = ".$this->_id; $db->setQuery($query); $db->query(); $this->_stats = $db->loadObject(); } return $this->_stats; } /** * Returns a list of backup statistics records, respecting the pagination * * @return unknown */ function &getStatisticsList($overrideLimits = false) { if( empty($this->_list) ) { $db =& $this->getDBO(); $query = "SELECT * FROM ".$db->nameQuote('#__jp_stats')." ORDER BY ".$db->nameQuote('id')." DESC"; $limitstart = $this->getState('limitstart'); $limit = $this->getState('limit'); if($overrideLimits) $this->_list = $this->_getList($query); else $this->_list = $this->_getList($query, $limitstart, $limit); } return $this->_list; } /** * Multiple backup attempts can share the same backup file name. Only * the last backup attempt's file is considered valid. Previous attempts * have to be deemed "obsolete". This method returns a list of backup * statistics ID's with "valid"-looking names. IT DOES NOT CHECK FOR THE * EXISTENCE OF THE BACKUP FILE! * * @return array A list of ID's for records w/ "valid"-looking backup files * */ function &getValidLookingBackupFiles($useprofile = false) { $db =& JFactory::getDBO(); $query = "SELECT MAX(id) as `id` FROM #__jp_stats as j WHERE (". $db->nameQuote('status') ."=".$db->Quote('complete').")"; if($useprofile) { $session =& JFactory::getSession(); $profile_id = $session->get('profile', null, 'joomlapack'); $query .= " AND (".$db->nameQuote('profile_id')." = ".$db->Quote($profile_id).")"; } $query .= " GROUP BY ".$db->nameQuote('absolute_path'); $db->setQuery($query); $array = $db->loadResultArray(); if( !is_array($array) ) $array = array(); return $array; } /** * Returns the same list as getStatisticsList(), but includes an extra field * named 'meta' which categorises attempts based on their backup archive status * * @return array An object array of backup attempts */ function &getStatisticsListWithMeta($overrideLimits = false) { $allStats =& $this->getStatisticsList($overrideLimits); $valid =& $this->getValidLookingBackupFiles(); jimport('joomla.filesystem.file'); if(!empty($allStats)) { $registry =& JoomlapackModelRegistry::getInstance(); $basedir = $registry->get('OutputDirectory'); reset($allStats); while(list($key, $value) = each($allStats)) { $stat =& $allStats[$key]; if(in_array($stat->id, $valid)) { $archiveFile = $stat->absolute_path; if(@file_exists($archiveFile)) { // In valid list, archive exists $stat->meta = 'ok'; $stat->size = filesize($archiveFile); } else { // Test if files exist in the current output folder location $archiveFile = $basedir.DS.$stat->archivename; if(@file_exists($archiveFile)) { // In valid list, archive exists $stat->meta = 'ok'; if($stat->multipart == 0) { $stat->size = filesize($archiveFile); } } else { // In valid list, archive does not exist $stat->meta = 'obsolete'; } } // For multipart downloads, calculate file size differently if($stat->multipart != 0) { $allFiles = $this->getAllFilenames($stat->id); $filesize = 0; if(count($allFiles) > 0) { foreach($allFiles as $filename) { $filesize += @filesize($filename); } } $stat->size = $filesize; } } else { switch($stat->status) { case 'run': $stat->meta = 'pending'; break; case 'fail': $stat->meta = 'fail'; break; default: $stat->meta = 'obsolete'; break; } } } } unset($valid); return $allStats; } function getLatestBackupFilename() { $db =& $this->getDBO(); $query = 'SELECT max(id) FROM #__jp_stats'; $db->setQuery($query); $id = $db->loadResult(); if(empty($id)) return ''; return $this->getFilename($id); } function getLatestBackupId() { $db =& $this->getDBO(); $query = 'SELECT max(id) FROM #__jp_stats'; $db->setQuery($query); $id = $db->loadResult(); return($id); } /** * Returns the details of the latest backup as HTML * * @return string HTML * * @todo Move this into a helper class */ function getLatestBackupDetails() { $db =& $this->getDBO(); $query = 'SELECT max(id) FROM #__jp_stats'; $db->setQuery($query); $id = $db->loadResult(); if(empty($id)) return '<p>'.JText::_('BACKUP_STATUS_NONE').'</p>'; $this->setId($id); $record =& $this->getStatistic(); jimport('joomla.utilities.date'); switch($record->status) { case 'run': $status = JText::_('STATS_LABEL_STATUS_PENDING'); break; case 'fail': $status = JText::_('STATS_LABEL_STATUS_FAIL'); break; case 'complete': $status = JText::_('STATS_LABEL_STATUS_OK'); break; } switch($record->origin) { case 'frontend': $origin = JText::_('STATS_LABEL_ORIGIN_FRONTEND'); break; case 'backend': $origin = JText::_('STATS_LABEL_ORIGIN_BACKEND'); break; } switch($record->type) { case 'full': $type = JText::_('STATS_LABEL_TYPE_FULL'); break; case 'dbonly': $type = JText::_('STATS_LABEL_TYPE_DBONLY'); break; case 'extradbonly': $type = JText::_('STATS_LABEL_TYPE_EXTRADBONLY'); break; } $startTime = new JDate($record->backupstart); $html = '<table>'; $html .= '<tr><td>'.JText::_('STATS_LABEL_START').'</td><td>'.$startTime->toFormat(JText::_('DATE_FORMAT_LC4')).'</td></tr>'; $html .= '<tr><td>'.JText::_('STATS_LABEL_DESCRIPTION').'</td><td>'.$record->description.'</td></tr>'; $html .= '<tr><td>'.JText::_('STATS_LABEL_STATUS').'</td><td>'.$status.'</td></tr>'; $html .= '<tr><td>'.JText::_('STATS_LABEL_ORIGIN').'</td><td>'.$origin.'</td></tr>'; $html .= '<tr><td>'.JText::_('STATS_LABEL_TYPE').'</td><td>'.$type.'</td></tr>'; $html .= '</table>'; return $html; } /** * Returns a list of (the absolute paths to) old backup files which should be deleted, * based on user's quota settings * * @return array */ function getOldBackupsToDelete() { // If no quota settings are enabled, quit $registry =& JoomlapackModelRegistry::getInstance(); $useCountQuotas = $registry->get('enableCountQuotas'); $useSizeQuotas = $registry->get('enableSizeQuotas'); if(! ($useCountQuotas || $useSizeQuotas) ) { JoomlapackLogger::WriteLog(_JP_LOG_DEBUG, "No quotas were defined; old backup files will be kept intact" ); return array(); // No quota limits were requested } $latestBackupId = $this->getLatestBackupId(); // Get quota values $countQuota = $registry->get('countQuota'); $sizeQuota = $registry->get('sizeQuota'); // Get valid-looking backup ID's $validIDs =& $this->getValidLookingBackupFiles(true); // Create a list of valid files $allFiles = array(); if(count($validIDs)) { foreach($validIDs as $id) { // Multipart processing $filenames = $this->getAllFilenames($id, true); if(!is_null($filenames)) { // Only process existing files $filesize = 0; foreach($filenames as $filename) { $filesize += @filesize($filename); } $allFiles[] = array('id' => $id, 'filenames' => $filenames, 'size' => $filesize); } } } unset($validIDs); // If there are no files, exit early if(count($allFiles) == 0) { JoomlapackLogger::WriteLog(_JP_LOG_DEBUG, "There were no old backup files to apply quotas on" ); return array(); } // Init arrays $ret = array(); $leftover = array(); // Do we need to apply count quotas? if($useCountQuotas && is_numeric($countQuota) && !($countQuota <= 0) ) { // Are there more files than the quota limit? if( !(count($allFiles) > $countQuota) ) { // No, effectively skip the quota checking $leftover =& $allFiles; } else { JoomlapackLogger::WriteLog(_JP_LOG_DEBUG, "Processing count quotas" ); // Yes, aply the quota setting. Add to $ret all entries minus the last // $countQuota ones. $count = 0; $checkLimit = count($allFiles) - $countQuota; // Only process if at least one file (current backup!) is to be left if(!($checkLimit == 0)) foreach($allFiles as $def) { $count++; if($count <= $checkLimit) { if($latestBackupId == $def['id']) { $count--; } else $ret[] = $def['filenames']; } else { $leftover[] = $def; } } unset($allFiles); } } else { // No count quotas are applied $leftover =& $allFiles; } // Do we need to apply size quotas? if( $useSizeQuotas && is_numeric($sizeQuota) && !($sizeQuota <= 0) && (count($leftover) > 0) ) { JoomlapackLogger::WriteLog(_JP_LOG_DEBUG, "Processing size quotas" ); // OK, let's start counting bytes! $sizeQuota = $sizeQuota * 1024 * 1024; // Convert size quota from Mb to bytes $runningSize = 0; while(count($leftover) > 0) { // Each time, remove the last element of the backup array and calculate // running size. If it's over the limit, add the archive to the return array. $def = array_pop($leftover); $runningSize += $def['size']; if($runningSize >= $sizeQuota) { if($latestBackupId == $def['id']) { $runningSize -= $def['size']; } else $ret[] = $def['filenames']; } } } // Convert the $ret 2-dimensional array to single dimensional $out = array(); foreach($ret as $temp) { foreach($temp as $filename) { $out[] = $filename; } } // Return the rest of the entries, if any return $out; } /** * Returns the filename of the backup archive for the specified backup ID, or null * if the backup type is wrong or the file doesn't exist * * @param int $id The backup ID * @return string|null The filename or null if it's not applicable */ function getFilename($id) { $this->setId($id); $stat =& $this->getStatistic(); $filename = $stat->absolute_path; // Check if it exists, otherwise attempt to provide relocated filename jimport('joomla.filesystem.file'); if(!@file_exists($filename)) { $registry =& JoomlapackModelRegistry::getInstance(); $basedir = $registry->get('OutputDirectory'); $archiveFile = $basedir.DS.$stat->archivename; $filename = @file_exists($archiveFile) ? $archiveFile : ''; } // Do not return filename for invalid backups if($stat->status != 'complete') { return null; } return $filename; } /** * Returns all the filenames of the backup archives for the specified backup ID, * or null if the backup type is wrong or the file doesn't exist. It takes into * account the multipart nature of Split Backup Archives. * * @param int $id The backup ID * @return array|null The filename or null if it's not applicable */ function getAllFilenames($id, $skipNonComplete = true) { $this->setId($id); $stat =& $this->getStatistic(); $basefile = $stat->absolute_path; $filenames = array(); // Calculate all the filenames for this backup if($stat->multipart == 0) { // Non-split archive $filenames[] = $basefile; } else { // Find the base filename and extension $dotpos = strrpos($basefile, '.'); $extension = substr($basefile, $dotpos); $basefile = substr($basefile, 0, $dotpos); // Calculate the multiple names $multipart = $stat->multipart; for($i = 1; $i <= $multipart; $i++ ) { // Note: For $multipart = 10, it will produce i.e. .z01 through .z10 // This is intentional. If the backup aborts and multipart=1, we // might be stuck with a .z01 file instead of a .zip. So do not // change the less than or equal with a straight less than. $filenames[] = $basefile.substr($extension,0,2).sprintf('%02d', $i); } $filenames[] = $stat->absolute_path; } // Check if it exists, otherwise attempt to provide relocated filename jimport('joomla.filesystem.file'); $ret = array(); foreach($filenames as $filename) { if(!file_exists($filename)) { // Get output directory $registry =& JoomlapackModelRegistry::getInstance(); $basedir = $registry->get('OutputDirectory'); // Get specific extension $dotpos = strrpos($filename, '.'); $extension_correct = substr($filename, -$dotpos); // Get new basename $basefile = $basedir.DS.$stat->archivename; $dotpos = strrpos($basefile, '.'); $archiveFile = substr($basefile, 0, $dotpos).$extension_correct; // Return the new filename IF IT EXISTS! $filename = @file_exists($archiveFile) ? $archiveFile : ''; } // Do not return filename for invalid backups if( ($stat->status == 'complete') && (!empty($filename)) ) { $ret[] = $filename; } } if((count($ret) == 0) && $skipNonComplete) $ret = null; return $ret; } /** * Gets a list of stats entries marked as "running". Used to detect failed * backup attempts * * @return array */ function &getRunningStatisticsList() { $db =& $this->getDBO(); $query = "SELECT * FROM ".$db->nameQuote('#__jp_stats') . ' WHERE '.$db->nameQuote('status').' = '.$db->Quote('run'); $limitstart = $this->getState('limitstart'); $limit = $this->getState('limit'); $list = $this->_getList($query); return $list; } /** * Saves a statistics record * * @param object|array $data The data to be bound and saved * @return bool True on success */ function save(&$data) { // Get the table $this->_table =& $this->getTable('Statistic'); // Try to save the data if(!$this->_table->save($data)) { // Oops... Something wrong happened $this->setError($this->_table->getError()); return false; } else { return true; } } /** * Returns the last saved table * * @return JTable */ function &getSavedTable() { return $this->_table; } /** * Delete the stats record whose ID is set in the model * * @return bool True on success */ function delete() { $db =& $this->getDBO(); if( (!is_numeric($this->_id)) || ($this->_id <= 0) ) { $this->setError(JText::_('STATS_ERROR_INVALIDID')); return false; } // Try to delete files $this->deleteFile(); // Delete record $sql = 'DELETE FROM '.$db->nameQuote('#__jp_stats').' WHERE '. $db->nameQuote('id').' = '.$this->_id; $db->setQuery($sql); if(!$db->query()) { $this->setError($db->getError()); return false; } return true; } /** * Delete the backup file of the stats record whose ID is set in the model * * @return bool True on success */ function deleteFile() { $db =& $this->getDBO(); if( (!is_numeric($this->_id)) || ($this->_id <= 0) ) { $this->setError(JText::_('STATS_ERROR_INVALIDID')); return false; } $allFiles = $this->getAllFilenames($this->_id, false); $status = true; foreach($allFiles as $filename) { jimport('joomla.filesystem.file'); if(@file_exists($filename)) { $registry =& JoomlapackModelRegistry::getInstance(); $basedir = $registry->get('OutputDirectory'); $archiveFile = $basedir.DS.basename($filename); $filename = @file_exists($archiveFile) ? $archiveFile : ''; } $new_status = @unlink($filename); $status = $status ? $new_status : false; } return $status; } /** * Get a pagination object * * @access public * @return JPagination * */ function &getPagination() { if( empty($this->_pagination) ) { // Import the pagination library jimport('joomla.html.pagination'); // Prepare pagination values $total = $this->getTotal(); $limitstart = $this->getState('limitstart'); $limit = $this->getState('limit'); // Create the pagination object $this->_pagination = new JPagination($total, $limitstart, $limit); } return $this->_pagination; } /** * Get number of profile items * * @access public * @return integer */ function getTotal() { if( empty($this->_total) ) { $db =& $this->getDBO(); $query = "SELECT * FROM ".$db->nameQuote('#__jp_stats'); $this->_total = $this->_getListCount($query); } return $this->_total; } }