예제 #1
0
 /**
  * Get warnings for active modules
  *
  * @return	array
  */
 public static function getWarnings()
 {
     // init vars
     $warnings = array();
     $activeModules = BackendModel::getModules(true);
     // add warnings
     $warnings = array_merge($warnings, BackendModel::checkSettings());
     // loop active modules
     foreach ($activeModules as $module) {
         // model class
         $class = 'Backend' . SpoonFilter::toCamelCase($module) . 'Model';
         // model file exists
         if (SpoonFile::exists(BACKEND_MODULES_PATH . '/' . $module . '/engine/model.php')) {
             // require class
             require_once BACKEND_MODULES_PATH . '/' . $module . '/engine/model.php';
         }
         // method exists
         if (is_callable(array($class, 'checkSettings'))) {
             // add possible warnings
             $warnings = array_merge($warnings, call_user_func(array($class, 'checkSettings')));
         }
     }
     // return
     return (array) $warnings;
 }
예제 #2
0
 /**
  * Execute the action
  *
  * @return	void
  */
 public function execute()
 {
     // call parent, this will probably add some general CSS/JS or other required files
     parent::execute();
     // user is god?
     $isGod = BackendAuthentication::getUser()->isGod();
     // get possible languages
     if ($isGod) {
         $possibleLanguages = array_unique(array_merge(BL::getWorkingLanguages(), BL::getInterfaceLanguages()));
     } else {
         $possibleLanguages = BL::getWorkingLanguages();
     }
     // get parameters
     $language = SpoonFilter::getPostValue('language', array_keys($possibleLanguages), null, 'string');
     $module = SpoonFilter::getPostValue('module', BackendModel::getModules(false), null, 'string');
     $name = SpoonFilter::getPostValue('name', null, null, 'string');
     $type = SpoonFilter::getPostValue('type', BackendModel::getDB()->getEnumValues('locale', 'type'), null, 'string');
     $application = SpoonFilter::getPostValue('application', array('backend', 'frontend'), null, 'string');
     $value = SpoonFilter::getPostValue('value', null, null, 'string');
     // validate values
     if (trim($value) == '' || $language == '' || $module == '' || $type == '' || $application == '' || $application == 'frontend' && $module != 'core') {
         $error = BL::err('InvalidValue');
     }
     // in case this is a 'act' type, there are special rules concerning possible values
     if ($type == 'act' && !isset($error)) {
         if (!SpoonFilter::isValidAgainstRegexp('|^([a-z0-9\\-\\_])+$|', $value)) {
             $error = BL::err('InvalidActionValue', $this->getModule());
         }
     }
     // no error?
     if (!isset($error)) {
         // build item
         $item['language'] = $language;
         $item['module'] = $module;
         $item['name'] = $name;
         $item['type'] = $type;
         $item['application'] = $application;
         $item['value'] = $value;
         $item['edited_on'] = BackendModel::getUTCDate();
         $item['user_id'] = BackendAuthentication::getUser()->getUserId();
         // does the translation exist?
         if (BackendLocaleModel::existsByName($name, $type, $module, $language, $application)) {
             // add the id to the item
             $item['id'] = (int) BackendLocaleModel::getByName($name, $type, $module, $language, $application);
             // update in db
             BackendLocaleModel::update($item);
         } else {
             // insert in db
             BackendLocaleModel::insert($item);
         }
         // output OK
         $this->output(self::OK);
     } else {
         $this->output(self::ERROR, null, $error);
     }
 }
