/** * Execute the action * We will build the classname, require the class and call the execute method. */ public function execute() { $this->loadConfig(); // is the requested action possible? If not we throw an exception. We don't redirect because that could trigger a redirect loop if (!in_array($this->getAction(), $this->config->getPossibleActions())) { throw new BackendException('This is an invalid action (' . $this->getAction() . ').'); } // build action-class-name $actionClassName = SpoonFilter::toCamelCase('backend_' . $this->getModule() . '_' . $this->getAction()); // require the config file, we know it is there because we validated it before (possible actions are defined by existance off the file). require_once BACKEND_MODULE_PATH . '/actions/' . $this->getAction() . '.php'; // validate if class exists (aka has correct name) if (!class_exists($actionClassName)) { throw new BackendException('The actionfile is present, but the classname should be: ' . $actionClassName . '.'); } // get working languages $languages = BackendLanguage::getWorkingLanguages(); $workingLanguages = array(); // loop languages and build an array that we can assign foreach ($languages as $abbreviation => $label) { $workingLanguages[] = array('abbr' => $abbreviation, 'label' => $label, 'selected' => $abbreviation == BackendLanguage::getWorkingLanguage()); } // assign the languages $this->tpl->assign('workingLanguages', $workingLanguages); // create action-object $object = new $actionClassName(); $object->execute(); }
/** * Execute the action */ public function execute() { parent::execute(); // add js $this->header->addJS('jstree/jquery.tree.js', null, false, false, false); $this->header->addJS('jstree/lib/jquery.cookie.js', null, false, false, false); $this->header->addJS('jstree/plugins/jquery.tree.cookie.js', null, false, false, false); // add css $this->header->addCSS('/backend/modules/pages/js/jstree/themes/fork/style.css', null, true); // check if the cached files exists if (!SpoonFile::exists(PATH_WWW . '/frontend/cache/navigation/keys_' . BackendLanguage::getWorkingLanguage() . '.php')) { BackendPagesModel::buildCache(BL::getWorkingLanguage()); } if (!SpoonFile::exists(PATH_WWW . '/frontend/cache/navigation/navigation_' . BackendLanguage::getWorkingLanguage() . '.php')) { BackendPagesModel::buildCache(BL::getWorkingLanguage()); } // load the dgRecentlyEdited $this->loadDataGrids(); // parse $this->parse(); // display the page $this->display(); }
/** * Add a JS-file. * If you don't specify a module, the current one will be used * If you set parseThroughPHP to true, the JS will be parsed by PHP (labels and vars will be assignes) * If you set overwritePath to true we expect a full path (It has to start with a /) * * @param string $fileName The file to load. * @param string[optional] $module The module wherin the file is located. * @param bool[optional] $parseThroughPHP Should the file be parsed by PHP? * @param bool[optional] $overwritePath Should we overwrite the full path? * @param bool[optional] $addTimestamp May we add a timestamp for caching purposes? */ public function addJS($fileName, $module = null, $parseThroughPHP = false, $overwritePath = false, $addTimestamp = null) { $fileName = (string) $fileName; $module = (string) ($module !== null) ? $module : $this->URL->getModule(); $parseThroughPHP = (bool) $parseThroughPHP; $overwritePath = (bool) $overwritePath; // validate parameters if ($parseThroughPHP && $overwritePath) { throw new BackendException('parseThroughPHP and overwritePath can\'t be both true.'); } // init var $realPath = ''; // is the given path the real path? if ($overwritePath) { $realPath = $fileName; } elseif ($parseThroughPHP) { $realPath = '/backend/js.php?module=' . $module . '&file=' . $fileName . '&language=' . BackendLanguage::getWorkingLanguage(); } elseif ($module !== 'core') { $realPath = '/backend/modules/' . $module . '/js/' . $fileName; } else { $realPath = '/backend/core/js/' . $fileName; } // add if not already added if (!in_array(array('path' => $realPath, 'add_timestamp' => $addTimestamp), $this->jsFiles)) { $this->jsFiles[] = array('path' => $realPath, 'add_timestamp' => $addTimestamp); } }
/** * Fetch data for this form from the database and reformat to csv rows. */ private function setItems() { // init header labels $lblSessionId = SpoonFilter::ucfirst(BL::lbl('SessionId')); $lblSentOn = SpoonFilter::ucfirst(BL::lbl('SentOn')); $this->columnHeaders = array($lblSessionId, $lblSentOn); // fetch query and parameters list($query, $parameters) = $this->buildQuery(); // get the data $records = (array) BackendModel::getDB()->getRecords($query, $parameters); $data = array(); // reformat data foreach ($records as $row) { // first row of a submission if (!isset($data[$row['data_id']])) { $data[$row['data_id']][$lblSessionId] = $row['session_id']; $data[$row['data_id']][$lblSentOn] = SpoonDate::getDate('Y-m-d H:i:s', $row['sent_on'], BackendLanguage::getWorkingLanguage()); } // value is serialized $value = unserialize($row['value']); // flatten arrays if (is_array($value)) { $value = implode(', ', $value); } // group submissions $data[$row['data_id']][$row['label']] = SpoonFilter::htmlentitiesDecode($value, null, ENT_QUOTES); // add into headers if not yet added if (!in_array($row['label'], $this->columnHeaders)) { $this->columnHeaders[] = $row['label']; } } // reorder data so they are in the correct column foreach ($data as $id => $row) { foreach ($this->columnHeaders as $header) { // submission has this field so add it if (isset($row[$header])) { $this->rows[$id][] = $row[$header]; } else { $this->rows[$id][] = ''; } } } // remove the keys $this->rows = array_values($this->rows); }
/** * Validate the form */ private function validateForm() { // is the form submitted? if ($this->frm->isSubmitted()) { // get the status $status = SpoonFilter::getPostValue('status', array('active', 'draft'), 'active'); // validate redirect $redirectValue = $this->frm->getField('redirect')->getValue(); if ($redirectValue == 'internal') { $this->frm->getField('internal_redirect')->isFilled(BL::err('FieldIsRequired')); } if ($redirectValue == 'external') { $this->frm->getField('external_redirect')->isURL(BL::err('InvalidURL')); } // set callback for generating an unique URL $this->meta->setURLCallback('BackendPagesModel', 'getURL', array($this->record['id'], $this->record['parent_id'], $this->frm->getField('is_action')->getChecked())); // cleanup the submitted fields, ignore fields that were added by hackers $this->frm->cleanupFields(); // validate fields $this->frm->getField('title')->isFilled(BL::err('TitleIsRequired')); // validate meta $this->meta->validate(); // no errors? if ($this->frm->isCorrect()) { // init var $data = null; // build data if ($this->frm->getField('is_action')->isChecked()) { $data['is_action'] = true; } if ($redirectValue == 'internal') { $data['internal_redirect'] = array('page_id' => $this->frm->getField('internal_redirect')->getValue(), 'code' => '301'); } if ($redirectValue == 'external') { $data['external_redirect'] = array('url' => $this->frm->getField('external_redirect')->getValue(), 'code' => '301'); } // build page record $page['id'] = $this->record['id']; $page['user_id'] = BackendAuthentication::getUser()->getUserId(); $page['parent_id'] = $this->record['parent_id']; $page['template_id'] = (int) $this->frm->getField('template_id')->getValue(); $page['meta_id'] = (int) $this->meta->save(); $page['language'] = BackendLanguage::getWorkingLanguage(); $page['type'] = $this->record['type']; $page['title'] = $this->frm->getField('title')->getValue(); $page['navigation_title'] = $this->frm->getField('navigation_title')->getValue() != '' ? $this->frm->getField('navigation_title')->getValue() : $this->frm->getField('title')->getValue(); $page['navigation_title_overwrite'] = $this->frm->getField('navigation_title_overwrite')->isChecked() ? 'Y' : 'N'; $page['hidden'] = $this->frm->getField('hidden')->getValue(); $page['status'] = $status; $page['publish_on'] = BackendModel::getUTCDate(null, $this->record['publish_on']); $page['created_on'] = BackendModel::getUTCDate(null, $this->record['created_on']); $page['edited_on'] = BackendModel::getUTCDate(); $page['allow_move'] = $this->record['allow_move']; $page['allow_children'] = $this->record['allow_children']; $page['allow_edit'] = $this->record['allow_edit']; $page['allow_delete'] = $this->record['allow_delete']; $page['sequence'] = $this->record['sequence']; $page['data'] = $data !== null ? serialize($data) : null; if ($this->isGod) { $page['allow_move'] = in_array('move', (array) $this->frm->getField('allow')->getValue()) ? 'Y' : 'N'; $page['allow_children'] = in_array('children', (array) $this->frm->getField('allow')->getValue()) ? 'Y' : 'N'; $page['allow_edit'] = in_array('edit', (array) $this->frm->getField('allow')->getValue()) ? 'Y' : 'N'; $page['allow_delete'] = in_array('delete', (array) $this->frm->getField('allow')->getValue()) ? 'Y' : 'N'; } // set navigation title if ($page['navigation_title'] == '') { $page['navigation_title'] = $page['title']; } // insert page, store the id, we need it when building the blocks $page['revision_id'] = BackendPagesModel::update($page); // loop blocks foreach ($this->blocksContent as $i => $block) { // add page revision id to blocks $this->blocksContent[$i]['revision_id'] = $page['revision_id']; // validate blocks, only save blocks for valid positions if (!in_array($block['position'], $this->templates[$this->frm->getField('template_id')->getValue()]['data']['names'])) { unset($this->blocksContent[$i]); } } // insert the blocks BackendPagesModel::insertBlocks($this->blocksContent); // trigger an event BackendModel::triggerEvent($this->getModule(), 'after_edit', array('item' => $page)); // save tags BackendTagsModel::saveTags($page['id'], $this->frm->getField('tags')->getValue(), $this->URL->getModule()); // build cache BackendPagesModel::buildCache(BL::getWorkingLanguage()); // active if ($page['status'] == 'active') { // init var $text = ''; // build search-text foreach ($this->blocksContent as $block) { $text .= ' ' . $block['html']; } // add to search index BackendSearchModel::saveIndex($this->getModule(), $page['id'], array('title' => $page['title'], 'text' => $text)); // everything is saved, so redirect to the overview $this->redirect(BackendModel::createURLForAction('edit') . '&id=' . $page['id'] . '&report=edited&var=' . urlencode($page['title']) . '&highlight=row-' . $page['id']); } elseif ($page['status'] == 'draft') { // everything is saved, so redirect to the edit action $this->redirect(BackendModel::createURLForAction('edit') . '&id=' . $page['id'] . '&report=saved-as-draft&var=' . urlencode($page['title']) . '&highlight=row-' . $page['id'] . '&draft=' . $page['revision_id']); } } } }
/** * Convert a var into a URL * syntax: {$var|geturl:<action>[:<module>]} * * @param string[optional] $var A placeholder variable, it will be replaced with the URL. * @param string[optional] $action The action to build the URL for. * @param string[optional] $module The module to build the URL for. * @param string[optional] $suffix A string to append. * @return string */ public static function getURL($var = null, $action = null, $module = null, $suffix = null) { // redefine $var = (string) $var; $action = $action !== null ? (string) $action : null; $module = $module !== null ? (string) $module : null; // build the url return BackendModel::createURLForAction($action, $module, BackendLanguage::getWorkingLanguage()) . $suffix; }
/** * Validate the form */ private function validateForm() { // is the form submitted? if ($this->frm->isSubmitted()) { // cleanup the submitted fields, ignore fields that were added by hackers $this->frm->cleanupFields(); // required fields $this->frm->getField('file')->isFilled(BL::err('FieldIsRequired')); $this->frm->getField('label')->isFilled(BL::err('FieldIsRequired')); $this->frm->getField('format')->isFilled(BL::err('FieldIsRequired')); // validate syntax $syntax = trim(str_replace(array("\n", "\r", ' '), '', $this->frm->getField('format')->getValue())); // init var $table = BackendExtensionsModel::templateSyntaxToArray($syntax); // validate the syntax if ($table === false) { $this->frm->getField('format')->addError(BL::err('InvalidTemplateSyntax')); } else { $html = BackendExtensionsModel::buildTemplateHTML($syntax); $cellCount = 0; $first = true; $errors = array(); // loop rows foreach ($table as $row) { // first row defines the cellcount if ($first) { $cellCount = count($row); } // not same number of cells if (count($row) != $cellCount) { // add error $errors[] = BL::err('InvalidTemplateSyntax'); // stop break; } // doublecheck position names foreach ($row as $cell) { // ignore unavailable space if ($cell != '/') { // not alphanumeric -> error if (!in_array($cell, $this->names)) { $errors[] = sprintf(BL::getError('NonExistingPositionName'), $cell); } elseif (substr_count($html, '"#position-' . $cell . '"') != 1) { $errors[] = BL::err('InvalidTemplateSyntax'); } } } // reset $first = false; } // add errors if ($errors) { $this->frm->getField('format')->addError(implode('<br />', array_unique($errors))); } } // no errors? if ($this->frm->isCorrect()) { // build array $item['id'] = $this->id; $item['theme'] = $this->frm->getField('theme')->getValue(); $item['label'] = $this->frm->getField('label')->getValue(); $item['path'] = 'core/layout/templates/' . $this->frm->getField('file')->getValue(); $item['active'] = $this->frm->getField('active')->getChecked() ? 'Y' : 'N'; $item['data']['format'] = trim(str_replace(array("\n", "\r", ' '), '', $this->frm->getField('format')->getValue())); $item['data']['names'] = $this->names; $item['data']['default_extras'] = $this->extras; $item['data']['default_extras_' . BackendLanguage::getWorkingLanguage()] = $this->extras; // serialize $item['data'] = serialize($item['data']); // if this is the default template make the template active if (BackendModel::getModuleSetting('pages', 'default_template') == $this->record['id']) { $item['active'] = 'Y'; } // if the template is in use we can't de-activate it if (BackendExtensionsModel::isTemplateInUse($item['id'])) { $item['active'] = 'Y'; } // insert the item BackendExtensionsModel::updateTemplate($item); // trigger event BackendModel::triggerEvent($this->getModule(), 'after_edit_template', array('item' => $item)); // set default template if ($this->frm->getField('default')->getChecked() && $item['theme'] == BackendModel::getModuleSetting('core', 'theme', 'core')) { BackendModel::setModuleSetting('pages', 'default_template', $item['id']); } // update all existing pages using this template to add the newly inserted block(s) if (BackendExtensionsModel::isTemplateInUse($item['id'])) { BackendPagesModel::updatePagesTemplates($item['id'], $item['id'], $this->frm->getField('overwrite')->getChecked()); } // everything is saved, so redirect to the overview $this->redirect(BackendModel::createURLForAction('theme_templates') . '&theme=' . $item['theme'] . '&report=edited-template&var=' . urlencode($item['label']) . '&highlight=row-' . $item['id']); } } }
/** * Save the tags * * @param int $otherId The id of the item to tag. * @param mixed $tags The tags for the item. * @param string $module The module wherin the item is located. * @param string[optional] $language The language wherin the tags will be inserted, if not provided the workinglanguage will be used. */ public static function saveTags($otherId, $tags, $module, $language = null) { $otherId = (int) $otherId; $tags = is_array($tags) ? (array) $tags : (string) $tags; $module = (string) $module; $language = $language != null ? (string) $language : BackendLanguage::getWorkingLanguage(); // redefine the tags as an array if (!is_array($tags)) { $tags = (array) explode(',', $tags); } // make sure the list of tags is unique $tags = array_unique($tags); // get db $db = BackendModel::getDB(true); // get current tags for item $currentTags = (array) $db->getPairs('SELECT i.tag, i.id 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 = ?', array($module, $otherId, $language)); // remove old links if (!empty($currentTags)) { $db->delete('modules_tags', 'tag_id IN (' . implode(', ', array_values($currentTags)) . ') AND other_id = ?', $otherId); } // tags provided if (!empty($tags)) { // loop tags foreach ($tags as $key => $tag) { // cleanup $tag = trim($tag); // unset if the tag is empty if ($tag == '') { unset($tags[$key]); } else { $tags[$key] = $tag; } } // get tag ids $tagsAndIds = (array) $db->getPairs('SELECT i.tag, i.id FROM tags AS i WHERE i.tag IN ("' . implode('", "', $tags) . '") AND i.language = ?', array($language)); // loop again and create tags that don't already exist foreach ($tags as $tag) { // doesn' exist yet if (!isset($tagsAndIds[$tag])) { // insert tag $tagsAndIds[$tag] = self::insert($tag, $language); } } // init items to insert $rowsToInsert = array(); // loop again foreach ($tags as $tag) { // get tagId $tagId = (int) $tagsAndIds[$tag]; // not linked before so increment the counter if (!isset($currentTags[$tag])) { $db->execute('UPDATE tags SET number = number + 1 WHERE id = ?', $tagId); } // add to insert array $rowsToInsert[] = array('module' => $module, 'tag_id' => $tagId, 'other_id' => $otherId); } // insert the rows at once if there are items to insert if (!empty($rowsToInsert)) { $db->insert('modules_tags', $rowsToInsert); } } // add to search index BackendSearchModel::editIndex($module, $otherId, array('tags' => implode(' ', (array) $tags)), $language); // decrement number foreach ($currentTags as $tag => $tagId) { // if the tag can't be found in the new tags we lower the number of tags by one if (array_search($tag, $tags) === false) { $db->execute('UPDATE tags SET number = number - 1 WHERE id = ?', $tagId); } } // remove all tags that don't have anything linked $db->delete('tags', 'number = ?', 0); }
/** * Parse & display the page */ protected function parse() { parent::parse(); // parse datagrid $this->tpl->assign('dgBackend', $this->dgBackend->getNumResults() != 0 ? $this->dgBackend->getContent() : false); $this->tpl->assign('dgFrontend', $this->dgFrontend->getNumResults() != 0 ? $this->dgFrontend->getContent() : false); // parse filter $this->tpl->assign('language', BackendLanguage::getWorkingLanguage()); }
/** * Get all the available extra's * * @return array */ public static function getExtrasData() { // get all extras $extras = (array) BackendModel::getDB()->getRecords('SELECT i.id, i.module, i.type, i.label, i.data FROM modules_extras AS i INNER JOIN modules AS m ON i.module = m.name WHERE i.hidden = ? ORDER BY i.module, i.sequence', array('N')); // build array $values = array(); // init var $itemsToRemove = array(); // loop extras foreach ($extras as $id => $row) { // unserialize data $row['data'] = @unserialize($row['data']); // remove items that are not for the current language if (isset($row['data']['language']) && $row['data']['language'] != BackendLanguage::getWorkingLanguage()) { continue; } // set URL if needed if (!isset($row['data']['url'])) { $row['data']['url'] = BackendModel::createURLForAction('index', $row['module']); } // build name $name = SpoonFilter::ucfirst(BL::lbl($row['label'])); if (isset($row['data']['extra_label'])) { $name = $row['data']['extra_label']; } if (isset($row['data']['label_variables'])) { $name = vsprintf($name, $row['data']['label_variables']); } // create modulename $moduleName = SpoonFilter::ucfirst(BL::lbl(SpoonFilter::toCamelCase($row['module']))); // build array if (!isset($values[$row['module']])) { $values[$row['module']] = array('value' => $row['module'], 'name' => $moduleName, 'items' => array()); } // add real extra $values[$row['module']]['items'][$row['type']][$name] = array('id' => $row['id'], 'label' => $name); } // loop foreach ($values as &$row) { if (!empty($row['items']['widget'])) { $row['items']['widget'] = SpoonFilter::arraySortKeys($row['items']['widget']); } if (!empty($row['items']['block'])) { $row['items']['block'] = SpoonFilter::arraySortKeys($row['items']['block']); } } return $values; }
/** * 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[optional] $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 ? BackendLanguage::getWorkingLanguage() : (string) $language; // get db $db = BackendModel::getDB(true); // 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 { $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') { // 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') { // 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; }
/** * Build the HTML for a navigation item * * @return string * @param array $value The current value. * @param string $key The current key. * @param array[optional] $selectedKeys The keys that are selected. * @param int[optional] $startDepth The depth to start from. * @param int[optional] $endDepth The depth to end. * @param int[optional] $currentDepth The depth the method is currently on. */ public function buildHTML(array $value, $key, array $selectedKeys = null, $startDepth = 0, $endDepth = null, $currentDepth = 0) { // return if ($endDepth !== null && $currentDepth >= $endDepth) { return ''; } // needed elements are set? if (isset($value['url']) && isset($value['label'])) { // init some vars $selected = isset($selectedKeys[$currentDepth]) && $selectedKeys[$currentDepth] == $key; $label = ucfirst(BL::lbl($value['label'])); $url = $value['url']; // start HTML $HTML = ''; // que? let's call this piece magic if ($currentDepth >= $startDepth - 1) { // start li if ($selected) { $HTML .= '<li class="selected">' . "\n"; } else { $HTML .= '<li>' . "\n"; } $HTML .= ' <a href="/' . NAMED_APPLICATION . '/' . BackendLanguage::getWorkingLanguage() . '/' . $url . '">' . $label . '</a>' . "\n"; } // children? if ($selected && isset($value['children'])) { // end depth not passed or isn't reached if ($endDepth === null || $currentDepth < $endDepth) { // start ul if needed if ($currentDepth != 0) { $HTML .= '<ul>' . "\n"; } // loop childs foreach ($value['children'] as $subKey => $row) { $HTML .= ' ' . $this->buildHTML($row, $subKey, $selectedKeys, $startDepth, $endDepth, $currentDepth + 1); } // end ul if needed if ($currentDepth != 0) { $HTML .= '</ul>' . "\n"; } } } // end if ($currentDepth >= $startDepth - 1) { $HTML .= '</li>' . "\n"; } } // return return $HTML; }
/** * Ping the known webservices * * @return bool If everything went fine true will be returned, otherwise false. * @param string[optional] $pageOrFeedURL The page/feed that has changed. * @param string[optional] $category An optional category for the site. */ public static function ping($pageOrFeedURL = null, $category = null) { // redefine $siteTitle = self::getModuleSetting('core', 'site_title_' . BackendLanguage::getWorkingLanguage(), SITE_DEFAULT_TITLE); $siteURL = SITE_URL; $pageOrFeedURL = $pageOrFeedURL !== null ? (string) $pageOrFeedURL : null; $category = $category !== null ? (string) $category : null; // get ping services $pingServices = self::getModuleSetting('core', 'ping_services', null); // no ping services available or older than one month ago if ($pingServices === null || $pingServices['date'] < strtotime('-1 month')) { // get ForkAPI-keys $publicKey = self::getModuleSetting('core', 'fork_api_public_key', ''); $privateKey = self::getModuleSetting('core', 'fork_api_private_key', ''); // validate keys if ($publicKey == '' || $privateKey == '') { return false; } // require the class require_once PATH_LIBRARY . '/external/fork_api.php'; // create instance $forkAPI = new ForkAPI($publicKey, $privateKey); // try to get the services try { $pingServices['services'] = $forkAPI->pingGetServices(); $pingServices['date'] = time(); } catch (Exception $e) { // check if the error should not be ignored if (strpos($e->getMessage(), 'Operation timed out') === false && strpos($e->getMessage(), 'Invalid headers') === false) { // in debugmode we want to see the exceptions if (SPOON_DEBUG) { throw $e; } else { return false; } } } // store the services self::setModuleSetting('core', 'ping_services', $pingServices); } // make sure services array will not trigger an error (even if we couldn't load any) if (!isset($pingServices['services']) || !$pingServices['services']) { $pingServices['services'] = array(); } // loop services foreach ($pingServices['services'] as $service) { // create new client $client = new SpoonXMLRPCClient($service['url']); // set some properties $client->setUserAgent('Fork ' . FORK_VERSION); $client->setTimeOut(10); // set port $client->setPort($service['port']); // try to ping try { // extended ping? if ($service['type'] == 'extended') { // no page or feed URL present? if ($pageOrFeedURL === null) { continue; } // build parameters $parameters[] = array('type' => 'string', 'value' => $siteTitle); $parameters[] = array('type' => 'string', 'value' => $siteURL); $parameters[] = array('type' => 'string', 'value' => $pageOrFeedURL); if ($category !== null) { $parameters[] = array('type' => 'string', 'value' => $category); } // make the call $client->execute('weblogUpdates.extendedPing', $parameters); } else { // build parameters $parameters[] = array('type' => 'string', 'value' => $siteTitle); $parameters[] = array('type' => 'string', 'value' => $siteURL); // make the call $client->execute('weblogUpdates.ping', $parameters); } } catch (Exception $e) { // check if the error should not be ignored if (strpos($e->getMessage(), 'Operation timed out') === false && strpos($e->getMessage(), 'Invalid headers') === false) { // in debugmode we want to see the exceptions if (SPOON_DEBUG) { throw $e; } } // next! continue; } } // return return true; }
/** * Get a category id by title * * @return int * @param string $title The title of the category. * @param string[optional] $language The language to use, if not provided we will use the working language. */ public static function getCategoryId($title, $language = null) { // redefine $title = (string) $title; $language = $language !== null ? (string) $language : BackendLanguage::getWorkingLanguage(); // exists? return (int) BackendModel::getDB()->getVar('SELECT i.id FROM blog_categories AS i WHERE i.title = ? AND i.language = ?', array($title, $language)); }
/** * Validate the form * * @return void */ private function validateForm() { // is the form submitted? if ($this->frm->isSubmitted()) { // cleanup the submitted fields, ignore fields that were added by hackers $this->frm->cleanupFields(); // required fields $this->frm->getField('file')->isFilled(BL::err('FieldIsRequired')); $this->frm->getField('label')->isFilled(BL::err('FieldIsRequired')); $this->frm->getField('format')->isFilled(BL::err('FieldIsRequired')); // loop the know fields and validate them for ($i = 1; $i <= $this->frm->getField('num_blocks')->getValue(); $i++) { $this->frm->getField('name_' . $i)->isFilled(BL::err('FieldIsRequired')); } // validate syntax $syntax = trim(str_replace(array("\n", "\r"), '', $this->frm->getField('format')->getValue())); // init var $table = BackendPagesModel::templateSyntaxToArray($syntax); $cellCount = 0; $first = true; // loop rows foreach ($table as $row) { // first row defines the cellcount if ($first) { $cellCount = count($row); } // not same number of cells if (count($row) != $cellCount) { // add error $this->frm->getField('format')->addError(BL::err('InvalidTemplateSyntax')); // stop break; } // reset $first = false; } // no errors? if ($this->frm->isCorrect()) { // build array $item['theme'] = $this->frm->getField('theme')->getValue(); $item['label'] = $this->frm->getField('label')->getValue(); $item['path'] = 'core/layout/templates/' . $this->frm->getField('file')->getValue(); $item['num_blocks'] = $this->frm->getField('num_blocks')->getValue(); $item['active'] = $this->frm->getField('active')->getChecked() ? 'Y' : 'N'; $item['data']['format'] = trim(str_replace(array("\n", "\r"), '', $this->frm->getField('format')->getValue())); // loop fields for ($i = 1; $i <= $this->frm->getField('num_blocks')->getValue(); $i++) { $item['data']['names'][] = $this->frm->getField('name_' . $i)->getValue(); $item['data']['default_extras'][] = $this->frm->getField('type_' . $i)->getValue(); $item['data']['default_extras_' . BackendLanguage::getWorkingLanguage()][] = $this->frm->getField('type_' . $i)->getValue(); } // serialize the data $item['data'] = serialize($item['data']); // insert the item $item['id'] = BackendPagesModel::insertTemplate($item); // trigger event BackendModel::triggerEvent($this->getModule(), 'after_add_template', array('item' => $item)); // set default template if ($this->frm->getField('default')->getChecked() && $item['theme'] == BackendModel::getModuleSetting('core', 'theme', 'core')) { BackendModel::setModuleSetting($this->getModule(), 'default_template', $item['id']); } // everything is saved, so redirect to the overview $this->redirect(BackendModel::createURLForAction('templates') . '&theme=' . $item['theme'] . '&report=added-template&var=' . urlencode($item['label']) . '&highlight=row-' . $item['id']); } } }
/** * Get all data for a given revision. * * @return array * @param string[optional] $language The language to use. */ public static function getLinkList($language = null) { // redefine $language = $language !== null ? (string) $language : BackendLanguage::getWorkingLanguage(); // there is no cache file if (!SpoonFile::exists(FRONTEND_CACHE_PATH . '/navigation/tinymce_link_list_' . $language . '.js')) { return array(); } // read the cache file $cacheFile = SpoonFile::getContent(FRONTEND_CACHE_PATH . '/navigation/tinymce_link_list_' . $language . '.js'); // get the array preg_match('/new Array\\((.*)\\);$/s', $cacheFile, $matches); // no matched if (empty($matches)) { return array(); } // create array $matches = explode('],', str_replace('[', '', $matches[count($matches) - 1])); // init vars $cacheList = array(); // loop list foreach ($matches as $item) { // trim item $item = explode('", "', trim($item, " \n\r\t\"")); // build cache list $cacheList[$item[1]] = $item[0]; } // return cache list return $cacheList; }
/** * Validate the form * * @return void */ private function validateForm() { // is the form submitted? if ($this->frm->isSubmitted()) { // get the status $status = SpoonFilter::getPostValue('status', array('active', 'draft'), 'active'); // validate redirect $redirectValue = $this->frm->getField('redirect')->getValue(); if ($redirectValue == 'internal') { $this->frm->getField('internal_redirect')->isFilled(BL::err('FieldIsRequired')); } if ($redirectValue == 'external') { $this->frm->getField('external_redirect')->isURL(BL::err('InvalidURL')); } // init var $templateId = (int) $this->frm->getField('template_id')->getValue(); // loop blocks in template for ($i = 0; $i < $this->templates[$templateId]['num_blocks']; $i++) { // get the extra id $extraId = (int) $this->frm->getField('block_extra_id_' . $i)->getValue(); // reset some stuff if ($extraId > 0) { // type of block if (isset($this->extras[$extraId]['type']) && $this->extras[$extraId]['type'] == 'block') { // home can't have blocks if ($this->record['id'] == 1) { $this->frm->getField('block_html_' . $i)->addError(BL::err('HomeCantHaveBlocks')); $this->frm->addError(BL::err('HomeCantHaveBlocks')); } } } } // set callback for generating an unique URL $this->meta->setURLCallback('BackendPagesModel', 'getURL', array($this->record['id'], $this->record['parent_id'], $this->frm->getField('is_action')->getChecked())); // cleanup the submitted fields, ignore fields that were edited by hackers $this->frm->cleanupFields(); // validate fields $this->frm->getField('title')->isFilled(BL::err('TitleIsRequired')); // validate meta $this->meta->validate(); // no errors? if ($this->frm->isCorrect()) { // init var $data = null; // build data if ($this->frm->getField('is_action')->isChecked()) { $data['is_action'] = true; } if ($redirectValue == 'internal') { $data['internal_redirect'] = array('page_id' => $this->frm->getField('internal_redirect')->getValue(), 'code' => '301'); } if ($redirectValue == 'external') { $data['external_redirect'] = array('url' => $this->frm->getField('external_redirect')->getValue(), 'code' => '301'); } // build page record $page['id'] = $this->record['id']; $page['user_id'] = BackendAuthentication::getUser()->getUserId(); $page['parent_id'] = $this->record['parent_id']; $page['template_id'] = (int) $this->frm->getField('template_id')->getValue(); $page['meta_id'] = (int) $this->meta->save(); $page['language'] = BackendLanguage::getWorkingLanguage(); $page['type'] = $this->record['type']; $page['title'] = $this->frm->getField('title')->getValue(); $page['navigation_title'] = $this->frm->getField('navigation_title')->getValue() != '' ? $this->frm->getField('navigation_title')->getValue() : $this->frm->getField('title')->getValue(); $page['navigation_title_overwrite'] = $this->frm->getField('navigation_title_overwrite')->isChecked() ? 'Y' : 'N'; $page['hidden'] = $this->frm->getField('hidden')->getValue(); $page['status'] = $status; $page['publish_on'] = BackendModel::getUTCDate(null, $this->record['publish_on']); $page['created_on'] = BackendModel::getUTCDate(null, $this->record['created_on']); $page['edited_on'] = BackendModel::getUTCDate(); $page['allow_move'] = $this->record['allow_move']; $page['allow_children'] = $this->record['allow_children']; $page['allow_edit'] = $this->record['allow_edit']; $page['allow_delete'] = $this->record['allow_delete']; $page['sequence'] = $this->record['sequence']; $page['data'] = $data !== null ? serialize($data) : null; // set navigation title if ($page['navigation_title'] == '') { $page['navigation_title'] = $page['title']; } // insert page, store the id, we need it when building the blocks $page['revision_id'] = BackendPagesModel::update($page); // init var $hasBlock = false; // build blocks $blocks = array(); // no blocks should go to waste; even if the new template has fewer blocks, retain existing content $maxNumBlocks = max(count($this->blocksContent), $this->templates[$page['template_id']]['num_blocks']); // loop blocks in template for ($i = 0; $i < $maxNumBlocks; $i++) { // check if this block has been submitted if (isset($_POST['block_extra_id_' . $i])) { // get the extra id $extraId = (int) $this->frm->getField('block_extra_id_' . $i)->getValue(); // reset some stuff if ($extraId <= 0) { $extraId = null; } // init var $html = null; // extra-type is HTML if ($extraId === null) { // reset vars $extraId = null; $html = (string) $this->frm->getField('block_html_' . $i)->getValue(); } else { // type of block if (isset($this->extras[$extraId]['type']) && $this->extras[$extraId]['type'] == 'block') { // home can't have blocks if ($this->record['id'] == 1) { throw new BackendException('Home can\'t have any blocks.'); } // set error if ($hasBlock) { throw new BackendException('Can\'t add 2 blocks'); } // reset var $hasBlock = true; } } // build block $block = array(); $block['id'] = isset($this->blocksContent[$i]['id']) ? $this->blocksContent[$i]['id'] : BackendPagesModel::getMaximumBlockId() + ($i + 1); $block['revision_id'] = $page['revision_id']; $block['extra_id'] = $extraId; $block['html'] = $html; $block['status'] = 'active'; $block['created_on'] = isset($this->blocksContent[$i]['created_on']) ? BackendModel::getUTCDate(null, $this->blocksContent[$i]['created_on']) : BackendModel::getUTCDate(); $block['edited_on'] = BackendModel::getUTCDate(); } else { $block = $this->blocksContent[$i]; $block['revision_id'] = $page['revision_id']; } // add block $blocks[] = $block; } // update the blocks BackendPagesModel::updateBlocks($blocks, $hasBlock); // trigger an event BackendModel::triggerEvent($this->getModule(), 'after_edit', array('item' => $page)); // save tags BackendTagsModel::saveTags($page['id'], $this->frm->getField('tags')->getValue(), $this->URL->getModule()); // build cache BackendPagesModel::buildCache(BL::getWorkingLanguage()); // active if ($page['status'] == 'active') { // edit search index if (is_callable(array('BackendSearchModel', 'editIndex'))) { // init var $text = ''; // build search-text foreach ($blocks as $block) { $text .= ' ' . $block['html']; } // add BackendSearchModel::editIndex($this->getModule(), $page['id'], array('title' => $page['title'], 'text' => $text)); } // build URL $redirectUrl = BackendModel::createURLForAction('edit') . '&id=' . $page['id'] . '&report=edited&var=' . urlencode($page['title']) . '&highlight=row-' . $page['id']; } elseif ($page['status'] == 'draft') { // everything is saved, so redirect to the edit action $redirectUrl = BackendModel::createURLForAction('edit') . '&id=' . $page['id'] . '&report=saved-as-draft&var=' . urlencode($page['title']) . '&highlight=row-' . $page['id'] . '&draft=' . $page['revision_id']; } // everything is saved, so redirect to the overview $this->redirect($redirectUrl); } } }