/** * Load the data, don't forget to validate the incoming data * * @return void */ private function getData() { // get categories $categories = FrontendBlogModel::getAllCategories(); $possibleCategories = array(); foreach ($categories as $category) { $possibleCategories[$category['url']] = $category['id']; } // requested category $requestedCategory = SpoonFilter::getValue($this->URL->getParameter(1, 'string'), array_keys($possibleCategories), 'false'); // requested page $requestedPage = $this->URL->getParameter('page', 'int', 1); // validate category if ($requestedCategory == 'false') { $this->redirect(FrontendNavigation::getURL(404)); } // set category $this->category = $categories[$possibleCategories[$requestedCategory]]; // set URL and limit $this->pagination['url'] = FrontendNavigation::getURLForBlock('blog', 'category') . '/' . $requestedCategory; $this->pagination['limit'] = FrontendModel::getModuleSetting('blog', 'overview_num_items', 10); // populate count fields in pagination $this->pagination['num_items'] = FrontendBlogModel::getAllForCategoryCount($requestedCategory); $this->pagination['num_pages'] = (int) ceil($this->pagination['num_items'] / $this->pagination['limit']); // redirect if the request page doesn't exists if ($requestedPage > $this->pagination['num_pages'] || $requestedPage < 1) { $this->redirect(FrontendNavigation::getURL(404)); } // populate calculated fields in pagination $this->pagination['requested_page'] = $requestedPage; $this->pagination['offset'] = $this->pagination['requested_page'] * $this->pagination['limit'] - $this->pagination['limit']; // get articles $this->items = FrontendBlogModel::getAllForCategory($requestedCategory, $this->pagination['limit'], $this->pagination['offset']); }
/** * Load necessary data. * * @return void. */ private function loadData() { // get data $this->selectedTheme = $this->getParameter('theme', 'string'); // build available themes $this->availableThemes = BackendModel::getThemes(); // determine selected theme, based upon submitted form or default theme $this->selectedTheme = SpoonFilter::getValue($this->selectedTheme, array_keys($this->availableThemes), BackendModel::getModuleSetting('core', 'theme', 'core')); }
/** * Load the selected theme, falling back to default if none specified. */ private function loadData() { // get data $this->selectedTheme = $this->getParameter('theme', 'string'); // build available themes foreach (Model::getThemes() as $theme) { $this->availableThemes[$theme['value']] = $theme['label']; } // determine selected theme, based upon submitted form or default theme $this->selectedTheme = \SpoonFilter::getValue($this->selectedTheme, array_keys($this->availableThemes), $this->get('fork.settings')->get('Core', 'theme', 'core')); }
/** * Redirect the browser with an optional delay and stop script execution. * * @return void * @param string $URL The URL. * @param int[optional] $code The redirect code. * @param int[optional] $delay A delay, expressed in seconds. */ public static function redirect($URL, $code = 302, $delay = null) { // redefine url $URL = (string) $URL; $code = SpoonFilter::getValue($code, array(301, 302), 302, 'int'); // redirect headers self::setHeadersByCode($code); // delay execution if ($delay !== null) { sleep((int) $delay); } // redirect self::setHeaders("Location: {$URL}"); // stop execution exit; }
public function testGetValue() { // setup $id = '1337'; $type = 'web'; $animal = 'donkey'; $animals = array('1337', 'web', 'donkey'); // perform tests $this->assertEquals(1337, SpoonFilter::getValue($id, null, 0, 'int')); $this->assertEquals('web', SpoonFilter::getValue($type, array('web', 'print'), 'print')); $this->assertEquals('whale', SpoonFilter::getValue($animal, array('whale', 'horse'), 'whale')); $this->assertEquals('donkey', SpoonFilter::getValue($animal, null, 'whale')); $this->assertEquals(array('1337', 'web', 'donkey'), SpoonFilter::getValue($animals, null, null, 'array')); $this->assertEquals(array('1337', 'web'), SpoonFilter::getValue($animals, array('1337', 'web'), array('soep'), 'array')); $this->assertEquals(array('soep'), SpoonFilter::getValue(array('blikken doos'), array('1337', 'web'), array('soep'), 'array')); }
/** * Write an error/custom message to the log. * * @return void * @param string $message The messages that should be logged. * @param string[optional] $type The type of message you want to log, possible values are: error, custom. */ public static function write($message, $type = 'error') { // milliseconds list($milliseconds) = explode(' ', microtime()); $milliseconds = round($milliseconds * 1000, 0); // redefine var $message = date('Y-m-d H:i:s') . ' ' . $milliseconds . 'ms | ' . $message . "\n"; $type = SpoonFilter::getValue($type, array('error', 'custom'), 'error'); // file $file = self::getPath() . '/' . $type . '.log'; // rename if needed if ((int) @filesize($file) >= self::MAX_FILE_SIZE * 1024) { // start new log file SpoonDirectory::move($file, $file . '.' . date('Ymdhis')); } // write content SpoonFile::setContent($file, $message, true, true); }
/** * Checks of the filesize is greater, equal or smaller than the given number + units. * * @return bool * @param int $size The size to use in the check. * @param string[optional] $unit The unit to use. * @param string[optional] $operator The operator to use, possible values are: smaller, equal, greater. * @param string[optional] $error The error message to set. */ public function isFilesize($size, $unit = 'kb', $operator = 'smaller', $error = null) { // file has been uploaded if ($this->isFilled()) { // define size $actualSize = $this->getFileSize($unit, 0); // operator $operator = SpoonFilter::getValue(strtolower($operator), array('smaller', 'equal', 'greater'), 'smaller'); // smaller if ($operator == 'smaller' && $actualSize < $size) { return true; } // equal if ($operator == 'equal' && $actualSize == $size) { return true; } // greater if ($operator == 'greater' && $actualSize > $size) { return true; } } // has error if ($error !== null) { $this->setError($error); } return false; }
/** * Retrieve the days of the week in a specified language. * * @return array An array with all the days in the requested language. * @param string[optional] $language The language to use (available languages can be found in SpoonLocale). * @param bool[optional] $abbreviated Should the days be abbreviated? * @param string[optional] $firstDay First day of the week (available options: monday, sunday). */ public static function getWeekDays($language = 'en', $abbreviated = false, $firstDay = 'monday') { // init vars $language = SpoonFilter::getValue($language, self::$languages, 'en'); $firstDay = SpoonFilter::getValue($firstDay, array('monday', 'sunday'), 'monday'); $locale = array(); // fetch file require 'data/' . $language . '.php'; // data array $days = $abbreviated ? $locale['date']['days']['abbreviated'] : $locale['date']['days']['full']; // in some regions monday is not the first day of the week. if ($firstDay == 'monday') { $sunday = $days['sun']; unset($days['sun']); $days['sun'] = $sunday; } return $days; }
/** * Set column for the widget * * @param string $column Possible values are: left, middle, right. */ protected function setColumn($column) { $allowedColumns = array('left', 'middle', 'right'); $this->column = \SpoonFilter::getValue((string) $column, $allowedColumns, 'left'); }
/** * Set the sorting method. * * @param string[optional] $sortingMethod Set the sorting method that should be used, possible values are: desc, asc. */ public function setSortingMethod($sortingMethod = 'desc') { $aAllowedSortingMethods = array('asc', 'desc'); // set sorting method self::$sortingMethod = SpoonFilter::getValue($sortingMethod, $aAllowedSortingMethods, 'desc'); }
/** * Process the querystring */ private function processQueryString() { // store the querystring local, so we don't alter it. $queryString = $this->getQueryString(); // find the position of ? (which separates real URL and GET-parameters) $positionQuestionMark = mb_strpos($queryString, '?'); // separate the GET-chunk from the parameters $getParameters = ''; if ($positionQuestionMark === false) { $processedQueryString = $queryString; } else { $processedQueryString = mb_substr($queryString, 0, $positionQuestionMark); $getParameters = mb_substr($queryString, $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 AJAX-file $isAJAX = isset($chunks[1]) && $chunks[1] == 'ajax'; // get the language, this will always be in front $language = ''; if (isset($chunks[1]) && $chunks[1] != '') { $language = \SpoonFilter::getValue($chunks[1], array_keys(BackendLanguage::getWorkingLanguages()), ''); } // no language provided? if ($language == '' && !$isAJAX) { // remove first element array_shift($chunks); // redirect to login $this->redirect('/' . NAMED_APPLICATION . '/' . SITE_DEFAULT_LANGUAGE . (empty($chunks) ? '' : '/') . implode('/', $chunks) . $getParameters); } // get the module, null will be the default $module = isset($chunks[2]) && $chunks[2] != '' ? $chunks[2] : 'Dashboard'; $module = \SpoonFilter::toCamelCase($module); // get the requested action, if it is passed if (isset($chunks[3]) && $chunks[3] != '') { $action = \SpoonFilter::toCamelCase($chunks[3]); } elseif (!$isAJAX) { // Check if we can load the config file $configClass = 'Backend\\Modules\\' . $module . '\\Config'; if ($module == 'Core') { $configClass = 'Backend\\Core\\Config'; } try { // when loading a backend url for a module that doesn't exist, without // providing an action, a FatalErrorException occurs, because the config // class we're trying to load doesn't exist. Let's just throw instead, // and catch it immediately. if (!class_exists($configClass)) { throw new Exception('The config class does not exist'); } /** @var BackendBaseConfig $config */ $config = new $configClass($this->getKernel(), $module); // set action $action = $config->getDefaultAction() !== null ? $config->getDefaultAction() : 'Index'; } catch (Exception $ex) { if (BackendModel::getContainer()->getParameter('kernel.debug')) { throw new Exception('The config file for the module (' . $module . ') can\'t be found.'); } else { // @todo don't use redirects for error, we should have something like an invoke method. // build the url $errorUrl = '/' . NAMED_APPLICATION . '/' . $language . '/error?type=action-not-allowed'; // add the querystring, it will be processed by the error-handler $errorUrl .= '&querystring=' . rawurlencode('/' . $this->getQueryString()); // redirect to the error page $this->redirect($errorUrl, 307); } } } // AJAX parameters are passed via GET or POST if ($isAJAX) { $module = isset($_GET['fork']['module']) ? $_GET['fork']['module'] : ''; $action = isset($_GET['fork']['action']) ? $_GET['fork']['action'] : ''; $language = isset($_GET['fork']['language']) ? $_GET['fork']['language'] : SITE_DEFAULT_LANGUAGE; $module = isset($_POST['fork']['module']) ? $_POST['fork']['module'] : $module; $action = isset($_POST['fork']['action']) ? $_POST['fork']['action'] : $action; $language = isset($_POST['fork']['language']) ? $_POST['fork']['language'] : $language; $this->setModule($module); $this->setAction($action); BackendLanguage::setWorkingLanguage($language); } else { $this->processRegularRequest($module, $action, $language); } }
/** * Get templates * * @param string[optional] $theme The theme we want to fetch the templates from. * @return array */ public static function getTemplates($theme = null) { // get db $db = BackendModel::getDB(); // validate input $theme = SpoonFilter::getValue((string) $theme, null, BackendModel::getModuleSetting('core', 'theme', 'core')); // get templates $templates = (array) $db->getRecords('SELECT i.id, i.label, i.path, i.data FROM themes_templates AS i WHERE i.theme = ? AND i.active = ? ORDER BY i.label ASC', array($theme, 'Y'), 'id'); // get extras $extras = (array) self::getExtras(); // init var $half = (int) ceil(count($templates) / 2); $i = 0; // loop templates to unserialize the data foreach ($templates as $key => &$row) { // unserialize $row['data'] = unserialize($row['data']); $row['has_block'] = false; // reset if (isset($row['data']['default_extras_' . BL::getWorkingLanguage()])) { $row['data']['default_extras'] = $row['data']['default_extras_' . BL::getWorkingLanguage()]; } // any extras? if (isset($row['data']['default_extras'])) { // loop extras foreach ($row['data']['default_extras'] as $value) { // store if the module has blocks if (SpoonFilter::isInteger($value) && isset($extras[$value]) && $extras[$value]['type'] == 'block') { $row['has_block'] = true; } } } // validate if (!isset($row['data']['format'])) { throw new BackendException('Invalid template-format.'); } // build template HTML $row['html'] = self::buildTemplateHTML($row['data']['format']); $row['htmlLarge'] = self::buildTemplateHTML($row['data']['format'], true); // add all data as json $row['json'] = json_encode($row); // add the break-element so the templates can be split in 2 columns in the templatechooser if ($i == $half) { $row['break'] = true; } // increment $i++; } return (array) $templates; }
/** * Inserts a record into the mailmotor_campaignmonitor_ids table * * @param string $type The type of the record. * @param string $id The id in CampaignMonitor. * @param string $otherId The id in our tables. * @return string */ public static function insertCampaignMonitorID($type, $id, $otherId) { $type = \SpoonFilter::getValue($type, array('campaign', 'list', 'template'), ''); if ($type == '') { throw new \CampaignMonitorException('No valid CM ID type given (only campaign, list, template).'); } BackendModel::getContainer()->get('database')->insert('mailmotor_campaignmonitor_ids', array('type' => $type, 'cm_id' => $id, 'other_id' => $otherId)); }
/** * Get a preview URL to the specific mailing * * @param int $id The id of the mailing. * @param string $contentType The content-type to set. * @param bool $forCM Will this URL be used in Campaign Monitor? * @return string */ public static function getMailingPreviewURL($id, $contentType = 'html', $forCM = false) { // check input $contentType = \SpoonFilter::getValue($contentType, array('html', 'plain'), 'html'); $forCM = \SpoonFilter::getValue($forCM, array(false, true), false, 'int'); // return the URL return SITE_URL . FrontendNavigation::getURLForBlock('Mailmotor', 'Detail') . '/' . $id . '?type=' . $contentType . ($forCM == 1 ? '&cm=' . $forCM : ''); }
/** * Fetch the time ago as a language dependant sentence. * * @return string String containing a sentence like 'x minutes ago' * @param int $timestamp Timestamp you want to make a sentence of. * @param string[optional] $language Language to use, check SpoonLocale::getAvailableLanguages(). * @param string[optional] $format The format to return if the time passed is greather then a week. */ public static function getTimeAgo($timestamp, $language = 'en', $format = null) { // init vars $timestamp = (int) $timestamp; $language = SpoonFilter::getValue($language, SpoonLocale::getAvailableLanguages(), 'en', 'string'); $locale = array(); // fetch language if (!isset(self::$locales[$language])) { require 'spoon/locale/data/' . $language . '.php'; self::$locales[$language] = $locale; } $locale = self::$locales[$language]; // get seconds between given timestamp and current timestamp $secondsBetween = time() - $timestamp; // calculate years ago $yearsAgo = floor($secondsBetween / (365.242199 * 24 * 60 * 60)); if ($yearsAgo > 1 && $format === null) { return sprintf($locale['time']['YearsAgo'], $yearsAgo); } if ($yearsAgo == 1 && $format === null) { return $locale['time']['YearAgo']; } if ($yearsAgo >= 1 && $format !== null) { return self::getDate($format, $timestamp, $language); } // calculate months ago $monthsAgo = floor($secondsBetween / (365.242199 / 12 * 24 * 60 * 60)); if ($monthsAgo > 1 && $format === null) { return sprintf($locale['time']['MonthsAgo'], $monthsAgo); } if ($monthsAgo == 1 && $format === null) { return $locale['time']['MonthAgo']; } if ($monthsAgo >= 1 && $format !== null) { return self::getDate($format, $timestamp, $language); } // calculate weeks ago $weeksAgo = floor($secondsBetween / (7 * 24 * 60 * 60)); if ($weeksAgo > 1 && $format === null) { return sprintf($locale['time']['WeeksAgo'], $weeksAgo); } if ($weeksAgo == 1 && $format === null) { return $locale['time']['WeekAgo']; } if ($weeksAgo >= 1 && $format !== null) { return self::getDate($format, $timestamp, $language); } // calculate days ago $daysAgo = floor($secondsBetween / (24 * 60 * 60)); if ($daysAgo > 1) { return sprintf($locale['time']['DaysAgo'], $daysAgo); } if ($daysAgo == 1) { return $locale['time']['DayAgo']; } // calculate hours ago $hoursAgo = floor($secondsBetween / (60 * 60)); if ($hoursAgo > 1) { return sprintf($locale['time']['HoursAgo'], $hoursAgo); } if ($hoursAgo == 1) { return $locale['time']['HourAgo']; } // calculate minutes ago $minutesAgo = floor($secondsBetween / 60); if ($minutesAgo > 1) { return sprintf($locale['time']['MinutesAgo'], $minutesAgo); } if ($minutesAgo == 1) { return $locale['time']['MinuteAgo']; } // calculate seconds ago $secondsAgo = floor($secondsBetween); if ($secondsAgo > 1) { return sprintf($locale['time']['SecondsAgo'], $secondsAgo); } if ($secondsAgo <= 1) { return $locale['time']['SecondAgo']; } }
/** * Get tags for an item * * @param string $module The module wherin will be searched. * @param int $otherId The id of the record. * @param string[optional] $type The type of the returnvalue, possible values are: array, string (tags will be joined by ,). * @param string[optional] $language The language to use, of not provided the working language will be used. * @return mixed */ public static function getTags($module, $otherId, $type = 'string', $language = null) { $module = (string) $module; $otherId = (int) $otherId; $type = (string) SpoonFilter::getValue($type, array('string', 'array'), 'string'); $language = $language != null ? (string) $language : BackendLanguage::getWorkingLanguage(); // fetch tags $tags = (array) BackendModel::getDB()->getColumn('SELECT i.tag FROM tags AS i INNER JOIN modules_tags AS mt ON i.id = mt.tag_id WHERE mt.module = ? AND mt.other_id = ? AND i.language = ? ORDER BY i.tag ASC', array($module, $otherId, $language)); // return as an imploded string if ($type == 'string') { return implode(',', $tags); } // return as array return $tags; }
/** * Sets the value to sort. * * @return void * @param string[optional] $value */ public function setSortParameter($value = 'desc') { $this->sortParameter = SpoonFilter::getValue($value, array('asc', 'desc'), 'asc'); }
/** * Sets the email priority level. * * @param int[optional] $level The e-mail's priority level (1-5, where 1 is not urgent). */ public function setPriority($level = 3) { // check input if (!SpoonFilter::isInteger($level) || !SpoonFilter::getValue($level, range(1, 5, 1), 3, 'int')) { throw new SpoonEmailException('No valid priority level given, integer from 1 to 5 required.'); } // store priority level $this->priority = $level; }
/** * Initializes the entire API; extract class+method from the request, call, and output. * * This method exists because the service container needs to be set before * the rest of API functionality gets loaded. */ public function initialize() { self::$content = null; /** * @var Request */ $request = $this->getContainer()->get('request'); // simulate $_REQUEST $parameters = array_merge((array) $request->query->all(), (array) $request->request->all()); $method = $request->get('method'); if ($method == '') { return self::output(self::BAD_REQUEST, array('message' => 'No method-parameter provided.')); } // process method $chunks = (array) explode('.', $method, 2); // validate method if (!isset($chunks[1])) { return self::output(self::BAD_REQUEST, array('message' => 'Invalid method.')); } // camelcase module name $chunks[0] = \SpoonFilter::toCamelCase($chunks[0]); // build the path to the backend API file if ($chunks[0] == 'Core') { $class = 'Backend\\Core\\Engine\\Api'; } else { $class = 'Backend\\Modules\\' . $chunks[0] . '\\Engine\\Api'; } // check if the file is present? If it isn't present there is a problem if (!class_exists($class)) { return self::output(self::BAD_REQUEST, array('message' => 'Invalid method.')); } // build config-object-name $methodName = \SpoonFilter::toCamelCase($chunks[1], '.', true); // validate if the method exists if (!is_callable(array($class, $methodName))) { return self::output(self::BAD_REQUEST, array('message' => 'Invalid method.')); } // call the method try { // init var $arguments = null; // create reflection method $reflectionMethod = new \ReflectionMethod($class, $methodName); $parameterDocumentation = array(); // get data from docs $matches = array(); preg_match_all('/@param[\\s\\t]+(.*)[\\s\\t]+\\$(.*)[\\s\\t]+(.*)$/Um', $reflectionMethod->getDocComment(), $matches); // documentation found if (!empty($matches[0])) { // loop matches foreach ($matches[0] as $i => $row) { // set documentation $parameterDocumentation[$matches[2][$i]] = array('type' => $matches[1][$i], 'optional' => substr_count($matches[1][$i], '[optional]') > 0, 'description' => $matches[3][$i]); } } // loop parameters foreach ($reflectionMethod->getParameters() as $parameter) { // init var $name = $parameter->getName(); // check if the parameter is available if (!$parameter->isOptional() && !isset($parameters[$name])) { return self::output(self::BAD_REQUEST, array('message' => 'No ' . $name . '-parameter provided.')); } // add not-passed arguments if ($parameter->isOptional() && !isset($parameters[$name])) { $arguments[] = $parameter->getDefaultValue(); } elseif (isset($parameterDocumentation[$name]['type'])) { // add argument if we know the type // get default value $defaultValue = null; if ($parameter->isOptional()) { $defaultValue = $parameter->getDefaultValue(); } // add argument $arguments[] = \SpoonFilter::getValue($parameters[$name], null, $defaultValue, $parameterDocumentation[$name]['type']); } else { // fallback $arguments[] = $parameters[$name]; } } // get the return $data = (array) call_user_func_array(array($class, $methodName), (array) $arguments); // output if (self::$content === null) { self::output(self::OK, $data); } } catch (\Exception $e) { // if we are debugging we should see the exceptions if ($this->getContainer()->getParameter('kernel.debug')) { if (isset($parameters['debug']) && $parameters['debug'] == 'false') { // do nothing } else { throw $e; } } // output return self::output(500, array('message' => $e->getMessage())); } }
/** * Get a preview URL to the specific mailing * * @param int $id The id of the mailing. * @param string[optional] $contentType The content-type, possible values are: html, plain. * @param bool[optional] $forCM Is the URL intended for Campaign Monitor. * @return string */ public static function getMailingPreviewURL($id, $contentType = 'html', $forCM = false) { $contentType = SpoonFilter::getValue($contentType, array('html', 'plain'), 'html'); $forCM = SpoonFilter::getValue($forCM, array(false, true), false, 'int'); // return the URL return SITE_URL . BackendModel::getURLForBlock('mailmotor', 'detail') . '/' . $id . '?type=' . $contentType . '&cm=' . $forCM; }
/** * Import a locale XML file. * * @param SimpleXMLElement $xml The locale XML. * @param bool[optional] $overwriteConflicts Should we overwrite when there is a conflict? * @param array[optional] $frontendLanguages The frontend languages to install locale for. * @param array[optional] $backendLanguages The backend languages to install locale for. * @param int[optional] $userId Id of the user these translations should be inserted for. * @param int[optional] $date The date the translation has been inserted. * @return array The import statistics */ public static function importXML(SimpleXMLElement $xml, $overwriteConflicts = false, $frontendLanguages = null, $backendLanguages = null, $userId = null, $date = null) { $overwriteConflicts = (bool) $overwriteConflicts; $statistics = array('total' => 0, 'imported' => 0); // set defaults if necessary // we can't simply use these right away, because this function is also calles by the installer, which does not have Backend-functions if ($frontendLanguages === null) { $frontendLanguages = array_keys(BL::getWorkingLanguages()); } if ($backendLanguages === null) { $backendLanguages = array_keys(BL::getInterfaceLanguages()); } if ($userId === null) { $userId = BackendAuthentication::getUser()->getUserId(); } if ($date === null) { $date = BackendModel::getUTCDate(); } // get database instance (don't use BackendModel::getDB() here because this function will also be called during install) $db = Spoon::get('database'); // possible values $possibleApplications = array('frontend', 'backend'); $possibleModules = (array) $db->getColumn('SELECT m.name FROM modules AS m'); // types $typesShort = (array) $db->getEnumValues('locale', 'type'); foreach ($typesShort as $type) { $possibleTypes[$type] = self::getTypeName($type); } // install English translations anyhow, they're fallback $possibleLanguages = array('frontend' => array_unique(array_merge(array('en'), $frontendLanguages)), 'backend' => array_unique(array_merge(array('en'), $backendLanguages))); // current locale items (used to check for conflicts) $currentLocale = (array) $db->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'] = $userId; $locale['language'] = $language; $locale['application'] = $application; $locale['module'] = $module; $locale['type'] = $type; $locale['name'] = $name; $locale['value'] = $translation; $locale['edited_on'] = $date; // found a conflict, overwrite it with the imported translation if ($overwriteConflicts && in_array($application . $module . $type . $language . $name, $currentLocale)) { // statistics $statistics['imported']++; // overwrite $db->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 $db->insert('locale', $locale); } } } } } // rebuild cache foreach ($possibleApplications as $application) { foreach ($possibleLanguages[$application] as $language) { self::buildCache($language, $application); } } return $statistics; }
/** * Move a page * * @param int $id The id for the page that has to be moved. * @param int $droppedOn The id for the page where to page has been dropped on. * @param string $typeOfDrop The type of drop, possible values are: before, after, inside. * @param string $tree The tree the item is dropped on, possible values are: main, meta, footer, root. * @param string $language The language to use, if not provided we will use the working language. * * @return bool */ public static function move($id, $droppedOn, $typeOfDrop, $tree, $language = null) { $id = (int) $id; $droppedOn = (int) $droppedOn; $typeOfDrop = \SpoonFilter::getValue($typeOfDrop, array('before', 'after', 'inside'), 'inside'); $tree = \SpoonFilter::getValue($tree, array('main', 'meta', 'footer', 'root'), 'inside'); $language = $language === null ? BL::getWorkingLanguage() : (string) $language; // get db $db = BackendModel::getContainer()->get('database'); // reset type of drop for special pages if ($droppedOn == 1) { $typeOfDrop = 'inside'; } if ($droppedOn == 0) { $typeOfDrop = 'inside'; } // get data for pages $page = self::get($id, null, $language); $droppedOnPage = self::get($droppedOn, null, $language); // reset if the drop was on 0 (new meta) if ($droppedOn == 0) { $droppedOnPage = self::get(1, null, $language); } // validate if (empty($page) || empty($droppedOnPage)) { return false; } // calculate new parent for items that should be moved inside if ($droppedOn == 0) { $newParent = 0; } elseif ($typeOfDrop == 'inside') { // check if item allows children if ($droppedOnPage['allow_children'] != 'Y') { return false; } // set new parent to the dropped on page. $newParent = $droppedOnPage['id']; } else { // if the item has to be moved before or after $newParent = $droppedOnPage['parent_id']; } // decide new type if ($droppedOn == 0) { if ($tree == 'footer') { $newType = 'footer'; } else { $newType = 'meta'; } } elseif ($newParent == 0) { $newType = $droppedOnPage['type']; } else { $newType = 'page'; } // calculate new sequence for items that should be moved inside if ($typeOfDrop == 'inside') { // get highest sequence + 1 $newSequence = (int) $db->getVar('SELECT MAX(i.sequence) FROM pages AS i WHERE i.id = ? AND i.language = ? AND i.status = ?', array($newParent, $language, 'active')) + 1; // update $db->update('pages', array('parent_id' => $newParent, 'sequence' => $newSequence, 'type' => $newType), 'id = ? AND language = ? AND status = ?', array($id, $language, 'active')); } elseif ($typeOfDrop == 'before') { // calculate new sequence for items that should be moved before // get new sequence $newSequence = (int) $db->getVar('SELECT i.sequence FROM pages AS i WHERE i.id = ? AND i.language = ? AND i.status = ? LIMIT 1', array($droppedOnPage['id'], $language, 'active')) - 1; // increment all pages with a sequence that is higher or equal to the current sequence; $db->execute('UPDATE pages SET sequence = sequence + 1 WHERE parent_id = ? AND language = ? AND sequence >= ?', array($newParent, $language, $newSequence + 1)); // update $db->update('pages', array('parent_id' => $newParent, 'sequence' => $newSequence, 'type' => $newType), 'id = ? AND language = ? AND status = ?', array($id, $language, 'active')); } elseif ($typeOfDrop == 'after') { // calculate new sequence for items that should be moved after // get new sequence $newSequence = (int) $db->getVar('SELECT i.sequence FROM pages AS i WHERE i.id = ? AND i.language = ? AND i.status = ? LIMIT 1', array($droppedOnPage['id'], $language, 'active')) + 1; // increment all pages with a sequence that is higher then the current sequence; $db->execute('UPDATE pages SET sequence = sequence + 1 WHERE parent_id = ? AND language = ? AND sequence > ?', array($newParent, $language, $newSequence)); // update $db->update('pages', array('parent_id' => $newParent, 'sequence' => $newSequence, 'type' => $newType), 'id = ? AND language = ? AND status = ?', array($id, $language, 'active')); } else { return false; } // get current URL $currentURL = (string) $db->getVar('SELECT url FROM meta AS m WHERE m.id = ?', array($page['meta_id'])); // rebuild url $newURL = self::getURL($currentURL, $id, $newParent, isset($page['data']['is_action']) && $page['data']['is_action'] == 'Y'); // store $db->update('meta', array('url' => $newURL), 'id = ?', array($page['meta_id'])); // return return true; }
/** * Set the form method. * * @return SpoonForm * @param string[optional] $method The method to use, possible values are: get, post. */ public function setMethod($method = 'post') { $this->method = SpoonFilter::getValue((string) $method, array('get', 'post'), 'post'); return $this; }
/** * 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; }
/** * Default constructor * * @return void */ public function __construct() { // simulate $_REQUEST $parameters = array_merge($_GET, $_POST); // validate parameters if (!isset($parameters['method'])) { self::output(self::BAD_REQUEST, array('message' => 'No method-parameter provided.')); } // check GET $method = SpoonFilter::getValue($parameters['method'], null, ''); // validate if ($method == '') { self::output(self::BAD_REQUEST, array('message' => 'No method-parameter provided.')); } // process method $chunks = (array) explode('.', $method, 2); // validate method if (!isset($chunks[1])) { self::output(self::BAD_REQUEST, array('message' => 'Invalid method.')); } // build the path to the backend API file if ($chunks[0] == 'core') { $path = BACKEND_CORE_PATH . '/engine/api.php'; } else { $path = BACKEND_MODULES_PATH . '/' . $chunks[0] . '/engine/api.php'; } // check if the fille is present? If it isn't present there is a problem if (!SpoonFile::exists($path)) { self::output(self::BAD_REQUEST, array('message' => 'Invalid method.')); } // build config-object-name $className = 'Backend' . SpoonFilter::toCamelCase($chunks[0]) . 'API'; $methodName = SpoonFilter::toCamelCase($chunks[1], '.', true); // require the class require_once $path; // validate if the method exists if (!is_callable(array($className, $methodName))) { self::output(self::BAD_REQUEST, array('message' => 'Invalid method.')); } // call the method try { // init var $arguments = null; // create reflection method $reflectionMethod = new ReflectionMethod($className, $methodName); $parameterDocumentation = array(); // get data from docs $matches = array(); preg_match_all('/@param[\\s\\t]+(.*)[\\s\\t]+\\$(.*)[\\s\\t]+(.*)$/Um', $reflectionMethod->getDocComment(), $matches); // documentation found if (!empty($matches[0])) { // loop matches foreach ($matches[0] as $i => $row) { // set documentation $parameterDocumentation[$matches[2][$i]] = array('type' => str_replace('[optional]', '', $matches[1][$i]), 'optional' => substr_count($matches[1][$i], '[optional]') > 0, 'description' => $matches[3][$i]); } } // loop parameters foreach ($reflectionMethod->getParameters() as $parameter) { // init var $name = $parameter->getName(); // check if the parameter is available if (!$parameter->isOptional() && !isset($parameters[$name])) { self::output(self::BAD_REQUEST, array('message' => 'No ' . $name . '-parameter provided.')); } // add not-passed arguments if ($parameter->isOptional() && !isset($parameters[$name])) { $arguments[] = $parameter->getDefaultValue(); } elseif (isset($parameterDocumentation[$name]['type'])) { // get default value $defaultValue = null; if ($parameter->isOptional()) { $defaultValue = $parameter->getDefaultValue(); } // add argument $arguments[] = SpoonFilter::getValue($parameters[$name], null, $defaultValue, $parameterDocumentation[$name]['type']); } else { $arguments[] = $parameters[$name]; } } // get the return $data = (array) call_user_func_array(array($className, $methodName), (array) $arguments); // output self::output(self::OK, $data); } catch (Exception $e) { // if we are debugging we should see the exceptions if (SPOON_DEBUG) { if (isset($parameters['debug']) && $parameters['debug'] == 'false') { // do nothing } else { throw $e; } } // output self::output(500, array('message' => $e->getMessage())); } }
/** * Get templates * * @param string $theme The theme we want to fetch the templates from. * * @return array * @throws Exception */ public static function getTemplates($theme = null) { $db = BackendModel::getContainer()->get('database'); $theme = \SpoonFilter::getValue((string) $theme, null, BackendModel::get('fork.settings')->get('Core', 'theme', 'Core')); $templates = (array) $db->getRecords('SELECT i.id, i.label, i.path, i.data FROM themes_templates AS i WHERE i.theme = ? AND i.active = ? ORDER BY i.label ASC', array($theme, 'Y'), 'id'); $extras = (array) self::getExtras(); $half = (int) ceil(count($templates) / 2); $i = 0; foreach ($templates as &$row) { $row['data'] = unserialize($row['data']); $row['has_block'] = false; // reset if (isset($row['data']['default_extras_' . BL::getWorkingLanguage()])) { $row['data']['default_extras'] = $row['data']['default_extras_' . BL::getWorkingLanguage()]; } // any extras? if (isset($row['data']['default_extras'])) { foreach ($row['data']['default_extras'] as $value) { if (\SpoonFilter::isInteger($value) && isset($extras[$value]) && $extras[$value]['type'] == 'block') { $row['has_block'] = true; } } } // validate if (!isset($row['data']['format'])) { throw new Exception('Invalid template-format.'); } $row['html'] = self::buildTemplateHTML($row['data']['format']); $row['htmlLarge'] = self::buildTemplateHTML($row['data']['format'], true); $row['json'] = json_encode($row); if ($i == $half) { $row['break'] = true; } ++$i; } return (array) $templates; }
/** * Get a parameter specified by the given index * The function will return null if the key is not available * By default we will cast the return value into a string, if you want * something else specify it by passing the wanted type. * * @param mixed $index The index of the parameter. * @param string $type The return type, possible values are: * bool, boolean, int, integer, float, double, string, array. * @param mixed $defaultValue The value that should be returned if the key is not available. * * @return mixed */ public function getParameter($index, $type = 'string', $defaultValue = null) { // does the index exists and isn't this parameter empty if ($this->hasParameter($index)) { return \SpoonFilter::getValue($this->parameters[$index], null, null, $type); } // fallback return $defaultValue; }
/** * Sets the default sorting method for this column. * * @return void * @param string[optional] $sort */ public function setSortingMethod($sort = 'asc') { $this->sortingMethod = SpoonFilter::getValue($sort, array('asc', 'desc'), 'asc'); }
/** * Adds a date field to the form * * @param string $name Name of the element. * @param mixed $value The value for the element. * @param string $type The type (from, till, range) of the datepicker. * @param int $date The date to use. * @param int $date2 The second date for a rangepicker. * @param string $class Class(es) that have to be applied on the element. * @param string $classError Class(es) that have to be applied when an error occurs on the element. * @return FrontendFormDate */ public function addDate($name, $value = null, $type = null, $date = null, $date2 = null, $class = null, $classError = null) { $name = (string) $name; $value = $value !== null ? $value !== '' ? (int) $value : '' : null; $type = \SpoonFilter::getValue($type, array('from', 'till', 'range'), 'none'); $date = $date !== null ? (int) $date : null; $date2 = $date2 !== null ? (int) $date2 : null; $class = $class !== null ? (string) $class : 'inputText inputDate'; $classError = $classError !== null ? (string) $classError : 'inputTextError inputDateError'; // validate if ($type == 'from' && ($date == 0 || $date == null)) { throw new Exception('A date field with type "from" should have a valid date-parameter.'); } if ($type == 'till' && ($date == 0 || $date == null)) { throw new Exception('A date field with type "till" should have a valid date-parameter.'); } if ($type == 'range' && ($date == 0 || $date2 == 0 || $date == null || $date2 == null)) { throw new Exception('A date field with type "range" should have 2 valid date-parameters.'); } // set mask and firstday $mask = Model::get('fork.settings')->get('Core', 'date_format_short'); $firstDay = 1; // build attributes $attributes['data-mask'] = str_replace(array('d', 'm', 'Y', 'j', 'n'), array('dd', 'mm', 'yy', 'd', 'm'), $mask); $attributes['data-firstday'] = $firstDay; $attributes['year'] = date('Y', $value); $attributes['month'] = date('n', $value); $attributes['day'] = date('j', $value); // add extra classes based on type switch ($type) { // start date case 'from': $class .= ' inputDatefieldFrom inputText'; $classError .= ' inputDatefieldFrom'; $attributes['data-startdate'] = date('Y-m-d', $date); break; // end date // end date case 'till': $class .= ' inputDatefieldTill inputText'; $classError .= ' inputDatefieldTill'; $attributes['data-enddate'] = date('Y-m-d', $date); break; // date range // date range case 'range': $class .= ' inputDatefieldRange inputText'; $classError .= ' inputDatefieldRange'; $attributes['data-startdate'] = date('Y-m-d', $date); $attributes['data-enddate'] = date('Y-m-d', $date2); break; // normal date field // normal date field default: $class .= ' inputDatefieldNormal inputText'; $classError .= ' inputDatefieldNormal'; break; } // create a datefield $this->add(new FrontendFormDate($name, $value, $mask, $class, $classError)); // set attributes parent::getField($name)->setAttributes($attributes); // return date field return parent::getField($name); }
/** * Set column for the widget * * @return void * @param string $column Possible values are: left, middle, right. */ protected function setColumn($column) { // allowed values $allowedColumns = array('left', 'middle', 'right'); // redefine $this->column = SpoonFilter::getValue((string) $column, $allowedColumns, 'left'); }