/** * Main controller interceptor for all multi upload methods with this utility. */ public function index() { $request = $this->getPageRequest(); $view = $this->getView(); // This page is ALWAYS json. $view->contenttype = View::CTYPE_JSON; $view->mode = View::MODE_AJAX; if (!is_array(\Core\Session::Get('multifileinputobjects'))) { return View::ERROR_BADREQUEST; } // Whatever the method, it should have a matching key that identifies which form it came from, // (since that form element has the metadata attached to it) // This can either be in the POST (for full uploads), or in the headers. $key = false; if (isset($_POST['key'])) { $key = $_POST['key']; } if (isset($_SERVER['HTTP_X_KEY'])) { $key = $_SERVER['HTTP_X_KEY']; } if (!$key) { return View::ERROR_BADREQUEST; } // The key also must exist! if (!isset(\Core\Session::Get('multifileinputobjects')[$key])) { return View::ERROR_BADREQUEST; } $this->_formelement = unserialize(\Core\Session::Get('multifileinputobjects')[$key]['obj']); if ($request->method == View::METHOD_POST) { //var_dump($_SERVER);die(); // Damn browsers that don't support DELETE... if (isset($_REQUEST['_method']) && $_REQUEST['_method'] === 'DELETE') { $view->jsondata = $this->_doDelete(); } elseif (sizeof($_FILES)) { $view->jsondata = $this->_doPost(); } elseif (isset($_SERVER['HTTP_CONTENT_DISPOSITION'])) { $view->jsondata = $this->_doStream(); } else { // NO COOKIE FOR YOU! return View::ERROR_BADREQUEST; } /*fix for IE not handling XMLHTTPRequest file uploads correctly */ header('Vary: Accept'); if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false) { $view->contenttype = "application/json"; } else { //$view->contenttype = "text/plain"; $view->mode = View::MODE_NOOUTPUT; echo json_decode($view->jsondata); } /*end IE fix*/ } else { // What, it's not a post even? return View::ERROR_BADREQUEST; } }
public function setValue($value) { if (!$value) { $this->_error = $this->get('title') . ' is required.'; return false; } if ($value != \Core\Session::Get('captcha')) { $this->_error = $this->get('title') . ' does not match image.'; return false; } parent::setValue(''); }
public function render() { if (!$this->get('basedir')) { throw new Exception('MultiFileInput cannot be rendered without a basedir attribute!'); } // Make sure it ends with a trailing slash. if (substr($this->get('basedir'), -1) != '/') { $this->_attributes['basedir'] .= '/'; } //var_dump($_SESSION['multifileinputobjects'], serialize($this->_attributes)); die(); // This is a slightly different element than the traditional form system, as it must be able to be called without // the rest of the form system on submit. // This is because this system will do an ajax submit to do the actual upload. if (!is_array(\Core\Session::Get('multifileinputobjects'))) { \Core\Session::Set('multifileinputobjects', []); } // I don't need this key to be cryptographically secure, just generally unique. $key = md5(serialize($this->_attributes)); foreach (\Core\Session::Get('multifileinputobjects') as $obj) { if (!isset($obj['key'])) { continue; } if ($obj['key'] == $key) { $this->set('id', $obj['id']); } } if (!isset($this->_attributes['id'])) { // This system requires a valid id. $this->set('id', 'multifileinput-' . Core::RandomHex('2')); } $this->set('key', $key); $this->set('uploadkey', $key); // Convert the string representation of a filesize to the raw bytes. $size = strtoupper(str_replace(' ', '', ini_get('upload_max_filesize'))); if (strpos($size, 'G') !== false) { $size = preg_replace('/[^0-9]/', '', $size); $size = $size * (1024 * 1024 * 1024); } elseif (strpos($size, 'M') !== false) { $size = preg_replace('/[^0-9]/', '', $size); $size = $size * (1024 * 1024); } elseif (strpos($size, 'K') !== false) { $size = preg_replace('/[^0-9]/', '', $size); $size = $size * 1024; } $this->set('maxsize', $size); // Now that the session variable has been initialized, the traditional session variable is reliable. $_SESSION['multifileinputobjects'][$key] = array('obj' => serialize($this), 'key' => $key, 'id' => $this->_attributes['id']); return parent::render(); }
/** * Internal function to parse and handle the configs in the theme.xml file. * This is used for installations and upgrades. * * Returns false if nothing changed, else will return the configuration options changed. * * @param boolean $install Set to false to force uninstall/disable mode. * @param int $verbosity (default 0) 0: standard output, 1: real-time, 2: real-time verbose output. * * @return false | array * * @throws \InstallerException */ public function _parseConfigs($install = true, $verbosity = 0) { // Keep track of if this changed anything. $changes = array(); $action = $install ? 'Installing' : 'Uninstalling'; $set = $install ? 'Set' : 'Unset'; \Core\Utilities\Logger\write_debug($action . ' configs for ' . $this->getName()); // I need to get the schema definitions first. $node = $this->_xmlloader->getElement('configs'); //$prefix = $node->getAttribute('prefix'); // Now, get every table under this node. foreach ($node->getElementsByTagName('config') as $confignode) { /** @var \DOMElement $confignode */ $key = $confignode->getAttribute('key'); $options = $confignode->getAttribute('options'); $type = $confignode->getAttribute('type'); $default = $confignode->getAttribute('default'); $title = $confignode->getAttribute('title'); $description = $confignode->getAttribute('description'); $mapto = $confignode->getAttribute('mapto'); $encrypted = $confignode->getAttribute('encrypted'); $formAtts = $confignode->getAttribute('form-attributes'); if ($encrypted === null || $encrypted === '') { $encrypted = '0'; } if ($verbosity == 2) { CLI::PrintActionStart($action . ' config ' . $key); } // Themes only allow for keys starting with "/theme/"! // This is to encourage that all themes share a common subset of configuration options. // EG: if the end user sees: "Site Logo", "Business Address", "Business Phone" on one theme, // they would be expecting to see those same options with the same values if they change the theme, // (and the new theme supports those same options). if (strpos($key, '/theme/') !== 0) { trigger_error('Please ensure that all config options in themes start with "/theme/"! (Mismatched config found in ' . $this->getName() . ':' . $key, E_USER_NOTICE); continue; } // Default if omitted. if (!$type) { $type = 'string'; } $m = \ConfigHandler::GetConfig($key); $m->set('options', $options); $m->set('type', $type); $m->set('default_value', $default); $m->set('title', $title); $m->set('description', $description); $m->set('mapto', $mapto); $m->set('encrypted', $encrypted); $m->set('form_attributes', $formAtts); // Default from the xml, only if it's not already set. if ($m->get('value') === null || !$m->exists()) { $m->set('value', $confignode->getAttribute('default')); } // Allow configurations to overwrite any value. This is useful on the initial installation. if (is_array(\Core\Session::Get('configs')) && isset(\Core\Session::Get('configs')[$key])) { $m->set('value', \Core\Session::Get('configs')[$key]); } if ($m->save()) { $changes[] = $set . ' configuration [' . $m->get('key') . '] to [' . $m->get('value') . ']'; if ($verbosity == 2) { CLI::PrintActionStatus(true); } } else { if ($verbosity == 2) { CLI::PrintActionStatus('skip'); } } // Make it available immediately \ConfigHandler::CacheConfig($m); } return sizeof($changes) ? $changes : false; }
/** * Load the values from the session data. * * This is automatically called by the load function. */ public function loadSession(){ if(!$this->_name){ // Ok, no name.. no loading. return; } if(($filters = \Core\Session::Get('filters/' . $this->_name)) !== null){ foreach($filters as $f => $v){ if(!isset($this->_elementindexes['filter[' . $f . ']'])) continue; /** @var $el FormElement */ $el = $this->_elementindexes['filter[' . $f . ']']; $el->setValue($v); } } if(\Core\Session::Get('filtersort/' . $this->_name) !== null){ $this->_sortkey = \Core\Session::Get('filtersort/' . $this->_name)['sortkey']; $this->_sortdir = \Core\Session::Get('filtersort/' . $this->_name)['sortdir']; } $cachedPageData = \Core\Session::Get('filterpage/' . $this->_name); if($cachedPageData !== null && isset($cachedPageData['page'])) { $this->_currentpage = \Core\Session::Get('filterpage/' . $this->_name)['page']; } if($cachedPageData !== null && isset($cachedPageData['limit'])){ $this->setLimit(\Core\Session::Get('filterpage/' . $this->_name)['limit']); } }
/** * @return null|int */ public function pageinsertables_update(){ $request = $this->getPageRequest(); $view = $this->getView(); $view->mode = View::MODE_AJAX; $view->contenttype = View::CTYPE_JSON; $view->record = false; // This is an ajax-only request. if(!$request->isAjax()){ return View::ERROR_BADREQUEST; } $formid = $request->getPost('___formid'); if(!$formid){ return View::ERROR_NOTFOUND; } // Lookup that form! if(\Core\Session::Get('FormData/' . $formid) === null){ return View::ERROR_NOTFOUND; } /** @var $form Form */ $form = unserialize(\Core\Session::Get('FormData/' . $formid)); if(!$form){ return View::ERROR_NOTFOUND; } // Run though each element submitted and try to validate it. if (strtoupper($form->get('method')) == 'POST') $src =& $_POST; else $src =& $_GET; $form->loadFrom($src, true); // Now that the form has been loaded with the data, reinitialize the page's insertable elements. foreach($form->getModels() as $prefix => $model){ if($model instanceof PageModel && $form->getElement($prefix . '[page_template]')){ $pagetemplate = $form->getElement($prefix . '[page_template]'); // Get all insertables currently present and remove them from the form. // (They will be added back shortly) foreach($form->getElements(true, false) as $el){ $name = $el->get('name'); if(strpos($name, $prefix . '[insertables]') === 0){ $form->removeElement($name); } } // Now that the previous insertables are removed, update the value on the model and add the new insertables. // This block of logic is required because the template systems look at the last template set in the database. // Since what's in the databse isn't what we want here, we need to spoof it so the correct template is // used for retrieving form elements. $model->set('page_template', $pagetemplate->get('value')); // Set the last_template so that the traditional queries to getTemplate work without reverting back to the default template. $t = $model->getBaseTemplateName(); // Allow the specific template to be overridden. if (($override = $model->get('page_template'))){ $t = substr($t, 0, -4) . '/' . $override; $model->set('last_template', $t); } else{ $model->set('last_template', null); } $tpl = Core\Templates\Template::Factory($model->getTemplateName()); if($tpl){ // My counter for which element was added last... I need this because I have "addElementAfter"... // so if I just kept adding the stack after a single element, they'd be in reverse order. // ie: stack: [a, b, c] -> {ref_el}, c, b, a $lastelementadded = $pagetemplate; $insertables = $tpl->getInsertables(); foreach($insertables as $key => $dat){ $type = $dat['type']; $dat['name'] = $prefix . '[insertables][' . $key . ']'; // This insertable may already have content from the database... if so I want to pull that! $i = InsertableModel::Construct($model->get('baseurl'), $key); if ($i->get('value') !== null){ $dat['value'] = $i->get('value'); } $dat['class'] = 'insertable'; $insertableelement = FormElement::Factory($type, $dat); $form->addElementAfter($insertableelement, $lastelementadded); $lastelementadded = $insertableelement; } } // Since there are new elements here, there may be old values that correspond to the new elements too. $form->loadFrom($src, true); // Don't forget to re-save these form updates back to the session! $form->persistent = true; $form->saveToSession(); $view->jsondata = array( 'status' => '1', 'message' => 'Switched templatename successfully', 'formid' => $form->get('uniqueid'), ); return null; } // if($model instanceof PageModel && $form->getElement($prefix . '[page_template]')) } // foreach($form->getModels() as $prefix => $model) // Ummmm..... $view->jsondata = array( 'status' => '0', 'message' => 'No page found :/', 'formid' => null, ); }
/** * Retrieve the messages and optionally clear the message stack. * * @param bool $returnSorted * @param bool $clearStack * * @return array */ static public function GetMessages($returnSorted = false, $clearStack = true) { $stack = Session::Get('message_stack', []); if($returnSorted){ $stack = \Core::SortByKey($stack, 'mtype'); } if($clearStack){ Session::UnsetKey('message_stack'); } return $stack; }
/** * Check the user's IP in the blacklist and see if it's found. * * If it is and has a high enough submission rate, (in a 24 hour period), then block the user completely and immediately. */ public static function CheckIP() { $record = \sfsBlacklistModel::Construct(REMOTE_IP); // It's not in there, YAY! if (!$record->exists()) { return; } // Is the submission score high enough? $highscore = 100; if ($record->get('submissions') > $highscore) { // YOU can haz good party tiem nau \SystemLogModel::LogSecurityEvent('/security/blocked', 'Blocking IP due to over ' . $highscore . ' submissions to sfs in a 24 hour period.'); die('IP Blocked due to high spam score'); } // Submissions listed, but not exceedingly high? $warnlevel = 5; if ($record->get('submissions') > $warnlevel) { if (\Core\Session::Get('security_antispam_allowed') === null) { $html = '<html><body>'; $html .= '<!-- You smell of spam.... are you sure you didn\'t come from a can?-->'; if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['happyfuntime']) && \Core\Session::Get('happyfuntimecheck')) { // It's an attempt! if ($_POST['happyfuntime'] == \Core\Session::Get('happyfuntimecheck')) { \SystemLogModel::LogSecurityEvent('/security/unblocked', 'User successfully answered an anti-bot math question, unblocking.'); \Core\Session::Set('security_antispam_allowed', true); } else { \SystemLogModel::LogSecurityEvent('/security/captchafailed', 'User attempted, but failed in answering an anti-bot math question.'); $html .= '<b>NOPE!</b>'; } } \SystemLogModel::LogSecurityEvent('/security/blocked', 'Blocking IP due to over ' . $warnlevel . ' submissions to sfs in a 24 hour period.'); $random1 = rand(4, 6) * 2; $random2 = rand(1, 3) * 2; $random3 = rand(1, 2); switch ($random3) { case 1: $result = $random1 / $random2; $operation = 'divided by'; break; case 2: $result = $random1 * $random2; $operation = 'multiplied by'; break; } \Core\Session::Set('happyfuntimecheck', $result); switch ($random2) { case 1: $random2 = 'oNe'; break; case 2: $random2 = 'Tw0'; break; case 3: $random2 = 'ThRe'; break; case 4: $random2 = 'Foor'; break; case 5: $random2 = 'fIve'; break; case 6: $random2 = 'Siix'; break; } $html .= '<form method="POST"><p>What is ' . $random1 . ' ' . $operation . ' ' . $random2 . '?</p><input type="text" name="happyfuntime" size="3"/><input type="submit" value="GO"/></form></body></html>'; die($html); } } }
/** * Handler to actually perform the import. * * @param \Form $form * @return bool */ public static function FormHandler2(\Form $form) { $filename = Session::Get('user-import/file'); $file = Factory::File($filename); /** @var $contents \Core\Filestore\Contents\ContentCSV */ $contents = $file->getContentsObject(); // If the user checked that it has a header... do that. $contents->_hasheader = $form->getElement('has_header')->get('checked'); // Merge $merge = $form->getElement('merge_duplicates')->get('checked'); // Handle the map-to directives. $maptos = array(); foreach($form->getElements() as $el){ if(strpos($el->get('name'), 'mapto[') === 0 && $el->get('value')){ $k = substr($el->get('name'), 6, -1); $maptos[$k] = $el->get('value'); } } // Handle the group mappings $groups = $form->getElement('groups[]')->get('value'); // And keep a log of the bad transfers and some other data. $counts = ['created' => 0, 'updated' => 0, 'failed' => 0, 'skipped' => 0]; Session::Set('user-import/fails', []); $incoming = $contents->parse(); foreach($incoming as $record){ try{ // Create a data map of this record for fields to actually map over. $dat = array(); foreach($maptos as $recordkey => $userkey){ $dat[$userkey] = $record[$recordkey]; } // No email, NO IMPORT! if(!$dat['email']){ $counts['skipped']++; continue; } // Try to find this record by email, since that's a primary key. $existing = \UserModel::Find(['email = ' . $dat['email'] ], 1); if($existing && !$merge){ // Skip existing records. $counts['skipped']++; } elseif($existing){ // Update! $existing->setFromArray($dat); $existing->setGroups($groups); if($existing->save()){ $counts['updated']++; } else{ $counts['skipped']++; } } else{ $new = new \UserModel(); $new->setFromArray($dat); $new->setGroups($groups); $new->save(); $counts['created']++; } } catch(\Exception $e){ // @todo Handle this die($e->getMessage()); } // } Session::Set('user-import/counts', $counts); return true; }
/** * Sync the search index fields of every model on the system. * * @return int */ public function syncSearchIndex(){ // Admin-only page. if(!\Core\user()->checkAccess('g:admin')){ return View::ERROR_ACCESSDENIED; } // Just run through every component currently installed and reinstall it. // This will just ensure that the component is up to date and correct as per the component.xml metafile. $view = $this->getView(); $changes = []; $outoftime = false; $counter = 0; $resume = \Core\Session::Get('syncsearchresume', 1); $timeout = ini_get('max_execution_time'); // Dunno why this is returning 0, but if it is, reset it to 30 seconds! if(!$timeout) $timeout = 30; $memorylimit = ini_get('memory_limit'); if(stripos($memorylimit, 'M') !== false){ $memorylimit = str_replace(['m', 'M'], '', $memorylimit); $memorylimit *= (1024*1024); } elseif(stripos($memorylimit, 'G') !== false){ $memorylimit = str_replace(['g', 'G'], '', $memorylimit); $memorylimit *= (1024*1024*1024); } foreach(\Core::GetComponents() as $c){ /** @var Component_2_1 $c */ if($outoftime){ break; } foreach($c->getClassList() as $class => $file){ if($outoftime){ break; } if($class == 'model'){ continue; } if(strrpos($class, 'model') !== strlen($class) - 5){ // If the class doesn't explicitly end with "Model", it's also not a model. continue; } if(strpos($class, '\\') !== false){ // If this "Model" class is namespaced, it's not a valid model! // All Models MUST reside in the global namespace in order to be valid. continue; } $ref = new ReflectionClass($class); if(!$ref->getProperty('HasSearch')->getValue()){ // This model doesn't have the searchable flag, skip it. continue; } $c = ['name' => $class, 'count' => 0]; $fac = new ModelFactory($class); while(($m = $fac->getNext())){ ++$counter; if($counter < $resume){ // Allow this process to be resumed where it left off, since it may take more than 30 seconds. continue; } if(\Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->getTime() + 5 >= $timeout){ // OUT OF TIME! // Remember where this process left off and exit. \Core\Session::Set('syncsearchresume', $counter); $outoftime = true; break; } if(memory_get_usage(true) + 40485760 >= $memorylimit){ // OUT OF MEMORY! // Remember where this process left off and exit. \Core\Session::Set('syncsearchresume', $counter); $outoftime = true; break; } /** @var Model $m */ $m->set('search_index_pri', '!'); $m->save(); $c['count']++; } $changes[] = $c; } } if(!$outoftime){ // It finished! Unset the resume counter. \Core\Session::UnsetKey('syncsearchresume'); } $view->title = 'Sync Searchable Index'; $view->assign('changes', $changes); $view->assign('outoftime', $outoftime); }
/** * Function that is fired off on page load. * This checks if a form was submitted and that form was present in the SESSION. * * @return null */ public static function CheckSavedSessionData() { // This needs to ignore the /form/savetemporary.ajax page! // This is a custom page that's meant to intercept all POST submissions. if(preg_match('#^/form/(.*)\.ajax$#', REL_REQUEST_PATH)) return; // There has to be data in the session. $forms = \Core\Session::Get('FormData/*'); $formid = (isset($_REQUEST['___formid'])) ? $_REQUEST['___formid'] : false; $form = false; foreach ($forms as $k => $v) { // If the object isn't a valid object after unserializing... if (!($el = unserialize($v))) { \Core\Session::UnsetKey('FormData/' . $k); continue; } // Check the expires time if ($el->get('expires') <= Time::GetCurrent()) { \Core\Session::UnsetKey('FormData/' . $k); continue; } if ($k == $formid) { // Remember this for after all the checks have finished. $form = $el; } } // No form found... simple enough if (!$form) return; // Otherwise /** @var $form Form */ // Ensure the submission types match up. if (strtoupper($form->get('method')) != $_SERVER['REQUEST_METHOD']) { \Core\set_message('t:MESSAGE_ERROR_FORM_SUBMISSION_TYPE_DOES_NOT_MATCH'); return; } // Ensure the REFERRER and original URL match up. if($_SERVER['HTTP_REFERER'] != $form->originalurl){ // @todo This is reported to be causing issues with production sites. // If found true, this check may need to be removed / refactored. //\Core\set_message('Form submission referrer does not match, please try your submission again.', 'error'); SystemLogModel::LogInfoEvent( 'Form Referrer Mismatch', 'Form referrer does not match! Submitted: [' . $_SERVER['HTTP_REFERER'] . '] Expected: [' . $form->originalurl . ']' ); //return; } // Run though each element submitted and try to validate it. if (strtoupper($form->get('method')) == 'POST') $src =& $_POST; else $src =& $_GET; $form->loadFrom($src); // Try to load the form from that form. That will call all of the model's validation logic // and will throw exceptions if it doesn't. try{ $form->getModel(); // Still good? if (!$form->hasError()){ $status = call_user_func($form->get('callsmethod'), $form); } else{ $status = false; } } catch(ModelValidationException $e){ \Core\set_message($e->getMessage(), 'error'); $status = false; } catch(GeneralValidationException $e){ \Core\set_message($e->getMessage(), 'error'); $status = false; } catch(Exception $e){ if(DEVELOPMENT_MODE){ // Developers get the full message \Core\set_message($e->getMessage(), 'error'); } else{ // While users of production-enabled sites get a friendlier message. \Core\set_message('t:MESSAGE_ERROR_FORM_SUBMISSION_UNHANDLED_EXCEPTION'); } Core\ErrorManagement\exception_handler($e); $status = false; } // The form was submitted. Set its persistent flag to true so that whatever may be listening for it can retrieve the user's values. $form->persistent = true; // Regardless, bundle this form back into the session so the controller can use it if needed. \Core\Session::Set('FormData/' . $formid, serialize($form)); // Fail statuses. if ($status === false) return; if ($status === null) return; // Guess it's not false and not null... must be good then. // @todo Handle an internal save procedure for "special" groups such as pageinsertables and what not. // Cleanup \Core\Session::UnsetKey('FormData/' . $formid); if ($status === 'die'){ // If it's set to die, simply exit the script without outputting anything. exit; } elseif($status === 'back'){ if($form->referrer && $form->referrer != REL_REQUEST_PATH){ // Go back to the original form's referrer. \Core\redirect($form->referrer); } else{ // Use Core to guess which page to redirect back to, (not as reliable). \Core\go_back(); } } elseif ($status === true){ // If the return code is boolean true, it's a reload. \Core\reload(); } elseif($status === REL_REQUEST_PATH || $status === CUR_CALL){ // If the page returned the same page as the current url, force a reload, (as redirect will ignore it) \Core\reload(); } else{ // Anything else gets sent to the redirect system. \core\redirect($status); } }
/** * Execute the controller and method this page request points to. */ public function execute() { \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record('Starting PageRequest->execute()'); if($this->isCacheable()){ $uakey = \Core\UserAgent::Construct()->getPseudoIdentifier(); $urlkey = $this->host . $this->uri; $expires = $this->getPageModel()->get('expires'); $key = 'page-cache-' . md5($urlkey . '-' . $uakey); $cached = \Core\Cache::Get($key, $expires); if($cached && $cached instanceof View){ $this->_pageview = $cached; $this->_cached = true; return; } } // Anything that needs to fire off *before* the page is rendered. // This includes widgets, script addons, and anything else that needs a CurrentPage. HookHandler::DispatchHook('/core/page/preexecute'); // Load the underlying controller. $pagedat = $this->splitParts(); /** @var View $view The valid view object for this page */ $view = $this->getView(); // The controller must exist first! // (note, the SplitParts logic already takes care of the "Is this a valid controller" logic) if (!(isset($pagedat['controller']) && $pagedat['controller'])) { $view->error = View::ERROR_NOTFOUND; return; } $component = Core::GetComponentByController($pagedat['controller']); ////////////////////////////////////////////////////////////////////////////// /// In this block of logic, either the page is executed and a view returned, /// or a view is generated with an error. ////////////////////////////////////////////////////////////////////////////// if (!$component) { // Not found $view->error = View::ERROR_NOTFOUND; return; } elseif(!is_a($component, 'Component_2_1')) { $view->error = View::ERROR_NOTFOUND; return; } // Any method that starts with a "_" is an internal-only method! if ($pagedat['method']{0} == '_') { $view->error = View::ERROR_NOTFOUND; return; } // It also must be a part of the class... obviously if (!method_exists($pagedat['controller'], $pagedat['method'])) { $view->error = View::ERROR_NOTFOUND; return; } /** @var $controller Controller_2_1 This will be a Controller object. */ $controller = Controller_2_1::Factory($pagedat['controller']); $view->baseurl = $this->getBaseURL(); $controller->setView($view); // Make sure that the controller can access this object. $controller->setPageRequest($this); // The main page object. $page = $this->getPageModel(); // Check the access string first, (if there is one) if ($controller->accessstring !== null) { // Update the page's access string, (just in case it's saved at the end of execution) $page->set('access', $controller->accessstring); // And if the user doesn't have access to it... if (!\Core\user()->checkAccess($controller->accessstring)) { $view->error = View::ERROR_ACCESSDENIED; return; } } if($page->get('password_protected')) { if(\Core\Session::Get('page-password-protected/' . $page->get('baseurl')) !== $page->get('password_protected')){ $view->templatename = '/pages/page/passwordprotected.tpl'; $form = new Form(); $form->set('callsmethod', 'PageRequest::PasswordProtectHandler'); $form->addElement( 'system', [ 'name' => 'page', 'value' => $page ] ); $form->addElement( 'password', [ 'name' => 'passinput', 'title' => 'Password', 'required' => 'required', 'maxlength' => 128 ] ); $form->addElement( 'submit', [ 'value' => 'Submit' ] ); $view->assign('form', $form); return; } } // If the parent Controller object has a method named $pagedat['method'], assume it's a security error! // This is because if the parent Controller object has a method, it's most likely a utility method // that shouldn't be called from the public web! foreach(get_class_methods('Controller_2_1') as $parentmethod){ $parentmethod = strtolower($parentmethod); if($parentmethod == $pagedat['method']){ $view->error = View::ERROR_BADREQUEST; return; } } // Additional security logic for existing pages in multi-site mode. // If this exact URL is registered to another site, then // don't allow this site to display it. if(!$page->exists() && Core::IsComponentAvailable('multisite') && MultiSiteHelper::IsEnabled()){ $site = MultiSiteHelper::GetCurrentSiteID(); $anypage = PageModel::Find(['baseurl = ' . $page->get('baseurl')], 1); if($anypage){ if($anypage->get('site') == -1){ // If this is a global page.... that's ok. // Just remap the page variable to this one! $page = $anypage; } elseif($anypage->get('site') == $site){ // Strange... it should have located this page... // Anyway, it's allowed, the site matches up. $page = $anypage; } else{ \Core\redirect($anypage->getResolvedURL()); } } } $return = call_user_func(array($controller, $pagedat['method'])); if (is_int($return)) { // A generic error code was returned. Create a View with that code and return that instead. $view->error = $return; //return; } elseif(is_a($return, 'View') && $return != $view){ // The controller method changed the view, (which is allowed), // but this needs to be remapped to this object so render knows about it. $this->_pageview = $view = $return; } elseif ($return === null) { // Hopefully it's setup! $return = $controller->getView(); if($return != $view){ $this->_pageview = $view = $return; } } elseif(!is_a($return, 'View')){ if(DEVELOPMENT_MODE){ var_dump('Controller method returned', $return); die('Sorry, but this controller did not return a valid object. Please ensure that your method returns either an integer, null, or a View object!'); } else{ $view->error = View::ERROR_SERVERERROR; return; } } // No else needed, else it's a valid object. // You may be asking why $view is one object, but $return is the return from page execution. // GREAT QUESTION, The $view is the original view object created from the page request. That is passed into // the controller and exposed via $this->getView(). The return can be a view, int, or other status indicator. // However since the controller can return a different view, that view should be used instead! ///** @var $return View */ // Allow the controller to assign controls via a shortcut function. if($view->error == View::ERROR_NOERROR){ $controls = $controller->getControls(); // This method may do absolutely nothing, add the controls to the view itself, or return an array of them. if(is_array($controls)){ foreach($controls as $control){ $view->addControl($control); } } } // For some of the options, there may be some that can be used for a fuzzy page, ie: a page's non-fuzzy template, // title, or meta information. if($view->error == View::ERROR_NOERROR){ if ($page->exists()) { $defaultpage = $page; } else { $defaultpage = null; $url = $view->baseurl; while ($url != '') { $url = substr($url, 0, strrpos($url, '/')); $p = PageModel::Find(array('baseurl' => $url, 'fuzzy' => 1), 1); if ($p === null) continue; if ($p->exists()) { $defaultpage = $p; break; } } if ($defaultpage === null) { // Fine.... $defaultpage = $page; } } $defaultmetas = $defaultpage->getLink('PageMeta'); // Make a list of the existing ones so I know which ones not to overwrite! // Just the key will suffice quite nicely. $currentmetas = array(); foreach($view->meta as $k => $meta){ $currentmetas[] = $k; } // Load some of the page information into the view now! foreach($defaultmetas as $meta){ /** @var $meta PageMetaModel */ $key = $meta->get('meta_key'); $viewmeta = $meta->getViewMetaObject(); // again, allow the executed controller have the final say on meta information. if ($meta->get('meta_value_title') && !in_array($key, $currentmetas)) { $view->meta[$key] = $viewmeta; } } // Since the controller already ran, do not overwrite the title. if ($view->title === null){ $view->title = $defaultpage->get('title'); } // Tracker to see if this page, (or a parent's page), is an admin-level page. // This is required because "admin" pages may have a different skin and should always have the dashboard as the top-level breadcrumb. /** @var boolean $isadmin */ $isadmin = ($page->get('admin') == '1'); $parents = array(); $parenttree = $page->getParentTree(); foreach ($parenttree as $parent) { /** @var PageModel $parent */ $parents[] = array( 'title' => $parent->get('title'), 'link' => $parent->getResolvedURL() ); // Since I'm here, check if this page is an admin page. if($parent->get('admin')){ $isadmin = true; } } $view->breadcrumbs = array_merge($parents, $view->breadcrumbs); if($isadmin && $view->baseurl != '/admin'){ // Make sure that admin is the top breadcrumb. // This block doesn't need to apply for the actual admin page itself, as that doesn't need its own breadcrumb :/ $adminlink = \Core\resolve_link('/admin'); if(!isset($view->breadcrumbs[0])){ // Nothing is even set! $view->breadcrumbs[] = ['title' => 'Administration', 'link' => $adminlink]; } elseif($view->breadcrumbs[0]['link'] != $adminlink){ // It's set, but not to admin. $view->breadcrumbs = array_merge([['title' => 'Administration', 'link' => $adminlink]], $view->breadcrumbs); } } } else{ $defaultpage = null; $isadmin = false; } if( $view->mode == View::MODE_PAGEORAJAX && $this->isAjax() && $view->jsondata !== null && $view->templatename === null ){ // Allow the content type to be overridden for ajax pages that have JSON data embedded in them. $view->contenttype = View::CTYPE_JSON; } if($view->mode == View::MODE_NOOUTPUT){ $view->mastertemplate = false; $view->templatename = null; } elseif( $view->error == View::ERROR_NOERROR && $view->contenttype == View::CTYPE_HTML && $view->templatename === null ){ // Try to guess the templatename if it wasn't set. // This $cnameshort = (strpos($pagedat['controller'], 'Controller') == strlen($pagedat['controller']) - 10) ? substr($pagedat['controller'], 0, -10) : $pagedat['controller']; $view->templatename = strtolower('/pages/' . $cnameshort . '/' . $pagedat['method'] . '.tpl'); } elseif( $view->error == View::ERROR_NOERROR && $view->contenttype == View::CTYPE_XML && $view->templatename === null ){ $cnameshort = (strpos($pagedat['controller'], 'Controller') == strlen($pagedat['controller']) - 10) ? substr($pagedat['controller'], 0, -10) : $pagedat['controller']; $view->templatename = Template::ResolveFile(strtolower('pages/' . $cnameshort . '/' . $pagedat['method'] . '.xml.tpl')); } // In addition to the autogeneration, also support the page_template from the datastore. if($defaultpage && $defaultpage->get('page_template')){ // Switch the template over to that custom one. // Some legacy data will have the fully resolved path for this template. // This has been switched to just the basename of the custom template, // but legacy data be legacy, 'yo. 0.o $base = substr($view->templatename, 0, -4); $override = $defaultpage->get('page_template'); if($base && strpos($override, $base) === 0){ $view->templatename = $override; } elseif($base){ $view->templatename = $base . '/' . $override; } } // Guess which theme skin (mastertemplate) should be used if one wasn't specified. if($view->mastertemplate == 'admin'){ // If the master template is set explictly to be the admin skin, then transpose that to the set admin skin. // This is useful for the pages that may not be under the "/admin" umbrella, but still rendered with the admin UI. $view->mastertemplate = ConfigHandler::Get('/theme/default_admin_template'); } elseif($view->mastertemplate){ // No change needed, just skip the below cases. } elseif($view->mastertemplate === false){ // If the master template is explictly set to false, the page wanted no master template! } elseif($isadmin){ // This page doesn't have a master template set, but it or a parent is set as an admin-level page. $view->mastertemplate = ConfigHandler::Get('/theme/default_admin_template'); } elseif ($defaultpage && $defaultpage->get('theme_template')) { // Master template set in the database? $view->mastertemplate = $defaultpage->get('theme_template'); } elseif($defaultpage && $defaultpage->exists() && $defaultpage->get('admin')){ // Or an admin level page? $view->mastertemplate = ConfigHandler::Get('/theme/default_admin_template'); } elseif(sizeof($view->breadcrumbs) && $view->breadcrumbs[0]['title'] == 'Administration'){ // Whatever, close e-damn-nough! // This happens for pages that don't actually exist, like "edit".... $view->mastertemplate = ConfigHandler::Get('/theme/default_admin_template'); } else{ $view->mastertemplate = ConfigHandler::Get('/theme/default_template'); } // First of all, if the current theme is not available, reset back to the first theme available! if(!($theme = ThemeHandler::GetTheme())){ /** @var \Theme\Theme $theme */ $theme = ThemeHandler::GetTheme('base-v2'); $view->mastertemplate = 'basic.tpl'; \Core\set_message('t:MESSAGE_ERROR_INVALID_THEME_SELECTED'); } // Make sure the selected mastertemplate actually exists! if($view->mastertemplate !== false){ $themeskins = $theme->getSkins(); $mastertplgood = false; foreach($themeskins as $skin){ if($skin['file'] == $view->mastertemplate){ // It's located! $mastertplgood =true; break; } } // A few special cases. if($view->mastertemplate == 'blank.tpl'){ // This is acceptable as a default one. $mastertplgood =true; } if(!$mastertplgood){ // Just use the first one instead! trigger_error('Invalid skin [' . $view->mastertemplate . '] selected for this page, skin is not located within the selected theme! Using first available instead.', E_USER_NOTICE); $view->mastertemplate = $themeskins[0]['file']; } } // Handle some of the new automatic meta data associated with Pages and the resulting View. if(\ConfigHandler::Get('/core/page/indexable') == 'deny'){ // Administratively set to noindex on all pages. $view->addMetaName('robots', 'noindex'); } elseif(!$page->get('indexable')){ // Bots have no business indexing user-action pages. $view->addMetaName('robots', 'noindex'); } if(!isset($view->meta['title'])){ $view->meta['title'] = $page->getSEOTitle(); } HookHandler::DispatchHook('/core/page/postexecute'); \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record('Completed PageRequest->execute()'); }
/** * {@inheritdoc} * * @see BaseFacebook::getPersistentData() */ protected function getPersistentData($key, $default = false) { if (!in_array($key, self::$kSupportedKeys)) { self::errorLog('Unsupported key passed to getPersistentData.'); return $default; } $session_var_name = $this->constructSessionVariableName($key); return \Core\Session::Get($session_var_name) !== null ? \Core\Session::Get($session_var_name) : $default; }
/** * Internal function to parse and handle the configs in the component.xml file. * This is used for installations and upgrades. * * Returns false if nothing changed, else will return an int of the number of configuration options changed. * * @param boolean $install Set to false to force uninstall/disable mode. * @param int $verbosity (default 0) 0: standard output, 1: real-time, 2: real-time verbose output. * * @return boolean | int * @throws InstallerException */ public function _parseConfigs($install = true, $verbosity = 0) { // Keep track of if this changed anything. $changes = array(); $action = $install ? 'Installing' : 'Uninstalling'; $set = $install ? 'Set' : 'Removed'; Core\Utilities\Logger\write_debug($action . ' configs for ' . $this->getName()); // I need to get the schema definitions first. $node = $this->_xmlloader->getElement('configs'); //$prefix = $node->getAttribute('prefix'); $componentName = $this->getKeyName(); // Now, get every table under this node. foreach ($node->getElementsByTagName('config') as $confignode) { /** @var DOMElement $confignode */ $key = $confignode->getAttribute('key'); $options = $confignode->getAttribute('options'); $type = $confignode->getAttribute('type'); $default = $confignode->getAttribute('default'); $title = $confignode->getAttribute('title'); $description = $confignode->getAttribute('description'); $mapto = $confignode->getAttribute('mapto'); $encrypted = $confignode->getAttribute('encrypted'); $formAtts = $confignode->getAttribute('form-attributes'); if($encrypted === null || $encrypted === '') $encrypted = '0'; // Default if omitted. if(!$type) $type = 'string'; if($verbosity == 2){ CLI::PrintActionStart($action . ' config ' . $key); } $m = ConfigHandler::GetConfig($key); if($install){ // Installation/Upgrade Logic $m->set('options', $options); $m->set('type', $type); $m->set('default_value', $default); $m->set('title', $title); $m->set('description', $description); $m->set('mapto', $mapto); $m->set('encrypted', $encrypted); $m->set('form_attributes', $formAtts); $m->set('component', $componentName); // Default from the xml, only if it's not already set. if ($m->get('value') === null || !$m->exists()){ $m->set('value', $confignode->getAttribute('default')); } // Allow configurations to overwrite any value. This is useful on the initial installation. if(\Core\Session::Get('configs/' . $key) !== null){ $m->set('value', \Core\Session::Get('configs/' . $key)); } if ($m->save()){ $changes[] = $set . ' configuration [' . $m->get('key') . '] to [' . $m->get('value') . ']'; if($verbosity == 2){ CLI::PrintActionStatus(true); } } else{ if($verbosity == 2){ CLI::PrintActionStatus('skip'); } } // Make it available immediately ConfigHandler::CacheConfig($m); } else{ // Uninstallation Logic $m->delete(); $changes[] = $set . ' configuration [' . $key . ']'; if($verbosity == 2){ CLI::PrintActionStatus(true); } } } return (sizeof($changes)) ? $changes : false; } // private function _parseConfigs
/** * Get the value for a given configuration key * * @param string $key * @return mixed */ private function _get($key){ // If it's a standard config, pull the value from config. if(isset($this->_cacheFromDB[$key])){ // Is it already overridden? if(isset($this->_overrides[$key])){ return ConfigModel::TranslateValue($this->_cacheFromDB[$key]->get('type'), $this->_overrides[$key]); } else{ return $this->_cacheFromDB[$key]->getValue(); } } // Not there either? Allow the SESSION to contain config variables. This is critical for installation. elseif(\Core\Session::Get('configs/' . $key) !== null){ return \Core\Session::Get('configs/' . $key); } // Else, just return null. else{ return null; } }
/** * Get the current system DMI based on configuration values. * * @throws DMI_Exception * @throws DMI_Authentication_Exception * * @return DMI */ public static function GetSystemDMI(){ if(self::$_Interface !== null) return self::$_Interface; self::$_Interface = new DMI(); if(file_exists(ROOT_PDIR . 'config/configuration.xml')){ // Because this is the system data connection, I also need to pull the settings automatically. // This will only be done if the configuration file exists. $cs = ConfigHandler::LoadConfigFile("configuration"); } elseif(\Core\Session::Get('configs/*') !== null){ // If the file doesn't exist, (ie: during installation), I need to check the session data. $cs = \Core\Session::Get('configs/*'); } else{ throw new DMI_Exception('No database settings defined for the DMI'); } self::$_Interface->setBackend($cs['database_type']); self::$_Interface->connect($cs['database_server'], $cs['database_user'], $cs['database_pass'], $cs['database_name']); return self::$_Interface; }
private function _import3(){ $view = $this->getView(); $request = $this->getPageRequest(); $view->templatename = 'pages/user/import3.tpl'; $view->assign('count', \Core\Session::Get('user-import/counts', 0)); $view->assign('fails', \Core\Session::Get('user-import/fails', 0)); // @todo Implement this \Core\Session::UnsetKey('user-import/*'); }
/** * Add a message to the user's stack. * It will be displayed the next time the user (or session) renders the page. * * @param string $message_text The message text or the MESSAGE_ string constant for i18n and automatic type detection! * @param string $message_type * * @return boolean (on success) */ function set_message($messageText, $messageType = 'info'){ if(strpos($messageText, 't:MESSAGE_') === 0){ // It's an i18n message! Retrieve the locale version of text and the message type. $messageText = substr($messageText, 2); if(strpos($messageText, 'MESSAGE_SUCCESS_') === 0){ $messageType = 'success'; } elseif(strpos($messageText, 'MESSAGE_ERROR_') === 0){ $messageType = 'error'; } elseif(strpos($messageText, 'MESSAGE_TUTORIAL_') === 0){ $messageType = 'tutorial'; } elseif(strpos($messageText, 'MESSAGE_WARNING_') === 0){ $messageType = 'warning'; } elseif(strpos($messageText, 'MESSAGE_INFO_') === 0){ $messageType = 'info'; } else{ $messageType = 'info'; } if(func_num_args() > 1){ // Use func_call to call 1, as I need to pass in the other options too! $messageText = call_user_func_array('t', func_get_args()); } else{ $messageText = t($messageText); } } // CLI doesn't use sessions, echo directly to stdout instead. if(EXEC_MODE == 'CLI'){ $messageText = preg_replace('/<br[^>]*>/i', "\n", $messageText); echo "[" . $messageType . "] - " . $messageText . "\n"; } else{ $stack = Session::Get('message_stack', []); $stack[] = array( 'mtext' => $messageText, 'mtype' => $messageType, ); Session::Set('message_stack', $stack); } }
/** * Get all the recorded events of this profiler as an array. * * @return array */ public function getEvents(){ return Session::Get('datamodel_profiler_events/events', []); }