/** * Display a wildcard in the back end * * @return string */ public function generate() { if (TL_MODE == 'BE') { /** @var BackendTemplate|object $objTemplate */ $objTemplate = new \BackendTemplate('be_wildcard'); $objTemplate->wildcard = '### ' . Utf8::strtoupper($GLOBALS['TL_LANG']['FMD']['rssReader'][0]) . ' ###'; $objTemplate->title = $this->headline; $objTemplate->id = $this->id; $objTemplate->link = $this->name; $objTemplate->href = 'contao/main.php?do=themes&table=tl_module&act=edit&id=' . $this->id; return $objTemplate->parse(); } $this->objFeed = new \SimplePie(); $arrUrls = \StringUtil::trimsplit('[\\n\\t ]', trim($this->rss_feed)); if (count($arrUrls) > 1) { $this->objFeed->set_feed_url($arrUrls); } else { $this->objFeed->set_feed_url($arrUrls[0]); } $this->objFeed->set_output_encoding(\Config::get('characterSet')); $this->objFeed->set_cache_location(TL_ROOT . '/system/tmp'); $this->objFeed->enable_cache(false); if ($this->rss_cache > 0) { $this->objFeed->enable_cache(true); $this->objFeed->set_cache_duration($this->rss_cache); } if (!$this->objFeed->init()) { $this->log('Error importing RSS feed "' . $this->rss_feed . '"', __METHOD__, TL_ERROR); return ''; } $this->objFeed->handle_content_type(); return parent::generate(); }
/** * Initialize the object * * @param string $strTable * @param integer $intPid */ public function __construct($strTable, $intPid) { $this->import('Database'); parent::__construct(); $this->strTable = $strTable; $this->intPid = $intPid; // Store the path if it is an editable file if ($strTable == 'tl_files') { $objFile = \FilesModel::findByPk($intPid); if ($objFile !== null && in_array($objFile->extension, \StringUtil::trimsplit(',', strtolower(\Config::get('editableFiles'))))) { $this->strPath = $objFile->path; } } }
/** * Ajax actions that do require a data container object * * @param DataContainer $dc * * @throws NoContentResponseException * @throws ResponseException * @throws BadRequestHttpException */ public function executePostActions(DataContainer $dc) { header('Content-Type: text/html; charset=' . \Config::get('characterSet')); // Bypass any core logic for non-core drivers (see #5957) if (!$dc instanceof DC_File && !$dc instanceof DC_Folder && !$dc instanceof DC_Table) { $this->executePostActionsHook($dc); throw new NoContentResponseException(); } switch ($this->strAction) { // Load nodes of the page structure tree case 'loadStructure': throw new ResponseException($this->convertToResponse($dc->ajaxTreeView($this->strAjaxId, intval(\Input::post('level'))))); // Load nodes of the file manager tree // Load nodes of the file manager tree case 'loadFileManager': throw new ResponseException($this->convertToResponse($dc->ajaxTreeView(\Input::post('folder', true), intval(\Input::post('level'))))); // Load nodes of the page tree // Load nodes of the page tree case 'loadPagetree': $varValue = null; $strField = $dc->field = \Input::post('name'); // Call the load_callback if (is_array($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField]['load_callback'])) { foreach ($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField]['load_callback'] as $callback) { if (is_array($callback)) { $this->import($callback[0]); $varValue = $this->{$callback[0]}->{$callback[1]}($varValue, $dc); } elseif (is_callable($callback)) { $varValue = $callback($varValue, $dc); } } } /** @var PageSelector $strClass */ $strClass = $GLOBALS['BE_FFL']['pageSelector']; /** @var PageSelector $objWidget */ $objWidget = new $strClass($strClass::getAttributesFromDca($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField], $dc->field, $varValue, $strField, $dc->table, $dc)); throw new ResponseException($this->convertToResponse($objWidget->generateAjax($this->strAjaxId, \Input::post('field'), intval(\Input::post('level'))))); // Load nodes of the file tree // Load nodes of the file tree case 'loadFiletree': $varValue = null; $strField = $dc->field = \Input::post('name'); // Call the load_callback if (is_array($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField]['load_callback'])) { foreach ($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField]['load_callback'] as $callback) { if (is_array($callback)) { $this->import($callback[0]); $varValue = $this->{$callback[0]}->{$callback[1]}($varValue, $dc); } elseif (is_callable($callback)) { $varValue = $callback($varValue, $dc); } } } /** @var FileSelector $strClass */ $strClass = $GLOBALS['BE_FFL']['fileSelector']; /** @var FileSelector $objWidget */ $objWidget = new $strClass($strClass::getAttributesFromDca($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField], $dc->field, $varValue, $strField, $dc->table, $dc)); // Load a particular node if (\Input::post('folder', true) != '') { throw new ResponseException($this->convertToResponse($objWidget->generateAjax(\Input::post('folder', true), \Input::post('field'), intval(\Input::post('level'))))); } throw new ResponseException($this->convertToResponse($objWidget->generate())); // Reload the page/file picker // Reload the page/file picker case 'reloadPagetree': case 'reloadFiletree': $intId = \Input::get('id'); $strField = $dc->inputName = \Input::post('name'); // Handle the keys in "edit multiple" mode if (\Input::get('act') == 'editAll') { $intId = preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', $strField); $strField = preg_replace('/(.*)_[0-9a-zA-Z]+$/', '$1', $strField); } $dc->field = $strField; // The field does not exist if (!isset($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField])) { $this->log('Field "' . $strField . '" does not exist in DCA "' . $dc->table . '"', __METHOD__, TL_ERROR); throw new BadRequestHttpException('Bad request'); } $objRow = null; $varValue = null; // Load the value if (\Input::get('act') != 'overrideAll') { if ($GLOBALS['TL_DCA'][$dc->table]['config']['dataContainer'] == 'File') { $varValue = \Config::get($strField); } elseif ($intId > 0 && $this->Database->tableExists($dc->table)) { $objRow = $this->Database->prepare("SELECT * FROM " . $dc->table . " WHERE id=?")->execute($intId); // The record does not exist if ($objRow->numRows < 1) { $this->log('A record with the ID "' . $intId . '" does not exist in table "' . $dc->table . '"', __METHOD__, TL_ERROR); throw new BadRequestHttpException('Bad request'); } $varValue = $objRow->{$strField}; $dc->activeRecord = $objRow; } } // Call the load_callback if (is_array($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField]['load_callback'])) { foreach ($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField]['load_callback'] as $callback) { if (is_array($callback)) { $this->import($callback[0]); $varValue = $this->{$callback[0]}->{$callback[1]}($varValue, $dc); } elseif (is_callable($callback)) { $varValue = $callback($varValue, $dc); } } } // Set the new value $varValue = \Input::post('value', true); $strKey = $this->strAction == 'reloadPagetree' ? 'pageTree' : 'fileTree'; // Convert the selected values if ($varValue != '') { $varValue = \StringUtil::trimsplit("\t", $varValue); // Automatically add resources to the DBAFS if ($strKey == 'fileTree') { foreach ($varValue as $k => $v) { if (\Dbafs::shouldBeSynchronized($v)) { $objFile = \FilesModel::findByPath($v); if ($objFile === null) { $objFile = \Dbafs::addResource($v); } $varValue[$k] = $objFile->uuid; } } } $varValue = serialize($varValue); } /** @var FileTree|PageTree $strClass */ $strClass = $GLOBALS['BE_FFL'][$strKey]; /** @var FileTree|PageTree $objWidget */ $objWidget = new $strClass($strClass::getAttributesFromDca($GLOBALS['TL_DCA'][$dc->table]['fields'][$strField], $dc->inputName, $varValue, $strField, $dc->table, $dc)); throw new ResponseException($this->convertToResponse($objWidget->generate())); // Feature/unfeature an element // Feature/unfeature an element case 'toggleFeatured': if (class_exists($dc->table, false)) { $dca = new $dc->table(); if (method_exists($dca, 'toggleFeatured')) { $dca->toggleFeatured(\Input::post('id'), \Input::post('state') == 1 ? true : false); } } throw new NoContentResponseException(); // Toggle subpalettes // Toggle subpalettes case 'toggleSubpalette': $this->import('BackendUser', 'User'); // Check whether the field is a selector field and allowed for regular users (thanks to Fabian Mihailowitsch) (see #4427) if (!is_array($GLOBALS['TL_DCA'][$dc->table]['palettes']['__selector__']) || !in_array(\Input::post('field'), $GLOBALS['TL_DCA'][$dc->table]['palettes']['__selector__']) || $GLOBALS['TL_DCA'][$dc->table]['fields'][\Input::post('field')]['exclude'] && !$this->User->hasAccess($dc->table . '::' . \Input::post('field'), 'alexf')) { $this->log('Field "' . \Input::post('field') . '" is not an allowed selector field (possible SQL injection attempt)', __METHOD__, TL_ERROR); throw new BadRequestHttpException('Bad request'); } if ($dc instanceof DC_Table) { if (\Input::get('act') == 'editAll') { $this->strAjaxId = preg_replace('/.*_([0-9a-zA-Z]+)$/', '$1', \Input::post('id')); $this->Database->prepare("UPDATE " . $dc->table . " SET " . \Input::post('field') . "='" . (intval(\Input::post('state') == 1) ? 1 : '') . "' WHERE id=?")->execute($this->strAjaxId); if (\Input::post('load')) { echo $dc->editAll($this->strAjaxId, \Input::post('id')); } } else { $this->Database->prepare("UPDATE " . $dc->table . " SET " . \Input::post('field') . "='" . (intval(\Input::post('state') == 1) ? 1 : '') . "' WHERE id=?")->execute($dc->id); if (\Input::post('load')) { throw new ResponseException($this->convertToResponse($dc->edit(false, \Input::post('id')))); } } } elseif ($dc instanceof DC_File) { $val = intval(\Input::post('state') == 1) ? true : false; \Config::persist(\Input::post('field'), $val); if (\Input::post('load')) { \Config::set(\Input::post('field'), $val); throw new ResponseException($this->convertToResponse($dc->edit(false, \Input::post('id')))); } } throw new NoContentResponseException(); // DropZone file upload // DropZone file upload case 'fileupload': $dc->move(); throw new NoContentResponseException(); // HOOK: pass unknown actions to callback functions // HOOK: pass unknown actions to callback functions default: $this->executePostActionsHook($dc); throw new NoContentResponseException(); } }
/** * Converts a palette string to a configuration array. * * @param string $palette The palette string * * @return array The configuration array */ private function explode($palette) { if ('' === (string) $palette) { return []; } $legendCount = 0; $legendMap = []; $groups = StringUtil::trimsplit(';', $palette); foreach ($groups as $group) { $hide = false; $fields = StringUtil::trimsplit(',', $group); if (preg_match('#\\{(.+?)(:hide)?\\}#', $fields[0], $matches)) { $legend = $matches[1]; $hide = count($matches) > 2 && ':hide' === $matches[2]; array_shift($fields); } else { $legend = $legendCount++; } $legendMap[$legend] = ['fields' => $fields, 'hide' => $hide]; } return $legendMap; }
/** * Generate the module */ protected function compile() { // Mark the x and y parameter as used (see #4277) if (isset($_GET['x'])) { \Input::get('x'); \Input::get('y'); } // Trigger the search module from a custom form if (!isset($_GET['keywords']) && \Input::post('FORM_SUBMIT') == 'tl_search') { $_GET['keywords'] = \Input::post('keywords'); $_GET['query_type'] = \Input::post('query_type'); $_GET['per_page'] = \Input::post('per_page'); } $blnFuzzy = $this->fuzzy; $strQueryType = \Input::get('query_type') ?: $this->queryType; $strKeywords = trim(\Input::get('keywords')); $this->Template->uniqueId = $this->id; $this->Template->queryType = $strQueryType; $this->Template->keyword = \StringUtil::specialchars($strKeywords); $this->Template->keywordLabel = $GLOBALS['TL_LANG']['MSC']['keywords']; $this->Template->optionsLabel = $GLOBALS['TL_LANG']['MSC']['options']; $this->Template->search = \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['searchLabel']); $this->Template->matchAll = \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['matchAll']); $this->Template->matchAny = \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['matchAny']); $this->Template->action = ampersand(\Environment::get('indexFreeRequest')); $this->Template->advanced = $this->searchType == 'advanced'; // Redirect page if ($this->jumpTo && ($objTarget = $this->objModel->getRelated('jumpTo')) instanceof PageModel) { /** @var PageModel $objTarget */ $this->Template->action = $objTarget->getFrontendUrl(); } $this->Template->pagination = ''; $this->Template->results = ''; // Execute the search if there are keywords if ($strKeywords != '' && $strKeywords != '*' && !$this->jumpTo) { // Reference page if ($this->rootPage > 0) { $intRootId = $this->rootPage; $arrPages = $this->Database->getChildRecords($this->rootPage, 'tl_page'); array_unshift($arrPages, $this->rootPage); } else { /** @var PageModel $objPage */ global $objPage; $intRootId = $objPage->rootId; $arrPages = $this->Database->getChildRecords($objPage->rootId, 'tl_page'); } // HOOK: add custom logic (see #5223) if (isset($GLOBALS['TL_HOOKS']['customizeSearch']) && is_array($GLOBALS['TL_HOOKS']['customizeSearch'])) { foreach ($GLOBALS['TL_HOOKS']['customizeSearch'] as $callback) { $this->import($callback[0]); $this->{$callback[0]}->{$callback[1]}($arrPages, $strKeywords, $strQueryType, $blnFuzzy); } } // Return if there are no pages if (!is_array($arrPages) || empty($arrPages)) { return; } $strCachePath = str_replace(TL_ROOT . DIRECTORY_SEPARATOR, '', \System::getContainer()->getParameter('kernel.cache_dir')); $arrResult = null; $strChecksum = md5($strKeywords . $strQueryType . $intRootId . $blnFuzzy); $query_starttime = microtime(true); $strCacheFile = $strCachePath . '/contao/search/' . $strChecksum . '.json'; // Load the cached result if (file_exists(TL_ROOT . '/' . $strCacheFile)) { $objFile = new \File($strCacheFile); if ($objFile->mtime > time() - 1800) { $arrResult = json_decode($objFile->getContent(), true); } else { $objFile->delete(); } } // Cache the result if ($arrResult === null) { try { $objSearch = \Search::searchFor($strKeywords, $strQueryType == 'or', $arrPages, 0, 0, $blnFuzzy); $arrResult = $objSearch->fetchAllAssoc(); } catch (\Exception $e) { $this->log('Website search failed: ' . $e->getMessage(), __METHOD__, TL_ERROR); $arrResult = array(); } \File::putContent($strCacheFile, json_encode($arrResult)); } $query_endtime = microtime(true); // Sort out protected pages if (\Config::get('indexProtected') && !BE_USER_LOGGED_IN) { $this->import('FrontendUser', 'User'); foreach ($arrResult as $k => $v) { if ($v['protected']) { if (!FE_USER_LOGGED_IN) { unset($arrResult[$k]); } else { $groups = \StringUtil::deserialize($v['groups']); if (!is_array($groups) || empty($groups) || !count(array_intersect($groups, $this->User->groups))) { unset($arrResult[$k]); } } } } $arrResult = array_values($arrResult); } $count = count($arrResult); $this->Template->count = $count; $this->Template->page = null; $this->Template->keywords = $strKeywords; // No results if ($count < 1) { $this->Template->header = sprintf($GLOBALS['TL_LANG']['MSC']['sEmpty'], $strKeywords); $this->Template->duration = substr($query_endtime - $query_starttime, 0, 6) . ' ' . $GLOBALS['TL_LANG']['MSC']['seconds']; return; } $from = 1; $to = $count; // Pagination if ($this->perPage > 0) { $id = 'page_s' . $this->id; $page = \Input::get($id) !== null ? \Input::get($id) : 1; $per_page = \Input::get('per_page') ?: $this->perPage; // Do not index or cache the page if the page number is outside the range if ($page < 1 || $page > max(ceil($count / $per_page), 1)) { throw new PageNotFoundException('Page not found: ' . \Environment::get('uri')); } $from = ($page - 1) * $per_page + 1; $to = $from + $per_page > $count ? $count : $from + $per_page - 1; // Pagination menu if ($to < $count || $from > 1) { $objPagination = new \Pagination($count, $per_page, \Config::get('maxPaginationLinks'), $id); $this->Template->pagination = $objPagination->generate("\n "); } $this->Template->page = $page; } // Get the results for ($i = $from - 1; $i < $to && $i < $count; $i++) { /** @var FrontendTemplate|object $objTemplate */ $objTemplate = new \FrontendTemplate($this->searchTpl); $objTemplate->url = $arrResult[$i]['url']; $objTemplate->link = $arrResult[$i]['title']; $objTemplate->href = $arrResult[$i]['url']; $objTemplate->title = \StringUtil::specialchars($arrResult[$i]['title']); $objTemplate->class = ($i == $from - 1 ? 'first ' : '') . ($i == $to - 1 || $i == $count - 1 ? 'last ' : '') . ($i % 2 == 0 ? 'even' : 'odd'); $objTemplate->relevance = sprintf($GLOBALS['TL_LANG']['MSC']['relevance'], number_format($arrResult[$i]['relevance'] / $arrResult[0]['relevance'] * 100, 2) . '%'); $objTemplate->filesize = $arrResult[$i]['filesize']; $objTemplate->matches = $arrResult[$i]['matches']; $arrContext = array(); $arrMatches = \StringUtil::trimsplit(',', $arrResult[$i]['matches']); // Get the context foreach ($arrMatches as $strWord) { $arrChunks = array(); preg_match_all('/(^|\\b.{0,' . $this->contextLength . '}\\PL)' . str_replace('+', '\\+', $strWord) . '(\\PL.{0,' . $this->contextLength . '}\\b|$)/ui', $arrResult[$i]['text'], $arrChunks); foreach ($arrChunks[0] as $strContext) { $arrContext[] = ' ' . $strContext . ' '; } } // Shorten the context and highlight all keywords if (!empty($arrContext)) { $objTemplate->context = trim(\StringUtil::substrHtml(implode('…', $arrContext), $this->totalLength)); $objTemplate->context = preg_replace('/(\\PL)(' . implode('|', $arrMatches) . ')(\\PL)/ui', '$1<mark class="highlight">$2</mark>$3', $objTemplate->context); $objTemplate->hasContext = true; } $this->Template->results .= $objTemplate->parse(); } $this->Template->header = vsprintf($GLOBALS['TL_LANG']['MSC']['sResults'], array($from, $to, $count, $strKeywords)); $this->Template->duration = substr($query_endtime - $query_starttime, 0, 6) . ' ' . $GLOBALS['TL_LANG']['MSC']['seconds']; } }
/** * Generate the filter panel and return it as HTML string * * @param integer $intFilterPanel * * @return string */ protected function filterMenu($intFilterPanel) { /** @var AttributeBagInterface $objSessionBag */ $objSessionBag = \System::getContainer()->get('session')->getBag('contao_backend'); $fields = ''; $this->bid = 'tl_buttons_a'; $sortingFields = array(); $session = $objSessionBag->all(); $filter = $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['mode'] == 4 ? $this->strTable . '_' . CURRENT_ID : $this->strTable; // Get the sorting fields foreach ($GLOBALS['TL_DCA'][$this->strTable]['fields'] as $k => $v) { if (intval($v['filter']) == $intFilterPanel) { $sortingFields[] = $k; } } // Return if there are no sorting fields if (empty($sortingFields)) { return ''; } // Set filter from user input if (\Input::post('FORM_SUBMIT') == 'tl_filters') { foreach ($sortingFields as $field) { if (\Input::post($field, true) != 'tl_' . $field) { $session['filter'][$filter][$field] = \Input::post($field, true); } else { unset($session['filter'][$filter][$field]); } } $objSessionBag->replace($session); } else { foreach ($sortingFields as $field) { if (isset($session['filter'][$filter][$field])) { // Sort by day if (in_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'], array(5, 6))) { if ($session['filter'][$filter][$field] == '') { $this->procedure[] = $field . "=''"; } else { $objDate = new \Date($session['filter'][$filter][$field]); $this->procedure[] = $field . ' BETWEEN ? AND ?'; $this->values[] = $objDate->dayBegin; $this->values[] = $objDate->dayEnd; } } elseif (in_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'], array(7, 8))) { if ($session['filter'][$filter][$field] == '') { $this->procedure[] = $field . "=''"; } else { $objDate = new \Date($session['filter'][$filter][$field]); $this->procedure[] = $field . ' BETWEEN ? AND ?'; $this->values[] = $objDate->monthBegin; $this->values[] = $objDate->monthEnd; } } elseif (in_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'], array(9, 10))) { if ($session['filter'][$filter][$field] == '') { $this->procedure[] = $field . "=''"; } else { $objDate = new \Date($session['filter'][$filter][$field]); $this->procedure[] = $field . ' BETWEEN ? AND ?'; $this->values[] = $objDate->yearBegin; $this->values[] = $objDate->yearEnd; } } elseif ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['eval']['multiple']) { // CSV lists (see #2890) if (isset($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['eval']['csv'])) { $this->procedure[] = $this->Database->findInSet('?', $field, true); $this->values[] = $session['filter'][$filter][$field]; } else { $this->procedure[] = $field . ' LIKE ?'; $this->values[] = '%"' . $session['filter'][$filter][$field] . '"%'; } } else { $this->procedure[] = $field . '=?'; $this->values[] = $session['filter'][$filter][$field]; } } } } // Add sorting options foreach ($sortingFields as $cnt => $field) { $arrValues = array(); $arrProcedure = array(); if ($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['mode'] == 4) { $arrProcedure[] = 'pid=?'; $arrValues[] = CURRENT_ID; } if (!$this->treeView && !empty($this->root) && is_array($this->root)) { $arrProcedure[] = "id IN(" . implode(',', array_map('intval', $this->root)) . ")"; } // Check for a static filter (see #4719) if (!empty($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['filter']) && is_array($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['filter'])) { foreach ($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['filter'] as $fltr) { $arrProcedure[] = $fltr[0]; $arrValues[] = $fltr[1]; } } // Support empty ptable fields if ($GLOBALS['TL_DCA'][$this->strTable]['config']['dynamicPtable']) { $arrProcedure[] = $this->ptable == 'tl_article' ? "(ptable=? OR ptable='')" : "ptable=?"; $arrValues[] = $this->ptable; } $what = $field; // Optimize the SQL query (see #8485) if (isset($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'])) { // Sort by day if (in_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'], array(5, 6))) { $what = "UNIX_TIMESTAMP(FROM_UNIXTIME({$field} , '%%Y-%%m-%%d')) AS {$field}"; } elseif (in_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'], array(7, 8))) { $what = "UNIX_TIMESTAMP(FROM_UNIXTIME({$field} , '%%Y-%%m-01')) AS {$field}"; } elseif (in_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'], array(9, 10))) { $what = "UNIX_TIMESTAMP(FROM_UNIXTIME({$field} , '%%Y-01-01')) AS {$field}"; } } $objFields = $this->Database->prepare("SELECT DISTINCT " . $what . " FROM " . $this->strTable . (is_array($arrProcedure) && strlen($arrProcedure[0]) ? ' WHERE ' . implode(' AND ', $arrProcedure) : ''))->execute($arrValues); // Begin select menu $fields .= ' <select name="' . $field . '" id="' . $field . '" class="tl_select' . (isset($session['filter'][$filter][$field]) ? ' active' : '') . '"> <option value="tl_' . $field . '">' . (is_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['label']) ? $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['label'][0] : $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['label']) . '</option> <option value="tl_' . $field . '">---</option>'; if ($objFields->numRows) { $options = $objFields->fetchEach($field); // Sort by day if (in_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'], array(5, 6))) { $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'] == 6 ? rsort($options) : sort($options); foreach ($options as $k => $v) { if ($v == '') { $options[$v] = '-'; } else { $options[$v] = \Date::parse(\Config::get('dateFormat'), $v); } unset($options[$k]); } } elseif (in_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'], array(7, 8))) { $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'] == 8 ? rsort($options) : sort($options); foreach ($options as $k => $v) { if ($v == '') { $options[$v] = '-'; } else { $options[$v] = date('Y-m', $v); $intMonth = date('m', $v) - 1; if (isset($GLOBALS['TL_LANG']['MONTHS'][$intMonth])) { $options[$v] = $GLOBALS['TL_LANG']['MONTHS'][$intMonth] . ' ' . date('Y', $v); } } unset($options[$k]); } } elseif (in_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'], array(9, 10))) { $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'] == 10 ? rsort($options) : sort($options); foreach ($options as $k => $v) { if ($v == '') { $options[$v] = '-'; } else { $options[$v] = date('Y', $v); } unset($options[$k]); } } // Manual filter if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['eval']['multiple']) { $moptions = array(); // TODO: find a more effective solution foreach ($options as $option) { // CSV lists (see #2890) if (isset($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['eval']['csv'])) { $doptions = \StringUtil::trimsplit($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['eval']['csv'], $option); } else { $doptions = \StringUtil::deserialize($option); } if (is_array($doptions)) { $moptions = array_merge($moptions, $doptions); } } $options = $moptions; } $options = array_unique($options); $options_callback = array(); // Call the options_callback if ((is_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['options_callback']) || is_callable($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['options_callback'])) && !$GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['reference']) { if (is_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['options_callback'])) { $strClass = $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['options_callback'][0]; $strMethod = $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['options_callback'][1]; $this->import($strClass); $options_callback = $this->{$strClass}->{$strMethod}($this); } elseif (is_callable($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['options_callback'])) { $options_callback = $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['options_callback']($this); } // Sort options according to the keys of the callback array $options = array_intersect(array_keys($options_callback), $options); } $options_sorter = array(); $blnDate = in_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'], array(5, 6, 7, 8, 9, 10)); // Options foreach ($options as $kk => $vv) { $value = $blnDate ? $kk : $vv; // Options callback if (!empty($options_callback) && is_array($options_callback)) { $vv = $options_callback[$vv]; } elseif (isset($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['foreignKey'])) { $key = explode('.', $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['foreignKey'], 2); $objParent = $this->Database->prepare("SELECT " . $key[1] . " AS value FROM " . $key[0] . " WHERE id=?")->limit(1)->execute($vv); if ($objParent->numRows) { $vv = $objParent->value; } } elseif ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['eval']['isBoolean'] || $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['inputType'] == 'checkbox' && !$GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['eval']['multiple']) { $vv = $vv != '' ? $GLOBALS['TL_LANG']['MSC']['yes'] : $GLOBALS['TL_LANG']['MSC']['no']; } elseif ($field == 'pid') { $this->loadDataContainer($this->ptable); $showFields = $GLOBALS['TL_DCA'][$this->ptable]['list']['label']['fields']; if (!$showFields[0]) { $showFields[0] = 'id'; } $objShowFields = $this->Database->prepare("SELECT " . $showFields[0] . " FROM " . $this->ptable . " WHERE id=?")->limit(1)->execute($vv); if ($objShowFields->numRows) { $vv = $objShowFields->{$showFields[0]}; } } $option_label = ''; // Use reference array if (isset($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['reference'])) { $option_label = is_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['reference'][$vv]) ? $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['reference'][$vv][0] : $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['reference'][$vv]; } elseif ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['eval']['isAssociative'] || array_is_assoc($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['options'])) { $option_label = $GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['options'][$vv]; } // No empty options allowed if (!strlen($option_label)) { $option_label = $vv ?: '-'; } $options_sorter[' <option value="' . \StringUtil::specialchars($value) . '"' . (isset($session['filter'][$filter][$field]) && $value == $session['filter'][$filter][$field] ? ' selected="selected"' : '') . '>' . $option_label . '</option>'] = Utf8::toAscii($option_label); } // Sort by option values if (!$blnDate) { natcasesort($options_sorter); if (in_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$field]['flag'], array(2, 4, 12))) { $options_sorter = array_reverse($options_sorter, true); } } $fields .= "\n" . implode("\n", array_keys($options_sorter)); } // End select menu $fields .= ' </select> '; // Force a line-break after six elements (see #3777) if (($cnt + 1) % 6 == 0) { $fields .= '<br>'; } } return ' <div class="tl_filter tl_subpanel"> <strong>' . $GLOBALS['TL_LANG']['MSC']['filter'] . ':</strong> ' . $fields . ' </div>'; }
/** * Return an array that can be used by the database installer * * @return array The data array */ public function getDbInstallerArray() { $return = array(); // Fields foreach ($this->arrFields as $k => $v) { $return['TABLE_FIELDS'][$k] = '`' . $k . '` ' . $v; } $quote = function ($item) { return '`' . $item . '`'; }; // Keys foreach ($this->arrKeys as $k => $v) { // Handle multi-column indexes (see #5556) if (strpos($k, ',') !== false) { $f = array_map($quote, \StringUtil::trimsplit(',', $k)); $k = str_replace(',', '_', $k); } else { $f = array($quote($k)); } // Handle key lengths (see #221) if (preg_match('/\\([0-9]+\\)/', $v)) { list($v, $length) = explode('(', rtrim($v, ')')); $f = array($quote($k) . '(' . $length . ')'); } if ($v == 'primary') { $k = 'PRIMARY'; $v = 'PRIMARY KEY (' . implode(', ', $f) . ')'; } elseif ($v == 'index') { $v = 'KEY `' . $k . '` (' . implode(', ', $f) . ')'; } else { $v = strtoupper($v) . ' KEY `' . $k . '` (' . implode(', ', $f) . ')'; } $return['TABLE_CREATE_DEFINITIONS'][$k] = $v; } $return['TABLE_OPTIONS'] = ''; // Options foreach ($this->arrMeta as $k => $v) { if ($k == 'engine') { $return['TABLE_OPTIONS'] .= ' ENGINE=' . $v; } elseif ($k == 'charset') { $return['TABLE_OPTIONS'] .= ' DEFAULT CHARSET=' . $v; } } return $return; }
/** * Auto-generate a form to edit the local configuration file * * @return string */ public function edit() { $return = ''; $ajaxId = null; if (\Environment::get('isAjaxRequest')) { $ajaxId = func_get_arg(1); } // Build an array from boxes and rows $this->strPalette = $this->getPalette(); $boxes = \StringUtil::trimsplit(';', $this->strPalette); $legends = array(); if (!empty($boxes)) { foreach ($boxes as $k => $v) { $boxes[$k] = \StringUtil::trimsplit(',', $v); foreach ($boxes[$k] as $kk => $vv) { if (preg_match('/^\\[.*\\]$/', $vv)) { continue; } if (preg_match('/^\\{.*\\}$/', $vv)) { $legends[$k] = substr($vv, 1, -1); unset($boxes[$k][$kk]); } elseif ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$vv]['exclude'] || !is_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$vv])) { unset($boxes[$k][$kk]); } } // Unset a box if it does not contain any fields if (empty($boxes[$k])) { unset($boxes[$k]); } } /** @var AttributeBagInterface $objSessionBag */ $objSessionBag = \System::getContainer()->get('session')->getBag('contao_backend'); // Render boxes $class = 'tl_tbox'; $fs = $objSessionBag->get('fieldset_states'); $blnIsFirst = true; foreach ($boxes as $k => $v) { $strAjax = ''; $blnAjax = false; $key = ''; $cls = ''; $legend = ''; if (isset($legends[$k])) { list($key, $cls) = explode(':', $legends[$k]); $legend = "\n" . '<legend onclick="AjaxRequest.toggleFieldset(this, \'' . $key . '\', \'' . $this->strTable . '\')">' . (isset($GLOBALS['TL_LANG'][$this->strTable][$key]) ? $GLOBALS['TL_LANG'][$this->strTable][$key] : $key) . '</legend>'; } if (isset($fs[$this->strTable][$key])) { $class .= $fs[$this->strTable][$key] ? '' : ' collapsed'; } else { $class .= $cls && $legend ? ' ' . $cls : ''; } $return .= "\n\n" . '<fieldset' . ($key ? ' id="pal_' . $key . '"' : '') . ' class="' . $class . ($legend ? '' : ' nolegend') . '">' . $legend; // Build rows of the current box foreach ($v as $vv) { if ($vv == '[EOF]') { if ($blnAjax && \Environment::get('isAjaxRequest')) { return $strAjax . '<input type="hidden" name="FORM_FIELDS[]" value="' . \StringUtil::specialchars($this->strPalette) . '">'; } $blnAjax = false; $return .= "\n " . '</div>'; continue; } if (preg_match('/^\\[.*\\]$/', $vv)) { $thisId = 'sub_' . substr($vv, 1, -1); $blnAjax = $ajaxId == $thisId && \Environment::get('isAjaxRequest') ? true : false; $return .= "\n " . '<div id="' . $thisId . '">'; continue; } $this->strField = $vv; $this->strInputName = $vv; $this->varValue = \Config::get($this->strField); // Handle entities if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]['inputType'] == 'text' || $GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]['inputType'] == 'textarea') { if ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]['eval']['multiple']) { $this->varValue = \StringUtil::deserialize($this->varValue); } if (!is_array($this->varValue)) { $this->varValue = htmlspecialchars($this->varValue); } else { foreach ($this->varValue as $k => $v) { $this->varValue[$k] = htmlspecialchars($v); } } } // Autofocus the first field if ($blnIsFirst && $GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]['inputType'] == 'text') { $GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]['eval']['autofocus'] = 'autofocus'; $blnIsFirst = false; } // Call load_callback if (is_array($GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]['load_callback'])) { foreach ($GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]['load_callback'] as $callback) { if (is_array($callback)) { $this->import($callback[0]); $this->varValue = $this->{$callback[0]}->{$callback[1]}($this->varValue, $this); } elseif (is_callable($callback)) { $this->varValue = $callback($this->varValue, $this); } } } // Build row $blnAjax ? $strAjax .= $this->row() : ($return .= $this->row()); } $class = 'tl_box'; $return .= "\n" . '</fieldset>'; } } $this->import('Files'); // Check whether the target file is writeable if (!$this->Files->is_writeable('system/config/localconfig.php')) { \Message::addError(sprintf($GLOBALS['TL_LANG']['ERR']['notWriteable'], 'system/config/localconfig.php')); } // Submit buttons $arrButtons = array(); $arrButtons['save'] = '<button type="submit" name="save" id="save" class="tl_submit" accesskey="s">' . $GLOBALS['TL_LANG']['MSC']['save'] . '</button>'; $arrButtons['saveNclose'] = '<button type="submit" name="saveNclose" id="saveNclose" class="tl_submit" accesskey="c">' . $GLOBALS['TL_LANG']['MSC']['saveNclose'] . '</button>'; // Call the buttons_callback (see #4691) if (is_array($GLOBALS['TL_DCA'][$this->strTable]['edit']['buttons_callback'])) { foreach ($GLOBALS['TL_DCA'][$this->strTable]['edit']['buttons_callback'] as $callback) { if (is_array($callback)) { $this->import($callback[0]); $arrButtons = $this->{$callback[0]}->{$callback[1]}($arrButtons, $this); } elseif (is_callable($callback)) { $arrButtons = $callback($arrButtons, $this); } } } // Add the buttons and end the form $return .= ' </div> <div class="tl_formbody_submit"> <div class="tl_submit_container"> ' . implode(' ', $arrButtons) . ' </div> </div> </form> <script> window.addEvent(\'domready\', function() { Theme.focusInput("' . $this->strTable . '"); }); </script>'; // Begin the form (-> DO NOT CHANGE THIS ORDER -> this way the onsubmit attribute of the form can be changed by a field) $return = ' <div id="tl_buttons"> <a href="' . $this->getReferer(true) . '" class="header_back" title="' . \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['backBTTitle']) . '" accesskey="b" onclick="Backend.getScrollOffset()">' . $GLOBALS['TL_LANG']['MSC']['backBT'] . '</a> </div> ' . \Message::generate() . ' <form action="' . ampersand(\Environment::get('request'), true) . '" id="' . $this->strTable . '" class="tl_form" method="post"' . (!empty($this->onsubmit) ? ' onsubmit="' . implode(' ', $this->onsubmit) . '"' : '') . '> <div class="tl_formbody_edit"> <input type="hidden" name="FORM_SUBMIT" value="' . $this->strTable . '"> <input type="hidden" name="REQUEST_TOKEN" value="' . REQUEST_TOKEN . '"> <input type="hidden" name="FORM_FIELDS[]" value="' . \StringUtil::specialchars($this->strPalette) . '">' . ($this->noReload ? ' <p class="tl_error">' . $GLOBALS['TL_LANG']['ERR']['general'] . '</p>' : '') . $return; // Reload the page to prevent _POST variables from being sent twice if (\Input::post('FORM_SUBMIT') == $this->strTable && !$this->noReload) { // Call onsubmit_callback if (is_array($GLOBALS['TL_DCA'][$this->strTable]['config']['onsubmit_callback'])) { foreach ($GLOBALS['TL_DCA'][$this->strTable]['config']['onsubmit_callback'] as $callback) { if (is_array($callback)) { $this->import($callback[0]); $this->{$callback[0]}->{$callback[1]}($this); } elseif (is_callable($callback)) { $callback($this); } } } // Reload if (isset($_POST['saveNclose'])) { \Message::reset(); \System::setCookie('BE_PAGE_OFFSET', 0, 0); $this->redirect($this->getReferer()); } $this->reload(); } // Set the focus if there is an error if ($this->noReload) { $return .= ' <script> window.addEvent(\'domready\', function() { Backend.vScrollTo(($(\'' . $this->strTable . '\').getElement(\'label.error\').getPosition().y - 20)); }); </script>'; } return $return; }
/** * Recursively validate an input variable * * @param mixed $varInput The user input * * @return mixed The original or modified user input */ protected function validator($varInput) { if (is_array($varInput)) { foreach ($varInput as $k => $v) { $varInput[$k] = $this->validator($v); } return $varInput; } if (!$this->doNotTrim) { $varInput = trim($varInput); } if ($varInput == '') { if (!$this->mandatory) { return ''; } else { if ($this->strLabel == '') { $this->addError($GLOBALS['TL_LANG']['ERR']['mdtryNoLabel']); } else { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['mandatory'], $this->strLabel)); } } } if ($this->minlength && $varInput != '' && Utf8::strlen($varInput) < $this->minlength) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['minlength'], $this->strLabel, $this->minlength)); } if ($this->maxlength && $varInput != '' && Utf8::strlen($varInput) > $this->maxlength) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['maxlength'], $this->strLabel, $this->maxlength)); } if ($this->minval && is_numeric($varInput) && $varInput < $this->minval) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['minval'], $this->strLabel, $this->minval)); } if ($this->maxval && is_numeric($varInput) && $varInput > $this->maxval) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['maxval'], $this->strLabel, $this->maxval)); } if ($this->rgxp != '') { switch ($this->rgxp) { // Special validation rule for style sheets case strncmp($this->rgxp, 'digit_', 6) === 0: $textual = explode('_', $this->rgxp); array_shift($textual); if (in_array($varInput, $textual) || strncmp($varInput, '$', 1) === 0) { break; } // DO NOT ADD A break; STATEMENT HERE // Numeric characters (including full stop [.] and minus [-]) // DO NOT ADD A break; STATEMENT HERE // Numeric characters (including full stop [.] and minus [-]) case 'digit': // Support decimal commas and convert them automatically (see #3488) if (substr_count($varInput, ',') == 1 && strpos($varInput, '.') === false) { $varInput = str_replace(',', '.', $varInput); } if (!\Validator::isNumeric($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['digit'], $this->strLabel)); } break; // Natural numbers (positive integers) // Natural numbers (positive integers) case 'natural': if (!\Validator::isNatural($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['natural'], $this->strLabel)); } break; // Alphabetic characters (including full stop [.] minus [-] and space [ ]) // Alphabetic characters (including full stop [.] minus [-] and space [ ]) case 'alpha': if (!\Validator::isAlphabetic($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['alpha'], $this->strLabel)); } break; // Alphanumeric characters (including full stop [.] minus [-], underscore [_] and space [ ]) // Alphanumeric characters (including full stop [.] minus [-], underscore [_] and space [ ]) case 'alnum': if (!\Validator::isAlphanumeric($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['alnum'], $this->strLabel)); } break; // Do not allow any characters that are usually encoded by class Input ([#<>()\=]) // Do not allow any characters that are usually encoded by class Input ([#<>()\=]) case 'extnd': if (!\Validator::isExtendedAlphanumeric(html_entity_decode($varInput))) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['extnd'], $this->strLabel)); } break; // Check whether the current value is a valid date format // Check whether the current value is a valid date format case 'date': if (!\Validator::isDate($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['date'], \Date::getInputFormat(\Date::getNumericDateFormat()))); } else { // Validate the date (see #5086) try { new \Date($varInput, \Date::getNumericDateFormat()); } catch (\OutOfBoundsException $e) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['invalidDate'], $varInput)); } } break; // Check whether the current value is a valid time format // Check whether the current value is a valid time format case 'time': if (!\Validator::isTime($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['time'], \Date::getInputFormat(\Date::getNumericTimeFormat()))); } break; // Check whether the current value is a valid date and time format // Check whether the current value is a valid date and time format case 'datim': if (!\Validator::isDatim($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['dateTime'], \Date::getInputFormat(\Date::getNumericDatimFormat()))); } else { // Validate the date (see #5086) try { new \Date($varInput, \Date::getNumericDatimFormat()); } catch (\OutOfBoundsException $e) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['invalidDate'], $varInput)); } } break; // Check whether the current value is a valid friendly name e-mail address // Check whether the current value is a valid friendly name e-mail address case 'friendly': list($strName, $varInput) = \StringUtil::splitFriendlyEmail($varInput); // no break; // Check whether the current value is a valid e-mail address // no break; // Check whether the current value is a valid e-mail address case 'email': if (!\Validator::isEmail($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['email'], $this->strLabel)); } if ($this->rgxp == 'friendly' && !empty($strName)) { $varInput = $strName . ' [' . $varInput . ']'; } break; // Check whether the current value is list of valid e-mail addresses // Check whether the current value is list of valid e-mail addresses case 'emails': $arrEmails = \StringUtil::trimsplit(',', $varInput); foreach ($arrEmails as $strEmail) { $strEmail = \Idna::encodeEmail($strEmail); if (!\Validator::isEmail($strEmail)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['emails'], $this->strLabel)); break; } } break; // Check whether the current value is a valid URL // Check whether the current value is a valid URL case 'url': if (!\Validator::isUrl($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['url'], $this->strLabel)); } break; // Check whether the current value is a valid alias // Check whether the current value is a valid alias case 'alias': if (!\Validator::isAlias($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['alias'], $this->strLabel)); } break; // Check whether the current value is a valid folder URL alias // Check whether the current value is a valid folder URL alias case 'folderalias': if (!\Validator::isFolderAlias($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['folderalias'], $this->strLabel)); } break; // Phone numbers (numeric characters, space [ ], plus [+], minus [-], parentheses [()] and slash [/]) // Phone numbers (numeric characters, space [ ], plus [+], minus [-], parentheses [()] and slash [/]) case 'phone': if (!\Validator::isPhone(html_entity_decode($varInput))) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['phone'], $this->strLabel)); } break; // Check whether the current value is a percent value // Check whether the current value is a percent value case 'prcnt': if (!\Validator::isPercent($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['prcnt'], $this->strLabel)); } break; // Check whether the current value is a locale // Check whether the current value is a locale case 'locale': if (!\Validator::isLocale($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['locale'], $this->strLabel)); } break; // Check whether the current value is a language code // Check whether the current value is a language code case 'language': if (!\Validator::isLanguage($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['language'], $this->strLabel)); } break; // Check whether the current value is a Google+ ID or vanity name // Check whether the current value is a Google+ ID or vanity name case 'google+': if (!\Validator::isGooglePlusId($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['invalidGoogleId'], $this->strLabel)); } break; // Check whether the current value is a field name // Check whether the current value is a field name case 'fieldname': if (!\Validator::isFieldName($varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['invalidFieldName'], $this->strLabel)); } break; // HOOK: pass unknown tags to callback functions // HOOK: pass unknown tags to callback functions default: if (isset($GLOBALS['TL_HOOKS']['addCustomRegexp']) && is_array($GLOBALS['TL_HOOKS']['addCustomRegexp'])) { foreach ($GLOBALS['TL_HOOKS']['addCustomRegexp'] as $callback) { $this->import($callback[0]); $break = $this->{$callback[0]}->{$callback[1]}($this->rgxp, $varInput, $this); // Stop the loop if a callback returned true if ($break === true) { break; } } } break; } } if ($this->isHexColor && $varInput != '' && strncmp($varInput, '$', 1) !== 0) { $varInput = preg_replace('/[^a-f0-9]+/i', '', $varInput); } if ($this->nospace && preg_match('/[\\t ]+/', $varInput)) { $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['noSpace'], $this->strLabel)); } if ($this->spaceToUnderscore) { $varInput = preg_replace('/\\s+/', '_', trim($varInput)); } if (is_bool($this->trailingSlash) && $varInput != '') { $varInput = preg_replace('/\\/+$/', '', $varInput) . ($this->trailingSlash ? '/' : ''); } return $varInput; }
/** * Add enclosures to a template * * @param object $objTemplate The template object to add the enclosures to * @param array $arrItem The element or module as array * @param string $strKey The name of the enclosures field in $arrItem */ public static function addEnclosuresToTemplate($objTemplate, $arrItem, $strKey = 'enclosure') { $arrEnclosures = \StringUtil::deserialize($arrItem[$strKey]); if (!is_array($arrEnclosures) || empty($arrEnclosures)) { return; } $objFiles = \FilesModel::findMultipleByUuids($arrEnclosures); if ($objFiles === null) { return; } $file = \Input::get('file', true); // Send the file to the browser and do not send a 404 header (see #5178) if ($file != '') { while ($objFiles->next()) { if ($file == $objFiles->path) { static::sendFileToBrowser($file); } } $objFiles->reset(); } /** @var PageModel $objPage */ global $objPage; $arrEnclosures = array(); $allowedDownload = \StringUtil::trimsplit(',', strtolower(\Config::get('allowedDownload'))); // Add download links while ($objFiles->next()) { if ($objFiles->type == 'file') { if (!in_array($objFiles->extension, $allowedDownload) || !is_file(TL_ROOT . '/' . $objFiles->path)) { continue; } $objFile = new \File($objFiles->path); $strHref = \Environment::get('request'); // Remove an existing file parameter (see #5683) if (preg_match('/(&(amp;)?|\\?)file=/', $strHref)) { $strHref = preg_replace('/(&(amp;)?|\\?)file=[^&]+/', '', $strHref); } $strHref .= (strpos($strHref, '?') !== false ? '&' : '?') . 'file=' . \System::urlEncode($objFiles->path); $arrMeta = \Frontend::getMetaData($objFiles->meta, $objPage->language); if (empty($arrMeta) && $objPage->rootFallbackLanguage !== null) { $arrMeta = \Frontend::getMetaData($objFiles->meta, $objPage->rootFallbackLanguage); } // Use the file name as title if none is given if ($arrMeta['title'] == '') { $arrMeta['title'] = \StringUtil::specialchars($objFile->basename); } $arrEnclosures[] = array('id' => $objFiles->id, 'uuid' => $objFiles->uuid, 'name' => $objFile->basename, 'title' => \StringUtil::specialchars(sprintf($GLOBALS['TL_LANG']['MSC']['download'], $objFile->basename)), 'link' => $arrMeta['title'], 'caption' => $arrMeta['caption'], 'href' => $strHref, 'filesize' => static::getReadableSize($objFile->filesize), 'icon' => \Image::getPath($objFile->icon), 'mime' => $objFile->mime, 'meta' => $arrMeta, 'extension' => $objFile->extension, 'path' => $objFile->dirname, 'enclosure' => $objFiles->path); } } $objTemplate->enclosure = $arrEnclosures; }
/** * Generate the form * * @return string */ protected function compile() { $hasUpload = false; $doNotSubmit = false; $arrSubmitted = array(); $this->loadDataContainer('tl_form_field'); $formId = $this->formID != '' ? 'auto_' . $this->formID : 'auto_form_' . $this->id; $this->Template->fields = ''; $this->Template->hidden = ''; $this->Template->formSubmit = $formId; $this->Template->method = $this->method == 'GET' ? 'get' : 'post'; $this->initializeSession($formId); $arrLabels = array(); // Get all form fields $arrFields = array(); $objFields = \FormFieldModel::findPublishedByPid($this->id); if ($objFields !== null) { while ($objFields->next()) { if ($objFields->name != '') { $arrFields[$objFields->name] = $objFields->current(); } else { $arrFields[] = $objFields->current(); } } } // HOOK: compile form fields if (isset($GLOBALS['TL_HOOKS']['compileFormFields']) && is_array($GLOBALS['TL_HOOKS']['compileFormFields'])) { foreach ($GLOBALS['TL_HOOKS']['compileFormFields'] as $callback) { $this->import($callback[0]); $arrFields = $this->{$callback[0]}->{$callback[1]}($arrFields, $formId, $this); } } // Process the fields if (!empty($arrFields) && is_array($arrFields)) { $row = 0; $max_row = count($arrFields); foreach ($arrFields as $objField) { /** @var FormFieldModel $objField */ $strClass = $GLOBALS['TL_FFL'][$objField->type]; // Continue if the class is not defined if (!class_exists($strClass)) { continue; } $arrData = $objField->row(); $arrData['decodeEntities'] = true; $arrData['allowHtml'] = $this->allowTags; $arrData['rowClass'] = 'row_' . $row . ($row == 0 ? ' row_first' : ($row == $max_row - 1 ? ' row_last' : '')) . ($row % 2 == 0 ? ' even' : ' odd'); // Increase the row count if its a password field if ($objField->type == 'password') { ++$row; ++$max_row; $arrData['rowClassConfirm'] = 'row_' . $row . ($row == $max_row - 1 ? ' row_last' : '') . ($row % 2 == 0 ? ' even' : ' odd'); } // Submit buttons do not use the name attribute if ($objField->type == 'submit') { $arrData['name'] = ''; } // Unset the default value depending on the field type (see #4722) if (!empty($arrData['value'])) { if (!in_array('value', \StringUtil::trimsplit('[,;]', $GLOBALS['TL_DCA']['tl_form_field']['palettes'][$objField->type]))) { $arrData['value'] = ''; } } /** @var Widget $objWidget */ $objWidget = new $strClass($arrData); $objWidget->required = $objField->mandatory ? true : false; // HOOK: load form field callback if (isset($GLOBALS['TL_HOOKS']['loadFormField']) && is_array($GLOBALS['TL_HOOKS']['loadFormField'])) { foreach ($GLOBALS['TL_HOOKS']['loadFormField'] as $callback) { $this->import($callback[0]); $objWidget = $this->{$callback[0]}->{$callback[1]}($objWidget, $formId, $this->arrData, $this); } } // Validate the input if (\Input::post('FORM_SUBMIT') == $formId) { $objWidget->validate(); // HOOK: validate form field callback if (isset($GLOBALS['TL_HOOKS']['validateFormField']) && is_array($GLOBALS['TL_HOOKS']['validateFormField'])) { foreach ($GLOBALS['TL_HOOKS']['validateFormField'] as $callback) { $this->import($callback[0]); $objWidget = $this->{$callback[0]}->{$callback[1]}($objWidget, $formId, $this->arrData, $this); } } if ($objWidget->hasErrors()) { $doNotSubmit = true; } elseif ($objWidget->submitInput()) { $arrSubmitted[$objField->name] = $objWidget->value; $_SESSION['FORM_DATA'][$objField->name] = $objWidget->value; unset($_POST[$objField->name]); // see #5474 } } if ($objWidget instanceof \uploadable) { $hasUpload = true; } if ($objWidget instanceof FormHidden) { $this->Template->hidden .= $objWidget->parse(); --$max_row; continue; } if ($objWidget->name != '' && $objWidget->label != '') { $arrLabels[$objWidget->name] = $this->replaceInsertTags($objWidget->label); // see #4268 } $this->Template->fields .= $objWidget->parse(); ++$row; } } // Process the form data if (\Input::post('FORM_SUBMIT') == $formId && !$doNotSubmit) { $this->processFormData($arrSubmitted, $arrLabels, $arrFields); } // Add a warning to the page title if ($doNotSubmit && !\Environment::get('isAjaxRequest')) { /** @var PageModel $objPage */ global $objPage; $title = $objPage->pageTitle ?: $objPage->title; $objPage->pageTitle = $GLOBALS['TL_LANG']['ERR']['form'] . ' - ' . $title; $_SESSION['FILES'] = array(); // see #3007 } $strAttributes = ''; $arrAttributes = \StringUtil::deserialize($this->attributes, true); if ($arrAttributes[1] != '') { $strAttributes .= ' class="' . $arrAttributes[1] . '"'; } $formId = $arrAttributes[0] ?: 'f' . $this->id; // Count up form usages if (isset(static::$arrFormUsages[$formId])) { static::$arrFormUsages[$formId]++; } else { static::$arrFormUsages[$formId] = 1; } // Adjust form id if (static::$arrFormUsages[$formId] > 1) { $formId .= '_' . static::$arrFormUsages[$formId]; } $this->Template->hasError = $doNotSubmit; $this->Template->attributes = $strAttributes; $this->Template->enctype = $hasUpload ? 'multipart/form-data' : 'application/x-www-form-urlencoded'; $this->Template->formId = $formId; $this->Template->action = \Environment::get('indexFreeRequest'); $this->Template->maxFileSize = $hasUpload ? $this->objModel->getMaxUploadFileSize() : false; $this->Template->novalidate = $this->novalidate ? ' novalidate' : ''; // Get the target URL if ($this->method == 'GET' && $this->jumpTo && ($objTarget = $this->objModel->getRelated('jumpTo')) instanceof PageModel) { /** @var PageModel $objTarget */ $this->Template->action = $objTarget->getFrontendUrl(); } return $this->Template->parse(); }
/** * Save the current value * * @param mixed $varValue * * @throws \Exception */ protected function save($varValue) { if (\Input::post('FORM_SUBMIT') != $this->strTable) { return; } $arrData = $GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]; // File names if ($this->strField == 'name') { if (!file_exists(TL_ROOT . '/' . $this->strPath . '/' . $this->varValue . $this->strExtension) || !$this->isMounted($this->strPath . '/' . $this->varValue . $this->strExtension) || $this->varValue === $varValue) { return; } $this->import('Files'); $varValue = Utf8::toAscii($varValue); // Trigger the save_callback if (is_array($arrData['save_callback'])) { foreach ($arrData['save_callback'] as $callback) { if (is_array($callback)) { $this->import($callback[0]); $varValue = $this->{$callback[0]}->{$callback[1]}($varValue, $this); } elseif (is_callable($callback)) { $varValue = $callback($varValue, $this); } } } // The target exists if (strcasecmp($this->strPath . '/' . $this->varValue . $this->strExtension, $this->strPath . '/' . $varValue . $this->strExtension) !== 0 && file_exists(TL_ROOT . '/' . $this->strPath . '/' . $varValue . $this->strExtension)) { throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['fileExists'], $varValue)); } $arrImageTypes = \StringUtil::trimsplit(',', strtolower(\Config::get('validImageTypes'))); // Remove potentially existing thumbnails (see #6641) if (in_array(substr($this->strExtension, 1), $arrImageTypes)) { foreach (glob(TL_ROOT . '/' . \System::getContainer()->getParameter('contao.image.target_path') . '/*/' . $this->varValue . '-*' . $this->strExtension) as $strThumbnail) { $this->Files->delete(str_replace(TL_ROOT . '/', '', $strThumbnail)); } } // Rename the file $this->Files->rename($this->strPath . '/' . $this->varValue . $this->strExtension, $this->strPath . '/' . $varValue . $this->strExtension); // New folders if (stristr($this->intId, '__new__') !== false) { // Update the database if ($this->blnIsDbAssisted && \Dbafs::shouldBeSynchronized($this->strPath . '/' . $varValue . $this->strExtension)) { $this->objActiveRecord = \Dbafs::addResource($this->strPath . '/' . $varValue . $this->strExtension); } $this->log('Folder "' . $this->strPath . '/' . $varValue . $this->strExtension . '" has been created', __METHOD__, TL_FILES); } else { // Update the database if ($this->blnIsDbAssisted) { $syncSource = \Dbafs::shouldBeSynchronized($this->strPath . '/' . $this->varValue . $this->strExtension); $syncTarget = \Dbafs::shouldBeSynchronized($this->strPath . '/' . $varValue . $this->strExtension); if ($syncSource && $syncTarget) { \Dbafs::moveResource($this->strPath . '/' . $this->varValue . $this->strExtension, $this->strPath . '/' . $varValue . $this->strExtension); } elseif ($syncSource) { \Dbafs::deleteResource($this->strPath . '/' . $this->varValue . $this->strExtension); } elseif ($syncTarget) { \Dbafs::addResource($this->strPath . '/' . $varValue . $this->strExtension); } } $this->log('File or folder "' . $this->strPath . '/' . $this->varValue . $this->strExtension . '" has been renamed to "' . $this->strPath . '/' . $varValue . $this->strExtension . '"', __METHOD__, TL_FILES); } // Update the symlinks if (is_link(TL_ROOT . '/web/' . $this->strPath . '/' . $this->varValue . $this->strExtension)) { $this->Files->delete('web/' . $this->strPath . '/' . $this->varValue . $this->strExtension); SymlinkUtil::symlink($this->strPath . '/' . $varValue . $this->strExtension, 'web/' . $this->strPath . '/' . $varValue . $this->strExtension, TL_ROOT); } // Set the new value so the input field can show it if (\Input::get('act') == 'editAll') { /** @var SessionInterface $objSession */ $objSession = \System::getContainer()->get('session'); $session = $objSession->all(); if (($index = array_search($this->strPath . '/' . $this->varValue . $this->strExtension, $session['CURRENT']['IDS'])) !== false) { $session['CURRENT']['IDS'][$index] = $this->strPath . '/' . $varValue . $this->strExtension; $objSession->replace($session); } } $this->varValue = $varValue; } elseif ($this->blnIsDbAssisted && $this->objActiveRecord !== null) { // Convert date formats into timestamps if ($varValue != '' && in_array($arrData['eval']['rgxp'], array('date', 'time', 'datim'))) { $objDate = new \Date($varValue, \Date::getFormatFromRgxp($arrData['eval']['rgxp'])); $varValue = $objDate->tstamp; } // Make sure unique fields are unique if ($arrData['eval']['unique'] && $varValue != '' && !$this->Database->isUniqueValue($this->strTable, $this->strField, $varValue, $this->objActiveRecord->id)) { throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['unique'], $arrData['label'][0] ?: $this->strField)); } // Handle multi-select fields in "override all" mode if (\Input::get('act') == 'overrideAll' && ($arrData['inputType'] == 'checkbox' || $arrData['inputType'] == 'checkboxWizard') && $arrData['eval']['multiple']) { if ($this->objActiveRecord !== null) { $new = \StringUtil::deserialize($varValue, true); $old = \StringUtil::deserialize($this->objActiveRecord->{$this->strField}, true); switch (\Input::post($this->strInputName . '_update')) { case 'add': $varValue = array_values(array_unique(array_merge($old, $new))); break; case 'remove': $varValue = array_values(array_diff($old, $new)); break; case 'replace': $varValue = $new; break; } if (!is_array($varValue) || empty($varValue)) { $varValue = ''; } elseif (isset($arrData['eval']['csv'])) { $varValue = implode($arrData['eval']['csv'], $varValue); // see #2890 } else { $varValue = serialize($varValue); } } } // Convert arrays (see #2890) if ($arrData['eval']['multiple'] && isset($arrData['eval']['csv'])) { $varValue = implode($arrData['eval']['csv'], \StringUtil::deserialize($varValue, true)); } // Trigger the save_callback if (is_array($arrData['save_callback'])) { foreach ($arrData['save_callback'] as $callback) { if (is_array($callback)) { $this->import($callback[0]); $varValue = $this->{$callback[0]}->{$callback[1]}($varValue, $this); } elseif (is_callable($callback)) { $varValue = $callback($varValue, $this); } } } // Save the value if there was no error if (($varValue != '' || !$arrData['eval']['doNotSaveEmpty']) && ($this->varValue != $varValue || $arrData['eval']['alwaysSave'])) { // If the field is a fallback field, empty all other columns if ($arrData['eval']['fallback'] && $varValue != '') { $this->Database->execute("UPDATE " . $this->strTable . " SET " . $this->strField . "=''"); } // Set the correct empty value (see #6284, #6373) if ($varValue === '') { $varValue = \Widget::getEmptyValueByFieldType($GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]['sql']); } $this->objActiveRecord->{$this->strField} = $varValue; $this->objActiveRecord->save(); $this->blnCreateNewVersion = true; $this->varValue = \StringUtil::deserialize($varValue); } } }
/** * Replace insert tags with their values * * @param string $strBuffer The text with the tags to be replaced * @param boolean $blnCache If false, non-cacheable tags will be replaced * * @return string The text with the replaced tags */ protected function doReplace($strBuffer, $blnCache) { /** @var PageModel $objPage */ global $objPage; // Preserve insert tags if (\Config::get('disableInsertTags')) { return \StringUtil::restoreBasicEntities($strBuffer); } $tags = preg_split('/{{([^{}]+)}}/', $strBuffer, -1, PREG_SPLIT_DELIM_CAPTURE); if (count($tags) < 2) { return \StringUtil::restoreBasicEntities($strBuffer); } $strBuffer = ''; // Create one cache per cache setting (see #7700) static $arrItCache; $arrCache =& $arrItCache[$blnCache]; for ($_rit = 0, $_cnt = count($tags); $_rit < $_cnt; $_rit += 2) { $strBuffer .= $tags[$_rit]; $strTag = $tags[$_rit + 1]; // Skip empty tags if ($strTag == '') { continue; } $flags = explode('|', $strTag); $tag = array_shift($flags); $elements = explode('::', $tag); // Load the value from cache if (isset($arrCache[$strTag]) && !in_array('refresh', $flags)) { $strBuffer .= $arrCache[$strTag]; continue; } // Skip certain elements if the output will be cached if ($blnCache) { if ($elements[0] == 'date' || $elements[0] == 'ua' || $elements[0] == 'post' || $elements[0] == 'file' && !\Validator::isStringUuid($elements[1]) || $elements[1] == 'back' || $elements[1] == 'referer' || $elements[0] == 'request_token' || $elements[0] == 'toggle_view' || strncmp($elements[0], 'cache_', 6) === 0 || in_array('uncached', $flags)) { /** @var FragmentHandler $fragmentHandler */ $fragmentHandler = \System::getContainer()->get('fragment.handler'); $strBuffer .= $fragmentHandler->render(new ControllerReference('contao.controller.insert_tags:renderAction', ['insertTag' => '{{' . $strTag . '}}']), 'esi'); continue; } } $arrCache[$strTag] = ''; // Replace the tag switch (strtolower($elements[0])) { // Date case 'date': $arrCache[$strTag] = \Date::parse($elements[1] ?: \Config::get('dateFormat')); break; // Accessibility tags // Accessibility tags case 'lang': if ($elements[1] == '') { $arrCache[$strTag] = '</span>'; } else { $arrCache[$strTag] = $arrCache[$strTag] = '<span lang="' . \StringUtil::specialchars($elements[1]) . '">'; } break; // Line break // Line break case 'br': $arrCache[$strTag] = '<br>'; break; // E-mail addresses // E-mail addresses case 'email': case 'email_open': case 'email_url': if ($elements[1] == '') { $arrCache[$strTag] = ''; break; } $strEmail = \StringUtil::encodeEmail($elements[1]); // Replace the tag switch (strtolower($elements[0])) { case 'email': $arrCache[$strTag] = '<a href="mailto:' . $strEmail . '" class="email">' . preg_replace('/\\?.*$/', '', $strEmail) . '</a>'; break; case 'email_open': $arrCache[$strTag] = '<a href="mailto:' . $strEmail . '" title="' . $strEmail . '" class="email">'; break; case 'email_url': $arrCache[$strTag] = $strEmail; break; } break; // Label tags // Label tags case 'label': $keys = explode(':', $elements[1]); if (count($keys) < 2) { $arrCache[$strTag] = ''; break; } $file = $keys[0]; // Map the key (see #7217) switch ($file) { case 'CNT': $file = 'countries'; break; case 'LNG': $file = 'languages'; break; case 'MOD': case 'FMD': $file = 'modules'; break; case 'FFL': $file = 'tl_form_field'; break; case 'CACHE': $file = 'tl_page'; break; case 'XPL': $file = 'explain'; break; case 'XPT': $file = 'exception'; break; case 'MSC': case 'ERR': case 'CTE': case 'PTY': case 'FOP': case 'CHMOD': case 'DAYS': case 'MONTHS': case 'UNITS': case 'CONFIRM': case 'DP': case 'COLS': $file = 'default'; break; } \System::loadLanguageFile($file); if (count($keys) == 2) { $arrCache[$strTag] = $GLOBALS['TL_LANG'][$keys[0]][$keys[1]]; } else { $arrCache[$strTag] = $GLOBALS['TL_LANG'][$keys[0]][$keys[1]][$keys[2]]; } break; // Front end user // Front end user case 'user': if (FE_USER_LOGGED_IN) { $this->import('FrontendUser', 'User'); $value = $this->User->{$elements[1]}; if ($value == '') { $arrCache[$strTag] = $value; break; } $this->loadDataContainer('tl_member'); if ($GLOBALS['TL_DCA']['tl_member']['fields'][$elements[1]]['inputType'] == 'password') { $arrCache[$strTag] = ''; break; } $value = \StringUtil::deserialize($value); // Decrypt the value if ($GLOBALS['TL_DCA']['tl_member']['fields'][$elements[1]]['eval']['encrypt']) { $value = \Encryption::decrypt($value); } $rgxp = $GLOBALS['TL_DCA']['tl_member']['fields'][$elements[1]]['eval']['rgxp']; $opts = $GLOBALS['TL_DCA']['tl_member']['fields'][$elements[1]]['options']; $rfrc = $GLOBALS['TL_DCA']['tl_member']['fields'][$elements[1]]['reference']; if ($rgxp == 'date') { $arrCache[$strTag] = \Date::parse(\Config::get('dateFormat'), $value); } elseif ($rgxp == 'time') { $arrCache[$strTag] = \Date::parse(\Config::get('timeFormat'), $value); } elseif ($rgxp == 'datim') { $arrCache[$strTag] = \Date::parse(\Config::get('datimFormat'), $value); } elseif (is_array($value)) { $arrCache[$strTag] = implode(', ', $value); } elseif (is_array($opts) && array_is_assoc($opts)) { $arrCache[$strTag] = isset($opts[$value]) ? $opts[$value] : $value; } elseif (is_array($rfrc)) { $arrCache[$strTag] = isset($rfrc[$value]) ? is_array($rfrc[$value]) ? $rfrc[$value][0] : $rfrc[$value] : $value; } else { $arrCache[$strTag] = $value; } // Convert special characters (see #1890) $arrCache[$strTag] = \StringUtil::specialchars($arrCache[$strTag]); } break; // Link // Link case 'link': case 'link_open': case 'link_url': case 'link_title': case 'link_target': case 'link_name': $strTarget = null; // Back link if ($elements[1] == 'back') { $strUrl = 'javascript:history.go(-1)'; $strTitle = $GLOBALS['TL_LANG']['MSC']['goBack']; // No language files if the page is cached if (!strlen($strTitle)) { $strTitle = 'Go back'; } $strName = $strTitle; } elseif (strncmp($elements[1], 'http://', 7) === 0 || strncmp($elements[1], 'https://', 8) === 0) { $strUrl = $elements[1]; $strTitle = $elements[1]; $strName = str_replace(array('http://', 'https://'), '', $elements[1]); } else { // User login page if ($elements[1] == 'login') { if (!FE_USER_LOGGED_IN) { break; } $this->import('FrontendUser', 'User'); $elements[1] = $this->User->loginPage; } $objNextPage = \PageModel::findByIdOrAlias($elements[1]); if ($objNextPage === null) { break; } // Page type specific settings (thanks to Andreas Schempp) switch ($objNextPage->type) { case 'redirect': $strUrl = $objNextPage->url; if (strncasecmp($strUrl, 'mailto:', 7) === 0) { $strUrl = \StringUtil::encodeEmail($strUrl); } break; case 'forward': if ($objNextPage->jumpTo) { /** @var PageModel $objNext */ $objNext = $objNextPage->getRelated('jumpTo'); } else { $objNext = \PageModel::findFirstPublishedRegularByPid($objNextPage->id); } if ($objNext instanceof PageModel) { $strUrl = $objNext->getFrontendUrl(); break; } // DO NOT ADD A break; STATEMENT // DO NOT ADD A break; STATEMENT default: $strUrl = $objNextPage->getFrontendUrl(); break; } $strName = $objNextPage->title; $strTarget = $objNextPage->target ? ' target="_blank"' : ''; $strTitle = $objNextPage->pageTitle ?: $objNextPage->title; } // Replace the tag switch (strtolower($elements[0])) { case 'link': $arrCache[$strTag] = sprintf('<a href="%s" title="%s"%s>%s</a>', $strUrl, \StringUtil::specialchars($strTitle), $strTarget, $strName); break; case 'link_open': $arrCache[$strTag] = sprintf('<a href="%s" title="%s"%s>', $strUrl, \StringUtil::specialchars($strTitle), $strTarget); break; case 'link_url': $arrCache[$strTag] = $strUrl; break; case 'link_title': $arrCache[$strTag] = \StringUtil::specialchars($strTitle); break; case 'link_target': $arrCache[$strTag] = $strTarget; break; case 'link_name': $arrCache[$strTag] = $strName; break; } break; // Closing link tag // Closing link tag case 'link_close': case 'email_close': $arrCache[$strTag] = '</a>'; break; // Insert article // Insert article case 'insert_article': if (($strOutput = $this->getArticle($elements[1], false, true)) !== false) { $arrCache[$strTag] = ltrim($strOutput); } else { $arrCache[$strTag] = '<p class="error">' . sprintf($GLOBALS['TL_LANG']['MSC']['invalidPage'], $elements[1]) . '</p>'; } break; // Insert content element // Insert content element case 'insert_content': $arrCache[$strTag] = $this->getContentElement($elements[1]); break; // Insert module // Insert module case 'insert_module': $arrCache[$strTag] = $this->getFrontendModule($elements[1]); break; // Insert form // Insert form case 'insert_form': $arrCache[$strTag] = $this->getForm($elements[1]); break; // Article // Article case 'article': case 'article_open': case 'article_url': case 'article_title': if (($objArticle = \ArticleModel::findByIdOrAlias($elements[1])) === null || !($objPid = $objArticle->getRelated('pid')) instanceof PageModel) { break; } /** @var PageModel $objPid */ $strUrl = $objPid->getFrontendUrl('/articles/' . ($objArticle->alias ?: $objArticle->id)); // Replace the tag switch (strtolower($elements[0])) { case 'article': $arrCache[$strTag] = sprintf('<a href="%s" title="%s">%s</a>', $strUrl, \StringUtil::specialchars($objArticle->title), $objArticle->title); break; case 'article_open': $arrCache[$strTag] = sprintf('<a href="%s" title="%s">', $strUrl, \StringUtil::specialchars($objArticle->title)); break; case 'article_url': $arrCache[$strTag] = $strUrl; break; case 'article_title': $arrCache[$strTag] = \StringUtil::specialchars($objArticle->title); break; } break; // Article teaser // Article teaser case 'article_teaser': $objTeaser = \ArticleModel::findByIdOrAlias($elements[1]); if ($objTeaser !== null) { $arrCache[$strTag] = \StringUtil::toHtml5($objTeaser->teaser); } break; // Last update // Last update case 'last_update': $strQuery = "SELECT MAX(tstamp) AS tc"; $bundles = \System::getContainer()->getParameter('kernel.bundles'); if (isset($bundles['ContaoNewsBundle'])) { $strQuery .= ", (SELECT MAX(tstamp) FROM tl_news) AS tn"; } if (isset($bundles['ContaoCalendarBundle'])) { $strQuery .= ", (SELECT MAX(tstamp) FROM tl_calendar_events) AS te"; } $strQuery .= " FROM tl_content"; $objUpdate = \Database::getInstance()->query($strQuery); if ($objUpdate->numRows) { $arrCache[$strTag] = \Date::parse($elements[1] ?: \Config::get('datimFormat'), max($objUpdate->tc, $objUpdate->tn, $objUpdate->te)); } break; // Version // Version case 'version': $arrCache[$strTag] = VERSION . '.' . BUILD; break; // Request token // Request token case 'request_token': $arrCache[$strTag] = REQUEST_TOKEN; break; // POST data // POST data case 'post': $arrCache[$strTag] = \Input::post($elements[1]); break; // Mobile/desktop toggle (see #6469) // Mobile/desktop toggle (see #6469) case 'toggle_view': $strUrl = ampersand(\Environment::get('request')); $strGlue = strpos($strUrl, '?') === false ? '?' : '&'; if (\Input::cookie('TL_VIEW') == 'mobile' || \Environment::get('agent')->mobile && \Input::cookie('TL_VIEW') != 'desktop') { $arrCache[$strTag] = '<a href="' . $strUrl . $strGlue . 'toggle_view=desktop" class="toggle_desktop" title="' . \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['toggleDesktop'][1]) . '">' . $GLOBALS['TL_LANG']['MSC']['toggleDesktop'][0] . '</a>'; } else { $arrCache[$strTag] = '<a href="' . $strUrl . $strGlue . 'toggle_view=mobile" class="toggle_mobile" title="' . \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['toggleMobile'][1]) . '">' . $GLOBALS['TL_LANG']['MSC']['toggleMobile'][0] . '</a>'; } break; // Conditional tags (if) // Conditional tags (if) case 'iflng': if ($elements[1] != '' && $elements[1] != $objPage->language) { for (; $_rit < $_cnt; $_rit += 2) { if ($tags[$_rit + 1] == 'iflng' || $tags[$_rit + 1] == 'iflng::' . $objPage->language) { break; } } } unset($arrCache[$strTag]); break; // Conditional tags (if not) // Conditional tags (if not) case 'ifnlng': if ($elements[1] != '') { $langs = \StringUtil::trimsplit(',', $elements[1]); if (in_array($objPage->language, $langs)) { for (; $_rit < $_cnt; $_rit += 2) { if ($tags[$_rit + 1] == 'ifnlng') { break; } } } } unset($arrCache[$strTag]); break; // Environment // Environment case 'env': switch ($elements[1]) { case 'host': $arrCache[$strTag] = \Idna::decode(\Environment::get('host')); break; case 'http_host': $arrCache[$strTag] = \Idna::decode(\Environment::get('httpHost')); break; case 'url': $arrCache[$strTag] = \Idna::decode(\Environment::get('url')); break; case 'path': $arrCache[$strTag] = \Idna::decode(\Environment::get('base')); break; case 'request': $arrCache[$strTag] = \Environment::get('indexFreeRequest'); break; case 'ip': $arrCache[$strTag] = \Environment::get('ip'); break; case 'referer': $arrCache[$strTag] = $this->getReferer(true); break; case 'files_url': $arrCache[$strTag] = TL_FILES_URL; break; case 'assets_url': case 'plugins_url': case 'script_url': $arrCache[$strTag] = TL_ASSETS_URL; break; case 'base_url': $arrCache[$strTag] = \System::getContainer()->get('request_stack')->getCurrentRequest()->getBaseUrl(); break; } break; // Page // Page case 'page': if ($elements[1] == 'pageTitle' && $objPage->pageTitle == '') { $elements[1] = 'title'; } elseif ($elements[1] == 'parentPageTitle' && $objPage->parentPageTitle == '') { $elements[1] = 'parentTitle'; } elseif ($elements[1] == 'mainPageTitle' && $objPage->mainPageTitle == '') { $elements[1] = 'mainTitle'; } // Do not use \StringUtil::specialchars() here (see #4687) $arrCache[$strTag] = $objPage->{$elements[1]}; break; // User agent // User agent case 'ua': $ua = \Environment::get('agent'); if ($elements[1] != '') { $arrCache[$strTag] = $ua->{$elements[1]}; } else { $arrCache[$strTag] = ''; } break; // Abbreviations // Abbreviations case 'abbr': case 'acronym': if ($elements[1] != '') { $arrCache[$strTag] = '<abbr title="' . \StringUtil::specialchars($elements[1]) . '">'; } else { $arrCache[$strTag] = '</abbr>'; } break; // Images // Images case 'image': case 'picture': $width = null; $height = null; $alt = ''; $class = ''; $rel = ''; $strFile = $elements[1]; $mode = ''; $size = null; $strTemplate = 'picture_default'; // Take arguments if (strpos($elements[1], '?') !== false) { $arrChunks = explode('?', urldecode($elements[1]), 2); $strSource = \StringUtil::decodeEntities($arrChunks[1]); $strSource = str_replace('[&]', '&', $strSource); $arrParams = explode('&', $strSource); foreach ($arrParams as $strParam) { list($key, $value) = explode('=', $strParam); switch ($key) { case 'width': $width = $value; break; case 'height': $height = $value; break; case 'alt': $alt = $value; break; case 'class': $class = $value; break; case 'rel': $rel = $value; break; case 'mode': $mode = $value; break; case 'size': $size = (int) $value; break; case 'template': $strTemplate = preg_replace('/[^a-z0-9_]/i', '', $value); break; } } $strFile = $arrChunks[0]; } if (\Validator::isUuid($strFile)) { // Handle UUIDs $objFile = \FilesModel::findByUuid($strFile); if ($objFile === null) { $arrCache[$strTag] = ''; break; } $strFile = $objFile->path; } elseif (is_numeric($strFile)) { // Handle numeric IDs (see #4805) $objFile = \FilesModel::findByPk($strFile); if ($objFile === null) { $arrCache[$strTag] = ''; break; } $strFile = $objFile->path; } else { // Check the path if (\Validator::isInsecurePath($strFile)) { throw new \RuntimeException('Invalid path ' . $strFile); } } // Check the maximum image width if (\Config::get('maxImageWidth') > 0 && $width > \Config::get('maxImageWidth')) { $width = \Config::get('maxImageWidth'); $height = null; } // Generate the thumbnail image try { // Image if (strtolower($elements[0]) == 'image') { $dimensions = ''; $src = \System::getContainer()->get('contao.image.image_factory')->create(TL_ROOT . '/' . rawurldecode($strFile), array($width, $height, $mode))->getUrl(TL_ROOT); $objFile = new \File(rawurldecode($src)); // Add the image dimensions if (($imgSize = $objFile->imageSize) !== false) { $dimensions = ' width="' . \StringUtil::specialchars($imgSize[0]) . '" height="' . \StringUtil::specialchars($imgSize[1]) . '"'; } $arrCache[$strTag] = '<img src="' . TL_FILES_URL . $src . '" ' . $dimensions . ' alt="' . \StringUtil::specialchars($alt) . '"' . ($class != '' ? ' class="' . \StringUtil::specialchars($class) . '"' : '') . '>'; } else { $picture = \System::getContainer()->get('contao.image.picture_factory')->create(TL_ROOT . '/' . $strFile, $size); $picture = array('img' => $picture->getImg(TL_ROOT), 'sources' => $picture->getSources(TL_ROOT)); $picture['alt'] = $alt; $picture['class'] = $class; $pictureTemplate = new \FrontendTemplate($strTemplate); $pictureTemplate->setData($picture); $arrCache[$strTag] = $pictureTemplate->parse(); } // Add a lightbox link if ($rel != '') { if (strncmp($rel, 'lightbox', 8) !== 0) { $attribute = ' rel="' . \StringUtil::specialchars($rel) . '"'; } else { $attribute = ' data-lightbox="' . \StringUtil::specialchars(substr($rel, 8)) . '"'; } $arrCache[$strTag] = '<a href="' . TL_FILES_URL . $strFile . '"' . ($alt != '' ? ' title="' . \StringUtil::specialchars($alt) . '"' : '') . $attribute . '>' . $arrCache[$strTag] . '</a>'; } } catch (\Exception $e) { $arrCache[$strTag] = ''; } break; // Files (UUID or template path) // Files (UUID or template path) case 'file': if (\Validator::isUuid($elements[1])) { $objFile = \FilesModel::findByUuid($elements[1]); if ($objFile !== null) { $arrCache[$strTag] = $objFile->path; break; } } $arrGet = $_GET; \Input::resetCache(); $strFile = $elements[1]; // Take arguments and add them to the $_GET array if (strpos($elements[1], '?') !== false) { $arrChunks = explode('?', urldecode($elements[1])); $strSource = \StringUtil::decodeEntities($arrChunks[1]); $strSource = str_replace('[&]', '&', $strSource); $arrParams = explode('&', $strSource); foreach ($arrParams as $strParam) { $arrParam = explode('=', $strParam); $_GET[$arrParam[0]] = $arrParam[1]; } $strFile = $arrChunks[0]; } // Check the path if (\Validator::isInsecurePath($strFile)) { throw new \RuntimeException('Invalid path ' . $strFile); } // Include .php, .tpl, .xhtml and .html5 files if (preg_match('/\\.(php|tpl|xhtml|html5)$/', $strFile) && file_exists(TL_ROOT . '/templates/' . $strFile)) { ob_start(); include TL_ROOT . '/templates/' . $strFile; $arrCache[$strTag] = ob_get_clean(); } $_GET = $arrGet; \Input::resetCache(); break; // HOOK: pass unknown tags to callback functions // HOOK: pass unknown tags to callback functions default: if (isset($GLOBALS['TL_HOOKS']['replaceInsertTags']) && is_array($GLOBALS['TL_HOOKS']['replaceInsertTags'])) { foreach ($GLOBALS['TL_HOOKS']['replaceInsertTags'] as $callback) { $this->import($callback[0]); $varValue = $this->{$callback[0]}->{$callback[1]}($tag, $blnCache, $arrCache[$strTag], $flags, $tags, $arrCache, $_rit, $_cnt); // see #6672 // Replace the tag and stop the loop if ($varValue !== false) { $arrCache[$strTag] = $varValue; break; } } } \System::getContainer()->get('monolog.logger.contao')->log(LogLevel::INFO, 'Unknown insert tag: ' . $strTag); break; } // Handle the flags if (!empty($flags)) { foreach ($flags as $flag) { switch ($flag) { case 'addslashes': case 'standardize': case 'ampersand': case 'specialchars': case 'nl2br': case 'nl2br_pre': case 'strtolower': case 'utf8_strtolower': case 'strtoupper': case 'utf8_strtoupper': case 'ucfirst': case 'lcfirst': case 'ucwords': case 'trim': case 'rtrim': case 'ltrim': case 'utf8_romanize': case 'urlencode': case 'rawurlencode': $arrCache[$strTag] = $flag($arrCache[$strTag]); break; case 'encodeEmail': $arrCache[$strTag] = \StringUtil::$flag($arrCache[$strTag]); break; case 'number_format': $arrCache[$strTag] = \System::getFormattedNumber($arrCache[$strTag], 0); break; case 'currency_format': $arrCache[$strTag] = \System::getFormattedNumber($arrCache[$strTag], 2); break; case 'readable_size': $arrCache[$strTag] = \System::getReadableSize($arrCache[$strTag]); break; case 'flatten': if (!is_array($arrCache[$strTag])) { break; } $it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($arrCache[$strTag])); $result = array(); foreach ($it as $leafValue) { $keys = array(); foreach (range(0, $it->getDepth()) as $depth) { $keys[] = $it->getSubIterator($depth)->key(); } $result[] = implode('.', $keys) . ': ' . $leafValue; } $arrCache[$strTag] = implode(', ', $result); break; // HOOK: pass unknown flags to callback functions // HOOK: pass unknown flags to callback functions default: if (isset($GLOBALS['TL_HOOKS']['insertTagFlags']) && is_array($GLOBALS['TL_HOOKS']['insertTagFlags'])) { foreach ($GLOBALS['TL_HOOKS']['insertTagFlags'] as $callback) { $this->import($callback[0]); $varValue = $this->{$callback[0]}->{$callback[1]}($flag, $tag, $arrCache[$strTag], $flags, $blnCache, $tags, $arrCache, $_rit, $_cnt); // see #5806 // Replace the tag and stop the loop if ($varValue !== false) { $arrCache[$strTag] = $varValue; break; } } } \System::getContainer()->get('monolog.logger.contao')->log(LogLevel::INFO, 'Unknown insert tag flag: ' . $flag); break; } } } $strBuffer .= $arrCache[$strTag]; } return \StringUtil::restoreBasicEntities($strBuffer); }
/** * Create a new object to handle an image * * @param File $file A file instance of the original image * * @throws \InvalidArgumentException If the file does not exists or cannot be processed * * @deprecated Deprecated since Contao 4.3, to be removed in Contao 5.0. * Use the contao.image.image_factory service instead. */ public function __construct(File $file) { @trigger_error('Using new Contao\\Image() has been deprecated and will no longer work in Contao 5.0. Use the contao.image.image_factory service instead.', E_USER_DEPRECATED); // Check whether the file exists if (!$file->exists()) { // Handle public bundle resources if (file_exists(TL_ROOT . '/web/' . $file->path)) { $file = new \File('web/' . $file->path); } else { throw new \InvalidArgumentException('Image "' . $file->path . '" could not be found'); } } $this->fileObj = $file; $arrAllowedTypes = \StringUtil::trimsplit(',', strtolower(\Config::get('validImageTypes'))); // Check the file type if (!in_array($this->fileObj->extension, $arrAllowedTypes)) { throw new \InvalidArgumentException('Image type "' . $this->fileObj->extension . '" was not allowed to be processed'); } }
/** * Compile the template * * @param bool $blnCheckRequest If true, check for unsued $_GET parameters * * @throws \UnusedArgumentsException If there are unused $_GET parameters * * @internal Do not call this method in your code. It will be made private in Contao 5.0. */ protected function compile($blnCheckRequest = false) { $this->keywords = ''; $arrKeywords = \StringUtil::trimsplit(',', $GLOBALS['TL_KEYWORDS']); // Add the meta keywords if (strlen($arrKeywords[0])) { $this->keywords = str_replace(array("\n", "\r", '"'), array(' ', '', ''), implode(', ', array_unique($arrKeywords))); } // Parse the template $this->strBuffer = $this->parse(); // HOOK: add custom output filters if (isset($GLOBALS['TL_HOOKS']['outputFrontendTemplate']) && is_array($GLOBALS['TL_HOOKS']['outputFrontendTemplate'])) { foreach ($GLOBALS['TL_HOOKS']['outputFrontendTemplate'] as $callback) { $this->import($callback[0]); $this->strBuffer = $this->{$callback[0]}->{$callback[1]}($this->strBuffer, $this->strTemplate); } } // Add the output to the cache $this->addToCache(); // Unset only after the output has been cached (see #7824) unset($_SESSION['LOGIN_ERROR']); // Replace insert tags and then re-replace the request_token tag in case a form element has been loaded via insert tag $this->strBuffer = $this->replaceInsertTags($this->strBuffer, false); $this->strBuffer = str_replace(array('{{request_token}}', '[{]', '[}]'), array(REQUEST_TOKEN, '{{', '}}'), $this->strBuffer); $this->strBuffer = $this->replaceDynamicScriptTags($this->strBuffer); // see #4203 // HOOK: allow to modify the compiled markup (see #4291) if (isset($GLOBALS['TL_HOOKS']['modifyFrontendPage']) && is_array($GLOBALS['TL_HOOKS']['modifyFrontendPage'])) { foreach ($GLOBALS['TL_HOOKS']['modifyFrontendPage'] as $callback) { $this->import($callback[0]); $this->strBuffer = $this->{$callback[0]}->{$callback[1]}($this->strBuffer, $this->strTemplate); } } // Check whether all $_GET parameters have been used (see #4277) if ($blnCheckRequest && \Input::hasUnusedGet()) { throw new \UnusedArgumentsException(); } parent::compile(); }
/** * Generate the module */ protected function compile() { \System::loadLanguageFile($this->list_table); $this->loadDataContainer($this->list_table); // List a single record if (\Input::get('show')) { $this->listSingleRecord(\Input::get('show')); return; } /** * Add the search menu */ $strWhere = ''; $varKeyword = ''; $strOptions = ''; $this->Template->searchable = false; $arrSearchFields = \StringUtil::trimsplit(',', $this->list_search); if (!empty($arrSearchFields) && is_array($arrSearchFields)) { $this->Template->searchable = true; if (\Input::get('search') && \Input::get('for')) { $varKeyword = '%' . \Input::get('for') . '%'; $strWhere = (!$this->list_where ? " WHERE " : " AND ") . \Input::get('search') . " LIKE ?"; } foreach ($arrSearchFields as $field) { $strOptions .= ' <option value="' . $field . '"' . ($field == \Input::get('search') ? ' selected="selected"' : '') . '>' . (strlen($label = $GLOBALS['TL_DCA'][$this->list_table]['fields'][$field]['label'][0]) ? $label : $field) . '</option>' . "\n"; } } $this->Template->search_fields = $strOptions; /** * Get the total number of records */ $strQuery = "SELECT COUNT(*) AS count FROM " . $this->list_table; if ($this->list_where) { $strQuery .= " WHERE (" . $this->list_where . ")"; } $strQuery .= $strWhere; $objTotal = $this->Database->prepare($strQuery)->execute($varKeyword); /** * Validate the page count */ $id = 'page_l' . $this->id; $page = \Input::get($id) !== null ? \Input::get($id) : 1; $per_page = \Input::get('per_page') ?: $this->perPage; // Thanks to Hagen Klemp (see #4485) if ($per_page > 0 && ($page < 1 || $page > max(ceil($objTotal->count / $per_page), 1))) { throw new PageNotFoundException('Page not found: ' . \Environment::get('uri')); } /** * Get the selected records */ $strQuery = "SELECT " . $this->strPk . "," . $this->list_fields; if ($this->list_info_where) { $strQuery .= ", (SELECT COUNT(*) FROM " . $this->list_table . " t2 WHERE t2." . $this->strPk . "=t1." . $this->strPk . " AND " . $this->list_info_where . ") AS _details"; } $strQuery .= " FROM " . $this->list_table . " t1"; if ($this->list_where) { $strQuery .= " WHERE (" . $this->list_where . ")"; } $strQuery .= $strWhere; // Cast date fields to int (see #5609) $isInt = function ($field) { return $GLOBALS['TL_DCA'][$this->list_table]['fields'][$field]['eval']['rgxp'] == 'date' || $GLOBALS['TL_DCA'][$this->list_table]['fields'][$field]['eval']['rgxp'] == 'time' || $GLOBALS['TL_DCA'][$this->list_table]['fields'][$field]['eval']['rgxp'] == 'datim'; }; // Order by if (\Input::get('order_by')) { if ($isInt(\Input::get('order_by'))) { $strQuery .= " ORDER BY CAST(" . \Input::get('order_by') . " AS SIGNED) " . \Input::get('sort'); } else { $strQuery .= " ORDER BY " . \Input::get('order_by') . ' ' . \Input::get('sort'); } } elseif ($this->list_sort) { if ($isInt($this->list_sort)) { $strQuery .= " ORDER BY CAST(" . $this->list_sort . " AS SIGNED)"; } else { $strQuery .= " ORDER BY " . $this->list_sort; } } $objDataStmt = $this->Database->prepare($strQuery); // Limit if (\Input::get('per_page')) { $objDataStmt->limit(\Input::get('per_page'), ($page - 1) * $per_page); } elseif ($this->perPage) { $objDataStmt->limit($this->perPage, ($page - 1) * $per_page); } $objData = $objDataStmt->execute($varKeyword); /** * Prepare the URL */ $strUrl = preg_replace('/\\?.*$/', '', \Environment::get('request')); $blnQuery = false; foreach (preg_split('/&(amp;)?/', \Environment::get('queryString')) as $fragment) { if ($fragment != '' && strncasecmp($fragment, 'order_by', 8) !== 0 && strncasecmp($fragment, 'sort', 4) !== 0 && strncasecmp($fragment, $id, strlen($id)) !== 0) { $strUrl .= (!$blnQuery ? '?' : '&') . $fragment; $blnQuery = true; } } $this->Template->url = $strUrl; $strVarConnector = $blnQuery ? '&' : '?'; /** * Prepare the data arrays */ $arrTh = array(); $arrTd = array(); $arrFields = \StringUtil::trimsplit(',', $this->list_fields); // THEAD for ($i = 0, $c = count($arrFields); $i < $c; $i++) { // Never show passwords if ($GLOBALS['TL_DCA'][$this->list_table]['fields'][$arrFields[$i]]['inputType'] == 'password') { continue; } $class = ''; $sort = 'asc'; $strField = strlen($label = $GLOBALS['TL_DCA'][$this->list_table]['fields'][$arrFields[$i]]['label'][0]) ? $label : $arrFields[$i]; // Add a CSS class to the order_by column if (\Input::get('order_by') == $arrFields[$i]) { $sort = \Input::get('sort') == 'asc' ? 'desc' : 'asc'; $class = ' sorted ' . \Input::get('sort'); } $arrTh[] = array('link' => $strField, 'href' => ampersand($strUrl) . $strVarConnector . 'order_by=' . $arrFields[$i] . '&sort=' . $sort, 'title' => \StringUtil::specialchars(sprintf($GLOBALS['TL_LANG']['MSC']['list_orderBy'], $strField)), 'class' => $class . ($i == 0 ? ' col_first' : '')); } $j = 0; $arrRows = $objData->fetchAllAssoc(); // TBODY for ($i = 0, $c = count($arrRows); $i < $c; $i++) { $j = 0; $class = 'row_' . $i . ($i == 0 ? ' row_first' : '') . ($i + 1 == count($arrRows) ? ' row_last' : '') . ($i % 2 == 0 ? ' even' : ' odd'); foreach ($arrRows[$i] as $k => $v) { // Skip the primary key if ($k == $this->strPk && !in_array($this->strPk, $arrFields)) { continue; } if ($k == '_details') { continue; } // Never show passwords if ($GLOBALS['TL_DCA'][$this->list_table]['fields'][$k]['inputType'] == 'password') { continue; } $value = $this->formatValue($k, $v); $arrTd[$class][$k] = array('raw' => $v, 'content' => $value ? $value : ' ', 'class' => 'col_' . $j . ($j++ == 0 ? ' col_first' : '') . ($this->list_info ? '' : ($j >= count($arrRows[$i]) - 1 ? ' col_last' : '')), 'id' => $arrRows[$i][$this->strPk], 'field' => $k, 'url' => $strUrl . $strVarConnector . 'show=' . $arrRows[$i][$this->strPk], 'details' => isset($arrRows[$i]['_details']) ? $arrRows[$i]['_details'] : 1); } } $this->Template->thead = $arrTh; $this->Template->tbody = $arrTd; /** * Pagination */ $objPagination = new \Pagination($objTotal->count, $per_page, \Config::get('maxPaginationLinks'), $id); $this->Template->pagination = $objPagination->generate("\n "); $this->Template->per_page = $per_page; $this->Template->total = $objTotal->count; /** * Template variables */ $this->Template->action = \Environment::get('indexFreeRequest'); $this->Template->details = $this->list_info != '' ? true : false; $this->Template->search_label = \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['search']); $this->Template->per_page_label = \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['list_perPage']); $this->Template->fields_label = $GLOBALS['TL_LANG']['MSC']['all_fields'][0]; $this->Template->keywords_label = $GLOBALS['TL_LANG']['MSC']['keywords']; $this->Template->search = \Input::get('search'); $this->Template->for = \Input::get('for'); $this->Template->order_by = \Input::get('order_by'); $this->Template->sort = \Input::get('sort'); $this->Template->col_last = 'col_' . $j; }
/** * Render a row of a box and return it as HTML string * * @param string $strPalette * * @return string * * @throws AccessDeniedException * @throws \Exception */ protected function row($strPalette = null) { $arrData = $GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]; // Check if the field is excluded if ($arrData['exclude']) { throw new AccessDeniedException('Field "' . $this->strTable . '.' . $this->strField . '" is excluded from being edited.'); } $xlabel = ''; // Toggle line wrap (textarea) if ($arrData['inputType'] == 'textarea' && !isset($arrData['eval']['rte'])) { $xlabel .= ' ' . \Image::getHtml('wrap.svg', $GLOBALS['TL_LANG']['MSC']['wordWrap'], 'title="' . \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['wordWrap']) . '" class="toggleWrap" onclick="Backend.toggleWrap(\'ctrl_' . $this->strInputName . '\')"'); } // Add the help wizard if ($arrData['eval']['helpwizard']) { $xlabel .= ' <a href="contao/help.php?table=' . $this->strTable . '&field=' . $this->strField . '" title="' . \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['helpWizard']) . '" onclick="Backend.openModalIframe({\'width\':735,\'title\':\'' . \StringUtil::specialchars(str_replace("'", "\\'", $arrData['label'][0])) . '\',\'url\':this.href});return false">' . \Image::getHtml('about.svg', $GLOBALS['TL_LANG']['MSC']['helpWizard'], 'style="vertical-align:text-bottom"') . '</a>'; } // Add a custom xlabel if (is_array($arrData['xlabel'])) { foreach ($arrData['xlabel'] as $callback) { if (is_array($callback)) { $this->import($callback[0]); $xlabel .= $this->{$callback[0]}->{$callback[1]}($this); } elseif (is_callable($callback)) { $xlabel .= $callback($this); } } } // Input field callback if (is_array($arrData['input_field_callback'])) { $this->import($arrData['input_field_callback'][0]); return $this->{$arrData['input_field_callback'][0]}->{$arrData['input_field_callback'][1]}($this, $xlabel); } elseif (is_callable($arrData['input_field_callback'])) { return $arrData['input_field_callback']($this, $xlabel); } /** @var Widget $strClass */ $strClass = $GLOBALS['BE_FFL'][$arrData['inputType']]; // Return if the widget class does not exists if (!class_exists($strClass)) { return ''; } $arrData['eval']['required'] = false; // Use strlen() here (see #3277) if ($arrData['eval']['mandatory']) { if (is_array($this->varValue)) { if (empty($this->varValue)) { $arrData['eval']['required'] = true; } } else { if (!strlen($this->varValue)) { $arrData['eval']['required'] = true; } } } // Convert insert tags in src attributes (see #5965) if (isset($arrData['eval']['rte']) && strncmp($arrData['eval']['rte'], 'tiny', 4) === 0) { $this->varValue = \StringUtil::insertTagToSrc($this->varValue); } /** @var Widget $objWidget */ $objWidget = new $strClass($strClass::getAttributesFromDca($arrData, $this->strInputName, $this->varValue, $this->strField, $this->strTable, $this)); $objWidget->xlabel = $xlabel; $objWidget->currentRecord = $this->intId; // Validate the field if (\Input::post('FORM_SUBMIT') == $this->strTable) { $key = \Input::get('act') == 'editAll' ? 'FORM_FIELDS_' . $this->intId : 'FORM_FIELDS'; // Calculate the current palette $postPaletteFields = implode(',', \Input::post($key)); $postPaletteFields = array_unique(\StringUtil::trimsplit('[,;]', $postPaletteFields)); // Compile the palette if there is none if ($strPalette === null) { $newPaletteFields = \StringUtil::trimsplit('[,;]', $this->getPalette()); } else { // Use the given palette ($strPalette is an array in editAll mode) $newPaletteFields = is_array($strPalette) ? $strPalette : \StringUtil::trimsplit('[,;]', $strPalette); // Re-check the palette if the current field is a selector field if (isset($GLOBALS['TL_DCA'][$this->strTable]['palettes']['__selector__']) && in_array($this->strField, $GLOBALS['TL_DCA'][$this->strTable]['palettes']['__selector__'])) { // If the field value has changed, recompile the palette if ($this->varValue != \Input::post($this->strInputName)) { $newPaletteFields = \StringUtil::trimsplit('[,;]', $this->getPalette()); } } } // Adjust the names in editAll mode if (\Input::get('act') == 'editAll') { foreach ($newPaletteFields as $k => $v) { $newPaletteFields[$k] = $v . '_' . $this->intId; } if ($this->User->isAdmin) { $newPaletteFields['pid'] = 'pid_' . $this->intId; $newPaletteFields['sorting'] = 'sorting_' . $this->intId; } } $paletteFields = array_intersect($postPaletteFields, $newPaletteFields); // Validate and save the field if (in_array($this->strInputName, $paletteFields) || \Input::get('act') == 'overrideAll') { $objWidget->validate(); if ($objWidget->hasErrors()) { // Skip mandatory fields on auto-submit (see #4077) if (\Input::post('SUBMIT_TYPE') != 'auto' || !$objWidget->mandatory || $objWidget->value != '') { $this->noReload = true; } } elseif ($objWidget->submitInput()) { $varValue = $objWidget->value; // Sort array by key (fix for JavaScript wizards) if (is_array($varValue)) { ksort($varValue); $varValue = serialize($varValue); } // Convert file paths in src attributes (see #5965) if ($varValue && isset($arrData['eval']['rte']) && strncmp($arrData['eval']['rte'], 'tiny', 4) === 0) { $varValue = \StringUtil::srcToInsertTag($varValue); } // Save the current value try { $this->save($varValue); } catch (\Exception $e) { $this->noReload = true; $objWidget->addError($e->getMessage()); } } } } $wizard = ''; $strHelpClass = ''; // Date picker if ($arrData['eval']['datepicker']) { $rgxp = $arrData['eval']['rgxp']; $format = \Date::formatToJs(\Config::get($rgxp . 'Format')); switch ($rgxp) { case 'datim': $time = ",\n timePicker:true"; break; case 'time': $time = ",\n pickOnly:\"time\""; break; default: $time = ''; break; } $wizard .= ' ' . \Image::getHtml('assets/datepicker/images/icon.svg', '', 'title="' . \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['datepicker']) . '" id="toggle_' . $objWidget->id . '" style="cursor:pointer"') . ' <script> window.addEvent("domready", function() { new Picker.Date($("ctrl_' . $objWidget->id . '"), { draggable: false, toggle: $("toggle_' . $objWidget->id . '"), format: "' . $format . '", positionOffset: {x:-211,y:-209}' . $time . ', pickerClass: "datepicker_bootstrap", useFadeInOut: !Browser.ie, startDay: ' . $GLOBALS['TL_LANG']['MSC']['weekOffset'] . ', titleFormat: "' . $GLOBALS['TL_LANG']['MSC']['titleFormat'] . '" }); }); </script>'; } // Color picker if ($arrData['eval']['colorpicker']) { // Support single fields as well (see #5240) $strKey = $arrData['eval']['multiple'] ? $this->strField . '_0' : $this->strField; $wizard .= ' ' . \Image::getHtml('pickcolor.svg', $GLOBALS['TL_LANG']['MSC']['colorpicker'], 'title="' . \StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['colorpicker']) . '" id="moo_' . $this->strField . '"') . ' <script> window.addEvent("domready", function() { new MooRainbow("moo_' . $this->strField . '", { id: "ctrl_' . $strKey . '", startColor: ((cl = $("ctrl_' . $strKey . '").value.hexToRgb(true)) ? cl : [255, 0, 0]), imgPath: "assets/colorpicker/images/", onComplete: function(color) { $("ctrl_' . $strKey . '").value = color.hex.replace("#", ""); } }); }); </script>'; } // Add a custom wizard if (is_array($arrData['wizard'])) { foreach ($arrData['wizard'] as $callback) { if (is_array($callback)) { $this->import($callback[0]); $wizard .= $this->{$callback[0]}->{$callback[1]}($this); } elseif (is_callable($callback)) { $wizard .= $callback($this); } } } $objWidget->wizard = $wizard; // Set correct form enctype if ($objWidget instanceof \uploadable) { $this->blnUploadable = true; } // Mark floated single checkboxes if ($arrData['inputType'] == 'checkbox' && !$arrData['eval']['multiple'] && strpos($arrData['eval']['tl_class'], 'w50') !== false) { $arrData['eval']['tl_class'] .= ' cbx'; } elseif ($arrData['inputType'] == 'text' && $arrData['eval']['multiple'] && strpos($arrData['eval']['tl_class'], 'wizard') !== false) { $arrData['eval']['tl_class'] .= ' inline'; } // No 2-column layout in "edit all" mode if (\Input::get('act') == 'editAll' || \Input::get('act') == 'overrideAll') { $arrData['eval']['tl_class'] = str_replace(array('w50', 'clr', 'wizard', 'long', 'm12', 'cbx'), '', $arrData['eval']['tl_class']); } $updateMode = ''; // Replace the textarea with an RTE instance if (!empty($arrData['eval']['rte'])) { list($file, $type) = explode('|', $arrData['eval']['rte'], 2); /** @var BackendTemplate|object $objTemplate */ $objTemplate = new \BackendTemplate("be_{$file}"); $objTemplate->selector = 'ctrl_' . $this->strInputName; // Deprecated since Contao 4.0, to be removed in Contao 5.0 $objTemplate->language = \Backend::getTinyMceLanguage(); $updateMode = $objTemplate->parse(); unset($file, $type); } elseif (\Input::get('act') == 'overrideAll' && ($arrData['inputType'] == 'checkbox' || $arrData['inputType'] == 'checkboxWizard') && $arrData['eval']['multiple']) { $updateMode = ' </div> <div> <fieldset class="tl_radio_container"> <legend>' . $GLOBALS['TL_LANG']['MSC']['updateMode'] . '</legend> <input type="radio" name="' . $this->strInputName . '_update" id="opt_' . $this->strInputName . '_update_1" class="tl_radio" value="add" onfocus="Backend.getScrollOffset()"> <label for="opt_' . $this->strInputName . '_update_1">' . $GLOBALS['TL_LANG']['MSC']['updateAdd'] . '</label><br> <input type="radio" name="' . $this->strInputName . '_update" id="opt_' . $this->strInputName . '_update_2" class="tl_radio" value="remove" onfocus="Backend.getScrollOffset()"> <label for="opt_' . $this->strInputName . '_update_2">' . $GLOBALS['TL_LANG']['MSC']['updateRemove'] . '</label><br> <input type="radio" name="' . $this->strInputName . '_update" id="opt_' . $this->strInputName . '_update_0" class="tl_radio" value="replace" checked="checked" onfocus="Backend.getScrollOffset()"> <label for="opt_' . $this->strInputName . '_update_0">' . $GLOBALS['TL_LANG']['MSC']['updateReplace'] . '</label> </fieldset>'; } $strPreview = ''; // Show a preview image (see #4948) if ($this->strTable == 'tl_files' && $this->strField == 'name' && $this->objActiveRecord !== null && $this->objActiveRecord->type == 'file') { $objFile = new \File($this->objActiveRecord->path); if ($objFile->isImage) { $image = \Image::getPath('placeholder.svg'); if ($objFile->isSvgImage || $objFile->height <= \Config::get('gdMaxImgHeight') && $objFile->width <= \Config::get('gdMaxImgWidth')) { if ($objFile->width > 699 || $objFile->height > 524 || !$objFile->width || !$objFile->height) { $image = rawurldecode(\Image::get($objFile->path, 699, 524, 'box')); } else { $image = $objFile->path; } } $objImage = new \File($image); $ctrl = 'ctrl_preview_' . substr(md5($image), 0, 8); $strPreview = ' <div id="' . $ctrl . '" class="tl_edit_preview" data-original-width="' . $objFile->viewWidth . '" data-original-height="' . $objFile->viewHeight . '"> <img src="' . $objImage->dataUri . '" width="' . $objImage->width . '" height="' . $objImage->height . '" alt=""> </div>'; // Add the script to mark the important part if (basename($image) !== 'placeholder.svg') { $strPreview .= '<script>Backend.editPreviewWizard($(\'' . $ctrl . '\'));</script>'; if (\Config::get('showHelp')) { $strPreview .= '<p class="tl_help tl_tip">' . $GLOBALS['TL_LANG'][$this->strTable]['edit_preview_help'] . '</p>'; } } } } return $strPreview . ' <div' . ($arrData['eval']['tl_class'] ? ' class="' . $arrData['eval']['tl_class'] . '"' : '') . '>' . $objWidget->parse() . $updateMode . (!$objWidget->hasErrors() ? $this->help($strHelpClass) : '') . ' </div>'; }
/** * Add templates to the archive * * @param ZipWriter $objArchive * @param string $strFolder */ protected function addTemplatesToArchive(ZipWriter $objArchive, $strFolder) { // Strip the templates folder name $strFolder = preg_replace('@^templates/@', '', $strFolder); // Re-add the templates folder name if ($strFolder == '') { $strFolder = 'templates'; } else { $strFolder = 'templates/' . $strFolder; } if (\Validator::isInsecurePath($strFolder)) { throw new \RuntimeException('Insecure path ' . $strFolder); } // Return if the folder does not exist if (!is_dir(TL_ROOT . '/' . $strFolder)) { return; } $arrAllowed = \StringUtil::trimsplit(',', strtolower(\Config::get('templateFiles'))); array_push($arrAllowed, 'sql'); // see #7048 // Add all template files to the archive foreach (scan(TL_ROOT . '/' . $strFolder) as $strFile) { if (preg_match('/\\.(' . implode('|', $arrAllowed) . ')$/', $strFile) && strncmp($strFile, 'be_', 3) !== 0 && strncmp($strFile, 'nl_', 3) !== 0) { $objArchive->addFile($strFolder . '/' . $strFile); } } }
/** * Generate a particular subpart of the file tree and return it as HTML string * * @param integer $strFolder * @param string $strField * @param integer $level * @param boolean $mount * * @return string */ public function generateAjax($strFolder, $strField, $level, $mount = false) { if (!\Environment::get('isAjaxRequest')) { return ''; } $this->strField = $strField; $this->loadDataContainer($this->strTable); // Load the current values switch ($GLOBALS['TL_DCA'][$this->strTable]['config']['dataContainer']) { case 'File': if (\Config::get($this->strField) != '') { $this->varValue = \Config::get($this->strField); } break; case 'Table': $this->import('Database'); if (!$this->Database->fieldExists($this->strField, $this->strTable)) { break; } $objField = $this->Database->prepare("SELECT " . $this->strField . " FROM " . $this->strTable . " WHERE id=?")->limit(1)->execute($this->strId); if ($objField->numRows) { $this->varValue = \StringUtil::deserialize($objField->{$this->strField}); } break; } $this->convertValuesToPaths(); if ($this->extensions != '') { $this->arrValidFileTypes = \StringUtil::trimsplit(',', $this->extensions); } return $this->renderFiletree(TL_ROOT . '/' . $strFolder, $level * 20, $mount, $this->isProtectedPath($strFolder)); }
/** * Compile the template * * @throws \UnusedArgumentsException If there are unused $_GET parameters * * @internal Do not call this method in your code. It will be made private in Contao 5.0. */ protected function compile() { $this->keywords = ''; $arrKeywords = \StringUtil::trimsplit(',', $GLOBALS['TL_KEYWORDS']); // Add the meta keywords if (strlen($arrKeywords[0])) { $this->keywords = str_replace(array("\n", "\r", '"'), array(' ', '', ''), implode(', ', array_unique($arrKeywords))); } // Parse the template $this->strBuffer = $this->parse(); // HOOK: add custom output filters if (isset($GLOBALS['TL_HOOKS']['outputFrontendTemplate']) && is_array($GLOBALS['TL_HOOKS']['outputFrontendTemplate'])) { foreach ($GLOBALS['TL_HOOKS']['outputFrontendTemplate'] as $callback) { $this->import($callback[0]); $this->strBuffer = $this->{$callback[0]}->{$callback[1]}($this->strBuffer, $this->strTemplate); } } // Replace insert tags $this->strBuffer = $this->replaceInsertTags($this->strBuffer); $this->strBuffer = $this->replaceDynamicScriptTags($this->strBuffer); // see #4203 // HOOK: allow to modify the compiled markup (see #4291) if (isset($GLOBALS['TL_HOOKS']['modifyFrontendPage']) && is_array($GLOBALS['TL_HOOKS']['modifyFrontendPage'])) { foreach ($GLOBALS['TL_HOOKS']['modifyFrontendPage'] as $callback) { $this->import($callback[0]); $this->strBuffer = $this->{$callback[0]}->{$callback[1]}($this->strBuffer, $this->strTemplate); } } // Check whether all $_GET parameters have been used (see #4277) if ($this->blnCheckRequest && \Input::hasUnusedGet()) { throw new \UnusedArgumentsException(); } parent::compile(); }