/** * Resets the Kettenrad state, wipping out any pending backups and/or stale * temporary data. * @param array $config Configuration parameters for the reset operation */ public static function reset( $config = array() ) { $default_config = array( 'global' => true, // Reset all origins when true 'log' => false, // Log our actions ); $config = (object)array_merge($default_config, $config); // Pause logging if so desired if(!$config->log) AEUtilLogger::WriteLog(false,''); $tag = null; if(!$config->global) { // If we're not resetting globally, get a list of running backups per tag $tag = AEPlatform::get_backup_origin(); } // Cache the factory before proceeding $factory = AEFactory::serialize(); $runningList = AEPlatform::get_running_backups($tag); // Origins we have to clean $origins = array( AEPlatform::get_backup_origin() ); // 1. Detect failed backups if(is_array($runningList) && !empty($runningList)) { // The current timestamp $now = time(); // Mark running backups as failed foreach($runningList as $running) { if(empty($tag)) { // Check the timestamp of the log file to decide if it's stuck, // but only if a tag is not set $tstamp = @filemtime( AEUtilLogger::logName($running['origin']) ); if($tstamp !== false) { // We can only check the timestamp if it's returned. If not, we assume the backup is stale $difference = abs($now - $tstamp); // Backups less than 3 minutes old are not considered stale if($difference < 180) continue; } } $filenames = AEUtilStatistics::get_all_filenames($running); // Process if there are files to delete... if(!is_null($filenames)) { // Delete the failed backup's archive, if exists foreach($filenames as $failedArchive) { AEPlatform::unlink($failedArchive); } } // Mark the backup failed $running['status'] = 'fail'; $running['multipart'] = 0; $dummy = null; AEPlatform::set_or_update_statistics( $running['id'], $running, $dummy ); $origins[] = $running['origin']; } } if(!empty($origins)) { $origins = array_unique($origins); foreach($origins as $tag) { AECoreKettenrad::load($tag); // Remove temporary files AEUtilTempfiles::deleteTempFiles(); // Delete any stale temporary data AEUtilTempvars::reset($tag); } } // Reload the factory AEFactory::unserialize($factory); unset($factory); // Unpause logging if it was previously paused if(!$config->log) AEUtilLogger::WriteLog(true,''); }
/** * Try to pack some files in the $file_list, restraining ourselves not to reach the max * number of files or max fragment size while doing so. If this process is over and we are * left without any more files, reset $done_scanning to false in order to instruct the class * to scan for more files. * * @return bool True if there were files packed, false otherwise (empty filelist) */ private function pack_files() { // Get a reference to the archiver and the timer classes $archiver =& AEFactory::getArchiverEngine(); $timer =& AEFactory::getTimer(); $configuration =& AEFactory::getConfiguration(); // If post-processing after part creation is enabled, make sure we do post-process each part before moving on if($configuration->get('engine.postproc.common.after_part',0)) { if(!empty($archiver->finishedPart)) { $filename = array_shift($archiver->finishedPart); AEUtilLogger::WriteLog(_AE_LOG_INFO, 'Preparing to post process '.basename($filename)); $post_proc =& AEFactory::getPostprocEngine(); $result = $post_proc->processPart( $filename ); $this->propagateFromObject($post_proc); if($result === false) { $this->setWarning('Failed to process file '.basename($filename)); } else { AEUtilLogger::WriteLog(_AE_LOG_INFO, 'Successfully processed file '.basename($filename)); } // Should we delete the file afterwards? if( $configuration->get('engine.postproc.common.delete_after',false) && $post_proc->allow_deletes && ($result !== false) ) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, 'Deleting already processed file '.basename($filename)); AEPlatform::unlink($filename); } if($post_proc->break_after && ($result !== false)) { $configuration->set('volatile.breakflag', true); return true; } // This is required to let the backup continue even after a post-proc failure $this->resetErrors(); $this->setState('running'); } } // If the archiver has work to do, make sure it finished up before continuing if( $configuration->get('volatile.engine.archiver.processingfile',false) ) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Continuing file packing from previous step"); $result = $archiver->addFile('', '', ''); $this->propagateFromObject($archiver); if($this->getError()) { return false; } // If that was the last step, mark a file done if( !$configuration->get('volatile.engine.archiver.processingfile',false) ) { $this->progressMarkFileDone(); } } // Did it finish, or does it have more work to do? if( $configuration->get('volatile.engine.archiver.processingfile',false) ) { // More work to do. Let's just tell our parent that we finished up successfully. return true; } // Normal file backup loop; we keep on processing the file list, packing files as we go. if( count($this->file_list) == 0 ) { // No files left to pack -- This should never happen! We catch this condition at the end of this method! $this->done_scanning = false; $this->progressMarkFolderDone(); return false; } else { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Packing files"); $packedSize = 0; $numberOfFiles = 0; list($usec, $sec) = explode(" ", microtime()); $opStartTime = ((float)$usec + (float)$sec); while( (count($this->file_list) > 0) ) { $file = @array_shift($this->file_list); $size = @filesize($file); // Anticipatory file size algorithm if( ($numberOfFiles > 0) && ($size > AELargeFileThreshold) ) { if(!AEFactory::getConfiguration()->get('akeeba.tuning.nobreak.beforelargefile',0)) { // If the file is bigger than the big file threshold, break the step // to avoid potential timeouts $this->setBreakFlag(); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Breaking step _before_ large file: ".$file." - size: ".$size); // Push the file back to the list. array_unshift($this->file_list, $file); // Mark that we are not done packing files $this->done_scanning = true; return true; } } // Proactive potential timeout detection // Rough estimation of packing speed in bytes per second list($usec, $sec) = explode(" ", microtime()); $opEndTime = ((float)$usec + (float)$sec); if( ($opEndTime - $opStartTime) == 0 ) { $_packSpeed = 0; } else { $_packSpeed = $packedSize / ($opEndTime - $opStartTime); } // Estimate required time to pack next file. If it's the first file of this operation, // do not impose any limitations. $_reqTime = ($_packSpeed - 0.01) <= 0 ? 0 : $size / $_packSpeed; // Do we have enough time? if($timer->getTimeLeft() < $_reqTime ) { if(!AEFactory::getConfiguration()->get('akeeba.tuning.nobreak.proactive',0)) { array_unshift($this->file_list, $file); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Proactive step break - file: ".$file." - size: ".$size." - req. time ".sprintf('%2.2f',$_reqTime) ); $this->setBreakFlag(); $this->done_scanning = true; return true; } } $packedSize += $size; $numberOfFiles++; $ret = $archiver->addFile($file, $this->remove_path_prefix, $this->path_prefix); // If no more processing steps are required, mark a done file if( !$configuration->get('volatile.engine.archiver.processingfile',false) ) { $this->progressMarkFileDone(); } // Error propagation $this->propagateFromObject($archiver); if($this->getError()) { return false; } // If this was the first file of the fragment and it exceeded the fragment's capacity, // break the step. Continuing with more operations after packing such a big file is // increasing the risk to hit a timeout. if( ($packedSize > AELargeFileThreshold) && ($numberOfFiles == 1) ) { if(!AEFactory::getConfiguration()->get('akeeba.tuning.nobreak.afterlargefile',0)) { AEUtilLogger::WriteLog(_AE_LOG_INFO, "Breaking step *after* large file: ".$file." - size: ".$size); $this->setBreakFlag(); return true; } } // If we have to continue processing the file, break the file packing loop forcibly if( $configuration->get('volatile.engine.archiver.processingfile',false) ) { return true; } } $this->done_scanning = count($this->file_list) > 0; if(!$this->done_scanning) $this->progressMarkFolderDone(); return true; } }
/** * Implements the _run() abstract method */ protected function _run() { // Check if we are already done if ($this->getState() == 'postrun') { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, __CLASS__." :: Already finished"); $this->setStep(""); $this->setSubstep(""); return; } // Mark ourselves as still running (we will test if we actually do towards the end ;) ) $this->setState('running'); // Check if we are still adding a database dump part to the archive, or if // we have to post-process a part if( AEUtilScripting::getScriptingParameter('db.saveasname','normal') != 'output' ) { $archiver =& AEFactory::getArchiverEngine(); $configuration =& AEFactory::getConfiguration(); if($configuration->get('engine.postproc.common.after_part',0)) { if(!empty($archiver->finishedPart)) { $filename = array_shift($archiver->finishedPart); AEUtilLogger::WriteLog(_AE_LOG_INFO, 'Preparing to post process '.basename($filename)); $post_proc =& AEFactory::getPostprocEngine(); $result = $post_proc->processPart( $filename ); $this->propagateFromObject($post_proc); if($result === false) { $this->setWarning('Failed to process file '.basename($filename)); } else { AEUtilLogger::WriteLog(_AE_LOG_INFO, 'Successfully processed file '.basename($filename)); } // Should we delete the file afterwards? if( $configuration->get('engine.postproc.common.delete_after',false) && $post_proc->allow_deletes && ($result !== false) ) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, 'Deleting already processed file '.basename($filename)); AEPlatform::unlink($filename); } if($post_proc->break_after) { $configuration->set('volatile.breakflag', true); return; } } } if($configuration->get('volatile.engine.archiver.processingfile',false)) { // We had already started archiving the db file, but it needs more time $finished = true; AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Continuing adding the SQL dump part to the archive"); $archiver->addFile(null,null,null); $this->propagateFromObject($archiver); if($this->getError()) return; $finished = !$configuration->get('volatile.engine.archiver.processingfile',false); if($finished) { $this->getNextDumpPart(); } else { return; } } } // Initialize local variables $db =& $this->getDB(); if($this->getError()) return; if( !is_object($db) || ($db === false) ) { $this->setError(__CLASS__.'::_run() Could not connect to database?!'); return; } $outData = ''; // Used for outputting INSERT INTO commands $this->enforceSQLCompatibility(); // Apply MySQL compatibility option if($this->getError()) return; // Touch SQL dump file $nada = ""; $this->writeline($nada); // Get this table's information $tableName = $this->nextTable; $tableAbstract = trim( $this->table_name_map[$tableName] ); $dump_records = $this->tables_data[$tableName]['dump_records']; // If it is the first run, find number of rows and get the CREATE TABLE command if( $this->nextRange == 0 ) { if($this->getError()) return; $outCreate = ''; if(is_array($this->tables_data[$tableName])) { if(array_key_exists('create', $this->tables_data[$tableName])) { $outCreate = $this->tables_data[$tableName]['create']; } } if(empty($outCreate) && !empty($tableName)) { // The CREATE command wasn't cached. Time to create it. The $type and $dependencies // variables will be thrown away. $type = 'table'; $outCreate = $this->get_create($tableAbstract, $tableName, $type, $dependencies); } // Write the CREATE command if(!$this->writeDump($outCreate)) return; // Create drop statements if required (the key is defined by the scripting engine) $configuration =& AEFactory::getConfiguration(); if( AEUtilScripting::getScriptingParameter('db.dropstatements',0) ) { if(array_key_exists('create', $this->tables_data[$tableName])) { // @todo This looks cheesy... $dropStatement = $this->createDrop($this->tables_data[$tableName]['create']); } else { $type = 'table'; $createStatement = $this->get_create($tableAbstract, $tableName, $type, $dependencies); $dropStatement = $this->createDrop($createStatement); } if(!empty($dropStatement)) { if(!$this->writeDump($outCreate)) return; } } if( $dump_records ) { // We are dumping data from a table, get the row count $this->getRowCount( $tableAbstract ); } else { // We should not dump any data AEUtilLogger::WriteLog(_AE_LOG_INFO, "Skipping dumping data of " . $tableAbstract); $this->maxRange = 0; $this->nextRange = 1; $outData = ''; $numRows = 0; } } // Check if we have more work to do on this table $configuration =& AEFactory::getConfiguration(); $batchsize = intval($configuration->get('engine.dump.common.batchsize', 1000)); if($batchsize <= 0) $batchsize = 1000; if( ($this->nextRange < $this->maxRange) ) { $timer =& AEFactory::getTimer(); // Get the number of rows left to dump from the current table $sql = "SELECT * FROM `$tableAbstract`"; if( $this->nextRange == 0 ) { // First run, get a cursor to all records $db->setQuery( $sql, 0, $batchsize ); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Beginning dump of " . $tableAbstract); } else { // Subsequent runs, get a cursor to the rest of the records $db->setQuery( $sql, $this->nextRange, $batchsize ); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Continuing dump of " . $tableAbstract . " from record #{$this->nextRange}"); } $this->query = ''; $numRows = 0; $use_abstract = AEUtilScripting::getScriptingParameter('db.abstractnames', 1); while( is_array($myRow = $db->loadAssoc(false)) && ( $numRows < ($this->maxRange - $this->nextRange) ) ) { $this->createNewPartIfRequired(); $numRows++; $numOfFields = count( $myRow ); if( (!$this->extendedInserts) || // Add header on simple INSERTs, or... ( $this->extendedInserts && empty($this->query) ) //...on extended INSERTs if there are no other data, yet ) { $newQuery = true; if( $numOfFields > 0 ) $this->query = "INSERT INTO `" . (!$use_abstract ? $tableName : $tableAbstract) . "` VALUES "; } else { // On other cases, just mark that we should add a comma and start a new VALUES entry $newQuery = false; } $outData = '('; // Step through each of the row's values $fieldID = 0; // Used in running backup fix $isCurrentBackupEntry = false; // Fix 1.2a - NULL values were being skipped if( $numOfFields > 0 ) foreach( $myRow as $value ) { // The ID of the field, used to determine placement of commas $fieldID++; // Fix 2.0: Mark currently running backup as successful in the DB snapshot if($tableAbstract == '#__ak_stats') { if($fieldID == 1) { // Compare the ID to the currently running $statistics =& AEFactory::getStatistics(); $isCurrentBackupEntry = ($value == $statistics->getId()); } elseif ($fieldID == 6) { // Treat the status field $value = $isCurrentBackupEntry ? 'complete' : $value; } } // Post-process the value if( is_null($value) ) { $outData .= "NULL"; // Cope with null values } else { // Accommodate for runtime magic quotes $value = @get_magic_quotes_runtime() ? stripslashes( $value ) : $value; $outData .= $db->Quote($value); } if( $fieldID < $numOfFields ) $outData .= ', '; } // foreach $outData .= ')'; if( $numOfFields ) { // If it's an existing query and we have extended inserts if($this->extendedInserts && !$newQuery) { // Check the existing query size $query_length = strlen($this->query); $data_length = strlen($outData); if( ($query_length + $data_length) > $this->packetSize ) { // We are about to exceed the packet size. Write the data so far. $this->query .= ";\n"; if(!$this->writeDump($this->query)) return; // Then, start a new query $this->query = ''; $this->query = "INSERT INTO `" . (!$use_abstract ? $tableName : $tableAbstract) . "` VALUES "; $this->query .= $outData; } else { // We have room for more data. Append $outData to the query. $this->query .= ', '; $this->query .= $outData; } } elseif($this->extendedInserts && $newQuery) // If it's a brand new insert statement in an extended INSERTs set { // Append the data to the INSERT statement $this->query .= $outData; // Let's see the size of the dumped data... $query_length = strlen($this->query); if($query_length >= $this->packetSize) { // This was a BIG query. Write the data to disk. $this->query .= ";\n"; if(!$this->writeDump($this->query)) return; // Then, start a new query $this->query = ''; } } else // It's a normal (not extended) INSERT statement { // Append the data to the INSERT statement $this->query .= $outData; // Write the data to disk. $this->query .= ";\n"; if(!$this->writeDump($this->query)) return; // Then, start a new query $this->query = ''; } } $outData = ''; // Check for imminent timeout if( $timer->getTimeLeft() <= 0 ) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Breaking dump of $tableAbstract after $numRows rows; will continue on next step"); break; } } // for (all rows left) // Advance the _nextRange pointer $this->nextRange += ($numRows != 0) ? $numRows : 1; $this->setStep($tableName); $this->setSubstep($this->nextRange . ' / ' . $this->maxRange); } // if more work on the table // Finalize any pending query // WARNING! If we do not do that now, the query will be emptied in the next operation and all // accumulated data will go away... if(!empty($this->query)) { $this->query .= ";\n"; if(!$this->writeDump($this->query)) return; $this->query = ''; } // Check for end of table dump (so that it happens inside the same operation) if( !($this->nextRange < $this->maxRange) ) { // Tell the user we are done with the table AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Done dumping " . $tableAbstract); if(count($this->tables) == 0) { // We have finished dumping the database! AEUtilLogger::WriteLog(_AE_LOG_INFO, "End of database detected; flushing the dump buffers..."); $null = null; $this->writeDump($null); AEUtilLogger::WriteLog(_AE_LOG_INFO, "Database has been successfully dumped to SQL file(s)"); $this->setState('postrun'); $this->setStep(''); $this->setSubstep(''); $this->nextTable = ''; $this->nextRange = 0; } elseif(count($this->tables) != 0) { // Switch tables $this->nextTable = array_shift( $this->tables ); $this->nextRange = 0; $this->setStep($this->nextTable); $this->setSubstep(''); } } $null = null; $this->writeline($null); }
public function apply_srp_quotas($parent) { $parent->relayStep('Applying quotas'); $parent->relaySubstep(''); // If no quota settings are enabled, quit $registry =& AEFactory::getConfiguration(); $srpQuotas = $registry->get('akeeba.quota.srp_size_quota'); if($srpQuotas <= 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "No restore point quotas were defined; old restore point files will be kept intact" ); return true; // No quota limits were requested } // Get valid-looking backup ID's $validIDs =& AEPlatform::get_valid_backup_records(true, array('restorepoint')); $statistics =& AEFactory::getStatistics(); $latestBackupId = $statistics->getId(); // Create a list of valid files $allFiles = array(); if(count($validIDs)) { foreach($validIDs as $id) { $stat = AEPlatform::get_statistics($id); // Multipart processing $filenames = AEUtilStatistics::get_all_filenames($stat, 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) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "There were no old restore points to apply quotas on" ); return true; } // Init arrays $killids = array(); $ret = array(); $leftover = array(); // Do we need to apply size quotas? AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Processing restore point size quotas" ); // OK, let's start counting bytes! $runningSize = 0; while(count($allFiles) > 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($allFiles); $runningSize += $def['size']; if($runningSize >= $srpQuotas) { if($latestBackupId == $def['id']) { $runningSize -= $def['size']; } else { $ret[] = $def['filenames']; $killids[] = $def['filenames']; } } } // Convert the $ret 2-dimensional array to single dimensional $quotaFiles = array(); foreach($ret as $temp) { foreach($temp as $filename) { $quotaFiles[] = $filename; } } // Update the statistics record with the removed remote files if(!empty($killids)) foreach($killids as $id) { $data = array('filesexist' => '0'); AEPlatform::set_or_update_statistics($id, $data, $parent); } // Apply quotas if(count($quotaFiles) > 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Applying quotas" ); jimport('joomla.filesystem.file'); foreach($quotaFiles as $file) { if(!@AEPlatform::unlink($file)) { $parent->setWarning("Failed to remove old system restore point file ".$file ); } } } return true; }
/** * Applies the size and count quotas * @return bool */ private function apply_quotas() { $this->setStep('Applying quotas'); $this->setSubstep(''); // If no quota settings are enabled, quit $registry =& AEFactory::getConfiguration(); $useDayQuotas = $registry->get('akeeba.quota.maxage.enable'); $useCountQuotas = $registry->get('akeeba.quota.enable_count_quota'); $useSizeQuotas = $registry->get('akeeba.quota.enable_size_quota'); if(! ($useDayQuotas || $useCountQuotas || $useSizeQuotas) ) { $this->apply_obsolete_quotas(); AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "No quotas were defined; old backup files will be kept intact" ); return true; // No quota limits were requested } // Try to find the files to be deleted due to quota settings $statistics =& AEFactory::getStatistics(); $latestBackupId = $statistics->getId(); // Get quota values $countQuota = $registry->get('akeeba.quota.count_quota'); $sizeQuota = $registry->get('akeeba.quota.size_quota'); $daysQuota = $registry->get('akeeba.quota.maxage.maxdays'); $preserveDay = $registry->get('akeeba.quota.maxage.keepday'); // Get valid-looking backup ID's $validIDs =& AEPlatform::get_valid_backup_records(true, array('NOT','restorepoint')); // Create a list of valid files $allFiles = array(); if(count($validIDs)) { foreach($validIDs as $id) { $stat = AEPlatform::get_statistics($id); try { $backupstart = new DateTime($stat['backupstart']); $backupTS = $backupstart->format('U'); $backupDay = $backupstart->format('d'); } catch (Exception $e) { $backupTS = 0; $backupDay = 0; } // Multipart processing $filenames = AEUtilStatistics::get_all_filenames($stat, 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, 'backupstart' => $backupTS, 'day' => $backupDay); } } } unset($validIDs); // If there are no files, exit early if(count($allFiles) == 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "There were no old backup files to apply quotas on" ); return true; } // Init arrays $killids = array(); $ret = array(); $leftover = array(); // Do we need to apply maximum backup age quotas? if($useDayQuotas) { $killDatetime = new DateTime(); $killDatetime->sub(new DateInterval('P'.$daysQuota.'D')); $killTS = $killDatetime->format('U'); foreach($allFiles as $file) { // Is this on a preserve day? if($preserveDay > 0) { if($preserveDay == $file['day']) { $leftover[] = $file; continue; } } // Otherwise, check the timestamp if($file['backupstart'] < $killTS) { $ret[] = $file['filenames']; $killids[] = $file['id']; } else { $leftover[] = $file; } } } // Do we need to apply count quotas? if($useCountQuotas && is_numeric($countQuota) && !($countQuota <= 0) && !$useDayQuotas ) { // Are there more files than the quota limit? if( !(count($allFiles) > $countQuota) ) { // No, effectively skip the quota checking $leftover =& $allFiles; } else { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Processing count quotas" ); // Yes, aply the quota setting. Add to $ret all entries minus the last // $countQuota ones. $totalRecords = count($allFiles); $checkLimit = $totalRecords - $countQuota; // Only process if at least one file (current backup!) is to be left for($count = 0; $count < $totalRecords; $count++) { $def = array_pop($allFiles); if(count($ret) < $checkLimit) { if($latestBackupId != $def['id']) { $ret[] = $def['filenames']; $killids[] = $def['id']; } } 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) && !$useDayQuotas ) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Processing size quotas" ); // OK, let's start counting 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']; $killids[] = $def['filenames']; } } } } // Convert the $ret 2-dimensional array to single dimensional $quotaFiles = array(); foreach($ret as $temp) { foreach($temp as $filename) { $quotaFiles[] = $filename; } } // Update the statistics record with the removed remote files if(!empty($killids)) foreach($killids as $id) { $data = array('filesexist' => '0'); AEPlatform::set_or_update_statistics($id, $data, $this); } // Apply quotas if(count($quotaFiles) > 0) { AEUtilLogger::WriteLog(_AE_LOG_DEBUG, "Applying quotas" ); jimport('joomla.filesystem.file'); foreach($quotaFiles as $file) { if(!@AEPlatform::unlink($file)) { $this->setWarning("Failed to remove old backup file ".$file ); } } } $this->apply_obsolete_quotas(); return true; }