Файл: contao-3.5.8/system/modules/core/library/Contao/Automator.php
Строк: 482
<?php
/**
* Contao Open Source CMS
*
* Copyright (c) 2005-2016 Leo Feyer
*
* @license LGPL-3.0+
*/
namespace Contao;
/**
* Provide methods to run automated jobs.
*
* @author Leo Feyer <https://github.com/leofeyer>
*/
class Automator extends System
{
/**
* Make the constuctor public
*/
public function __construct()
{
parent::__construct();
}
/**
* Check for new Contao versions
*/
public function checkForUpdates()
{
if (!is_numeric(BUILD))
{
return;
}
// HOOK: proxy module
if (Config::get('useProxy')) {
$objRequest = new ProxyRequest();
} else {
$objRequest = new Request();
}
$objRequest->send(Config::get('liveUpdateBase') . (LONG_TERM_SUPPORT ? 'lts-version.txt' : 'version.txt'));
if (!$objRequest->hasError())
{
Config::set('latestVersion', $objRequest->response);
Config::persist('latestVersion', $objRequest->response);
}
// Add a log entry
$this->log('Checked for Contao updates', __METHOD__, TL_CRON);
}
/**
* Purge the search tables
*/
public function purgeSearchTables()
{
$objDatabase = Database::getInstance();
// Truncate the tables
$objDatabase->execute("TRUNCATE TABLE tl_search");
$objDatabase->execute("TRUNCATE TABLE tl_search_index");
// Purge the cache folder
$objFolder = new Folder('system/cache/search');
$objFolder->purge();
// Add a log entry
$this->log('Purged the search tables', __METHOD__, TL_CRON);
}
/**
* Purge the undo table
*/
public function purgeUndoTable()
{
$objDatabase = Database::getInstance();
// Truncate the table
$objDatabase->execute("TRUNCATE TABLE tl_undo");
// Add a log entry
$this->log('Purged the undo table', __METHOD__, TL_CRON);
}
/**
* Purge the version table
*/
public function purgeVersionTable()
{
$objDatabase = Database::getInstance();
// Truncate the table
$objDatabase->execute("TRUNCATE TABLE tl_version");
// Add a log entry
$this->log('Purged the version table', __METHOD__, TL_CRON);
}
/**
* Purge the system log
*/
public function purgeSystemLog()
{
$objDatabase = Database::getInstance();
// Truncate the table
$objDatabase->execute("TRUNCATE TABLE tl_log");
// Add a log entry
$this->log('Purged the system log', __METHOD__, TL_CRON);
}
/**
* Purge the image cache
*/
public function purgeImageCache()
{
// Walk through the subfolders
foreach (scan(TL_ROOT . '/assets/images') as $dir)
{
if ($dir != 'index.html' && strncmp($dir, '.', 1) !== 0)
{
// Purge the folder
$objFolder = new Folder('assets/images/' . $dir);
$objFolder->purge();
// Restore the index.html file
$objFile = new File('templates/index.html', true);
$objFile->copyTo('assets/images/' . $dir . '/index.html');
}
}
// Also empty the page cache so there are no links to deleted images
$this->purgePageCache();
// Add a log entry
$this->log('Purged the image cache', __METHOD__, TL_CRON);
}
/**
* Purge the script cache
*/
public function purgeScriptCache()
{
// assets/js and assets/css
foreach (array('assets/js', 'assets/css') as $dir)
{
// Purge the folder
$objFolder = new Folder($dir);
$objFolder->purge();
// Restore the index.html file
$objFile = new File('templates/index.html', true);
$objFile->copyTo($dir . '/index.html');
}
// Recreate the internal style sheets
$this->import('StyleSheets');
$this->StyleSheets->updateStylesheets();
// Also empty the page cache so there are no links to deleted scripts
$this->purgePageCache();
// Add a log entry
$this->log('Purged the script cache', __METHOD__, TL_CRON);
}
/**
* Purge the page cache
*/
public function purgePageCache()
{
// Purge the folder
$objFolder = new Folder('system/cache/html');
$objFolder->purge();
// Add a log entry
$this->log('Purged the page cache', __METHOD__, TL_CRON);
}
/**
* Purge the search cache
*/
public function purgeSearchCache()
{
// Purge the folder
$objFolder = new Folder('system/cache/search');
$objFolder->purge();
// Add a log entry
$this->log('Purged the search cache', __METHOD__, TL_CRON);
}
/**
* Purge the internal cache
*/
public function purgeInternalCache()
{
// Check whether the cache exists
if (is_dir(TL_ROOT . '/system/cache/dca'))
{
foreach (array('config', 'dca', 'language', 'sql') as $dir)
{
// Purge the folder
$objFolder = new Folder('system/cache/' . $dir);
$objFolder->delete();
}
}
// Add a log entry
$this->log('Purged the internal cache', __METHOD__, TL_CRON);
}
/**
* Purge the temp folder
*/
public function purgeTempFolder()
{
// Purge the folder
$objFolder = new Folder('system/tmp');
$objFolder->purge();
// Restore the .gitignore file
$objFile = new File('system/logs/.gitignore', true);
$objFile->copyTo('system/tmp/.gitignore');
// Add a log entry
$this->log('Purged the temp folder', __METHOD__, TL_CRON);
}
/**
* Regenerate the XML files
*/
public function generateXmlFiles()
{
// Sitemaps
$this->generateSitemap();
// HOOK: add custom jobs
if (isset($GLOBALS['TL_HOOKS']['generateXmlFiles']) && is_array($GLOBALS['TL_HOOKS']['generateXmlFiles']))
{
foreach ($GLOBALS['TL_HOOKS']['generateXmlFiles'] as $callback)
{
$this->import($callback[0]);
$this->{$callback[0]}->{$callback[1]}();
}
}
// Also empty the page cache so there are no links to deleted files
$this->purgePageCache();
// Add a log entry
$this->log('Regenerated the XML files', __METHOD__, TL_CRON);
}
/**
* Remove old XML files from the share directory
*
* @param boolean $blnReturn If true, only return the finds and don't delete
*
* @return array An array of old XML files
*/
public function purgeXmlFiles($blnReturn=false)
{
$arrFeeds = array();
$objDatabase = Database::getInstance();
// XML sitemaps
$objFeeds = $objDatabase->execute("SELECT sitemapName FROM tl_page WHERE type='root' AND createSitemap=1 AND sitemapName!=''");
while ($objFeeds->next())
{
$arrFeeds[] = $objFeeds->sitemapName;
}
// HOOK: preserve third party feeds
if (isset($GLOBALS['TL_HOOKS']['removeOldFeeds']) && is_array($GLOBALS['TL_HOOKS']['removeOldFeeds']))
{
foreach ($GLOBALS['TL_HOOKS']['removeOldFeeds'] as $callback)
{
$this->import($callback[0]);
$arrFeeds = array_merge($arrFeeds, $this->{$callback[0]}->{$callback[1]}());
}
}
// Delete the old files
if (!$blnReturn)
{
foreach (scan(TL_ROOT . '/share') as $file)
{
if (is_dir(TL_ROOT . '/share/' . $file))
{
continue; // see #6652
}
$objFile = new File('share/' . $file, true);
if ($objFile->extension == 'xml' && !in_array($objFile->filename, $arrFeeds))
{
$objFile->delete();
}
}
}
return $arrFeeds;
}
/**
* Generate the Google XML sitemaps
*
* @param integer $intId The root page ID
*/
public function generateSitemap($intId=0)
{
$time = Date::floorToMinute();
$objDatabase = Database::getInstance();
$this->purgeXmlFiles();
// Only root pages should have sitemap names
$objDatabase->execute("UPDATE tl_page SET createSitemap='', sitemapName='' WHERE type!='root'");
// Get a particular root page
if ($intId > 0)
{
do
{
$objRoot = $objDatabase->prepare("SELECT * FROM tl_page WHERE id=?")
->limit(1)
->execute($intId);
if ($objRoot->numRows < 1)
{
break;
}
$intId = $objRoot->pid;
}
while ($objRoot->type != 'root' && $intId > 0);
// Make sure the page is published
if (!$objRoot->published || ($objRoot->start != '' && $objRoot->start > $time) || ($objRoot->stop != '' && $objRoot->stop <= ($time + 60)))
{
return;
}
// Check the sitemap name
if (!$objRoot->createSitemap || !$objRoot->sitemapName)
{
return;
}
$objRoot->reset();
}
// Get all published root pages
else
{
$objRoot = $objDatabase->execute("SELECT id, language, sitemapName FROM tl_page WHERE type='root' AND createSitemap='1' AND sitemapName!='' AND (start='' OR start<='$time') AND (stop='' OR stop>'" . ($time + 60) . "') AND published='1'");
}
// Return if there are no pages
if ($objRoot->numRows < 1)
{
return;
}
// Create the XML file
while ($objRoot->next())
{
$objFile = new File('share/' . $objRoot->sitemapName . '.xml', true);
$objFile->truncate();
$objFile->append('<?xml version="1.0" encoding="UTF-8"?>');
$objFile->append('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">');
// Find the searchable pages
$arrPages = Backend::findSearchablePages($objRoot->id, '', true);
// HOOK: take additional pages
if (isset($GLOBALS['TL_HOOKS']['getSearchablePages']) && is_array($GLOBALS['TL_HOOKS']['getSearchablePages']))
{
foreach ($GLOBALS['TL_HOOKS']['getSearchablePages'] as $callback)
{
$this->import($callback[0]);
$arrPages = $this->{$callback[0]}->{$callback[1]}($arrPages, $objRoot->id, true, $objRoot->language);
}
}
// Add pages
foreach ($arrPages as $strUrl)
{
$strUrl = rawurlencode($strUrl);
$strUrl = str_replace(array('%2F', '%3F', '%3D', '%26', '%3A//'), array('/', '?', '=', '&', '://'), $strUrl);
$strUrl = ampersand($strUrl, true);
$objFile->append(' <url><loc>' . $strUrl . '</loc></url>');
}
$objFile->append('</urlset>');
$objFile->close();
// Add a log entry
$this->log('Generated sitemap "' . $objRoot->sitemapName . '.xml"', __METHOD__, TL_CRON);
}
}
/**
* Rotate the log files
*/
public function rotateLogs()
{
$arrFiles = preg_grep('/.log$/', scan(TL_ROOT . '/system/logs'));
foreach ($arrFiles as $strFile)
{
$objFile = new File('system/logs/' . $strFile . '.9', true);
// Delete the oldest file
if ($objFile->exists())
{
$objFile->delete();
}
// Rotate the files (e.g. error.log.4 becomes error.log.5)
for ($i=8; $i>0; $i--)
{
$strGzName = 'system/logs/' . $strFile . '.' . $i;
if (file_exists(TL_ROOT . '/' . $strGzName))
{
$objFile = new File($strGzName, true);
$objFile->renameTo('system/logs/' . $strFile . '.' . ($i+1));
}
}
// Add .1 to the latest file
$objFile = new File('system/logs/' . $strFile, true);
$objFile->renameTo('system/logs/' . $strFile . '.1');
}
}
/**
* Build the internal cache
*/
public function generateInternalCache()
{
// Purge
$this->purgeInternalCache();
// Rebuild
$this->generateConfigCache();
$this->generateDcaCache();
$this->generateLanguageCache();
$this->generateDcaExtracts();
}
/**
* Create the config cache files
*/
public function generateConfigCache()
{
// Generate the class/template laoder cache file
$objCacheFile = new File('system/cache/config/autoload.php', true);
$objCacheFile->write('<?php '); // add one space to prevent the "unexpected $end" error
foreach (ModuleLoader::getActive() as $strModule)
{
$strFile = 'system/modules/' . $strModule . '/config/autoload.php';
if (file_exists(TL_ROOT . '/' . $strFile))
{
$objCacheFile->append(static::readPhpFileWithoutTags($strFile));
}
}
// Close the file (moves it to its final destination)
$objCacheFile->close();
// Generate the module loader cache file
$objCacheFile = new File('system/cache/config/modules.php', true);
$objCacheFile->write("<?phpnn");
$objCacheFile->append(sprintf("static::$active = %s;n", var_export(ModuleLoader::getActive(), true)));
$objCacheFile->append(sprintf("static::$disabled = %s;", var_export(ModuleLoader::getDisabled(), true)));
// Close the file (moves it to its final destination)
$objCacheFile->close();
// Generate the config cache file
$objCacheFile = new File('system/cache/config/config.php', true);
$objCacheFile->write('<?php '); // add one space to prevent the "unexpected $end" error
foreach (ModuleLoader::getActive() as $strModule)
{
$strFile = 'system/modules/' . $strModule . '/config/config.php';
if (file_exists(TL_ROOT . '/' . $strFile))
{
$objCacheFile->append(static::readPhpFileWithoutTags($strFile));
}
}
// Close the file (moves it to its final destination)
$objCacheFile->close();
// Generate the page mapping array
$arrMapper = array();
$objPages = PageModel::findPublishedRootPages();
if ($objPages !== null)
{
while ($objPages->next())
{
$strBase = ($objPages->dns ?: '*');
if ($objPages->fallback)
{
$arrMapper[$strBase . '/empty.fallback'] = $strBase . '/empty.' . $objPages->language;
}
$arrMapper[$strBase . '/empty.' . $objPages->language] = $strBase . '/empty.' . $objPages->language;
}
}
// Generate the page mapper file
$objCacheFile = new File('system/cache/config/mapping.php', true);
$objCacheFile->write(sprintf("<?phpnnreturn %s;n", var_export($arrMapper, true)));
$objCacheFile->close();
// Add a log entry
$this->log('Generated the config cache', __METHOD__, TL_CRON);
}
/**
* Create the data container cache files
*/
public function generateDcaCache()
{
$arrFiles = array();
// Parse all active modules
foreach (ModuleLoader::getActive() as $strModule)
{
$strDir = 'system/modules/' . $strModule . '/dca';
if (!is_dir(TL_ROOT . '/' . $strDir))
{
continue;
}
foreach (scan(TL_ROOT . '/' . $strDir) as $strFile)
{
if (strncmp($strFile, '.', 1) !== 0 && substr($strFile, -4) == '.php')
{
$arrFiles[] = substr($strFile, 0, -4);
}
}
}
$arrFiles = array_values(array_unique($arrFiles));
// Create one file per table
foreach ($arrFiles as $strName)
{
// Generate the cache file
$objCacheFile = new File('system/cache/dca/' . $strName . '.php', true);
$objCacheFile->write('<?php '); // add one space to prevent the "unexpected $end" error
// Parse all active modules
foreach (ModuleLoader::getActive() as $strModule)
{
$strFile = 'system/modules/' . $strModule . '/dca/' . $strName . '.php';
if (file_exists(TL_ROOT . '/' . $strFile))
{
$objCacheFile->append(static::readPhpFileWithoutTags($strFile));
}
}
// Close the file (moves it to its final destination)
$objCacheFile->close();
}
// Add a log entry
$this->log('Generated the DCA cache', __METHOD__, TL_CRON);
}
/**
* Create the language cache files
*/
public function generateLanguageCache()
{
$arrLanguages = array('en');
$objLanguages = Database::getInstance()->query("SELECT language FROM tl_member UNION SELECT language FROM tl_user UNION SELECT REPLACE(language, '-', '_') FROM tl_page WHERE type='root'");
// Only cache the languages which are in use (see #6013)
while ($objLanguages->next())
{
if ($objLanguages->language == '')
{
continue;
}
$arrLanguages[] = $objLanguages->language;
// Also cache "de" if "de-CH" is requested
if (strlen($objLanguages->language) > 2)
{
$arrLanguages[] = substr($objLanguages->language, 0, 2);
}
}
$arrLanguages = array_unique($arrLanguages);
foreach ($arrLanguages as $strLanguage)
{
$arrFiles = array();
// Parse all active modules
foreach (ModuleLoader::getActive() as $strModule)
{
$strDir = 'system/modules/' . $strModule . '/languages/' . $strLanguage;
if (!is_dir(TL_ROOT . '/' . $strDir))
{
continue;
}
foreach (scan(TL_ROOT . '/' . $strDir) as $strFile)
{
if (strncmp($strFile, '.', 1) !== 0 && (substr($strFile, -4) == '.php' || substr($strFile, -4) == '.xlf'))
{
$arrFiles[] = substr($strFile, 0, -4);
}
}
}
$arrFiles = array_values(array_unique($arrFiles));
// Create one file per table
foreach ($arrFiles as $strName)
{
$strCacheFile = 'system/cache/language/' . $strLanguage . '/' . $strName . '.php';
// Add a short header with links to transifex.com
$strHeader = "<?phpnn"
. "/**n"
. " * Contao Open Source CMSn"
. " * n"
. " * Copyright (c) 2005-2016 Leo Feyern"
. " * n"
. " * Core translations are managed using Transifex. To create a new translationn"
. " * or to help to maintain an existing one, please register at transifex.com.n"
. " * n"
. " * @link http://help.transifex.com/intro/translating.htmln"
. " * @link https://www.transifex.com/projects/p/contao/language/%s/n"
. " * n"
. " * @license LGPL-3.0+n"
. " */n";
// Generate the cache file
$objCacheFile = new File($strCacheFile, true);
$objCacheFile->write(sprintf($strHeader, $strLanguage));
// Parse all active modules and append to the cache file
foreach (ModuleLoader::getActive() as $strModule)
{
$strFile = 'system/modules/' . $strModule . '/languages/' . $strLanguage . '/' . $strName;
if (file_exists(TL_ROOT . '/' . $strFile . '.xlf'))
{
$objCacheFile->append(static::convertXlfToPhp($strFile . '.xlf', $strLanguage));
}
elseif (file_exists(TL_ROOT . '/' . $strFile . '.php'))
{
$objCacheFile->append(static::readPhpFileWithoutTags($strFile . '.php'));
}
}
// Close the file (moves it to its final destination)
$objCacheFile->close();
}
}
// Add a log entry
$this->log('Generated the language cache', __METHOD__, TL_CRON);
}
/**
* Create the DCA extract cache files
*/
public function generateDcaExtracts()
{
$included = array();
$arrExtracts = array();
// Only check the active modules (see #4541)
foreach (ModuleLoader::getActive() as $strModule)
{
$strDir = 'system/modules/' . $strModule . '/dca';
if (!is_dir(TL_ROOT . '/' . $strDir))
{
continue;
}
foreach (scan(TL_ROOT . '/' . $strDir) as $strFile)
{
// Ignore non PHP files and files which have been included before
if (strncmp($strFile, '.', 1) === 0 || substr($strFile, -4) != '.php' || in_array($strFile, $included))
{
continue;
}
$strTable = substr($strFile, 0, -4);
$objExtract = DcaExtractor::getInstance($strTable);
if ($objExtract->isDbTable())
{
$arrExtracts[$strTable] = $objExtract;
}
$included[] = $strFile;
}
}
/** @var DcaExtractor[] $arrExtracts */
foreach ($arrExtracts as $strTable=>$objExtract)
{
// Create the file
$objFile = new File('system/cache/sql/' . $strTable . '.php', true);
$objFile->write("<?phpnn");
$objFile->append(sprintf("$this->arrMeta = %s;n", var_export($objExtract->getMeta(), true)));
$objFile->append(sprintf("$this->arrFields = %s;n", var_export($objExtract->getFields(), true)));
$objFile->append(sprintf("$this->arrOrderFields = %s;n", var_export($objExtract->getOrderFields(), true)));
$objFile->append(sprintf("$this->arrKeys = %s;n", var_export($objExtract->getKeys(), true)));
$objFile->append(sprintf("$this->arrRelations = %s;n", var_export($objExtract->getRelations(), true)));
// Set the database table flag
$objFile->append("$this->blnIsDbTable = true;", "n");
// Close the file (moves it to its final destination)
$objFile->close();
}
// Add a log entry
$this->log('Generated the DCA extracts', __METHOD__, TL_CRON);
}
}