/** * 取得缓存类实例 * @static * @access public * @return mixed */ static function getInstance($type = '', $options = array()) { static $_instance = array(); $guid = $type . to_guid_string($options); if (!isset($_instance[$guid])) { $obj = new Cache(); $_instance[$guid] = $obj->connect($type, $options); } return $_instance[$guid]; }
function __construct(array $params = array()) { parent::__construct($params); if (array_key_exists('DelegateTarget', $params)) { $this->_pathDelegate = new \Core\Delegate($params['DelegateTarget'], $params['DelegateMethod']); } }
private function get_jsapi_ticket($key = 'ticket', $default = null) { \Core\Cache::init_config_params(); $this->ticket = \Core\Cache::get('jsapi_ticket'); if (empty($this->ticket)) { $host = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=%s"; $url = sprintf($host, $this->get_accesstoken()); $info = \Core\Curl::get($url); $respone = json_decode($info->response, true); if (isset($respone['ticket'])) { $this->ticket = $respone; } \Core\Cache::set('jsapi_ticket', $respone); } if (!isset($this->ticket[$key])) { $this->ticket[$key] = $default; } return $this->ticket[$key]; }
/** * 获取token */ public function get_accesstoken($key = 'access_token', $default = null) { \Core\Cache::init_config_params(); $this->accesstoken = \Core\Cache::get('access_token'); if (empty($this->accesstoken)) { $host = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; $url = sprintf($host, $this->appid, $this->appsecret); $info = \Core\Curl::get($url); $respone = json_decode($info->response, true); if (isset($respone['access_token'])) { $this->accesstoken = $respone; } \Core\Cache::set('access_token', $respone); } if (!isset($this->accesstoken[$key])) { $this->accesstoken[$key] = $default; } return $this->accesstoken[$key]; }
private function _registerFeature($feature, $url, $component){ if(!defined('SERVER_ID')){ throw new \Exception('Unable to load the licenser without a valid server id set!'); } // Lookup any matching feature and do not allow two components to register the same feature code! if(isset($this->_features[$feature]) && $this->_features[$feature]['component'] != $component){ throw new \Exception('Feature ' . $feature . ' already defined in another component! (' . $this->_features[$feature]['component'] . ' vs ' . $component . ')'); } // Lookup the cache for this licensed key value. $cached = \Core\Cache::Get(md5('LICENSER:' . SERVER_ID . $feature)); $this->_features[$feature] = [ 'feature' => $feature, 'url' => $url, 'component' => $component, 'value' => $cached, ]; }
/** * Lookup query * * @param string $query IP or hostname to lookup * @param bool $is_utf Require UTF-8 * * @return WhoisResult */ public static function Lookup($query = '', $is_utf = true) { // See if this query has been cached by Core <3 $cachekey = \Core\str_to_url('whois-' . $query); $cached = \Core\Cache::Get($cachekey); if ($cached) { $result = $cached; } else { $whois = new phpwhois\Whois(); $result = $whois->lookup($query, $is_utf); // Cache the results for 6 hours \Core\Cache::Set($cachekey, $result, 3600 * 6); } if (!is_array($result)) { return new WhoisNotFoundResult($query); } if (!sizeof($result)) { return new WhoisNotFoundResult($query); } return new WhoisResult($result, $query); }
/** * 请求token */ public function get_accesstoken($key = 'access_token', $default = null) { \Core\Cache::init_config_params(); $this->enterprise_accesstoken = \Core\Cache::get('enterprise_accesstoken'); if (empty($this->enterprise_accesstoken)) { $host = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s'; $url = sprintf($host, $this->corpid, $this->corpsecret); $info = \Core\Curl::get($url); $respone = json_decode($info->response, true); if (isset($respone['access_token'])) { $this->enterprise_accesstoken = $respone; } \Core\Cache::set('enterprise_accesstoken', $respone); } if (!isset($this->accesstoken[$key])) { $this->accesstoken[$key] = $default; } return $this->accesstoken[$key]; }
/** * Copy in all the assets for this theme into the assets location. * * Returns false if nothing changed, else will return an array of all the changes that occured. * * @param bool $install Set to false to uninstall the assets instead of installing. * @param int $verbosity 0 for standard output, 1 for real-time, 2 for real-time verbose output. * * @return false | array * @throws \InstallerException */ public function _parseAssets($install = true, $verbosity = 0) { $assetbase = \Core\Filestore\get_asset_path(); $coretheme = \ConfigHandler::Get('/theme/selected'); // WHY is core theme set to blank?!? // Damn installer... // this happens in the installer. if ($coretheme === null) { $coretheme = 'default'; } $theme = $this->getKeyName(); $changes = []; foreach ($this->_xmlloader->getElements('/assets/file') as $node) { // Cannot install assets if the directory is not setup! if (!$this->getAssetDir()) { continue; } $b = $this->getBaseDir(); // The base filename with the directory. $filename = $node->getAttribute('filename'); // The new theme asset will be installed into the same directory as its theme. // This differs from usual components because they just follow whatever theme is currently running. //$nf = Core::File($assetbase . $theme . '/' . $filename); $trimmedfilename = substr($b . $node->getAttribute('filename'), strlen($this->getAssetDir())); $themespecificfilename = $assetbase . $theme . '/' . $trimmedfilename; $newfilename = 'assets/' . $trimmedfilename; // Before anything, check and see if this file has a custom override file present. if (file_exists(ROOT_PDIR . 'themes/custom/' . $newfilename)) { // If so, then copy that asset to the custom directory too! $f = \Core\Filestore\Factory::File(ROOT_PDIR . 'themes/custom/' . $newfilename); $srcname = '!CUSTOM!'; } else { // Otherwise, the local file is guaranteed to be a local file. $f = new \Core\Filestore\Backends\FileLocal($b . $filename); $srcname = '-theme- '; } if ($verbosity == 2) { CLI::PrintActionStart('Installing ' . $srcname . ' asset ' . $f->getBasename()); } $nf = \Core\Filestore\Factory::File($newfilename); /* // The various replacement possibilities for this file. // The new destination must be in the theme-specific directory, this is a // bit of a hack from the usual behaviour of the filestore system. // Since that's designed to return the default if the theme-specific doesn't exist. $replacements = array( // The theme is not default, but the system translated the path to the default directory. // This is because the file doesn't exist in any theme. // This is actually expected behaviour, except unwanted here. 'default/' . $trimmedfilename => $theme . '/' . $trimmedfilename, // The theme is not the currently installed, but the system translated the path to the that directory. // This is because the filename is the same as the installed theme, so the system just translated there. // We don't want that. $coretheme . '/' . $trimmedfilename => $theme . '/' . $trimmedfilename, ); foreach($replacements as $k => $v){ if($k == $v) continue; if(strpos($nf->getFilename(), $k) !== false){ $nf->setFilename( str_replace($k, $v, $nf->getFilename()) ); } } */ // Check if this file even needs updated. (this is primarily used for reporting reasons) if ($nf->exists() && $nf->identicalTo($f)) { //echo "Skipping file, it's identical.<br/>"; if ($verbosity == 2) { CLI::PrintActionStatus('skip'); } continue; } elseif ($nf->exists()) { $action = 'Replaced'; } else { $action = 'Installed'; } if (!$f->isReadable()) { throw new \InstallerException('Source file [' . $f->getFilename() . '] is not readable.'); } try { $f->copyTo($nf, true); } catch (\Exception $e) { throw new \InstallerException('Unable to copy [' . $f->getFilename() . '] to [' . $nf->getFilename() . ']'); } $change = $action . ' ' . $nf->getFilename(); $changes[] = $change; if ($verbosity == 1) { CLI::PrintLine($change); } elseif ($verbosity == 2) { CLI::PrintActionStatus('ok'); } } // If there are custom assets not registered by any application, install them too! // This will allow an admin to upload additional css resources and images easily. $directory = \Core\Filestore\Factory::Directory('themes/custom/assets'); $ls = $directory->ls(null, true); $baseStrLen = strlen(ROOT_PDIR . '/themes/custom/assets'); foreach ($ls as $fileOrDir) { if ($fileOrDir instanceof File) { $newfilename = substr($fileOrDir->getFilename(), $baseStrLen); if ($verbosity == 2) { CLI::PrintActionStart('Installing CUSTOM asset ' . $newfilename); } $nf = \Core\Filestore\Factory::File('asset/' . $newfilename); if ($nf->exists() && $nf->identicalTo($fileOrDir)) { //echo "Skipping file, it's identical.<br/>"; if ($verbosity == 2) { CLI::PrintActionStatus('skip'); } continue; } elseif ($nf->exists()) { $action = 'Replaced'; } else { $action = 'Installed'; } try { $fileOrDir->copyTo($nf, true); } catch (\Exception $e) { throw new \InstallerException('Unable to copy [' . $fileOrDir->getFilename() . '] to [' . $nf->getFilename() . ']'); } $change = $action . ' ' . $nf->getFilename(); $changes[] = $change; if ($verbosity == 1) { CLI::PrintLine($change); } elseif ($verbosity == 2) { CLI::PrintActionStatus('ok'); } } } if (!sizeof($changes)) { if ($verbosity > 0) { CLI::PrintLine('No changes required'); } return false; } // Make sure the asset cache is purged! \Core\Cache::Delete('asset-resolveurl'); return $changes; }
/** * Get the temporary local version of the file. * This is useful for doing operations such as hash and identicalto. * * @return FileLocal */ protected function _getTmpLocal() { if ($this->_tmplocal === null) { $f = md5($this->getFilename()); // Gotta love obviously-named flags. $needtodownload = true; $this->_tmplocal = Filestore\Factory::File('tmp/remotefile-cache/' . $f); // File exists already! Check and see if it needs to be redownloaded. if ($this->cacheable && $this->_tmplocal->exists()) { // Lookup this file in the system cache. $systemcachedata = Cache::Get('remotefile-cache-header-' . $f); if ($systemcachedata && isset($systemcachedata['headers'])) { // I can only look them up if the cache is available. // First check will be the expires header. if(isset($systemcachedata['headers']['Expires']) && strtotime($systemcachedata['headers']['Expires']) > time()){ $needtodownload = false; // And set the headers! // This is required $this->_headers = $systemcachedata['headers']; $this->_response = $systemcachedata['response']; } // Next, try ETag. elseif ($this->_getHeader('ETag') && isset($systemcachedata['headers']['ETag'])) { $needtodownload = ($this->_getHeader('ETag') != $systemcachedata['headers']['ETag']); } // No? How 'bout elseif ($this->_getHeader('Last-Modified') && isset($systemcachedata['headers']['Last-Modified'])) { $needtodownload = ($this->_getHeader('Last-Modified') != $systemcachedata['headers']['Last-Modified']); } // Still no? The default is to download it anyway. } } if ($needtodownload || !$this->cacheable) { // Make sure that the headers are updated, this is a requirement to use the 302 tag. $this->_getHeaders(); if(($this->_response == '302' || $this->_response == '301') && $this->_redirectFile !== null){ $this->_tmplocal = $this->_redirectFile->_getTmpLocal(); } else{ // BTW, use cURL. $curl = curl_init(); curl_setopt_array( $curl, array( CURLOPT_HEADER => false, CURLOPT_NOBODY => false, CURLOPT_RETURNTRANSFER => true, CURLOPT_URL => $this->getURL(), CURLOPT_HTTPHEADER => \Core::GetStandardHTTPHeaders(true), ) ); $result = curl_exec($curl); if($result === false){ switch(curl_errno($curl)){ case CURLE_COULDNT_CONNECT: case CURLE_COULDNT_RESOLVE_HOST: case CURLE_COULDNT_RESOLVE_PROXY: $this->_response = 404; return $this->_tmplocal; break; default: $this->_response = 500; return $this->_tmplocal; break; } } curl_close($curl); // Copy the data down to the local file. $this->_tmplocal->putContents($result); } // And remember this header data for nexttime. Cache::Set( 'remotefile-cache-header-' . $f, [ 'headers' => $this->_getHeaders(), 'response' => $this->_response, ] ); } } return $this->_tmplocal; }
public static function _UploadHandler(Form $form) { $localfile = \Core\Filestore\Factory::File($form->getElement('upload')->get('value')); $localobj = $localfile->getContentsObject(); if(!$localobj instanceof Core\Filestore\Contents\ContentTGZ){ $localfile->delete(); \Core\set_message('Invalid file uploaded', 'error'); return false; } $tmpdir = $localobj->extract('tmp/installer-' . Core::RandomHex(4)); // There should now be a package.xml metafile inside that temporary directory. // Parse it to get the necessary information for this package. $metafile = \Core\Filestore\Factory::File($tmpdir->getPath() . 'package.xml'); if(!$metafile->exists()){ $localfile->delete(); $tmpdir->delete(); \Core\set_message('Invalid package, package does not contain a "package.xml" file.'); return false; } $pkg = new PackageXML($metafile->getFilename()); $key = str_replace(' ', '-', strtolower($pkg->getName())); $name = $pkg->getName(); $type = $pkg->getType(); $version = $pkg->getVersion(); // Validate the contents of the package. if(!( $type == 'component' || $type == 'theme' || $type == 'core' )){ $localfile->delete(); $tmpdir->delete(); \Core\set_message('Invalid package, package does not appear to be a valid Core package.'); return false; } // Now that the data is extracted in a temporary directory, extract every file in the destination. /** @var $datadir \Core\Filestore\Directory */ $datadir = $tmpdir->get('data/'); if(!$datadir){ \Core\set_message('Invalid package, package does not contain a "data" directory.'); return false; } if($type == 'component'){ $destdir = ROOT_PDIR . 'components/' . $key . '/'; } elseif($type == 'theme'){ $destdir = ROOT_PDIR . 'themes/' . $key . '/'; } else{ $destdir = ROOT_PDIR . '/'; } try{ // Will give me an array of Files in the data directory. $files = $datadir->ls(null, true); // Used to get the relative path for each contained file. $datalen = strlen($datadir->getPath()); foreach($files as $file){ if(!$file instanceof \Core\Filestore\Backends\FileLocal) continue; // It's a file, copy it over. // To do so, resolve the directory path inside the temp data dir. $dest = \Core\Filestore\Factory::File($destdir . substr($file->getFilename(), $datalen)); /** @var $dest \Core\Filestore\Backends\FileLocal */ $dest->copyFrom($file, true); } } catch(Exception $e){ // OH NOES! $localfile->delete(); $tmpdir->delete(); \Core\set_message($e->getMessage(), 'error'); return false; } // Cleanup everything $localfile->delete(); $tmpdir->delete(); // Clear the cache so the next pageload will pick up on the new components and goodies. \Core\Cache::Flush(); \Core\Templates\Backends\Smarty::FlushCache(); // Print a nice message to the user that it completed. \Core\set_message('Successfully installed ' . $name . ' ' . $version, 'success'); return '/updater'; }
/** * Get an array of locales currently available on the base system. * * @return array */ public static function GetLocalesAvailable(){ $cacheKey = 'core-i18n-locales'; $cacheTime = DEVELOPMENT_MODE ? 3600 : 604800; $cached = Cache::Get($cacheKey, $cacheTime); if($cached){ return $cached; } exec('locale -a', $output); $locales = []; foreach($output as $line){ if($line == 'C' || $line == 'POSIX' || $line == 'C.UTF-8'){ // Yeah, skip these! continue; } if(($dotpos = strpos($line, '.')) !== false){ // Trim anything after the ".", this is the charset which we, (in theory), don't need. // ex: .UTF-8 or .UTF-16. $line = substr($line, 0, $dotpos); } if(isset(self::$Languages[$line])){ $locales[$line] = self::$Languages[$line]; } } // Cache this so I don't have to execute the command and lookup the values all over again! Cache::Set($cacheKey, $locales, $cacheTime); return $locales; }
/** * 异步任务 * * @param $serv * @param $task_id * @param $from_id * @param $data */ public function task($serv, $task_id, $from_id, $data) { static $db = null; static $cache = null; if (!$db) { $db = new DB(); } if (!$cache) { $cache = new Cache(); } $d = json_decode($data, true); $type = $d['type']; unset($d['type']); //获取在线用户列表 $onlines = $db->fetch_all('select * from %t where 1', array('online')); $notme = false; $message = ""; switch ($type) { //处理消息 case 'message': #1 将用户消息入库 $db->insert("message", $d); $notme = true; $message = $d['msg']; $type = self::TYPE_NORMAL; break; //推送在线会员列表 //推送在线会员列表 case 'pushOnline': $message = $db->fetch_all("select * from %t where 1", array('online')); $type = self::TYPE_PUSH_ONLINEMEMBERS; //给单独用户推送在线会员列表 if ($d['sendto']) { $msg = $this->createRecMsg($message, $type); $serv->send((int) $d['sendto'], $this->frame($msg), $from_id); } break; case 'login': if ($db->fetch_first("select * from %t where openid=%s", array('online', $d['openid']))) { $db->update('online', array('fd' => $d['fd']), " openid='" . $d['openid'] . "'"); $cache->set($d['fd'], $d['openid']); } else { $serv->close($d['fd']); } break; case 'logout': $cache->delete($d['fd']); break; } //检查登陆授权 var_dump($cache->get($d['fd'])); // if(!$cache->get($d['fd'])) { $serv->close($d['fd']); } //将消息发给在线用户 if ($onlines) { echo date("H:i:s") . " : 当前有 " . count($onlines) . " 人在线,开始发送消息 == " . json_encode($message) . " \n"; foreach ($onlines as $key => $val) { if ($notme && $val['fd'] == $d['fd']) { continue; } $msg = $this->createRecMsg($message, $type, $val['fd']); //开始发送消息 $serv->send((int) $val['fd'], $this->frame($msg), $from_id); } } else { echo "没有在线用户"; } }
public static function PerformInstall($type, $name, $version, $dryrun = false, $verbose = false){ if($verbose){ // These are needed to force the output to be sent immediately. while ( @ob_end_flush() ); // even if there is no nested output buffer if(function_exists('apache_setenv')){ // This function doesn't exist in CGI mode :/ apache_setenv('no-gzip', '1'); } ini_set('output_buffering','on'); ini_set('zlib.output_compression', 0); ob_implicit_flush(); // Give some basic styles for this output. echo '<html> <head> <!-- Yes, the following is 1024 spaces. This is because some browsers have a 1Kb buffer before they start rendering text --> ' . str_repeat(" ", 1024) . ' <style> body { background: none repeat scroll 0 0 black; color: #22EE33; font-family: monospace; } </style> </head> <body>'; } $timer = microtime(true); // Give this script a few more seconds to run. set_time_limit(max(90, ini_get('max_execution_time'))); // This will get a list of all available updates and their sources :) if($verbose) self::_PrintHeader('Retrieving Updates'); $updates = UpdaterHelper::GetUpdates(); if($verbose){ self::_PrintInfo('Found ' . $updates['sitecount'] . ' repository site(s)!', $timer); self::_PrintInfo('Found ' . $updates['pkgcount'] . ' packages!', $timer); } // A list of changes that are to be applied, (mainly for the dry run). $changes = array(); // Target in on the specific object we're installing. Useful for a shortcut. switch($type){ case 'core': $initialtarget = &$updates['core']; break; case 'components': $initialtarget = &$updates['components'][$name]; break; case 'themes': $initialtarget = &$updates['themes'][$name]; break; default: return [ 'status' => 0, 'message' => '[' . $type . '] is not a valid installation type!', ]; } // This is a special case for testing the installer UI. $test = ($type == 'core' && $version == '99.1337~(test)'); if($test && $verbose){ self::_PrintHeader('Performing a test installation!'); } if($test){ if($verbose){ self::_PrintInfo('Sleeping for a few seconds... because servers are always slow when you don\'t want them to be!', $timer); } sleep(4); // Also overwrite some of the target's information. $repo = UpdateSiteModel::Find(null, 1); $initialtarget['source'] = 'repo-' . $repo->get('id'); $initialtarget['location'] = 'http://corepl.us/api/2_4/tests/updater-test.tgz.asc'; $initialtarget['destdir'] = ROOT_PDIR; $initialtarget['key'] = 'B2BEDCCB'; $initialtarget['status'] = 'update'; //if($verbose){ // echo '[DEBUG]' . $nl; // var_dump($initialtarget); //} } // Make sure the name and version exist in the updates list. // In theory, the latest version of core is the only one displayed. if(!$test && $initialtarget['version'] != $version){ return [ 'status' => 0, 'message' => $initialtarget['typetitle'] . ' does not have the requested version available.', 'debug' => [ 'versionrequested' => $version, 'versionfound' => $initialtarget['version'], ], ]; } // A queue of components to check. $pendingqueue = array($initialtarget); // A queue of components that will be installed that have satisfied dependencies. $checkedqueue = array(); // This will assemble the list of required installs in the correct order. // If a given dependency can't be met, the installation will be aborted. if($verbose){ self::_PrintHeader('CHECKING DEPENDENCIES'); } do{ $lastsizeofqueue = sizeof($pendingqueue); foreach($pendingqueue as $k => $c){ $good = true; if(isset($c['requires'])){ if($verbose){ self::_PrintInfo('Checking dependencies for ' . $c['typetitle'], $timer); } foreach($c['requires'] as $r){ // Sometimes there will be blank requirements in the metafile. if(!$r['name']) continue; $result = UpdaterHelper::CheckRequirement($r, $checkedqueue, $updates); if($result === false){ // Dependency not met return [ 'status' => 0, 'message' => $c['typetitle'] . ' requires ' . $r['name'] . ' ' . $r['version'] ]; } elseif($result === true){ // Dependency met via either installed components or new components // yay if($verbose){ self::_PrintInfo('Dependency [' . $r['name'] . ' ' . $r['version'] . '] met with already-installed packages.', $timer); } } else{ if($verbose){ self::_PrintInfo('Additional package [' . $result['typetitle'] . '] required to meet dependency [' . $r['name'] . ' ' . $r['version'] . '], adding to queue and retrying!', $timer); } // It's an array of requirements that are needed to satisfy this installation. $pendingqueue = array_merge(array($result), $pendingqueue); $good = false; } } } else{ if($verbose){ self::_PrintInfo('Skipping dependency check for ' . $c['typetitle'] . ', no requirements present', $timer); } // The require key isn't present... OK! // This happens with themes, as they do not have any dependency logic. } if($good === true){ $checkedqueue[] = $c; $changes[] = (($c['status'] == 'update') ? 'Update' : 'Install') . ' ' . $c['typetitle'] . ' ' . $c['version']; unset($pendingqueue[$k]); } } } while(sizeof($pendingqueue) && sizeof($pendingqueue) != $lastsizeofqueue); // Do validation checks on all these changes. I need to make sure I have the GPG key for each one. // This is done here to save having to download the files from the remote server first. foreach($checkedqueue as $target){ // It'll be validated prior to installation anyway. if(!$target['key']) continue; $output = array(); exec('gpg --homedir "' . GPG_HOMEDIR . '" --list-public-keys "' . $target['key'] . '"', $output, $result); if($result > 0){ // Key validation failed! if($verbose){ echo implode("<br/>\n", $output); } return [ 'status' => 0, 'message' => $c['typetitle'] . ' failed GPG verification! Is the key ' . $target['key'] . ' installed?' ]; } } // Check that the queued packages have not been locally modified if installed. if($verbose){ self::_PrintHeader('Checking for local modifications'); } foreach($checkedqueue as $target){ if($target['status'] == 'update'){ switch($target['type']){ case 'core': $c = Core::GetComponent('core'); break; case 'components': $c = Core::GetComponent($target['name']); break; case 'themes': $c = null; break; } if($c){ // Are there changes? if(sizeof($c->getChangedAssets())){ foreach($c->getChangedAssets() as $change){ $changes[] = 'Overwrite locally-modified asset ' . $change; } } if(sizeof($c->getChangedFiles())){ foreach($c->getChangedFiles() as $change){ $changes[] = 'Overwrite locally-modified file ' . $change; } } if(sizeof($c->getChangedTemplates())){ foreach($c->getChangedTemplates() as $change){ $changes[] = 'Overwrite locally-modified template ' . $change; } } } } } // If dry run is enabled, stop here. // After this stage, dragons be let loose from thar cages. if($dryrun){ return [ 'status' => 1, 'message' => 'All dependencies are met, ok to install', 'changes' => $changes, ]; } // Reset changes, in this case it'll be what was installed. $changes = array(); // By now, $checkedqueue will contain all the pending changes, theoretically with // the initially requested package at the end of the list. foreach($checkedqueue as $target){ if($verbose){ self::_PrintHeader('PERFORMING INSTALL (' . strtoupper($target['typetitle']) . ')'); } // This package is already installed and up to date. if($target['source'] == 'installed'){ return [ 'status' => 0, 'message' => $target['typetitle'] . ' is already installed and at the newest version.', ]; } // If this package is coming from a repo, install it from that repo. elseif(strpos($target['source'], 'repo-') !== false){ /** @var $repo UpdateSiteModel */ $repo = new UpdateSiteModel(substr($target['source'], 5)); if($verbose){ self::_PrintInfo('Using repository ' . $repo->get('url') . ' for installation source', $timer); } // Setup the remote file that will be used to download from. $file = new \Core\Filestore\Backends\FileRemote($target['location']); $file->username = $repo->get('username'); $file->password = $repo->get('password'); // The initial HEAD request pulls the metadata for the file, and sees if it exists. if($verbose){ self::_PrintInfo('Performing HEAD lookup on ' . $file->getFilename(), $timer); } if(!$file->exists()){ return [ 'status' => 0, 'message' => $target['location'] . ' does not seem to exist!' ]; } if($verbose){ self::_PrintInfo('Found a(n) ' . $file->getMimetype() . ' file that returned a ' . $file->getStatus() . ' status.', $timer); } // Get file contents will download the file. if($verbose){ self::_PrintInfo('Downloading ' . $file->getFilename(), $timer); } $downloadtimer = microtime(true); $obj = $file->getContentsObject(); // Getting the object simply sets it up, it doesn't download the contents yet. $obj->getContents(); // Now it has :p // How long did it take? if($verbose){ self::_PrintInfo('Downloaded ' . $file->getFilesize(true) . ' in ' . (round(microtime(true) - $downloadtimer, 2) . ' seconds'), $timer); } if(!($obj instanceof \Core\Filestore\Contents\ContentASC)){ return [ 'status' => 0, 'message' => $target['location'] . ' does not appear to be a valid GPG signed archive' ]; } if(!$obj->verify()){ // Maybe it can at least get the key.... if($key = $obj->getKey()){ return [ 'status' => 0, 'message' => 'Unable to locate public key for ' . $key . '. Is it installed?' ]; } return [ 'status' => 0, 'message' => 'Invalid GPG signature for ' . $target['typetitle'], ]; } // The object's key must also match what's in the repo. if($obj->getKey() != $target['key']){ return [ 'status' => 0, 'message' => '!!!WARNING!!!, Key for ' . $target['typetitle'] . ' is valid, but does not match what was expected form the repository data! This could be a major risk!', 'debug' => [ 'detectedkey' => $obj->getKey(), 'expectedkey' => $target['key'], ], ]; } if($verbose){ self::_PrintInfo('Found key ' . $target['key'] . ' for package maintainer, appears to be valid.', $timer); exec('gpg --homedir "' . GPG_HOMEDIR . '" --list-public-keys "' . $target['key'] . '"', $output, $result); foreach($output as $line){ if(trim($line)) self::_PrintInfo(htmlentities($line), $timer); } } if($verbose) self::_PrintInfo('Checking write permissions', $timer); $dir = \Core\directory($target['destdir']); if(!$dir->isWritable()){ return [ 'status' => 0, 'message' => $target['destdir'] . ' is not writable!' ]; } if($verbose) self::_PrintInfo('OK!', $timer); // Decrypt the signed file. if($verbose) self::_PrintInfo('Decrypting signed file', $timer); if(version_compare(Core::GetComponent('core')->getVersionInstalled(), '4.1.1', '<=') && $file->getBaseFilename() == 'download'){ // HACK < 4.1.2 // Retrieve the filename from the last part of the URL. // This is required because the URL may be /download?file=component/blah.tgz.asc $f = substr($file->getFilename(), strrpos($file->getFilename(), '/'), -4); /** @var $localfile \Core\Filestore\File */ $localfile = $obj->decrypt('tmp/updater/' . $f); } else{ /** @var $localfile \Core\Filestore\File */ $localfile = $obj->decrypt('tmp/updater/'); } /** @var $localobj \Core\Filestore\Contents\ContentTGZ */ $localobj = $localfile->getContentsObject(); if($verbose) self::_PrintInfo('OK!', $timer); // This tarball will be extracted to a temporary directory, then copied from there. if($verbose){ self::_PrintInfo('Extracting tarball ' . $localfile->getFilename(), $timer); } $tmpdir = $localobj->extract('tmp/installer-' . Core::RandomHex(4)); // Now that the data is extracted in a temporary directory, extract every file in the destination. /** @var $datadir \Core\Filestore\Directory */ $datadir = $tmpdir->get('data/'); if(!$datadir){ return [ 'status' => 0, 'message' => 'Invalid package, ' . $target['typetitle'] . ', does not contain a "data" directory.' ]; } if($verbose) self::_PrintInfo('OK!', $timer); if($verbose){ self::_PrintInfo('Installing files into ' . $target['destdir'], $timer); } // Will give me an array of Files in the data directory. $files = $datadir->ls(null, true); // Used to get the relative path for each contained file. $datalen = strlen($datadir->getPath()); foreach($files as $file){ if(!$file instanceof \Core\Filestore\Backends\FileLocal) continue; // It's a file, copy it over. // To do so, resolve the directory path inside the temp data dir. $dest = \Core\Filestore\Factory::File($target['destdir'] . substr($file->getFilename(), $datalen)); /** @var $dest \Core\Filestore\Backends\FileLocal */ if($verbose){ self::_PrintInfo('...' . substr($dest->getFilename(''), 0, 67), $timer); } $dest->copyFrom($file, true); } if($verbose) self::_PrintInfo('OK!', $timer); // Cleanup the temp directory if($verbose){ self::_PrintInfo('Cleaning up temporary directory', $timer); } $tmpdir->remove(); if($verbose) self::_PrintInfo('OK!', $timer); $changes[] = 'Installed ' . $target['typetitle'] . ' ' . $target['version']; } } // Clear the cache so the next pageload will pick up on the new components and goodies. \Core\Cache::Flush(); \Core\Templates\Backends\Smarty::FlushCache(); // Yup, that's it. // Just extract the files and Core will autoinstall/autoupgrade everything on the next page view. // yay... return [ 'status' => 1, 'message' => 'Performed all operations successfully!', 'changes' => $changes, ]; }
/** * Load all the components in the system, replacement for the Core. * @throws CoreException */ private function _loadComponents() { // cannot reload components. if ($this->_components) return null; $this->_components = array(); $this->_libraries = array(); $tempcomponents = false; Core\Utilities\Logger\write_debug('Starting loading of component metadata'); // If the site is in DEVELOPMENT mode, component caching would probably be a bad idea; ie: the developer probably wants // those component files loaded everytime. if(DEVELOPMENT_MODE){ $enablecache = false; } else{ $enablecache = true; } // Is there a cache of elements available? This is a primary system cache that greatly increases performance, // since it will no longer have to run through each component.xml file to register each one. if($enablecache){ Core\Utilities\Logger\write_debug('Checking core-components cache'); // Try to load up the cached components and check them first. $tempcomponents = \Core\Cache::Get('core-components', (3600 * 24)); if($tempcomponents !== false){ // Cached components only need to be loaded. foreach ($tempcomponents as $c) { try { $c->load(); } catch (Exception $e) { // Don't completely bail out here, just invalidate the cache and continue on. \Core\Cache::Delete('core-components'); $tempcomponents = false; } } } } if(!$enablecache || $tempcomponents == false){ \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record('Scanning for component.xml files manually'); Core\Utilities\Logger\write_debug('Scanning for component.xml files manually'); // Core is first, (obviously) $tempcomponents['core'] = ComponentFactory::Load(ROOT_PDIR . 'core/component.xml'); Core\Utilities\Logger\write_debug('Core component loaded'); // First, build my cache of components, regardless if the component is installed or not. $dh = opendir(ROOT_PDIR . 'components'); if (!$dh) throw new CoreException('Unable to open directory [' . ROOT_PDIR . 'components/] for reading.'); // This will read through every directory in 'components', which is // where all components in the system are installed to. while (($file = readdir($dh)) !== false) { // skip hidden directories. if ($file{0} == '.') continue; // skip non-directories if (!is_dir(ROOT_PDIR . 'components/' . $file)) continue; // Skip directories that do not have a readable component.xml file. if (!is_readable(ROOT_PDIR . 'components/' . $file . '/component.xml')) continue; //Core\Utilities\Logger\write_debug(' * Loading component ' . $file); $c = ComponentFactory::Load(ROOT_PDIR . 'components/' . $file . '/component.xml'); Core\Utilities\Logger\write_debug('Opened component ' . $file); // All further operations are case insensitive. // The original call to Component needs to be case sensitive because it sets the filename to pull. $file = strtolower($file); // If the component was flagged as invalid.. just skip to the next one. if (!$c->isValid()) { if (DEVELOPMENT_MODE) { \Core\set_message('Component ' . $c->getName() . ' appears to be invalid.'); } continue; } $tempcomponents[$file] = $c; unset($c); } closedir($dh); \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record('Component XML files scanned'); // Now I probably could actually load the components! foreach ($tempcomponents as $c) { /** @var Component_2_1 $c */ try { // Load some of the data in the class so that it's available in the cached version. // This is because the component 2.1 has built-in caching for many of the XML requests. // by calling them once, that lookup data is cached in that component, which in turn gets // copied to the cache version here! $c->load(); $c->getClassList(); $c->getViewSearchDir(); $c->getSmartyPluginDirectory(); $c->getWidgetList(); } catch (Exception $e) { var_dump($e); die(); } } // Cache this list! if($enablecache){ Core\Utilities\Logger\write_debug(' * Caching core-components for next pass'); \Core\Cache::Set('core-components', $tempcomponents, (3600 * 24)); } } $list = $tempcomponents; \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record('Component metadata loaded, starting registration'); Core\Utilities\Logger\write_debug(' * Component metadata loaded, starting registration'); // The core component at a minimum needs to be loaded and registered. // $this->_registerComponent($list['core']); // $this->_components['core']->loadFiles(); // unset($list['core']); // Now that I have a list of components available, copy them into a list of // components that are installed. do { $size = sizeof($list); foreach ($list as $n => $c) { /** @var $c Component_2_1 */ // Disabled components don't get recognized. if($c->isInstalled() && !$c->isEnabled()){ // But they do get sent to the disabled list! $this->_componentsDisabled[$n] = $c; unset($list[$n]); continue; } // Clear out the temporary class list $this->_tmpclasses = []; // If it's loaded, register it and remove it from the list! if ($c->isInstalled() && $c->isLoadable() && $c->loadFiles()) { try{ // Allow for on-the-fly package upgrading regardless of DEV mode or not. if ($c->needsUpdated()) { // Load this component's classes in case an upgrade operation requires one. // This allows a component to be loaded partially without completely being loaded. $this->_tmpclasses = $c->getClassList(); // Lock the site first! // This is because some upgrade procedures take a long time to upgrade. file_put_contents(TMP_DIR . 'lock.message', 'Core Plus is being upgraded, please try again in a minute. '); $c->upgrade(); unlink(TMP_DIR . 'lock.message'); } } catch(Exception $e){ SystemLogModel::LogErrorEvent('/core/component/failedupgrade', 'Ignoring component [' . $n . '] due to an error during upgrading!', $e->getMessage()); unlink(TMP_DIR . 'lock.message'); //$c->disable(); $this->_componentsDisabled[$n] = $c; unset($list[$n]); continue; } try{ $this->_components[$n] = $c; $this->_registerComponent($c); $c->loadSupplementalModels(); } catch(Exception $e){ SystemLogModel::LogErrorEvent('/core/component/failedregister', 'Ignoring component [' . $n . '] due to an error during registration!', $e->getMessage()); //$c->disable(); $this->_componentsDisabled[$n] = $c; unset($list[$n]); continue; } unset($list[$n]); continue; } // Allow for on-the-fly package upgrading regardless of DEV mode or not. // Guess this is needed for the loadFiles part... if ($c->isInstalled() && $c->needsUpdated() && $c->isLoadable()) { // Lock the site first! // This is because some upgrade procedures take a long time to upgrade. file_put_contents(TMP_DIR . 'lock.message', 'Core Plus is being upgraded, please try again in a minute. '); $c->upgrade(); $c->loadFiles(); $this->_components[$n] = $c; $this->_registerComponent($c); unlink(TMP_DIR . 'lock.message'); unset($list[$n]); continue; } // Allow packages to be auto-installed if in DEV mode. // If DEV mode is not enabled, just install the new component, do not enable it. if (!$c->isInstalled() && $c->isLoadable()) { // Load this component's classes in case an install operation requires one. // This allows a component to be loaded partially without completely being loaded. $this->_tmpclasses = $c->getClassList(); // w00t $c->install(); // BLAH, until I fix the disabled-packages-not-viewable bug... $c->enable(); $c->loadFiles(); $this->_components[$n] = $c; $this->_registerComponent($c); /* if(!DEVELOPMENT_MODE){ $c->disable(); } else{ $c->enable(); $c->loadFiles(); $this->_components[$n] = $c; $this->_registerComponent($c); } */ unset($list[$n]); continue; } } } while ($size > 0 && ($size != sizeof($list))); // If dev mode is enabled, display a list of components installed but not loadable. foreach ($list as $n => $c) { //$this->_components[$n] = $c; $this->_componentsDisabled[$n] = $c; // Ignore anything with the execmode different, those should be minor notices for debugging if anything. if ($c->error & Component_2_1::ERROR_WRONGEXECMODE) continue; if (DEVELOPMENT_MODE) { SystemLogModel::LogErrorEvent('/core/component/missingrequirement', 'Could not load installed component ' . $n . ' due to requirement failed.', $c->getErrors()); } } // Don't forget to load the themes too! if(class_exists('ThemeHandler')){ foreach(ThemeHandler::GetAllThemes() as $theme){ /** @var $theme Theme */ $theme->load(); } } // Lastly, make sure that the template path cache is updated! if(class_exists('\\Core\\Templates\\Template')){ \Core\Templates\Template::RequeryPaths(); } }
/** * Run through and reinstall all components and themes. * * @return int */ public function reinstallAll() { // Admin-only page. if(!\Core\user()->checkAccess('g:admin')){ return View::ERROR_ACCESSDENIED; } // Just run through every component currently installed and reinstall it. // This will just ensure that the component is up to date and correct as per the component.xml metafile. $view = $this->getView(); $request = $this->getPageRequest(); if($request->isPost()){ $view->mode = View::MODE_NOOUTPUT; $view->contenttype = View::CTYPE_HTML; $view->record = false; $view->templatename = null; $view->render(); // Try to perform the reinstall. $changes = array(); $errors = array(); $allpages = []; $t = ThemeHandler::GetTheme(); CLI::PrintHeader('Reinstalling Theme ' . $t->getName()); if (($change = $t->reinstall(1)) !== false) { SystemLogModel::LogInfoEvent('/updater/theme/reinstall', 'Theme ' . $t->getName() . ' reinstalled successfully', implode("\n", $change)); $changes[] = '<b>Changes to theme [' . $t->getName() . ']:</b><br/>' . "\n" . implode("<br/>\n", $change) . "<br/>\n<br/>\n"; } foreach (Core::GetComponents() as $c) { /** @var $c Component_2_1 */ try{ if(!$c->isInstalled()) continue; if(!$c->isEnabled()) continue; CLI::PrintHeader('Reinstalling Component ' . $c->getName()); // Request the reinstallation $change = $c->reinstall(1); // 1.0 version components don't support verbose changes :( if ($change === true) { $changes[] = '<b>Changes to component [' . $c->getName() . ']:</b><br/>' . "\n(list of changes not supported with this component!)<br/>\n<br/>\n"; } // 2.1 components support an array of changes, yay! elseif ($change !== false) { $changes[] = '<b>Changes to component [' . $c->getName() . ']:</b><br/>' . "\n" . implode("<br/>\n", $change) . "<br/>\n<br/>\n"; } // I don't care about "else", nothing changed if it was false. // Get the pages, (for the cleanup operation) $allpages = array_merge($allpages, $c->getPagesDefined()); } catch(DMI_Query_Exception $e){ $changes[] = 'Attempted database changes to component [' . $c->getName() . '], but failed!<br/>'; //var_dump($e); die(); $errors[] = array( 'type' => 'component', 'name' => $c->getName(), 'message' => $e->getMessage() . '<br/>' . $e->query, ); } catch(Exception $e){ $changes[] = 'Attempted changes to component [' . $c->getName() . '], but failed!<br/>'; //var_dump($e); die(); $errors[] = array( 'type' => 'component', 'name' => $c->getName(), 'message' => $e->getMessage(), ); } } // Flush any non-existent admin page. // These can be created from developers changing their page URLs after the page is already registered. // Purging admin-only pages is relatively safe because these are defined in component metafiles anyway. CLI::PrintHeader('Cleaning up non-existent pages'); $pageremovecount = 0; foreach( \Core\Datamodel\Dataset::Init() ->select('baseurl') ->table('page') ->where('admin = 1') ->execute() as $row ){ $baseurl = $row['baseurl']; // This page existed already, no need to do anything :) if(isset($allpages[$baseurl])) continue; ++$pageremovecount; // Otherwise, this page was deleted or for some reason doesn't exist in the component list..... // BUH BAI \Core\Datamodel\Dataset::Init()->delete()->table('page')->where('baseurl = ' . $baseurl)->execute(); \Core\Datamodel\Dataset::Init()->delete()->table('page_meta')->where('baseurl = ' . $baseurl)->execute(); CLI::PrintLine("Flushed non-existent admin page: " . $baseurl); $changes[] = "<b>Flushed non-existent admin page:</b> " . $baseurl; } if($pageremovecount == 0){ CLI::PrintLine('No pages flushed'); } if(sizeof($errors) > 0){ CLI::PrintHeader('Done, but with errors'); foreach($errors as $e){ CLI::PrintError('Error while processing ' . $e['type'] . ' ' . $e['name'] . ': ' . $e['message']); } } else{ CLI::PrintHeader('DONE!'); } foreach($changes as $str){ echo $str; } // Flush the system cache, just in case \Core\Cache::Flush(); \Core\Templates\Backends\Smarty::FlushCache(); // Increment the version counter. $version = ConfigHandler::Get('/core/filestore/assetversion'); ConfigHandler::Set('/core/filestore/assetversion', ++$version); } // End if is post. //$page->title = 'Reinstall All Components'; $this->setTemplate('/pages/admin/reinstallall.tpl'); }
/** * Render the View to the browser. */ public function render(){ \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record('Starting PageRequest->render()'); $view = $this->getView(); $page = $this->getPageModel(); // Dispatch the hooks here if it's a 404 or 403. if ($view->error == View::ERROR_ACCESSDENIED || $view->error == View::ERROR_NOTFOUND) { // Let other things chew through it... (optionally) HookHandler::DispatchHook('/core/page/error-' . $view->error, $view); } try { // This will pre-fetch the contents of the entire page and store it into memory. // If it is cacheable, then it will be cached and used for the next execution. // If the user has the view user activity permission, add the link to that page! if(\Core\user()->checkAccess('p:user_activity_list') && $page && $page->exists()){ $view->addControl( 'User Activity Details', '/useractivity/details?filter[baseurl]=' . $page->get('baseurl'), 'eye' ); } $view->fetch(); } catch (Exception $e) { // If something happens in the rendering of the template... consider it a server error. $view->error = View::ERROR_SERVERERROR; $view->baseurl = '/error/error/500'; $view->setParameters(array()); $view->templatename = '/pages/error/error500.tpl'; $view->mastertemplate = ConfigHandler::Get('/theme/default_template'); $view->assignVariable('exception', $e); \Core\ErrorManagement\exception_handler($e); $view->fetch(); } if($this->isCacheable()){ $uakey = \Core\UserAgent::Construct()->getPseudoIdentifier(); $urlkey = $this->host . $this->uri; $expires = $page->get('expires'); // Number of seconds. $key = 'page-cache-' . md5($urlkey . '-' . $uakey); $d = new \Core\Date\DateTime(); $d->modify('+' . $expires . ' seconds'); $view->headers['Cache-Control'] = 'max-age=' . $expires; $view->headers['Expires'] = $d->format('r', \Core\Date\Timezone::TIMEZONE_GMT); $view->headers['Vary'] = 'Accept-Encoding,User-Agent,Cookie'; $view->headers['X-Core-Cached-Date'] = \Core\Date\DateTime::NowGMT('r'); $view->headers['X-Core-Cached-Server'] = 1; // @todo Implement multi-server support. $view->headers['X-Core-Cached-Render-Time'] = \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->getTimeFormatted(); // Record the actual View into cache. \Core\Cache::Set($key, $view, $expires); // And record the key onto an index cache record so there's a record of what to delete on updates. $indexkey = $page->getIndexCacheKey(); $index = \Core\Cache::Get($indexkey, SECONDS_ONE_DAY); if(!$index){ $index = []; } $index[] = $key; \Core\Cache::Set($indexkey, $index, SECONDS_ONE_DAY); } elseif(($reason = $this->isNotCacheableReason()) !== null){ $view->headers['X-Core-NotCached-Reason'] = $reason; } $view->headers['X-Core-Render-Time'] = \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->getTimeFormatted(); $view->render(); // Make sure I update any existing page now that the controller has ran. if ($page && $page->exists() && $view->error == View::ERROR_NOERROR) { // Only increase the pageview count if the visitor is not a bot. // UA detection isn't very accurate, but this isn't for precision accuracy, merely a rough estimate. if(!\Core\UserAgent::Construct()->isBot()){ $page->set('pageviews', $page->get('pageviews') + 1); } $page->set('last_template', $view->templatename); $page->set('body', $view->fetchBody()); $page->save(); } // Just before the page stops execution... HookHandler::DispatchHook('/core/page/postrender'); }
// All Models MUST reside in the global namespace in order to be valid. continue; } $ref = new ReflectionClass($class); if(!$ref->getProperty('HasSearch')->getValue()){ // This model doesn't have the searchable flag, skip it. continue; } CLI::PrintActionStart("Syncing searchable model $class"); $fac = new ModelFactory($class); foreach($fac->get() as $m){ /** @var Model $m */ $m->set('search_index_pri', '!'); $m->save(); } CLI::PrintActionStatus('ok'); $changes[] = "Synced searchable model " . $class; } } // Flush the system cache, just in case \Core\Cache::Flush(); \Core\Templates\Backends\Smarty::FlushCache(); CLI::PrintHeader('DONE!'); foreach($changes as $line){ CLI::PrintLine($line); }
public function __construct($ip_addr) { try { if ($ip_addr == '127.0.0.1') { // Load local connections up with Columbus, OH. // Why? ;) $cache = ['city' => 'Columbus', 'province' => 'OH', 'country' => 'US', 'timezone' => 'America/New_York', 'postal' => '43215']; } else { $cacheKey = 'iplookup-' . $ip_addr; $cache = Cache::Get($cacheKey); if (!$cache) { $reader = new \GeoIp2\Database\Reader(ROOT_PDIR . 'components/geographic-codes/libs/maxmind-geolite-db/GeoLite2-City.mmdb'); /** @var \GeoIp2\Model\CityIspOrg $geo */ $geo = $reader->cityIspOrg($ip_addr); //$geo = $reader->cityIspOrg('67.149.214.236'); $reader->close(); $sd = isset($geo->subdivisions[0]) ? $geo->subdivisions[0] : null; $cache = ['city' => $geo->city->name, 'province' => $sd ? $sd->isoCode : '', 'country' => $geo->country->isoCode, 'timezone' => $geo->location->timeZone, 'postal' => $geo->postal->code]; Cache::Set($cacheKey, $cache, SECONDS_ONE_WEEK); } } } catch (\Exception $e) { // Well, we tried! Load something at least. $cacheKey = 'iplookup-' . $ip_addr; $cache = ['city' => 'McMurdo Base', 'province' => '', 'country' => 'AQ', 'timezone' => 'CAST', 'postal' => '']; Cache::Set($cacheKey, $cache, SECONDS_ONE_HOUR); } $this->city = $cache['city']; $this->province = $cache['province']; $this->country = $cache['country']; $this->timezone = $cache['timezone']; $this->postal = $cache['postal']; }
/** * Construct a UserAgent object with full caching enabled. * * @param $useragent * * @return UserAgent */ public static function Construct($useragent = null){ if($useragent === null){ $useragent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''; } $cachekey = 'useragent-constructor-' . md5($useragent); $cache = Cache::Get($cachekey); if(!$cache){ $cache = new UserAgent($useragent); Cache::Set($cachekey, $cache, SECONDS_ONE_WEEK); } return $cache; }
/** * Component installation operations all share common actions, (mostly). * * Returns false if nothing changed, else will return an array containing all changes. * * @param int $verbosity 0 for standard output, 1 for real-time, 2 for real-time verbose output. * * @return false | array * @throws InstallerException */ private function _performInstall($verbosity = 0) { // make sure that some of the installer elements are available! require_once(ROOT_PDIR . 'core/libs/core/InstallerException.php'); #SKIPCOMPILER $changed = array(); $change = $this->_parseDBSchema(true, $verbosity); if ($change !== false){ $changed = array_merge($changed, $change); } $change = $this->_parseConfigs(true, $verbosity); if ($change !== false){ $changed = array_merge($changed, $change); } $change = $this->_parseUserConfigs(true, $verbosity); if ($change !== false){ $changed = array_merge($changed, $change); } $change = $this->_parsePages(true, $verbosity); if ($change !== false){ $changed = array_merge($changed, $change); } $change = $this->_parseWidgets(true, $verbosity); if ($change !== false){ $changed = array_merge($changed, $change); } $change = $this->_parseAssets(true, $verbosity); if ($change !== false){ $changed = array_merge($changed, $change); } // Core has some additional things that need to ran through. if($this->getKeyName() == 'core'){ // Make sure that files/private has a restrictive .htaccess file installed. $f = \Core\Filestore\Factory::File('private/.htaccess'); if(!$f->exists() && $f->isWritable()){ $src = \Core\Filestore\Factory::File('core/htaccess.private'); if($src->copyTo($f)){ $changed[] = 'Installed private htaccess file into ' . $f->getFilename(); } } // Make sure that files/public has the appropriate .htaccess file installed. $f = \Core\Filestore\Factory::File('public/.htaccess'); if(!$f->exists() && $f->isWritable()){ $src = \Core\Filestore\Factory::File('core/htaccess.public'); if($src->copyTo($f)){ $changed[] = 'Installed public htaccess file into ' . $f->getFilename(); } } // Make sure that files/public has the appropriate .htaccess file installed. $f = \Core\Filestore\Factory::File('asset/.htaccess'); // This is a bit of a hack because I need the parent directory for assets, not the theme-specific version. $f->setFilename(dirname(dirname($f->getFilename())) . '/.htaccess'); if(!$f->exists() && $f->isWritable()){ $src = \Core\Filestore\Factory::File('core/htaccess.assets'); if($src->copyTo($f)){ $changed[] = 'Installed assets htaccess file into ' . $f->getFilename(); } } } // Ensure that the core component cache is purged too! \Core\Cache::Delete('core-components'); return (sizeof($changed)) ? $changed : false; }