Example #1
0
	/**
	 * 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;
	}
Example #2
0
	/**
	 * 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);
	}
Example #3
0
	/**
	 * 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');
	}
Example #6
0
/**
 * 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();
	}
}
Example #7
0
	/**
	 * 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();
	}
Example #9
0
	/**
	 * 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);
	}
Example #11
0
	/**
	 * 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);
	}
Example #12
0
/**
 * 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>');
}
Example #13
0
	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;
	}
Example #14
0
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);
Example #15
0
 /**
  * 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;
	}
Example #17
0
	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')){
Example #20
0
	/**
	 * 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';
		}
	}
Example #21
0
//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) {