예제 #3
0
 /**
  * Load the datagrid
  *
  * @return	void
  */
 private function loadDataGrid()
 {
     // init var
     $items = array();
     // get active modules
     $modules = BackendModel::getModules();
     // loop active modules
     foreach ($modules as $module) {
         // check if their is a model-file
         if (SpoonFile::exists(BACKEND_MODULES_PATH . '/' . $module . '/engine/model.php')) {
             // require the model-file
             require_once BACKEND_MODULES_PATH . '/' . $module . '/engine/model.php';
             // build class name
             $className = SpoonFilter::toCamelCase('backend_' . $module . '_model');
             // check if the getByTag-method is available
             if (is_callable(array($className, 'getByTag'))) {
                 // make the call and get the item
                 $moduleItems = (array) call_user_func(array($className, 'getByTag'), $this->id);
                 // loop items
                 foreach ($moduleItems as $row) {
                     // check if needed fields are available
                     if (isset($row['url'], $row['name'], $row['module'])) {
                         // add
                         $items[] = array('module' => ucfirst(BL::lbl(SpoonFilter::toCamelCase($row['module']))), 'name' => $row['name'], 'url' => $row['url']);
                     }
                 }
             }
         }
     }
     // create datagrid
     $this->dgUsage = new BackendDataGridArray($items);
     // disable paging
     $this->dgUsage->setPaging(false);
     // hide columns
     $this->dgUsage->setColumnsHidden(array('url'));
     // set headers
     $this->dgUsage->setHeaderLabels(array('name' => ucfirst(BL::lbl('Title')), 'url' => ''));
     // set url
     $this->dgUsage->setColumnURL('name', '[url]', ucfirst(BL::lbl('Edit')));
     // add use column
     $this->dgUsage->addColumn('edit', null, ucfirst(BL::lbl('Edit')), '[url]', BL::lbl('Edit'));
 }
예제 #4
0
 /**
  * Loops all backend modules, and builds a list of those that have an
  * api.php file in their engine.
  */
 protected function loadModules()
 {
     $modules = BackendModel::getModules();
     foreach ($modules as &$module) {
         /*
          * check if the api.php file exists for this module, and load it so our methods are
          * accessible by the Reflection API.
          */
         $moduleAPIFile = BACKEND_MODULES_PATH . '/' . $module . '/engine/api.php';
         if (!file_exists($moduleAPIFile)) {
             continue;
         }
         require_once $moduleAPIFile;
         // class names of the API file are always based on the name o/t module
         $className = 'Backend' . SpoonFilter::toCamelCase($module) . 'API';
         $methods = get_class_methods($className);
         // we will need the parameters + PHPDoc to generate our textfields
         foreach ($methods as $key => $method) {
             $methods[$key] = array('name' => $method, 'parameters' => $this->loadParameters($className, $method));
         }
         // properly format so an iteration can do the work for us
         $this->modules[] = array('name' => $module, 'methods' => $methods);
     }
 }
