/** * Set the module * * @param string $module The module to load. */ public function setModule($module) { // is this module allowed? if (!BackendAuthentication::isAllowedModule($module)) { // set correct headers SpoonHTTP::setHeadersByCode(403); // throw exception throw new BackendException('Module not allowed.'); } // set property $this->module = $module; }
/** * Get the widgets * * @return void */ private function getWidgets() { // get all active modules $modules = BackendModel::getModules(true); // loop all modules foreach ($modules as $module) { // you have sufficient rights? if (BackendAuthentication::isAllowedModule($module)) { // build pathName $pathName = BACKEND_MODULES_PATH . '/' . $module; // check if the folder exists if (SpoonDirectory::exists($pathName . '/widgets')) { // get widgets $widgets = (array) SpoonFile::getList($pathName . '/widgets', '/(.*)\\.php/i'); // loop through widgets foreach ($widgets as $widget) { // require the classes require_once $pathName . '/widgets/' . $widget; // init var $widgetName = str_replace('.php', '', $widget); // build classname $className = 'Backend' . SpoonFilter::toCamelCase($module) . 'Widget' . SpoonFilter::toCamelCase($widgetName); // validate if the class exists if (!class_exists($className)) { // throw exception throw new BackendException('The widgetfile is present, but the classname should be: ' . $className . '.'); } else { // add to array $this->widgetInstances[] = array('module' => $module, 'widget' => $widgetName, 'className' => $className); // create reflection class $reflection = new ReflectionClass('Backend' . SpoonFilter::toCamelCase($module) . 'Widget' . SpoonFilter::toCamelCase($widgetName)); // get the offset $offset = strpos($reflection->getDocComment(), '*', 7); // get the first sentence $description = substr($reflection->getDocComment(), 0, $offset); // replacements $description = str_replace('*', '', $description); $description = trim(str_replace('/', '', $description)); } // check if model file exists if (SpoonFile::exists($pathName . '/engine/model.php')) { // require model require_once $pathName . '/engine/model.php'; } // add to array $this->widgets[] = array('label' => SpoonFilter::toCamelCase($widgetName), 'value' => $widgetName, 'description' => $description); } } } } }
/** * Set module * * @param string $value The module to use. */ private function setModule($value) { // set property $this->module = (string) $value; // core is a module that contains general stuff, so it has to be allowed if ($this->module !== 'core') { // is this module allowed? if (!BackendAuthentication::isAllowedModule($this->module)) { // set correct headers SpoonHTTP::setHeadersByCode(403); // stop script execution exit; } } // create URL instance, the templatemodifiers need this object $URL = new BackendURL(); // set the module $URL->setModule($this->module); }
/** * Validate the forms */ private function validateForm() { if ($this->frm->isSubmitted()) { $txtEmail = $this->frm->getField('backend_email'); $txtPassword = $this->frm->getField('backend_password'); // required fields if (!$txtEmail->isFilled() || !$txtPassword->isFilled()) { // add error $this->frm->addError('fields required'); // show error $this->tpl->assign('hasError', true); } // invalid form-token? if ($this->frm->getToken() != $this->frm->getField('form_token')->getValue()) { // set a correct header, so bots understand they can't mess with us. if (!headers_sent()) { header('400 Bad Request', true, 400); } } // all fields are ok? if ($txtEmail->isFilled() && $txtPassword->isFilled() && $this->frm->getToken() == $this->frm->getField('form_token')->getValue()) { // try to login the user if (!BackendAuthentication::loginUser($txtEmail->getValue(), $txtPassword->getValue())) { // add error $this->frm->addError('invalid login'); // store attempt in session $current = SpoonSession::exists('backend_login_attempts') ? (int) SpoonSession::get('backend_login_attempts') : 0; // increment and store SpoonSession::set('backend_login_attempts', ++$current); // show error $this->tpl->assign('hasError', true); } } // check sessions if (SpoonSession::exists('backend_login_attempts') && (int) SpoonSession::get('backend_login_attempts') >= 5) { // get previous attempt $previousAttempt = SpoonSession::exists('backend_last_attempt') ? SpoonSession::get('backend_last_attempt') : time(); // calculate timeout $timeout = 5 * (SpoonSession::get('backend_login_attempts') - 4); // too soon! if (time() < $previousAttempt + $timeout) { // sleep untill the user can login again sleep($timeout); // set a correct header, so bots understand they can't mess with us. if (!headers_sent()) { header('503 Service Unavailable', true, 503); } } else { // increment and store SpoonSession::set('backend_last_attempt', time()); } // too many attempts $this->frm->addEditor('too many attempts'); // show error $this->tpl->assign('hasTooManyAttemps', true); $this->tpl->assign('hasError', false); } // no errors in the form? if ($this->frm->isCorrect()) { // cleanup sessions SpoonSession::delete('backend_login_attempts'); SpoonSession::delete('backend_last_attempt'); // create filter with modules which may not be displayed $filter = array('authentication', 'error', 'core'); // get all modules $modules = array_diff(BackendModel::getModules(), $filter); // loop through modules and break on first allowed module foreach ($modules as $module) { if (BackendAuthentication::isAllowedModule($module)) { break; } } // redirect to the correct URL (URL the user was looking for or fallback) $this->redirect($this->getParameter('querystring', 'string', BackendModel::createUrlForAction(null, $module))); } } // is the form submitted if ($this->frmForgotPassword->isSubmitted()) { // backend email $email = $this->frmForgotPassword->getField('backend_email_forgot')->getValue(); // required fields if ($this->frmForgotPassword->getField('backend_email_forgot')->isEmail(BL::err('EmailIsInvalid'))) { // check if there is a user with the given emailaddress if (!BackendUsersModel::existsEmail($email)) { $this->frmForgotPassword->getField('backend_email_forgot')->addError(BL::err('EmailIsUnknown')); } } // no errors in the form? if ($this->frmForgotPassword->isCorrect()) { // generate the key for the reset link and fetch the user ID for this email $key = BackendAuthentication::getEncryptedString($email, uniqid()); // insert the key and the timestamp into the user settings $userId = BackendUsersModel::getIdByEmail($email); $user = new BackendUser($userId); $user->setSetting('reset_password_key', $key); $user->setSetting('reset_password_timestamp', time()); // variables to parse in the e-mail $variables['resetLink'] = SITE_URL . BackendModel::createURLForAction('reset_password') . '&email=' . $email . '&key=' . $key; // send e-mail to user BackendMailer::addEmail(SpoonFilter::ucfirst(BL::msg('ResetYourPasswordMailSubject')), BACKEND_MODULE_PATH . '/layout/templates/mails/reset_password.tpl', $variables, $email); // clear post-values $_POST['backend_email_forgot'] = ''; // show success message $this->tpl->assign('isForgotPasswordSuccess', true); // show form $this->tpl->assign('showForm', true); } else { $this->tpl->assign('showForm', true); } } }
/** * Set module * * @return void * @param string $value The module to use. */ public function setModule($value) { // set property $this->module = (string) $value; // is this module allowed? if (!BackendAuthentication::isAllowedModule($this->module)) { // set correct headers SpoonHTTP::setHeadersByCode(403); // output $fakeAction = new BackendBaseAJAXAction('', ''); $fakeAction->output(BackendBaseAJAXAction::FORBIDDEN, null, 'Module not allowed.'); } // create URL instance, the templatemodifiers need this object $URL = new BackendURL(); // set the module $URL->setModule($this->module); }
/** * Clean the navigation * * @return array * @param array $navigation The navigation array. */ private function cleanup(array $navigation) { // loop elements foreach ($navigation as $key => $value) { // init var $allowedChildren = array(); // error? $allowed = true; // get rid of invalid items if (!isset($value['url']) || !isset($value['label'])) { $allowed = false; } // split up chunks list($module, $action) = explode('/', $value['url']); // no rights for this module? if (!BackendAuthentication::isAllowedModule($module)) { $allowed = false; } // no rights for this action? if (!BackendAuthentication::isAllowedAction($action, $module)) { $allowed = false; } // has children if (isset($value['children']) && is_array($value['children']) && !empty($value['children'])) { // loop children foreach ($value['children'] as $keyB => $valueB) { // error? $allowed = true; // init var $allowedChildrenB = array(); // get rid of invalid items if (!isset($valueB['url']) || !isset($valueB['label'])) { $allowed = false; } // split up chunks list($module, $action) = explode('/', $valueB['url']); // no rights for this module? if (!BackendAuthentication::isAllowedModule($module)) { $allowed = false; } // no rights for this action? if (!BackendAuthentication::isAllowedAction($action, $module)) { $allowed = false; } // has children if (isset($valueB['children']) && is_array($valueB['children']) && !empty($valueB['children'])) { // loop children foreach ($valueB['children'] as $keyC => $valueC) { // error? $allowed = true; // get rid of invalid items if (!isset($valueC['url']) || !isset($valueC['label'])) { $allowed = false; } // split up chunks list($module, $action) = explode('/', $valueC['url']); // no rights for this module? if (!BackendAuthentication::isAllowedModule($module)) { $allowed = false; } // no rights for this action? if (!BackendAuthentication::isAllowedAction($action, $module)) { $allowed = false; } // error occured if (!$allowed) { unset($navigation[$key]['children'][$keyB]['children'][$keyC]); continue; } elseif (!in_array($navigation[$key]['children'][$keyB]['children'][$keyC], $allowedChildrenB)) { $allowedChildrenB[] = $navigation[$key]['children'][$keyB]['children'][$keyC]; } } } // error occured and no allowed children on level B if (!$allowed && empty($allowedChildrenB)) { unset($navigation[$key]['children'][$keyB]); continue; } elseif (!in_array($navigation[$key]['children'][$keyB], $allowedChildren)) { $allowedChildren[] = $navigation[$key]['children'][$keyB]; } // assign new base url for level B if (!empty($allowedChildrenB)) { $navigation[$key]['children'][$keyB]['url'] = $allowedChildrenB[0]['url']; } } } // error occured and no allowed children if (!$allowed && empty($allowedChildren)) { unset($navigation[$key]); continue; } elseif (!empty($allowedChildren)) { // init var $allowed = true; // split up chunks list($module, $action) = explode('/', $allowedChildren[0]['url']); // no rights for this module? if (!BackendAuthentication::isAllowedModule($module)) { $allowed = false; } // no rights for this action? if (!BackendAuthentication::isAllowedAction($action, $module)) { $allowed = false; } // allowed? assign base URL if ($allowed) { $navigation[$key]['url'] = $allowedChildren[0]['url']; } else { // get first child $child = reset($navigation[$key]['children']); // assign base URL $navigation[$key]['url'] = $child['url']; } } } return $navigation; }
/** * Process the querystring * * @return void */ private function processQueryString() { // store the querystring local, so we don't alter it. $queryString = $this->getQueryString(); // find the position of ? (which seperates real URL and GET-parameters) $positionQuestionMark = strpos($queryString, '?'); // remove the GET-chunk from the parameters $processedQueryString = $positionQuestionMark === false ? $queryString : substr($queryString, 0, $positionQuestionMark); // split into chunks, a Backend URL will always look like /<lang>/<module>/<action>(?GET) $chunks = (array) explode('/', trim($processedQueryString, '/')); // check if this is a request for a JS-file $isJS = isset($chunks[1]) && $chunks[1] == 'js.php'; // check if this is a request for a AJAX-file $isAJAX = isset($chunks[1]) && $chunks[1] == 'ajax.php'; // get the language, this will always be in front $language = isset($chunks[1]) && $chunks[1] != '' ? SpoonFilter::getValue($chunks[1], array_keys(BackendLanguage::getWorkingLanguages()), '') : ''; // no language provided? if ($language == '' && !$isJS && !$isAJAX) { // remove first element array_shift($chunks); // redirect to login SpoonHTTP::redirect('/' . NAMED_APPLICATION . '/' . SITE_DEFAULT_LANGUAGE . '/' . implode('/', $chunks)); } // get the module, null will be the default $module = isset($chunks[2]) && $chunks[2] != '' ? $chunks[2] : 'dashboard'; // get the requested action, if it is passed if (isset($chunks[3]) && $chunks[3] != '') { $action = $chunks[3]; } elseif (!$isJS && !$isAJAX) { // build path to the module and define it. This is a constant because we can use this in templates. if (!defined('BACKEND_MODULE_PATH')) { define('BACKEND_MODULE_PATH', BACKEND_MODULES_PATH . '/' . $module); } // check if the config is present? If it isn't present there is a huge problem, so we will stop our code by throwing an error if (!SpoonFile::exists(BACKEND_MODULE_PATH . '/config.php')) { throw new BackendException('The configfile for the module (' . $module . ') can\'t be found.'); } // build config-object-name $configClassName = 'Backend' . SpoonFilter::toCamelCase($module . '_config'); // require the config file, we validated before for existence. require_once BACKEND_MODULE_PATH . '/config.php'; // validate if class exists (aka has correct name) if (!class_exists($configClassName)) { throw new BackendException('The config file is present, but the classname should be: ' . $configClassName . '.'); } // create config-object, the constructor will do some magic $config = new $configClassName($module); // set action $action = $config->getDefaultAction() !== null ? $config->getDefaultAction() : 'index'; } // if it is an request for a JS-file or an AJAX-file we only need the module if ($isJS || $isAJAX) { // set the working language, this is not the interface language BackendLanguage::setWorkingLanguage(SpoonFilter::getGetValue('language', null, SITE_DEFAULT_LANGUAGE)); // set current module $this->setModule(SpoonFilter::getGetValue('module', null, null)); // set action $this->setAction('index'); } else { // the person isn't logged in? or the module doesn't require authentication if (!BackendAuthentication::isLoggedIn() && !BackendAuthentication::isAllowedModule($module)) { // redirect to login SpoonHTTP::redirect('/' . NAMED_APPLICATION . '/' . $language . '/authentication/?querystring=' . urlencode('/' . $this->getQueryString())); } else { // does our user has access to this module? if (!BackendAuthentication::isAllowedModule($module)) { // the user doesn't have access, redirect to error page SpoonHTTP::redirect('/' . NAMED_APPLICATION . '/' . $language . '/error?type=module-not-allowed&querystring=' . urlencode('/' . $this->getQueryString())); } else { // can our user execute the requested action? if (!BackendAuthentication::isAllowedAction($action, $module)) { // the user hasn't access, redirect to error page SpoonHTTP::redirect('/' . NAMED_APPLICATION . '/' . $language . '/error?type=action-not-allowed&querystring=' . urlencode('/' . $this->getQueryString())); } else { // set the working language, this is not the interface language BackendLanguage::setWorkingLanguage($language); // is the user authenticated if (BackendAuthentication::getUser()->isAuthenticated()) { // set interface language based on the user preferences BackendLanguage::setLocale(BackendAuthentication::getUser()->getSetting('interface_language', 'nl')); } else { // init var $interfaceLanguage = BackendModel::getModuleSetting('core', 'default_interface_language'); // override with cookie value if that exists if (SpoonCookie::exists('interface_language') && in_array(SpoonCookie::get('interface_language'), array_keys(BackendLanguage::getInterfaceLanguages()))) { // set interface language based on the perons' cookies $interfaceLanguage = SpoonCookie::get('interface_language'); } // set interface language BackendLanguage::setLocale($interfaceLanguage); } // set current module $this->setModule($module); $this->setAction($action); } } } } }
/** * Process a regular request * * @param string $module The requested module. * @param string $action The requested action. * @param strring $language The requested language. */ private function processRegularRequest($module, $action, $language) { // the person isn't logged in? or the module doesn't require authentication if (!BackendAuthentication::isLoggedIn() && !BackendAuthentication::isAllowedModule($module)) { // redirect to login SpoonHTTP::redirect('/' . NAMED_APPLICATION . '/' . $language . '/authentication/?querystring=' . urlencode('/' . $this->getQueryString())); } else { // does our user has access to this module? if (!BackendAuthentication::isAllowedModule($module)) { // if the module is the dashboard redirect to the first allowed module if ($module == 'dashboard') { // require navigation-file require_once BACKEND_CACHE_PATH . '/navigation/navigation.php'; // loop the navigation to find the first allowed module foreach ($navigation as $key => $value) { // split up chunks list($module, $action) = explode('/', $value['url']); // user allowed? if (BackendAuthentication::isAllowedModule($module)) { // redirect to the page SpoonHTTP::redirect('/' . NAMED_APPLICATION . '/' . $language . '/' . $value['url']); } } } // the user doesn't have access, redirect to error page SpoonHTTP::redirect('/' . NAMED_APPLICATION . '/' . $language . '/error?type=module-not-allowed&querystring=' . urlencode('/' . $this->getQueryString())); } else { // can our user execute the requested action? if (!BackendAuthentication::isAllowedAction($action, $module)) { // the user hasn't access, redirect to error page SpoonHTTP::redirect('/' . NAMED_APPLICATION . '/' . $language . '/error?type=action-not-allowed&querystring=' . urlencode('/' . $this->getQueryString())); } else { // set the working language, this is not the interface language BackendLanguage::setWorkingLanguage($language); $this->setLocale(); $this->setModule($module); $this->setAction($action); } } } }
/** * Load the data */ private function loadData() { // get all modules $modules = BackendModel::getModules(); // get user sequence $userSequence = BackendAuthentication::getUser()->getSetting('dashboard_sequence'); // user sequence does not exist? if (!isset($userSequence)) { // get group ID of user $groupId = BackendAuthentication::getUser()->getGroupId(); // get group preset $userSequence = BackendGroupsModel::getSetting($groupId, 'dashboard_sequence'); } // loop all modules foreach ($modules as $module) { // you have sufficient rights? if (BackendAuthentication::isAllowedModule($module)) { // build pathName $pathName = BACKEND_MODULES_PATH . '/' . $module; // check if the folder exists if (SpoonDirectory::exists($pathName . '/widgets')) { // get widgets $widgets = (array) SpoonFile::getList($pathName . '/widgets', '/(.*)\\.php/i'); // loop widgets foreach ($widgets as $widget) { // require the class require_once $pathName . '/widgets/' . $widget; // init var $widgetName = str_replace('.php', '', $widget); // build classname $className = 'Backend' . SpoonFilter::toCamelCase($module) . 'Widget' . SpoonFilter::toCamelCase($widgetName); // validate if the class exists if (!class_exists($className)) { throw new BackendException('The widgetfile is present, but the classname should be: ' . $className . '.'); } // check if model file exists if (SpoonFile::exists($pathName . '/engine/model.php')) { // require model require_once $pathName . '/engine/model.php'; } // present? $present = isset($userSequence[$module][$widgetName]['present']) ? $userSequence[$module][$widgetName]['present'] : false; // if not present, continue if (!$present) { continue; } // create instance $instance = new $className(); // has rights if (!$instance->isAllowed()) { continue; } // hidden? $hidden = isset($userSequence[$module][$widgetName]['hidden']) ? $userSequence[$module][$widgetName]['hidden'] : false; // execute instance if it is not hidden if (!$hidden) { $instance->execute(); } // user sequence provided? $column = isset($userSequence[$module][$widgetName]['column']) ? $userSequence[$module][$widgetName]['column'] : $instance->getColumn(); $position = isset($userSequence[$module][$widgetName]['position']) ? $userSequence[$module][$widgetName]['position'] : $instance->getPosition(); $title = SpoonFilter::ucfirst(BL::lbl(SpoonFilter::toCamelCase($module))) . ': ' . BL::lbl(SpoonFilter::toCamelCase($widgetName)); $templatePath = $instance->getTemplatePath(); // reset template path if ($templatePath == null) { $templatePath = BACKEND_PATH . '/modules/' . $module . '/layout/widgets/' . $widgetName . '.tpl'; } // build item $item = array('template' => $templatePath, 'module' => $module, 'widget' => $widgetName, 'title' => $title, 'hidden' => $hidden); // add on new position if no position is set or if the position is already used if ($position === null || isset($this->widgets[$column][$position])) { $this->widgets[$column][] = $item; } else { $this->widgets[$column][$position] = $item; } } } } } // sort the widgets foreach ($this->widgets as &$column) { ksort($column); } }