--- /dev/null
+{include file='documentHeader'}
+
+<head>
+ <title>{if !$page->isLandingPage}{$content[title]} - {/if}{PAGE_TITLE|language}</title>
+
+ {include file='headInclude'}
+
+ <link rel="canonical" href="{$canonicalURL}">
+</head>
+
+<body id="tpl{$templateName|ucfirst}" data-template="{$templateName}" data-application="{$templateNameApplication}">
+
+{include file='header'}
+
+{if $page->isLandingPage}
+ <header class="boxHeadline">
+ <h1>{PAGE_TITLE|language}</h1>
+ {hascontent}<p>{content}{PAGE_DESCRIPTION|language}{/content}</p>{/hascontent}
+ </header>
+{else}
+ <header class="boxHeadline">
+ <h1>{$content[title]}</h1>
+ </header>
+{/if}
+
+{include file='userNotice'}
+
+<div class="contentNavigation">
+ {hascontent}
+ <nav>
+ <ul>
+ {content}
+ {event name='contentNavigationButtonsTop'}
+ {/content}
+ </ul>
+ </nav>
+ {/hascontent}
+</div>
+
+<section class="cmsContent htmlContent">
+ {@$content[content]}
+</section>
+
+<div class="contentNavigation">
+ {hascontent}
+ <nav>
+ <ul>
+ {content}
+ {event name='contentNavigationButtonsBottom'}
+ {/content}
+ </ul>
+ </nav>
+ {/hascontent}
+</div>
+
+{include file='footer'}
+
+</body>
+</html>
<?php
namespace wcf\data\page;
use wcf\data\DatabaseObject;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
use wcf\system\WCF;
/**
/**
* Returns the page content.
*
- * @return array
+ * @return array content data
*/
public function getPageContent() {
- $content = array();
+ $content = [];
+
$sql = "SELECT *
FROM wcf".WCF_N."_page_content
- WHERE pageID = ?";
+ WHERE pageID = ?";
$statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute(array($this->pageID));
+ $statement->execute([$this->pageID]);
while ($row = $statement->fetchArray()) {
- $content[($row['languageID'] ?: 0)] = array(
+ $content[($row['languageID'] ?: 0)] = [
'title' => $row['title'],
'content' => $row['content'],
'metaDescription' => $row['metaDescription'],
'metaKeywords' => $row['metaKeywords'],
'customURL' => $row['customURL']
- );
+ ];
}
return $content;
}
+ /**
+ * Returns content for a single language, passing `null` for `$languageID` is undefined
+ * for multilingual pages.
+ *
+ * @param integer $languageID language id or `null` if there are no localized versions
+ * @return string[] page content data
+ * @throws \wcf\system\database\DatabaseException
+ */
+ public function getPageContentByLanguage($languageID = null) {
+ $conditions = new PreparedStatementConditionBuilder();
+ $conditions->add("pageID = ?", [$this->pageID]);
+ if ($this->isMultilingual) $conditions->add("languageID = ?", [$languageID]);
+ else $conditions->add("languageID IS NULL");
+
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_page_content
+ ".$conditions;
+ $statement = WCF::getDB()->prepareStatement($sql, 1);
+ $statement->execute($conditions->getParameters());
+ $row = $statement->fetchSingleRow();
+
+ return ($row !== false) ? $row : [];
+ }
+
/**
* Returns the page URL.
*
// @todo
}
+ /**
+ * Returns the page with the given identifier.
+ *
+ * @param string $identifier unique page identifier
+ * @return Page
+ */
+ public static function getPageByIdentifier($identifier) {
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_page
+ WHERE identifier = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute([$identifier]);
+
+ return $statement->fetchObject(Page::class);
+ }
+
/**
* Returns the page with the given name.
*
FROM wcf".WCF_N."_page
WHERE name = ?";
$statement = WCF::getDB()->prepareStatement($sql);
- $statement->execute(array($name));
- $row = $statement->fetchArray();
- if ($row !== false) return new Page(null, $row);
+ $statement->execute([$name]);
- return null;
+ return $statement->fetchObject(Page::class);
}
}
--- /dev/null
+<?php
+namespace wcf\page;
+use wcf\data\page\Page;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\request\LinkHandler;
+use wcf\system\WCF;
+
+/**
+ * Generic controller to display cms content.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage page
+ * @category Community Framework
+ */
+class CmsPage extends AbstractPage {
+ /**
+ * @var string[]
+ */
+ public $content;
+
+ /**
+ * @var integer
+ */
+ public $languageID;
+
+ /**
+ * @var Page
+ */
+ public $page;
+
+ /**
+ * @var integer
+ */
+ public $pageID;
+
+ /**
+ * @inheritDoc
+ * @throws IllegalLinkException
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (isset($_GET['languageID'])) $this->languageID = intval($_GET['languageID']);
+ if (isset($_GET['pageID'])) $this->pageID = intval($_GET['pageID']);
+
+ if ($this->pageID) {
+ $this->page = new Page($this->pageID);
+ }
+
+ if ($this->page === null) {
+ throw new IllegalLinkException();
+ }
+
+ $this->content = $this->page->getPageContentByLanguage($this->languageID);
+ if (empty($this->content)) {
+ throw new IllegalLinkException();
+ }
+
+ $this->canonicalURL = LinkHandler::getInstance()->getCmsLink($this->pageID, $this->languageID);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign([
+ 'canonicalURL' => $this->canonicalURL,
+ 'content' => $this->content,
+ 'contentLanguageID' => $this->languageID,
+ 'page' => $this->page,
+ 'pageID' => $this->pageID
+ ]);
+ }
+}
$data['reverse'][$abbreviations[$packageID]][preg_replace('~^.*?([A-Za-z0-9]+)(?:Action|Form|Page)~', '$1', $row['controller'])] = $customUrl;
}
else {
- $cmsIdentifier = '__WCF_CMS__' . $row['pageID'] . '-' . $row['languageID'];
+ $cmsIdentifier = '__WCF_CMS__' . $row['pageID'] . '-' . ($row['languageID'] ?: 0);
$data['lookup'][$abbreviations[$packageID]][$customUrl] = $cmsIdentifier;
$data['reverse'][$abbreviations[$packageID]][$cmsIdentifier] = $customUrl;
}
--- /dev/null
+<?php
+namespace wcf\system\request;
+use wcf\system\SingletonFactory;
+
+class CmsLinkHandler extends SingletonFactory {
+ public function getLink($pageID, $languageID = -1) {
+ return LinkHandler::getInstance()->getLink('Cms', [
+ 'application' => ''
+ ]);
+ }
+}
<?php
namespace wcf\system\request;
+use wcf\page\CmsPage;
use wcf\system\cache\builder\RoutingCacheBuilder;
use wcf\system\exception\SystemException;
use wcf\system\SingletonFactory;
-use wcf\util\StringUtil;
/**
* Resolves incoming requests and performs lookups for controller to url mappings.
* @category Community Framework
*/
class ControllerMap extends SingletonFactory {
+ /**
+ * @var string[][]
+ */
protected $ciControllers;
+ /**
+ * @var string[][]
+ */
protected $customUrls;
/**
*/
protected $lookupCache = [];
+ /**
+ * @inheritDoc
+ * @throws SystemException
+ */
protected function init() {
$this->ciControllers = RoutingCacheBuilder::getInstance()->getData([], 'ciControllers');
$this->customUrls = RoutingCacheBuilder::getInstance()->getData([], 'customUrls');
if (isset($this->customUrls['lookup'][$application]) && isset($this->customUrls['lookup'][$application][$controller])) {
$data = $this->customUrls['lookup'][$application][$controller];
if (preg_match('~^__WCF_CMS__(?P<pageID>\d+)-(?P<languageID>\d+)$~', $data, $matches)) {
- // TODO: this does not work, it should match the returned array below
return [
- 'controller' => '\\wcf\\page\\CmsPage',
+ 'className' => CmsPage::class,
+ 'controller' => 'cms',
+ 'pageType' => 'page',
'languageID' => $matches['languageID'],
'pageID' => $matches['pageID']
];
return $urlController;
}
+ /**
+ * Looks up a cms page URL, returns an array containing the application identifier
+ * and url controller name or null if there was no match.
+ *
+ * @param integer $pageID page id
+ * @param integer $languageID content language id
+ * @return string[]|null
+ */
+ public function lookupCmsPage($pageID, $languageID) {
+ $key = '__WCF_CMS__' . $pageID . '-' . ($languageID ?: 0);
+ foreach ($this->customUrls['reverse'] as $application => $reverseURLs) {
+ if (isset($reverseURLs[$key])) {
+ return [
+ 'application' => $application,
+ 'controller' => $reverseURLs[$key]
+ ];
+ }
+ }
+
+ return null;
+ }
+
/**
* Returns the class data for the active request or null if for the given
* configuration no proper class exist.
namespace wcf\system\request;
use wcf\data\DatabaseObjectDecorator;
use wcf\system\application\ApplicationHandler;
+use wcf\system\language\LanguageFactory;
use wcf\system\menu\page\PageMenu;
use wcf\system\Regex;
use wcf\system\SingletonFactory;
return $url;
}
+
+ /**
+ * Returns the full URL to a CMS page. The `$languageID` parameter is optional and if not
+ * present (or the integer value `-1` is given) will cause the handler to pick the correct
+ * language version based upon the user's language.
+ *
+ * Passing in an illegal page id will cause this method to fail silently, returning an
+ * empty string.
+ *
+ * @param integer $pageID page id
+ * @param integer $languageID language id, optional
+ * @return string full URL of empty string if `$pageID` is invalid
+ * @throws \wcf\system\exception\SystemException
+ */
+ public function getCmsLink($pageID, $languageID = -1) {
+ // use current language
+ if ($languageID === -1) {
+ $data = ControllerMap::getInstance()->lookupCmsPage($pageID, WCF::getLanguage()->languageID);
+
+ // no result
+ if ($data === null) {
+ // attempt to use the default language instead
+ if (LanguageFactory::getInstance()->getDefaultLanguageID() != WCF::getLanguage()->languageID) {
+ $data = ControllerMap::getInstance()->lookupCmsPage($pageID, LanguageFactory::getInstance()->getDefaultLanguageID());
+ }
+
+ // no result, possibly this is a non-multilingual page
+ if ($data === null) {
+ $data = ControllerMap::getInstance()->lookupCmsPage($pageID, null);
+ }
+
+ // still no result, page does not exist at all
+ if ($data === null) {
+ return '';
+ }
+ }
+ }
+ else {
+ $data = ControllerMap::getInstance()->lookupCmsPage($pageID, $languageID);
+
+ // no result, page does not exist or at least not in the given language
+ if ($data === null) {
+ return '';
+ }
+ }
+
+ return $this->getLink($data['controller'], [
+ 'application' => $data['application']
+ ]);
+ }
}
$this->activeRequest = new Request($classData['className'], $classData['controller'], $classData['pageType']);
}
catch (SystemException $e) {
+ die("<pre>".$e->getMessage());
throw new IllegalLinkException();
}
}
// set default controller
$applicationObj = WCF::getApplicationObject(ApplicationHandler::getInstance()->getApplication($application));
$routeData['controller'] = preg_replace('~^.*?\\\([^\\\]+)(?:Action|Form|Page)$~', '\\1', $applicationObj->getPrimaryController());
- $routeData['controller'] = ControllerMap::getInstance()->lookup($routeData['controller']);
+ $routeData['controller'] = ControllerMap::getInstance()->lookup($application, $routeData['controller']);
}
/**
--- /dev/null
+<?php
+namespace wcf\system\template\plugin;
+use wcf\data\page\Page;
+use wcf\system\exception\SystemException;
+use wcf\system\language\LanguageFactory;
+use wcf\system\request\LinkHandler;
+use wcf\system\template\TemplateEngine;
+
+/**
+ * Template block plugin which generates a link to a CMS page.
+ *
+ * The unique identifier and the `pageID` attribute as well as the `language` and
+ * `languageID` attribute are mutually exclusive. Combining these values will not
+ * cause this plugin to fail, but instead `pageID` and `languageID` will be considered
+ * to be of higher specificity and the unique identifier / `language` attribute will
+ * be ignored.
+ *
+ * Usage, language is automatically resolved:
+ * {page}com.woltlab.wcf.CookiePolicy{/page}
+ *
+ * `pageID` attribute instead of unique identifier:
+ * {page pageID=1}{/page}
+ *
+ * `language` attribute to force a localized version:
+ * {page language='de'}com.woltlab.wcf.CookiePolicy{/page}
+ *
+ * `languageID` attribute similar to the `language` attribute:
+ * {page languageID=2}com.woltlab.wcf.CookiePolicy{/page}
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.template.plugin
+ * @category Community Framework
+ */
+class PageBlockTemplatePlugin implements IBlockTemplatePlugin {
+ /**
+ * internal loop counter
+ * @var integer
+ */
+ protected $counter = 0;
+
+ /**
+ * @inheritDoc
+ */
+ public function execute($tagArgs, $blockContent, TemplateEngine $tplObj) {
+ $pageID = null;
+ if (!empty($tagArgs['pageID'])) {
+ $pageID = intval($tagArgs['pageID']);
+ }
+ else if (!empty($blockContent)) {
+ $page = Page::getPageByIdentifier($blockContent);
+ $pageID = ($page) ? $page->pageID : 0;
+ }
+
+ if ($pageID === null) {
+ throw new SystemException("Missing 'pageID' attribute or unique identifier.");
+ }
+
+ $languageID = -1;
+ if (!empty($tagArgs['languageID'])) {
+ $languageID = intval($tagArgs['languageID']);
+ }
+ else if (!empty($tagArgs['language'])) {
+ $language = LanguageFactory::getInstance()->getLanguageByCode($tagArgs['language']);
+ if ($language !== null) $languageID = $language->languageID;
+ }
+
+ return LinkHandler::getInstance()->getCmsLink($pageID, $languageID);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function init($tagArgs, TemplateEngine $tplObj) {
+ $this->counter = 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function next(TemplateEngine $tplObj) {
+ if ($this->counter == 0) {
+ $this->counter++;
+ return true;
+ }
+
+ return false;
+ }
+}
}
}
}
+
+.nativeList {
+ margin: 1em 0 1em 40px;
+
+ ul,
+ ol {
+ margin-bottom: 0;
+ margin-top: 0;
+ }
+
+ li {
+ margin: 5px 0;
+ }
+}
+
+ul.nativeList {
+ list-style-type: disc;
+}
+ol.nativeList {
+ list-style-type: decimal;
+}
+
+/* simulate native HTML styles for certain elements */
+.htmlContent {
+ p {
+ margin: 1em 0;
+ }
+
+ h1 {
+ @extend .wcfFontTitle;
+ }
+
+ h2 {
+ @extend .wcfFontHeadline;
+ }
+
+ h1, h2, h3, h4, h5, h6 {
+ font-weight: bold;
+ margin: 0.5em 0;
+ }
+
+ ul, ol {
+ @extend .nativeList;
+ }
+
+ ul {
+ list-style-type: disc;
+ }
+
+ ol {
+ list-style-type: decimal;
+ }
+}