/** * PHP >= 5.4.0<br/> * Initialize session * @link http://php.net/manual/en/sessionhandlerinterafce.open.php * * @param string $save_path The path where to store/retrieve the session. * @param string $session_id The session id. * * @return bool <p> * The return value (usually TRUE on success, FALSE on failure). * Note this value is returned internally to PHP for processing. * </p> */ public function open($save_path, $session_id) { // Anything waiting for the session? \HookHandler::DispatchHook('/core/session/ready'); self::$_IsReady = true; // Open really doesn't need to do anything, everything that will be controlling the session will be built into Core Plus. return true; }
/** * Internally used method to notify the rest of the system that a given * component has been loaded and is available. * * Expects all checks to be done already. */ public function _registerComponent(Component_2_1 $c) { $name = str_replace(' ', '-', strtolower($c->getName())); if ($c->hasLibrary()) { $liblist = $c->getLibraryList(); $this->_libraries = array_merge($this->_libraries, $liblist); // Register the include paths if set. foreach ($c->getIncludePaths() as $path) { set_include_path(get_include_path() . PATH_SEPARATOR . $path); } } $this->_scriptlibraries = array_merge($this->_scriptlibraries, $c->getScriptLibraryList()); if ($c->hasModule()) $this->_modules[$name] = $c->getVersionInstalled(); $this->_classes = array_merge($this->_classes, $c->getClassList()); $this->_viewClasses = array_merge($this->_viewClasses, $c->getViewClassList()); $this->_widgets = array_merge($this->_widgets, $c->getWidgetList()); $this->_components[$name] = $c; // Permissions were not enabled prior to 2.1, so the legacy components do not have the function. if($c instanceof Component_2_1){ $this->_permissions = array_merge($this->_permissions, $c->getPermissions()); ksort($this->_permissions); // Register this component's user authdrivers, if any. $auths = $c->getUserAuthDrivers(); foreach($auths as $name => $class){ \Core\User\Helper::$AuthDrivers[$name] = $class; } } // All models get a control link registered automatically :) $models = $c->getModelList(); foreach($models as $class => $file){ if(!HookHandler::GetHook('/core/controllinks/' . $class)){ $h = new Hook('/core/controllinks/' . $class); $h->returnType = Hook::RETURN_TYPE_ARRAY; $h->description = 'Automatic hook for control links on the ' . $class . ' object. Attach onto this hook if you want to add a custom link anytime this object\'s control is displayed.'; } } $licenser = $c->getLicenseData(); if($licenser && defined('SERVER_ID') && class_exists('\\Core\\Licenser')){ // This will contain a url key and the features. $url = $licenser['url']; $comp = $c->getKeyName(); foreach($licenser['features'] as $key){ \Core\Licenser::RegisterFeature($key, $url, $comp); } } // Lastly, mark this component as available! $c->_setReady(true); }
/** * Connect to the set hostname using the appropriate credentials. * * @throws \Exception */ public function connect(){ if($this->connected){ // Already connected, YAY! return; } if(!$this->host){ throw new \Exception('Please set the host before connecting to an FTP server.'); } if(!$this->root){ throw new \Exception('Please set the root path before connecting to an FTP server.'); } $this->conn = ftp_connect($this->host); if(!$this->conn){ throw new \Exception('Unable to connect to the FTP server at ' . $this->host); } if($this->username){ if(!ftp_login($this->conn, $this->username, $this->password)){ throw new \Exception('Bad FTP username or password for ' . $this->host); } // Now that it's connected, hide the password. $this->password = '******'; } else{ if(!ftp_login($this->conn, 'anonymous', '')){ throw new \Exception('Anonymous logins are disabled for ' . $this->host); } } ftp_set_option($this->conn, FTP_TIMEOUT_SEC, 600); $this->reset(); if($this->host == '127.0.0.1'){ $this->isLocal = true; } $this->connected = true; $this->lastSave = DateTime::NowGMT(); self::$_OpenConnections[] = $this; if(sizeof(self::$_OpenConnections) == 1){ // First open connection, register the shutdown hook! \HookHandler::AttachToHook('/core/shutdown', '\\Core\\Filestore\\FTP\\FTPConnection::ShutdownHook'); } }
/** * The weekly health report to email to admins. * * Will only send an email if there was an issue found with the site. * Otherwise, this will be used by the upstream maintainers to know what versions clients are connecting with. */ public static function _HealthCheckReport(){ $checks = HookHandler::DispatchHook('/core/healthcheck'); $toReport = []; foreach($checks as $check){ /** @var \Core\HealthCheckResult $check */ if($check->result != \Core\HealthCheckResult::RESULT_GOOD){ $toReport[] = $check; } } if(sizeof($toReport) == 0){ // YAY! return true; } if(!defined('SERVER_ADMIN_EMAIL') || SERVER_ADMIN_EMAIL == ''){ echo 'Health issues found but unable to send an email, please set SERVER_ADMIN_EMAIL in your configuration.xml!'; return false; } $email = new Email(); if(strpos(SERVER_ADMIN_EMAIL, ',') !== false){ $emails = explode(',', SERVER_ADMIN_EMAIL); foreach($emails as $e){ $e = trim($e); if($e){ $email->addAddress($e); } } } else{ $email->addAddress(SERVER_ADMIN_EMAIL); } $email->setSubject('Site Health Report'); $email->templatename = 'emails/admin/health_report.tpl'; $email->assign('checks', $toReport); try{ if($email->send()){ echo 'Sent health issues to the server admin successfully.'; return true; } else{ echo 'Unable to send health issues to server admin, please check your email settings.'; return false; } } catch(Exception $e){ echo 'Unable to send health issues to server admin, please check your email settings.'; return false; } }
/** * 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'); }
/** * Handle an error and report it to the Core system log. * * @param $errno * @param $errstr * @param $errfile * @param $errline * @param null $errcontext */ function error_handler($errno, $errstr, $errfile, $errline, $errcontext = null){ $type = null; $fatal = false; $code = null; $class = ''; // The exception to this is when error_reporting is explictly set to 0. // This happens when a function is called with the "@" error suppressor. // In this event, I still want to log the error, but simply do not display it on the screen. // Damn f*****g "@" operator..... $suppressed = (error_reporting() === 0); switch($errno){ case E_ERROR: case E_USER_ERROR: $fatal = true; $type = 'error'; $class = 'error'; $code = 'PHP Error'; break; case E_WARNING: case E_USER_WARNING: $type = 'error'; $class = 'warning'; $code = 'PHP Warning'; break; case E_NOTICE: case E_USER_NOTICE: $type = 'info'; $class = 'info'; $code = 'PHP Notice'; break; case E_DEPRECATED: case E_USER_DEPRECATED: $type = 'info'; $class = 'deprecated'; $code = 'PHP Deprecated Notice'; break; case E_STRICT: $type = 'info'; $class = 'warning'; $code = 'PHP Strict Warning'; $suppressed = true; break; default: $type = 'info'; $class = 'unknown'; $code = 'Unknown PHP Error [' . $errno . ']'; break; } if($suppressed){ // Ignore suppressed errors when on production. // This is required because PHP < 7.0 has some functions that can only be called with the '@' operator. // Such as LDAP binding or many things in Smarty. if(!DEVELOPMENT_MODE){ return; } $code .= ' @SUPPRESSED'; } // All errors/warnings/notices get logged! if($errfile && strpos($errfile, ROOT_PDIR) === 0){ $details = '[src: ' . '/' . substr($errfile, strlen(ROOT_PDIR)) . ':' . $errline . '] '; } elseif($errfile){ $details = '[src: ' . $errfile . ':' . $errline . '] '; } else{ $details = ''; } try{ if(!\Core::GetComponent()){ // SQUAK! Core isn't even loaded yet! return; } // Allow external systems to hook into this event. \HookHandler::DispatchHook('/core/error_handler', $code, $errstr); $log = \SystemLogModel::Factory(); $log->setFromArray([ 'type' => $type, 'code' => $code, 'message' => $details . $errstr ]); $log->save(); } catch(\Exception $e){ // meh, try a traditional log. try{ if(class_exists('Core\\Utilities\\Logger\\LogFile')){ $log = new LogFile($type); $log->write($details . $errstr, $code); } else{ error_log($details . $errstr); } } catch(\Exception $e){ // Really meh now! } } // Display all errors when in development mode. if(DEVELOPMENT_MODE && !$suppressed){ // The correct way to handle output is via EXEC_MODE. // HOWEVER, since the unit tests emulate a WEB mode so that the scripts behave as they would in the web browser, // this is not a reliable test here. if(isset($_SERVER['TERM']) || isset($_SERVER['SHELL'])){ print_error_as_text($class, $code, $errstr); } elseif(EXEC_MODE == 'WEB'){ print_error_as_html($class, $code, $errstr); } else{ print_error_as_text($class, $code, $errstr); } } // If it's a fatal error and it's not in development mode, simply display a friendly error page instead. if($fatal){ if(EXEC_MODE == 'WEB'){ require(ROOT_PDIR . 'core/templates/halt_pages/fatal_error.inc.html'); } exit(); } }
/** * Save this Model into the datastore. * * Return true if saved successfully, false if no change required, * and will throw a DMI_Exception if there was an error. * * As of 5.0.0, bulk inserts can be performed by passing TRUE as the one argument. * If this is done, you MUST call CommitSaves() after all data has been stored! * * @param bool $defer Set to true to batch-save this data as a BULK INSERT. * * @return boolean * @throws DMI_Exception */ public function save($defer = false) { \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record( 'Issuing save() on ' . $this->get('__CLASS__') . ' ' . $this->getLabel() ); // Only do the same operation if it's been changed. // This is actually a little more in depth than simply seeing if it's been modified, as I also // have to look into the linked classes and see if it exists $save = false; // new models always get saved. if(!$this->_exists){ $save = true; } // Models that have changed content always get saved. elseif($this->changed()){ $save = true; } else{ foreach($this->_linked as $l){ if(isset($l['records'])){ // If there are any linked models in the records array, trigger the save. $save = true; break; } if(isset($l['purged'])){ // If there are any linked models in the purged array, trigger the save, (to delete them). $save = true; break; } } } if(!$save){ \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record( 'No column detected as changed, skipping save.' ); return false; } // Dispatch the pre-save hook for models. // This allows utilities to hook in and modify the model or perform some other action. $classname = get_called_class(); // Allow all supplemental models to tap into this too! if(isset(self::$_ModelSupplementals[$classname])){ foreach(self::$_ModelSupplementals[$classname] as $supplemental){ if(class_exists($supplemental)){ $ref = new ReflectionClass($supplemental); if($ref->hasMethod('PreSaveHook')){ $ref->getMethod('PreSaveHook')->invoke(null, $this); } } } } HookHandler::DispatchHook('/core/model/presave', $this); // NEW in 2.8, I need to run through the linked models and see if any local key is a foreign key of a linked model. // If it is, then I need to save that foreign model so I can get its updated primary key, (if it changed). foreach($this->_linked as $l){ // If this link has a 1-sized relationship and the local node is set as a FK, then read it as such. if(!is_array($l['on'])){ // make sure it's an array. $l['on'] = [$l['on'] => $l['on'] ]; } if($l['link'] == Model::LINK_HASONE && sizeof($l['on']) == 1){ reset($l['on']); $remotek = key($l['on']); $localk = $l['on'][$remotek]; $locals = $this->getKeySchema($localk); // No schema? Ok, nothing to save to! if(!$locals) continue; // If this is not a FK, then I don't care! if($locals['type'] != Model::ATT_TYPE_UUID_FK) continue; // OTHERWISE..... ;) if(isset($l['records'])){ /** @var Model $model */ $model = $l['records']; } elseif(isset($this->_columns[$localk]) && $this->_columns[$localk]->value instanceof Model){ // The alternative location for this linked model to be. // This can happen when the link is established in the Schema data and the parent record doesn't exist yet. /** @var Model $model */ $model = $this->_columns[$localk]; } else{ // No valid model found... :/ continue; } $model->save(); $this->set($localk, $model->get($remotek)); } } if ($this->_exists){ $changed = $this->_saveExisting(); \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record( 'Saved existing record to database' ); } else{ // Inserts can be deferred! $this->_saveNew($defer); $changed = true; \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record( 'Saved new record to database' ); } // Go through any linked tables and ensure that they're saved as well. foreach($this->_linked as $k => $l){ // No need to save if it was never loaded. //if(!(isset($l['records']))) continue; // No need to save if it was never loaded. //if(!(isset($l['records']) || $this->changed())) continue; switch($l['link']){ case Model::LINK_HASONE: $models = isset($l['records']) ? [$l['records']] : null; $deletes = isset($l['purged']) ? $l['purged'] : null; break; case Model::LINK_HASMANY: $models = isset($l['records']) ? $l['records'] : null; $deletes = isset($l['purged']) ? $l['purged'] : null; break; default: // There is no default behaviour... other than to ignore it. $models = null; $deletes = null; break; } // Are there deletes requested? // Deletes MUST happen before creates because if there is a record that overrides an existing record, // but that existing record is set to be deleted, the delete operation must be before the create operation. // // This happens on the page updates with meta fields. // The meta fields are completely re-created, and saved at one time only. // Since the incoming data has the same PK as the existing data, (that's already marked as deteled), // the operation fails if these orders are reversed. if($deletes){ foreach($deletes as $model){ $model->delete(); $changed = true; } unset($l['purged']); } // Are there saves requested? if($models){ foreach($models as $model){ /** @var $model Model */ // Ensure all linked fields still match up. Something may have been changed in the parent. $model->setFromArray($this->_getLinkWhereArray($k)); if($model->save()){ $changed = true; } } } } // Commit all the columns $this->_exists = true; foreach($this->_columns as $c){ /** @var \Core\Datamodel\Columns\SchemaColumn $c */ $c->commit(); } // Check and see if this model extends another model. // If it does, then create/update that parent object to keep it in sync! if(($class = get_parent_class($this)) != 'Model'){ $idx = self::GetIndexes(); if(isset($idx['primary']) && sizeof($idx['primary']) == 1){ $schema = $this->getKeySchema($idx['primary'][0]); if($schema['type'] == Model::ATT_TYPE_UUID){ $refp = new ReflectionClass($class); $refm = $refp->getMethod('Construct'); /** @var Model $parent */ $parent = $refm->invoke(null, $this->get($idx['primary'][0])); // Populate the parent with this child's data. // Any non-existent field will simply be ignored. $parent->setFromArray($this->getAsArray()); if($parent->save()){ $changed = true; } } } } if($changed){ $classname = get_called_class(); // Allow all supplemental models to tap into this too! if(isset(self::$_ModelSupplementals[$classname])){ foreach(self::$_ModelSupplementals[$classname] as $supplemental){ if(class_exists($supplemental)){ $ref = new ReflectionClass($supplemental); if($ref->hasMethod('PostSaveHook')){ $ref->getMethod('PostSaveHook')->invoke(null, $this); } } } } HookHandler::DispatchHook('/core/model/postsave', $this); // Indicate that something happened. return true; } else{ // Nothing happened! return false; } }
/** * Shortcut function to dispatch the /core/controllinks hook to request functions for a given subject. * * @param string $baseurl The baseurl, (excluding /core/controllinks), of the request * @param mixed $subject The subject matter of this hook, (if any) * * @return string HTML of the <ul/> tag. */ public static function DispatchAndFetch($baseurl, $subject){ $links = HookHandler::DispatchHook('/core/controllinks' . $baseurl, $subject); $controls = new ViewControls(); $controls->addLinks($links); return $controls->fetch(); }
/** * Fetch this view as an HTML string. * @return mixed|null|string */ public function fetch() { if($this->_fetchCache !== null){ // w00t ;) return $this->_fetchCache; } try{ $body = $this->fetchBody(); \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record( 'Fetched application content from within View->fetch() for ' . $this->templatename ); } catch(Exception $e){ $this->error = View::ERROR_SERVERERROR; \Core\ErrorManagement\exception_handler($e, ($this->mode == View::MODE_PAGE)); $body = ''; } // If there's no template, I have nothing to even do! if ($this->mastertemplate === false) { return $body; } // Else if it's null, it's just not set yet :p // @deprecated here! elseif ($this->mastertemplate === null) { $this->mastertemplate = ConfigHandler::Get('/theme/default_template'); } // Whee! //var_dump($this->templatename, Core\Templates\Template::ResolveFile($this->templatename)); // Content types take priority on controlling the master template. if ($this->contenttype == View::CTYPE_JSON) { $mastertpl = false; } else { // Master template depends on the render mode. switch ($this->mode) { case View::MODE_PAGEORAJAX: if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){ $mastertpl = false; $this->mode = View::MODE_AJAX; } else{ $mastertpl = ROOT_PDIR . 'themes/' . ConfigHandler::Get('/theme/selected') . '/skins/' . $this->mastertemplate; $this->mode = View::MODE_PAGE; } break; case View::MODE_NOOUTPUT: case View::MODE_AJAX: $mastertpl = false; break; case View::MODE_PAGE: case View::MODE_EMAILORPRINT: $mastertpl = Core\Templates\Template::ResolveFile('skins/' . $this->mastertemplate); //$mastertpl = ROOT_PDIR . 'themes/' . ConfigHandler::Get('/theme/selected') . '/skins/' . $this->mastertemplate; break; case View::MODE_WIDGET: $mastertpl = Core\Templates\Template::ResolveFile('widgetcontainers/' . $this->mastertemplate); break; } } // If there's *still* no template, I still have nothing to do. if (!$mastertpl) return $body; $template = \Core\Templates\Template::Factory($mastertpl); // Ensure that the template is linked to this View correctly. $template->setView($this); //$template = new Core\Templates\Template(); //$template->setBaseURL('/'); // Page-level views have some special variables. if ($this->mode == View::MODE_PAGE) { $template->assign('breadcrumbs', $this->getBreadcrumbs()); $template->assign('controls', $this->controls); $template->assign('messages', Core::GetMessages()); // Tack on the pre and post body variables from the current page. //$body = CurrentPage::GetBodyPre() . $body . CurrentPage::GetBodyPost(); } // Widgets need some special variables too. //if($this->mode == View::MODE_WIDGET){ // //var_dump($this->getVariable('widget')); die(); // $template->assign('widget', $this->getVariable('widget')); //} // This logic is needed for the SEO title, since that's usually completely human unfriendly. if(isset($this->meta['title']) && $this->meta['title']){ $template->assign('seotitle', $this->meta['title']); } else{ $template->assign('seotitle', $this->getTitle()); } $template->assign('title', $this->getTitle()); $template->assign('body', $body); // The body needs some custom classes for assisting the designers. // These are mainly pulled from the UA. $ua = \Core\UserAgent::Construct(); $this->bodyclasses = array_merge($this->bodyclasses, $ua->getPseudoIdentifier(true)); // Provide a way for stylesheets to target this page specifically. switch ($this->error) { case View::ERROR_BADREQUEST: case View::ERROR_PAYMENTREQUIRED: case View::ERROR_ACCESSDENIED: case View::ERROR_NOTFOUND: case View::ERROR_METHODNOTALLOWED: case View::ERROR_NOTACCEPTABLE: case View::ERROR_PROXYAUTHENTICATIONREQUIRED: case View::ERROR_REQUESTTIMEOUT: case View::ERROR_CONFLICT: case View::ERROR_GONE: case View::ERROR_LENGTHREQUIRED: case View::ERROR_PRECONDITIONFAILED: case View::ERROR_ENTITYTOOLARGE: case View::ERROR_URITOOLARGE: case View::ERROR_UNSUPPORTEDMEDIATYPE: case View::ERROR_RANGENOTSATISFIABLE: case View::ERROR_EXPECTATIONFAILED: case View::ERROR_UNAUTHORIZED: $url = 'error-' . $this->error; break; case 403: $url = "error-403 page-user-login"; break; default: $url = strtolower(trim(preg_replace('/[^a-z0-9\-]*/i', '', str_replace('/', '-', $this->baseurl)), '-')); } while($url != ''){ $this->bodyclasses[] = 'page-' . $url; $url = substr($url, 0, strrpos($url, '-')); } $bodyclasses = strtolower(implode(' ', $this->bodyclasses)); $template->assign('body_classes', $bodyclasses); try{ $data = $template->fetch(); } catch(SmartyException $e){ $this->error = View::ERROR_SERVERERROR; error_log('[view error]'); error_log('Template name: [' . $mastertpl . ']'); \Core\ErrorManagement\exception_handler($e); require(ROOT_PDIR . 'core/templates/halt_pages/fatal_error.inc.html'); die(); } catch(TemplateException $e){ $this->error = View::ERROR_SERVERERROR; error_log('[view error]'); error_log('Template name: [' . $mastertpl . ']'); \Core\ErrorManagement\exception_handler($e); require(ROOT_PDIR . 'core/templates/halt_pages/fatal_error.inc.html'); die(); } if($this->mode == View::MODE_EMAILORPRINT && $this->contenttype == View::CTYPE_HTML){ // Inform other elements that the page is just about to be rendered. HookHandler::DispatchHook('/core/page/rendering', $this); // Replace the </head> tag with the head data from the current page // and the </body> with the foot data from the current page. // This is needed to be done at this stage because some element in // the template after rendering may add additional script to the head. // Also tack on any attributes for the <html> tag. if(preg_match('#</head>#i', $data)){ // I need to do preg_replace because I only want to replace the FIRST instance of </head> $data = preg_replace('#</head>#i', $this->getHeadContent() . "\n" . '</head>', $data, 1); } } elseif ($this->mode == View::MODE_PAGE && $this->contenttype == View::CTYPE_HTML) { // Inform other elements that the page is just about to be rendered. HookHandler::DispatchHook('/core/page/rendering', $this); // Metadata! w00t // Replace the </head> tag with the head data from the current page // and the </body> with the foot data from the current page. // This is needed to be done at this stage because some element in // the template after rendering may add additional script to the head. // Also tack on any attributes for the <html> tag. if(preg_match('#</head>#i', $data)){ // I need to do preg_replace because I only want to replace the FIRST instance of </head> $data = preg_replace('#</head>#i', $this->getHeadContent() . "\n" . '</head>', $data, 1); } if(preg_match('#</body>#i', $data)){ // I need to use strrpos because I only want the LAST instance of </body> $match = strrpos($data, '</body>'); $foot = $this->getFootContent(); if(defined('ENABLE_XHPROF') && function_exists('xhprof_disable')){ require_once('xhprof_lib/utils/xhprof_lib.php'); #SKIPCOMPILER require_once('xhprof_lib/utils/xhprof_runs.php'); #SKIPCOMPILER $xhprof_data = xhprof_disable(); $namespace = trim(str_replace(['.', '/'], '-', HOST . REL_REQUEST_PATH), '-'); $xhprof_runs = new XHProfRuns_Default(); $run_id = $xhprof_runs->save_run($xhprof_data, $namespace); define('XHPROF_RUN', $run_id); define('XHPROF_SOURCE', $namespace); $xhprof_link = sprintf( '<a href="' . SERVERNAME . '/xhprof/index.php?run=%s&source=%s" target="_blank">View XHprof Profiler Report</a>' . "\n", $run_id, $namespace ); } else{ $xhprof_link = ''; } // If the viewmode is regular and DEVELOPMENT_MODE is enabled, show some possibly useful information now that everything's said and done. if (DEVELOPMENT_MODE) { $legend = '<div class="fieldset-title">%s<i class="icon-chevron-down expandable-hint"></i><i class="icon-chevron-up collapsible-hint"></i></div>' . "\n"; $debug = ''; $debug .= '<pre class="xdebug-var-dump screen">'; $debug .= '<fieldset class="debug-section collapsible" id="debug-section-template-information">'; $debug .= sprintf($legend, 'Template Information'); $debug .= "<span>"; $debug .= 'Base URL: ' . $this->baseurl . "\n"; $debug .= 'Template Requested: ' . $this->templatename . "\n"; $debug .= 'Template Actually Used: ' . \Core\Templates\Template::ResolveFile($this->templatename) . "\n"; $debug .= 'Master Skin: ' . $this->mastertemplate . "\n"; $debug .= "</span>"; $debug .= '</fieldset>'; $debug .= '<fieldset class="debug-section collapsible" id="debug-section-performance-information">'; $debug .= sprintf($legend, 'Performance Information'); $debug .= "<span>"; $debug .= $xhprof_link; $debug .= "Database Reads: " . \Core\Utilities\Profiler\DatamodelProfiler::GetDefaultProfiler()->readCount() . "\n"; $debug .= "Database Writes: " . \Core\Utilities\Profiler\DatamodelProfiler::GetDefaultProfiler()->writeCount() . "\n"; //$debug .= "Number of queries: " . DB::Singleton()->counter . "\n"; //$debug .= "Amount of memory used by PHP: " . \Core\Filestore\format_size(memory_get_usage()) . "\n"; $debug .= "Amount of memory used by PHP: " . \Core\Filestore\format_size(memory_get_peak_usage(true)) . "\n"; $profiler = Core\Utilities\Profiler\Profiler::GetDefaultProfiler(); $debug .= "Total processing time: " . $profiler->getTimeFormatted() . "\n"; $debug .= "</span>"; $debug .= '</fieldset>'; $debug .= '<fieldset class="debug-section collapsible" id="debug-section-profiler-information">'; $debug .= sprintf($legend, 'Core Profiler'); $debug .= "<span>"; $debug .= $profiler->getEventTimesFormatted(); $debug .= "</span>"; $debug .= '</fieldset>'; $debug .= '<fieldset class="debug-section collapsible collapsed" id="debug-section-components-information">'; // Tack on what components are currently installed. $debug .= sprintf($legend, 'Available Components'); $debugcomponents = array_merge(Core::GetComponents(), Core::GetDisabledComponents()); $debug .= "<span>"; // Give me sorting! ksort($debugcomponents); foreach ($debugcomponents as $l => $v) { if($v->isEnabled() && $v->isReady()){ $debug .= '[<span style="color:green;">Enabled</span>]'; } elseif($v->isEnabled() && !$v->isReady()){ $debug .= '[<span style="color:red;">!ERROR!</span>]'; } else{ $debug .= '[<span style="color:red;">Disabled</span>]'; } $debug .= $v->getName() . ' ' . $v->getVersion() . "<br/>"; } $debug .= "</span>"; $debug .= '</fieldset>'; $debug .= '<fieldset class="debug-section collapsible collapsed" id="debug-section-hooks-information">'; // I wanna see what hooks are registered too! $debug .= sprintf($legend, 'Registered Hooks'); foreach(HookHandler::GetAllHooks() as $hook){ $debug .= "<span>"; /** @var $hook Hook */ $debug .= $hook->name; if($hook->description) $debug .= ' <em> - ' . $hook->description . '</em>'; $debug .= "\n" . '<span style="color:#999;">Return expected: ' . $hook->returnType . '</span>'; $debug .= "\n" . '<span style="color:#999;">Attached by ' . $hook->getBindingCount() . ' binding(s).</span>'; foreach($hook->getBindings() as $b){ $debug .= "\n" . ' * ' . $b['call']; } $debug .= "\n\n"; $debug .= "</span>"; } $debug .= '</fieldset>'; // Display the licensed content on this application $debug .= '<fieldset class="debug-section collapsible collapsed" id="debug-section-licenser-information">'; $debug .= sprintf($legend, 'Licensed Information'); $lic = \Core\Licenser::GetRaw(); $debug .= '<div>'; foreach($lic as $dat){ $debug .= $dat['url'] . '::' . $dat['feature'] . ' => ' . $dat['value'] . "\n"; } $debug .= '</div></fieldset>'; $debug .= '<fieldset class="debug-section collapsible collapsed" id="debug-section-includes-information">'; // I want to see how many files were included. $debug .= sprintf($legend, 'Included Files'); $debug .= '<span>Number: ' . sizeof(get_included_files()) . "</span>"; $debug .= '<span>'. implode("<br/>", get_included_files()) . "</span>"; $debug .= '</fieldset>'; $debug .= '<fieldset class="debug-section collapsible collapsed" id="debug-section-query-information">'; $debug .= sprintf($legend, 'Query Log'); $profiler = \Core\Utilities\Profiler\DatamodelProfiler::GetDefaultProfiler(); $debug .= '<div>' . $profiler->getEventTimesFormatted() . '</div>'; $debug .= '</fieldset>'; // Display all the i18n strings available on the system. $debug .= '<fieldset class="debug-section collapsible collapsed" id="debug-section-i18nstrings-information">'; $debug .= sprintf($legend, 'I18N Strings Available'); $strings = \Core\i18n\I18NLoader::GetAllStrings(); $debug .= '<ul>'; foreach($strings as &$s){ $debug .= '<li>' . $s['key'] . '</li>'; } $debug .= '</ul>'; $debug .= '</fieldset>'; $debug .= '</pre>'; // And append! $foot .= "\n" . $debug; } $data = substr_replace($data, $foot . "\n" . '</body>', $match, 7); } $data = preg_replace('#<html#', '<html ' . $this->getHTMLAttributes(), $data, 1); // This logic has been migrated to the {$body_classes} variable. /* if(preg_match('/<body[^>]*>/', $data, $matches)){ // body is $matches[0]. $fullbody = $matches[0]; if($fullbody == '<body>'){ $body = '<body class="' . $bodyclass . '">'; } elseif(strpos($fullbody, 'class=') === false){ // Almost as easy, other elements but no class. $body = substr($fullbody, 0, -1) . ' class="' . $bodyclass . '">'; } else{ // parsing HTML is far easier with XML objects. $node = new SimpleXMLElement($fullbody . '</body>'); $body = '<body'; foreach($node->attributes() as $k => $v){ if($k == 'class'){ $body .= ' ' . $k . '="' . $bodyclass . ' ' . $v . '"'; } else{ $body .= ' ' . $k . '="' . $v . '"'; } } $body .= '>'; } // And replace! $data = preg_replace('#<body[^>]*>#', $body, $data, 1); } */ } $this->_fetchCache = $data; return $data; }
/** * Instantiate a new generic hook object and register it with the global HookHandler. * * @param string $name */ public function __construct($name) { $this->name = $name; HookHandler::RegisterHook($this); }
/** * The actual Core registration page. * * This renders all the user's configurable options at registration. */ public function register2(){ $view = $this->getView(); $request = $this->getPageRequest(); $manager = \Core\user()->checkAccess('p:/user/users/manage'); // Current user an admin? // Anonymous users should have access to this if it's allow public. if(!\Core\user()->exists() && !ConfigHandler::Get('/user/register/allowpublic')){ return View::ERROR_BADREQUEST; } // Authenticated users must check the permission to manage users. if(\Core\user()->exists() && !$manager){ return View::ERROR_ACCESSDENIED; } /** @var NonceModel $nonce */ $nonce = NonceModel::Construct($request->getParameter(0)); if(!$nonce->isValid()){ \Core\set_message('Invalid nonce token, please try again.', 'error'); \Core\go_back(); } $nonce->decryptData(); $data = $nonce->get('data'); if(!isset($data['user']) || !($data['user'] instanceof UserModel)){ if(DEVELOPMENT_MODE){ \Core\set_message('Your nonce does not include a "user" key. Please ensure that this is set to a non-existent UserModel object!', 'error'); } else{ \Core\set_message('Invalid login type, please try again later.', 'error'); } \Core\go_back(); } /** @var UserModel $user */ $user = $data['user']; $form = \Core\User\Helper::GetForm($user); // If the total number of form elements here are only 2, then only the user object and submit button are present. // Instead of showing the form, auto-submit to that destination. if(sizeof($form->getElements()) <= 2){ $user->setDefaultGroups(); $user->setDefaultMetaFields(); $user->setDefaultActiveStatuses(); $user->generateNewApiKey(); $user->save(); // User created... make a log of this! \SystemLogModel::LogSecurityEvent('/user/register', 'User registration successful', null, $user->get('id')); // Send a thank you for registering email to the user. try{ $user->sendWelcomeEmail(); } catch(\Exception $e){ \Core\ErrorManagement\exception_handler($e); \Core\set_message('t:MESSAGE_ERROR_CANNOT_SEND_WELCOME_EMAIL'); } // "login" this user if not already logged in. if(!\Core\user()->exists()){ if($user->get('active')){ $user->set('last_login', \CoreDateTime::Now('U', \Time::TIMEZONE_GMT)); $user->save(); \Core\Session::SetUser($user); } \Core\set_message('t:MESSAGE_SUCCESS_CREATED_USER_ACCOUNT'); if(($overrideurl = \HookHandler::DispatchHook('/user/postlogin/getredirecturl'))){ // Allow an external script to override the redirecting URL. $url = $overrideurl; } elseif($form->getElementValue('redirect')){ // The preferred default redirect method. // This is set from /user/register2, which is in turn passed in, (hopefully), by the original callee registration page. $url = $form->getElementValue('redirect'); } elseif(strpos(REL_REQUEST_PATH, '/user/register') === 0){ // If the user came from the registration page, get the page before that. $url = '/'; } else{ // else the registration link is now on the same page as the 403 handler. $url = REL_REQUEST_PATH; } \Core\redirect($url); } // It was created administratively; redirect there instead. else{ \Core\set_message('t:MESSAGE_SUCCESS_CREATED_USER_ACCOUNT'); \Core\redirect('/user/admin'); } } $form->addElement('hidden', ['name' => 'redirect', 'value' => $data['redirect']]); $view->title = 'Complete Registration'; $view->assign('form', $form); }
/** * Utility function to reload the current page */ function reload(){ $movetext = '302 Moved Temporarily'; header('X-Content-Encoded-By: Core Plus ' . (DEVELOPMENT_MODE ? \Core::GetComponent()->getVersion() : '')); if(\ConfigHandler::Get('/core/security/x-frame-options')){ header('X-Frame-Options: ' . \ConfigHandler::Get('/core/security/x-frame-options')); } if(\ConfigHandler::Get('/core/security/csp-frame-ancestors')){ header('Content-Security-Policy: frame-ancestors \'self\' ' . \ConfigHandler::Get('/core/security/content-security-policy')); } header('HTTP/1.1 302 Moved Temporarily'); header('Location:' . CUR_CALL); // Just before the page stops execution... \HookHandler::DispatchHook('/core/page/postrender'); die('If your browser does not refresh, please <a href="' . CUR_CALL . '">Click Here</a>'); }
public function loadFiles() { // First of all, this cannot be called on disabled or uninstalled components. if(!$this->isInstalled()) return false; if(!$this->isEnabled()) return false; if($this->_filesloaded) return true; Core\Utilities\Logger\write_debug('Loading files for component [' . $this->getName() . ']'); $dir = $this->getBaseDir(); // Include any includes requested. // This adds support for namespaced functions. // <includes> // <include filename="core/functions/Core.functions.php"/> // </includes> foreach ($this->_xmlloader->getElements('/includes/include') as $f) { require_once($dir . $f->getAttribute('filename')); } // Register any hooks that may be present. foreach ($this->_xmlloader->getElementsByTagName('hookregister') as $h) { $hook = new Hook($h->getAttribute('name')); $hook->description = $h->getAttribute('description'); if($h->getAttribute('return')){ $hook->returnType = $h->getAttribute('return'); } } // Register any events that may be present. foreach ($this->_xmlloader->getElementsByTagName('hook') as $h) { $event = $h->getAttribute('name'); $call = $h->getAttribute('call'); $type = @$h->getAttribute('type'); HookHandler::AttachToHook($event, $call, $type); } // This component may have special form elements registered. Check! foreach ($this->_xmlloader->getElements('/forms/formelement') as $node) { Form::$Mappings[$node->getAttribute('name')] = $node->getAttribute('class'); } if(DEVELOPMENT_MODE && defined('AUTO_INSTALL_ASSETS') && AUTO_INSTALL_ASSETS && EXEC_MODE == 'WEB' && CDN_TYPE == 'local'){ Core\Utilities\Logger\write_debug('Auto-installing assets for component [' . $this->getName() . ']'); $this->_parseAssets(); } $this->_filesloaded = true; return true; }
define('REMOTE_PROVINCE', $geoprovince); /** * The country ISO code of the remote user * eg: "US", "DE", "AQ", etc. */ define('REMOTE_COUNTRY', $geocountry); /** * The timezone of the remote user * eg: "America/New_York", etc. */ define('REMOTE_TIMEZONE', $geotimezone); /** * The postal code of the remote user * eg: "43215" * Note, this define CAN be NULL if the IP does not resolve to a valid address */ define('REMOTE_POSTAL', $geopostal); // And cleanup the geo information unset($geocity, $geoprovince, $geocountry, $geotimezone, $geopostal); HookHandler::DispatchHook('/core/components/ready'); // And we don't need the profiler object anymore. unset($profiler);
/** * Execute the actual cron for the requested type. * * @param string $cron Cron type to execute. * * @return CronLogModel * @throws Exception */ private function _performcron($cron) { switch ($cron) { case '1-minute': case '5-minute': case '15-minute': case '30-minute': case 'hourly': case '2-hour': case '3-hour': case '6-hour': case '12-hour': case 'daily': case 'weekly': case 'monthly': break; default: throw new Exception('Unsupported cron type: [' . $cron . ']'); } if (!ConfigHandler::Get('/cron/enabled')) { $msg = 'Cron execution is globally disabled via the site configuration, not executing cron!'; SystemLogModel::LogInfoEvent('/cron/' . $cron, $msg); // It needs to return something. $log = new CronLogModel(); $log->set('status', 'fail'); return $log; } // First, check and see if there's one that's still running. $runninglogs = CronLogModel::Find(array('cron' => $cron, 'status' => 'running')); if (sizeof($runninglogs)) { foreach ($runninglogs as $log) { /** @var $log CronLogModel */ $log->set('status', 'fail'); $log->set('log', $log->get('log') . "\n------------\nTIMED OUT!"); $log->save(); } } // Start recording. $log = new CronLogModel(); $log->set('cron', $cron); $log->set('status', 'running'); $log->set('memory', memory_get_usage()); $log->set('ip', REMOTE_IP); $log->save(); $start = microtime(true) * 1000; $sep = '==========================================' . "\n"; $contents = "Starting cron execution for {$cron}\n{$sep}"; // This uses the hook system, but will be slightly different than most things. $overallresult = true; $hook = HookHandler::GetHook('/cron/' . $cron); $hookcount = 0; $hooksuccesses = 0; if ($hook) { if ($hook->getBindingCount()) { $hookcount = $hook->getBindingCount(); $bindings = $hook->getBindings(); foreach ($bindings as $b) { $contents .= sprintf("\nExecuting Binding %s...\n", $b['call']); // Since these systems will just be writing to STDOUT, I'll need to capture that. try { ob_start(); $execution = $hook->callBinding($b, array()); $executiondata = ob_get_clean(); } catch (Exception $e) { $execution = false; $executiondata = 'EXCEPTION: ' . $e->getMessage() . ob_get_clean(); } if ($executiondata == '' && $execution) { $contents .= "Cron executed successfully with no output\n"; ++$hooksuccesses; } elseif ($execution) { $contents .= $executiondata . "\n"; ++$hooksuccesses; } else { $contents .= $executiondata . "\n!!FAILED\n"; $overallresult = false; } } } else { $contents = 'No bindings located for requested cron'; $overallresult = true; } } else { $contents = 'Invalid hook requested: ' . $cron; $overallresult = false; } // Just in case the contents are returning html... (they should be plain text). // Replace the most common line endings with things that make sense for plain text. // This is to ensure that all the available scenarios are met and saved/displayed without extra whitespace. // // Since some systems will provide plain text (easy!), windows/os9 line endings, // HTML (br and br/), and formatted HTML (br + \n). $contents = str_ireplace(["\r\n<br>", "\r\n<br/>", "\r\n<br />", "\n<br>", "\n<br/>", "\n<br />", "<br>", "<br/>", "<br />", "\r\n", "\r"], "\n", $contents); // Save the results. $log->set('completed', Time::GetCurrentGMT()); $log->set('duration', microtime(true) * 1000 - $start); $log->set('log', $contents); $log->set('status', $overallresult ? 'pass' : 'fail'); $log->save(); // Make a copy of this in the system log too if applicable. // This time is listed in ms $time = microtime(true) * 1000 - $start; // 0.01 = 10 ns // 1 = 1 ms // 1000 = 1 second if ($time < 1) { // TIME is less than 1, which means it executed faster than 1ms, display in nanoseconds. $time = round($time, 4) * 1000 . ' ns'; } elseif ($time < 1000) { // TIME is less than 1000, which means it executed faster than 1 second, display in milliseconds. $time = round($time, 0) . ' ms'; } else { // TIME is at least 1 second or longer... Display in minutes:seconds, (no need to display 1.453 seconds!) // First, convert the milliseconds to seconds; they are more manageable for what I need to do. // This will change time from 12345(ms) to 13(seconds) $time = ceil($time / 1000); $minutes = floor($time / 60); $seconds = $time - $minutes * 60; if ($minutes > 0) { $time = $minutes . 'm ' . str_pad($seconds, 2, '0', STR_PAD_LEFT) . 's'; } else { $time = $seconds . ' seconds'; } } if ($hookcount > 0) { $msg = 'Cron ' . $cron . ' completed in ' . $time . '. ' . $hooksuccesses . ' out of ' . $hookcount . ' hooks called successfully.'; SystemLogModel::LogInfoEvent('/cron/' . $cron, $msg, $contents); } // Just to notify the calling function. return $log; }
/** * Form Handler for logging in. * * @static * * @param \Form $form * * @return bool|null|string */ public static function LoginHandler(\Form $form){ /** @var \FormElement $e */ $e = $form->getElement('email'); /** @var \FormElement $p */ $p = $form->getElement('pass'); /** @var \UserModel $u */ $u = \UserModel::Find(array('email' => $e->get('value')), 1); if(!$u){ // Log this as a login attempt! $logmsg = 'Failed Login. Email not registered' . "\n" . 'Email: ' . $e->get('value') . "\n"; \SystemLogModel::LogSecurityEvent('/user/login', $logmsg); $e->setError('t:MESSAGE_ERROR_USER_LOGIN_EMAIL_NOT_FOUND'); return false; } if($u->get('active') == 0){ // The model provides a quick cut-off for active/inactive users. // This is the control managed with in the admin. $logmsg = 'Failed Login. User tried to login before account activation' . "\n" . 'User: '******'email') . "\n"; \SystemLogModel::LogSecurityEvent('/user/login', $logmsg, null, $u->get('id')); $e->setError('t:MESSAGE_ERROR_USER_LOGIN_ACCOUNT_NOT_ACTIVE'); return false; } elseif($u->get('active') == -1){ // The model provides a quick cut-off for active/inactive users. // This is the control managed with in the admin. $logmsg = 'Failed Login. User tried to login after account deactivation.' . "\n" . 'User: '******'email') . "\n"; \SystemLogModel::LogSecurityEvent('/user/login', $logmsg, null, $u->get('id')); $e->setError('t:MESSAGE_ERROR_USER_LOGIN_ACCOUNT_DEACTIVATED'); return false; } try{ /** @var \Core\User\AuthDrivers\datastore $auth */ $auth = $u->getAuthDriver('datastore'); } catch(Exception $e){ $e->setError('t:MESSAGE_ERROR_USER_LOGIN_PASSWORD_AUTH_DISABLED'); return false; } // This is a special case if the password isn't set yet. // It can happen with imported users or if a password is invalidated. if($u->get('password') == ''){ // Use the Nonce system to generate a one-time key with this user's data. $nonce = \NonceModel::Generate( '20 minutes', ['type' => 'password-reset', 'user' => $u->get('id')] ); $link = '/datastoreauth/forgotpassword?e=' . urlencode($u->get('email')) . '&n=' . $nonce; $email = new \Email(); $email->setSubject('Initial Password Request'); $email->to($u->get('email')); $email->assign('link', \Core\resolve_link($link)); $email->assign('ip', REMOTE_IP); $email->templatename = 'emails/user/initialpassword.tpl'; try{ $email->send(); \SystemLogModel::LogSecurityEvent('/user/initialpassword/send', 'Initial password request sent successfully', null, $u->get('id')); \Core\set_message('t:MESSAGE_INFO_USER_LOGIN_MUST_SET_NEW_PASSWORD_INSTRUCTIONS_HAVE_BEEN_EMAILED'); return true; } catch(\Exception $e){ \Core\ErrorManagement\exception_handler($e); \Core\set_message('t:MESSAGE_ERROR_USER_LOGIN_MUST_SET_NEW_PASSWORD_UNABLE_TO_SEND_EMAIL'); return false; } } if(!$auth->checkPassword($p->get('value'))){ // Log this as a login attempt! $logmsg = 'Failed Login. Invalid password' . "\n" . 'Email: ' . $e->get('value') . "\n"; \SystemLogModel::LogSecurityEvent('/user/login/failed_password', $logmsg, null, $u->get('id')); // Also, I want to look up and see how many login attempts there have been in the past couple minutes. // If there are too many, I need to start slowing the attempts. $time = new \CoreDateTime(); $time->modify('-5 minutes'); $securityfactory = new \ModelFactory('SystemLogModel'); $securityfactory->where('code = /user/login/failed_password'); $securityfactory->where('datetime > ' . $time->getFormatted(\Time::FORMAT_EPOCH, \Time::TIMEZONE_GMT)); $securityfactory->where('ip_addr = ' . REMOTE_IP); $attempts = $securityfactory->count(); if($attempts > 4){ // Start slowing down the response. This should help deter brute force attempts. // (x+((x-7)/4)^3)-4 sleep( ($attempts+(($attempts-7)/4)^3)-4 ); // This makes a nice little curve with the following delays: // 5th attempt: 0.85 // 6th attempt: 2.05 // 7th attempt: 3.02 // 8th attempt: 4.05 // 9th attempt: 5.15 // 10th attempt: 6.52 // 11th attempt: 8.10 // 12th attempt: 10.05 } $e->setError('t:MESSAGE_ERROR_USER_LOGIN_INCORRECT_PASSWORD'); $p->set('value', ''); return false; } if($form->getElementValue('redirect')){ // The page was set via client-side javascript on the login page. // This is the most reliable option. $url = $form->getElementValue('redirect'); } elseif(REL_REQUEST_PATH == '/user/login'){ // If the user came from the registration page, get the page before that. $url = $form->referrer; } else{ // else the registration link is now on the same page as the 403 handler. $url = REL_REQUEST_PATH; } // Well, record this too! \SystemLogModel::LogSecurityEvent('/user/login', 'Login successful (via password)', null, $u->get('id')); // yay... $u->set('last_login', \CoreDateTime::Now('U', \Time::TIMEZONE_GMT)); $u->save(); \Core\Session::SetUser($u); // Allow an external script to override the redirecting URL. $overrideurl = \HookHandler::DispatchHook('/user/postlogin/getredirecturl'); if($overrideurl){ $url = $overrideurl; } return $url; }
public function sitemap(){ $view = $this->getView(); $req = $this->getPageRequest(); // Give me every registered (public) page! $factory = new ModelFactory('PageModel'); $factory->where('indexable = 1'); $factory->order('title'); // Multisite? if(Core::IsComponentAvailable('multisite') && MultiSiteHelper::IsEnabled()){ $factory->whereGroup( 'OR', array( 'site = ' . MultiSiteHelper::GetCurrentSiteID(), 'site = -1' ) ); $site = MultiSiteHelper::GetCurrentSiteID(); } else{ $site = null; } // Run this through the streamer, just in case there are a lot of pages... $stream = new \Core\Datamodel\DatasetStream($factory->getDataset()); $user = \Core\user(); $toshow = array(); while(($record = $stream->getRecord())){ if(!$user->checkAccess( $record['access'] )){ // Skip any further operations if the user does not have access to this page continue; } if($record['published_status'] != 'published'){ // Skip any further operations if the page isn't even marked as published. continue; } $page = new PageModel(); $page->_loadFromRecord($record); if(!$page->isPublished()){ // Skip out if the page is not marked as published. // This has extended checks other than simply if the status is set as "published", // such as publish date and expiration date. continue; } $toshow[] = $page; } // Anything else? $extra = HookHandler::DispatchHook('/sitemap/getlisting'); $toshow = array_merge($toshow, $extra); // This page allows for a few content types. switch($req->ctype){ case View::CTYPE_XML: $view->contenttype = View::CTYPE_XML; break; case View::CTYPE_HTML: $view->contenttype = View::CTYPE_HTML; break; } $view->title = 'Sitemap'; $view->assign('pages', $toshow); $view->assign('site', $site); }
/** * View to accept and process the FB login post. * * This will redirect to the registration page if the user doesn't exist, * will throw an error and display a link to enable FB if it's not enabled already, * or will simply log the user in via Facebook and sync his/her settings. */ public function login() { $view = $this->getView(); $request = $this->getPageRequest(); $view->ssl = true; $view->record = false; $auths = \Core\User\Helper::GetEnabledAuthDrivers(); if (!isset($auths['facebook'])) { // Facebook isn't enabled, simply redirect to the home page. \Core\redirect('/'); } if (!FACEBOOK_APP_ID) { \Core\redirect('/'); } if (!FACEBOOK_APP_SECRET) { \Core\redirect('/'); } if (!$request->isPost()) { return View::ERROR_BADREQUEST; } $facebook = new Facebook(['appId' => FACEBOOK_APP_ID, 'secret' => FACEBOOK_APP_SECRET]); // Did the user submit the facebook login request? if (isset($_POST['login-method']) && $_POST['login-method'] == 'facebook' && $_POST['access-token']) { try { $facebook->setAccessToken($_POST['access-token']); /** @var int $fbid The user ID from facebook */ $fbid = $facebook->getUser(); /** @var array $user_profile The array of user data from Facebook */ $user_profile = $facebook->api('/me'); } catch (Exception $e) { \Core\set_message($e->getMessage(), 'error'); \Core\go_back(); return null; } /** @var \UserModel|null $user */ $user = UserModel::Find(['email' => $user_profile['email']], 1); if (!$user) { if (ConfigHandler::Get('/user/register/allowpublic')) { // If public registration is enabled, then redirect the user to the registration page to complete their registration. $user = new UserModel(); $user->set('email', $user_profile['email']); $user->enableAuthDriver('facebook'); $user->disableAuthDriver('datastore'); /** @var \Facebook\UserAuth $auth */ $auth = $user->getAuthDriver('facebook'); $auth->syncUser($_POST['access-token']); // Otherwise, w00t! Record this user into a nonce and forward to step 2 of registration. $nonce = NonceModel::Generate('20 minutes', null, ['user' => $user, 'redirect' => $_POST['redirect']]); \Core\redirect('/user/register2/' . $nonce); } else { // Log this as a login attempt! $logmsg = 'Failed Login (Facebook). Email not registered' . "\n" . 'Email: ' . $user_profile['email'] . "\n"; \SystemLogModel::LogSecurityEvent('/user/login', $logmsg); \Core\set_message('Your Facebook email (' . $user_profile['email'] . ') does not appear to be registered on this site.', 'error'); \Core\go_back(); return null; } } elseif (!$user->get('active')) { // The model provides a quick cut-off for active/inactive users. // This is the control managed with in the admin. $logmsg = 'Failed Login. User tried to login before account activation' . "\n" . 'User: '******'email') . "\n"; \SystemLogModel::LogSecurityEvent('/user/login', $logmsg, null, $user->get('id')); \Core\set_message('Your account is not active yet.', 'error'); \Core\go_back(); return null; } try { /** @var \Facebook\UserAuth $auth */ $auth = $user->getAuthDriver('facebook'); } catch (Exception $e) { \Core\set_message('Your account does not have Facebook logins enabled! <a href="' . \Core\resolve_link('/facebook/enable') . '">Do you want to enable Facebook?</a>', 'error'); \Core\go_back(); return null; } if (!$user->isActive()) { \Core\set_message('Your account is not active!', 'error'); \Core\go_back(); return null; } // Well yay the user is available and authencation driver is ready! $auth->syncUser($_POST['access-token']); if ($_POST['redirect']) { // The page was set via client-side javascript on the login page. // This is the most reliable option. $url = $_POST['redirect']; } elseif (REL_REQUEST_PATH == '/facebook/login') { // If the user came from the registration page, get the page before that. $url = '/'; } else { // else the registration link is now on the same page as the 403 handler. $url = REL_REQUEST_PATH; } // Well, record this too! \SystemLogModel::LogSecurityEvent('/user/login', 'Login successful (via Facebook)', null, $user->get('id')); // yay... $user->set('last_login', \CoreDateTime::Now('U', \Time::TIMEZONE_GMT)); $user->save(); \Core\Session::SetUser($user); // Allow an external script to override the redirecting URL. $overrideurl = \HookHandler::DispatchHook('/user/postlogin/getredirecturl'); if ($overrideurl) { $url = $overrideurl; } \Core\redirect($url); } else { \Core\go_back(); } }
require_once(ROOT_PDIR . 'core/libs/core/templates/TemplateInterface.php'); require_once(ROOT_PDIR . 'core/libs/core/templates/Exception.php'); require_once(ROOT_PDIR . 'core/libs/core/templates/backends/PHTML.php'); require_once(ROOT_PDIR . 'install/classes/InstallerStep.php'); require_once(ROOT_PDIR . 'core/functions/Core.functions.php'); require_once(ROOT_PDIR . 'core/libs/core/utilities/logger/functions.php'); require_once(ROOT_PDIR . 'install/utilities.php'); require_once(ROOT_PDIR . "core/libs/core/ConfigHandler.class.php"); // If the configuration file has been written already, load up those config options! if(file_exists(ROOT_PDIR . 'config/configuration.xml')){ require_once(ROOT_PDIR . 'core/libs/core/datamodel/DMI.class.php'); require_once(ROOT_PDIR . "core/libs/core/HookHandler.class.php"); try { HookHandler::singleton(); ConfigHandler::LoadConfigFile('configuration'); } catch (Exception $e) { // Yeah... I probably don't care at this stage... but maybe I do... \Core\ErrorManagement\exception_handler($e); } } if(!defined('CDN_TYPE')){ define('CDN_TYPE', 'local'); } if(!defined('CDN_LOCAL_ASSETDIR')){ define('CDN_LOCAL_ASSETDIR', 'files/assets/'); } if(!defined('CDN_LOCAL_PUBLICDIR')){
/** * Form handler for the rest of the user system, (auth handler has already been executed). * * @param \Form $form * * @return bool|string */ public static function RegisterHandler(\Form $form){ /////// VALIDATION \\\\\\\\ // All other validation can be done from the model. // All set calls will throw a ModelValidationException if the validation fails. try{ /** @var \UserModel $user */ $user = $form->getElement('user')->get('value'); // setFromForm will handle all attributes and custom values. $user->setFromForm($form); } catch(\ModelValidationException $e){ // Make a note of this! \SystemLogModel::LogSecurityEvent('/user/register', $e->getMessage()); \Core\set_message($e->getMessage(), 'error'); return false; } catch(\Exception $e){ // Make a note of this! \SystemLogModel::LogSecurityEvent('/user/register', $e->getMessage()); if(DEVELOPMENT_MODE){ \Core\set_message($e->getMessage(), 'error'); } else{ \Core\set_message('t:MESSAGE_ERROR_FORM_SUBMISSION_UNHANDLED_EXCEPTION'); } return false; } if( \Core\user()->checkAccess('g:admin') ) { $active = ($form->getElementValue('active') === "on" ? 1 : 0); $user->set('active', $active); } else { $user->setDefaultActiveStatuses(); } $user->setDefaultGroups(); $user->setDefaultMetaFields(); $user->generateNewApiKey(); $user->save(); // User created... make a log of this! \SystemLogModel::LogSecurityEvent('/user/register', 'User registration successful', null, $user->get('id')); // Send a thank you for registering email to the user. try{ $user->sendWelcomeEmail(); } catch(\Exception $e){ \Core\ErrorManagement\exception_handler($e); \Core\set_message('t:MESSAGE_ERROR_CANNOT_SEND_WELCOME_EMAIL'); } // "login" this user if not already logged in. if(!\Core\user()->exists()){ if($user->get('active')){ $user->set('last_login', \CoreDateTime::Now('U', \Time::TIMEZONE_GMT)); $user->save(); Session::SetUser($user); } \Core\set_message('t:MESSAGE_SUCCESS_CREATED_USER_ACCOUNT'); if(($overrideurl = \HookHandler::DispatchHook('/user/postlogin/getredirecturl'))){ // Allow an external script to override the redirecting URL. $url = $overrideurl; } elseif($form->getElementValue('redirect')){ // The preferred default redirect method. // This is set from /user/register2, which is in turn passed in, (hopefully), by the original callee registration page. $url = $form->getElementValue('redirect'); } elseif(strpos(REL_REQUEST_PATH, '/user/register') === 0){ // If the user came from the registration page, get the page before that. $url = '/'; } else{ // else the registration link is now on the same page as the 403 handler. $url = REL_REQUEST_PATH; } return $url; } // It was created administratively; redirect there instead. else{ \Core\set_message('t:MESSAGE_SUCCESS_CREATED_USER_ACCOUNT'); return '/user/admin'; } }
//require_once(ROOT_PDIR . 'core/libs/core/Exception.php'); require_once(ROOT_PDIR . 'install/classes/InstallerStep.php'); require_once(ROOT_PDIR . 'core/functions/Core.functions.php'); require_once(ROOT_PDIR . 'install/utilities.php'); require_once(ROOT_PDIR . "core/libs/core/ConfigHandler.class.php"); // If the configuration file has been written already, load up those config options! if(file_exists(ROOT_PDIR . 'config/configuration.xml')){ require_once(ROOT_PDIR . 'core/libs/core/datamodel/DMI.class.php'); require_once(ROOT_PDIR . "core/libs/core/HookHandler.class.php"); try { HookHandler::singleton(); // Register some low-level hooks so it doesn't complain. HookHandler::RegisterNewHook('/core/model/presave'); HookHandler::RegisterNewHook('/core/model/postsave'); $core_settings = ConfigHandler::LoadConfigFile('configuration'); // It was able to pull a valid configuration file... see if I can connect to the database $dbconn = DMI::GetSystemDMI(); // And the backend connection... $backend = $dbconn->connection(); // If I can poll the components table.... just stop right the f**k here! // This is a pretty good indication that the system is installed. if($backend->tableExists('component')){ die('Core Plus has been successfully installed! <a href="../">Continue</a>'); } } catch (Exception $e) {