예제 #5
0
 /**
  * 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);
                 }
             }
         }
     }
 }
예제 #6
0
    /**
     * Is the given action allowed for the current user
     *
     * @param string $action The action to check for.
     * @param string $module The module wherin the action is located.
     * @return bool
     */
    public static function isAllowedAction($action = null, $module = null)
    {
        // GOD's rule them all!
        if (self::getUser()->isGod()) {
            return true;
        }
        // always allowed actions (yep, hardcoded, because we don't want other people to f**k up)
        $alwaysAllowed = array('dashboard' => array('index' => 7), 'core' => array('generate_url' => 7, 'content_css' => 7), 'error' => array('index' => 7), 'authentication' => array('index' => 7, 'reset_password' => 7, 'logout' => 7));
        // grab the URL from the reference
        $URL = Spoon::get('url');
        $action = $action !== null ? (string) $action : $URL->getAction();
        $module = $module !== null ? (string) $module : $URL->getModule();
        // is this action an action that doesn't require authentication?
        if (isset($alwaysAllowed[$module][$action])) {
            return true;
        }
        // we will cache everything
        if (empty(self::$allowedActions)) {
            // init var
            $db = BackendModel::getDB();
            // get modules
            $modules = BackendModel::getModules();
            // add always allowed
            foreach ($alwaysAllowed as $allowedModule => $actions) {
                $modules[] = $allowedModule;
            }
            // get allowed actions
            $allowedActionsRows = (array) $db->getRecords('SELECT gra.module, gra.action, gra.level
				 FROM users_sessions AS us
				 INNER JOIN users AS u ON us.user_id = u.id
				 INNER JOIN users_groups AS ug ON u.id = ug.user_id
				 INNER JOIN groups_rights_actions AS gra ON ug.group_id = gra.group_id
				 WHERE us.session_id = ? AND us.secret_key = ?', array(SpoonSession::getSessionId(), SpoonSession::get('backend_secret_key')));
            // add all actions and there level
            foreach ($allowedActionsRows as $row) {
                // add if the module is installed
                if (in_array($row['module'], $modules)) {
                    self::$allowedActions[$row['module']][$row['action']] = (int) $row['level'];
                }
            }
        }
        // do we know a level for this action
        if (isset(self::$allowedActions[$module][$action])) {
            // is the level greater than zero? aka: do we have access?
            if ((int) self::$allowedActions[$module][$action] > 0) {
                return true;
            }
        }
        // fallback
        return false;
    }
예제 #7
0
 /**
  * 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);
         }
     }
 }
예제 #8
0
 /**
  * Get the filetree
  *
  * @param string $path The path to get the filetree for.
  * @param array[optional] $tree An array to hold the results.
  * @return array
  */
 private static function getTree($path, array $tree = array())
 {
     // paths that should be ignored
     $ignore = array(BACKEND_CACHE_PATH, BACKEND_CORE_PATH . '/js/ckeditor', BACKEND_CACHE_PATH, BACKEND_CORE_PATH . '/js/ckfinder', FRONTEND_CACHE_PATH);
     // get modules
     $modules = BackendModel::getModules();
     // get the folder listing
     $items = SpoonDirectory::getList($path, true, array('.svn', '.git'));
     // already in the modules?
     if (substr_count($path, '/modules/') > 0) {
         // get last chunk
         $start = strpos($path, '/modules') + 9;
         $end = strpos($path, '/', $start + 1);
         if ($end === false) {
             $moduleName = substr($path, $start);
         } else {
             $moduleName = substr($path, $start, $end - $start);
         }
         // don't go any deeper
         if (!in_array($moduleName, $modules)) {
             return $tree;
         }
     }
     foreach ($items as $item) {
         // if the path should be ignored, skip it
         if (in_array($path . '/' . $item, $ignore)) {
             continue;
         }
         // if the item is a directory we should index it also (recursive)
         if (is_dir($path . '/' . $item)) {
             $tree = self::getTree($path . '/' . $item, $tree);
         } else {
             // if the file has an extension that has to be processed add it into the tree
             if (in_array(SpoonFile::getExtension($item), array('js', 'php', 'tpl'))) {
                 $tree[] = $path . '/' . $item;
             }
         }
     }
     return $tree;
 }
예제 #9
0
 /**
  * Fetch the list of modules that require Google Maps API key
  *
  * @return array
  */
 public static function getModulesThatRequireGoogleMaps()
 {
     // init vars
     $modules = array();
     $installedModules = BackendModel::getModules();
     // loop modules
     foreach ($installedModules as $module) {
         // fetch setting
         $setting = BackendModel::getModuleSetting($module, 'requires_google_maps', false);
         // add to the list
         if ($setting) {
             $modules[] = $module;
         }
     }
     // return
     return $modules;
 }
예제 #10
0
 /**
  * Import a locale XML file.
  *
  * @return	array								Import statistics.
  * @param	SimpleXMLElement $xml				The locale XML.
  * @param	bool[optional] $overwriteConflicts	Should we overwrite when there is a conflict?
  */
 public static function importXML(SimpleXMLElement $xml, $overwriteConflicts = false)
 {
     // init
     $overwriteConflicts = (bool) $overwriteConflicts;
     $statistics = array('total' => 0, 'imported' => 0);
     // possible values
     $possibleApplications = array('frontend', 'backend');
     $possibleModules = BackendModel::getModules(false);
     $possibleLanguages = array('frontend' => array_keys(BL::getWorkingLanguages()), 'backend' => array_keys(BL::getInterfaceLanguages()));
     $possibleTypes = array();
     // types
     $typesShort = (array) BackendModel::getDB()->getEnumValues('locale', 'type');
     foreach ($typesShort as $type) {
         $possibleTypes[$type] = self::getTypeName($type);
     }
     // current locale items (used to check for conflicts)
     $currentLocale = (array) BackendModel::getDB()->getColumn('SELECT CONCAT(application, module, type, language, name) FROM locale');
     // applications
     foreach ($xml as $application => $modules) {
         // application does not exist
         if (!in_array($application, $possibleApplications)) {
             continue;
         }
         // modules
         foreach ($modules as $module => $items) {
             // module does not exist
             if (!in_array($module, $possibleModules)) {
                 continue;
             }
             // items
             foreach ($items as $item) {
                 // attributes
                 $attributes = $item->attributes();
                 $type = SpoonFilter::getValue($attributes['type'], $possibleTypes, '');
                 $name = SpoonFilter::getValue($attributes['name'], null, '');
                 // missing attributes
                 if ($type == '' || $name == '') {
                     continue;
                 }
                 // real type (shortened)
                 $type = array_search($type, $possibleTypes);
                 // translations
                 foreach ($item->translation as $translation) {
                     // statistics
                     $statistics['total']++;
                     // attributes
                     $attributes = $translation->attributes();
                     $language = SpoonFilter::getValue($attributes['language'], $possibleLanguages[$application], '');
                     // language does not exist
                     if ($language == '') {
                         continue;
                     }
                     // the actual translation
                     $translation = (string) $translation;
                     // locale item
                     $locale['user_id'] = BackendAuthentication::getUser()->getUserId();
                     $locale['language'] = $language;
                     $locale['application'] = $application;
                     $locale['module'] = $module;
                     $locale['type'] = $type;
                     $locale['name'] = $name;
                     $locale['value'] = $translation;
                     $locale['edited_on'] = BackendModel::getUTCDate();
                     // found a conflict, overwrite it with the imported translation
                     if ($overwriteConflicts && in_array($application . $module . $type . $language . $name, $currentLocale)) {
                         // statistics
                         $statistics['imported']++;
                         // overwrite
                         BackendModel::getDB(true)->update('locale', $locale, 'application = ? AND module = ? AND type = ? AND language = ? AND name = ?', array($application, $module, $type, $language, $name));
                     } elseif (!in_array($application . $module . $type . $language . $name, $currentLocale)) {
                         // statistics
                         $statistics['imported']++;
                         // insert
                         BackendModel::getDB(true)->insert('locale', $locale);
                     }
                 }
             }
         }
     }
     // rebuild cache
     foreach ($possibleApplications as $application) {
         foreach ($possibleLanguages[$application] as $language) {
             self::buildCache($language, $application);
         }
     }
     // return statistics
     return $statistics;
 }
예제 #11
0
    /**
     * Edit an index
     *
     * @param string $module The module wherin will be searched.
     * @param int $otherId The id of the record.
     * @param  array $fields A key/value pair of fields to index.
     * @param string[optional] $language The frontend language for this entry.
     */
    public static function saveIndex($module, $otherId, array $fields, $language = null)
    {
        // module exists?
        if (!in_array('search', BackendModel::getModules())) {
            return;
        }
        // no fields?
        if (empty($fields)) {
            return;
        }
        // set language
        if (!$language) {
            $language = BL::getWorkingLanguage();
        }
        // get db
        $db = BackendModel::getDB(true);
        // insert search index
        foreach ($fields as $field => $value) {
            // reformat value
            $value = strip_tags((string) $value);
            // update search index
            $db->execute('INSERT INTO search_index (module, other_id, language, field, value, active)
				 VALUES (?, ?, ?, ?, ?, ?)
				 ON DUPLICATE KEY UPDATE value = ?, active = ?', array((string) $module, (int) $otherId, (string) $language, (string) $field, $value, 'Y', $value, 'Y'));
        }
        // invalidate the cache for search
        self::invalidateCache();
    }
예제 #12
0
파일: model.php 프로젝트: richsage/forkcms
 /**
  * Remove an index
  *
  * @param string $module The module wherin will be searched.
  * @param int $otherId The id of the record.
  * @param string[optional] $language The language to use.
  */
 public static function removeIndex($module, $otherId, $language = null)
 {
     // module exists?
     if (!in_array('search', BackendModel::getModules())) {
         return;
     }
     // set language
     if (!$language) {
         $language = BL::getWorkingLanguage();
     }
     // delete indexes
     BackendModel::getDB(true)->delete('search_index', 'module = ? AND other_id = ? AND language = ?', array((string) $module, (int) $otherId, (string) $language));
     // invalidate the cache for search
     self::invalidateCache();
 }
예제 #13
0
 /**
  * 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);
     }
 }