--- /dev/null
+<?php
+namespace wcf\system\package\plugin;
+use wcf\system\WCF;
+
+/**
+ * Installs, updates and deletes CMS pages.
+ *
+ * @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 acp.package.plugin
+ * @category Community Framework
+ */
+class PagePackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin {
+ /**
+ * @see AbstractXMLPackageInstallationPlugin::$className
+ */
+ public $className = 'wcf\data\clipboard\action\ClipboardActionEditor';
+
+ /**
+ * @see AbstractXMLPackageInstallationPlugin::$tagName
+ */
+ public $tagName = 'page';
+
+ /**
+ * @see AbstractXMLPackageInstallationPlugin::handleDelete()
+ */
+ protected function handleDelete(array $items) {
+ $sql = "DELETE FROM wcf".WCF_N."_".$this->tableName."
+ WHERE actionName = ?
+ AND actionClassName = ?
+ AND packageID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ foreach ($items as $item) {
+ $statement->execute(array(
+ $item['attributes']['name'],
+ $item['elements']['actionclassname'],
+ $this->installation->getPackageID()
+ ));
+ }
+ }
+
+ /**
+ * @see AbstractXMLPackageInstallationPlugin::getElement()
+ */
+ protected function getElement(\DOMXPath $xpath, array &$elements, \DOMElement $element) {
+ $nodeValue = $element->nodeValue;
+
+ // read pages
+ if ($element->tagName == 'pages') {
+ $nodeValue = array();
+
+ $pages = $xpath->query('child::ns:page', $element);
+ foreach ($pages as $page) {
+ $nodeValue[] = $page->nodeValue;
+ }
+ }
+
+ $elements[$element->tagName] = $nodeValue;
+ }
+
+ /**
+ * @see AbstractXMLPackageInstallationPlugin::prepareImport()
+ */
+ protected function prepareImport(array $data) {
+ $isStatic = (!empty($data['content']));
+
+ return [
+ 'controller' => ($isStatic) ? '' : $data['elements']['controller'],
+ 'controllerCustomURL' => ($isStatic) ? '' : $data['elements']['customurl'],
+ 'displayName' => $data['elements']['displayname'],
+ 'name' => $data['attributes']['name']
+ ];
+
+ $showOrder = (isset($data['elements']['showorder'])) ? intval($data['elements']['showorder']) : null;
+ $showOrder = $this->getShowOrder($showOrder, $data['elements']['actionclassname'], 'actionClassName');
+
+ return array(
+ 'actionClassName' => $data['elements']['actionclassname'],
+ 'actionName' => $data['attributes']['name'],
+ 'pages' => $data['elements']['pages'],
+ 'showOrder' => $showOrder
+ );
+ }
+
+ /**
+ * @see AbstractXMLPackageInstallationPlugin::findExistingItem()
+ */
+ protected function findExistingItem(array $data) {
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_".$this->tableName."
+ WHERE actionName = ?
+ AND actionClassName = ?
+ AND packageID = ?";
+ $parameters = array(
+ $data['actionName'],
+ $data['actionClassName'],
+ $this->installation->getPackageID()
+ );
+
+ return array(
+ 'sql' => $sql,
+ 'parameters' => $parameters
+ );
+ }
+
+ /**
+ * @see AbstractXMLPackageInstallationPlugin::import()
+ */
+ protected function import(array $row, array $data) {
+ // extract pages
+ $pages = $data['pages'];
+ unset($data['pages']);
+
+ // import or update action
+ $object = parent::import($row, $data);
+
+ // store pages for later import
+ $this->pages[$object->actionID] = $pages;
+ }
+
+ /**
+ * @see AbstractXMLPackageInstallationPlugin::postImport()
+ */
+ protected function postImport() {
+ // clear pages
+ $sql = "DELETE FROM wcf".WCF_N."_clipboard_page
+ WHERE packageID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($this->installation->getPackageID()));
+
+ if (!empty($this->pages)) {
+ // insert pages
+ $sql = "INSERT INTO wcf".WCF_N."_clipboard_page
+ (pageClassName, packageID, actionID)
+ VALUES (?, ?, ?)";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ foreach ($this->pages as $actionID => $pages) {
+ foreach ($pages as $pageClassName) {
+ $statement->execute(array(
+ $pageClassName,
+ $this->installation->getPackageID(),
+ $actionID
+ ));
+ }
+ }
+ }
+ }
+}
return $classData;
}
+ /**
+ * Attempts to resolve a custom controller, will return an empty array
+ * regardless if given controller would match an actual controller class.
+ *
+ * @param string $application application identifier
+ * @param string $controller url controller
+ * @return array empty array if there is no exact match
+ */
+ public function resolveCustomController($application, $controller) {
+ // TODO: check custom controller mappings
+
+ return [];
+ }
+
/**
* Transforms given controller into its url representation.
*
$this->routeHandler->setRequestHandler($this);
$this->routeHandler->setDefaultRoutes();
- if (!$this->routeHandler->matches()) {
+ if (!$this->routeHandler->matches($application)) {
if (ENABLE_DEBUG_MODE) {
throw new SystemException("Cannot handle request, no valid route provided.");
}
use wcf\system\event\EventHandler;
use wcf\system\exception\SystemException;
use wcf\system\request\route\DynamicRequestRoute;
+use wcf\system\request\route\IRequestRoute;
use wcf\system\SingletonFactory;
use wcf\system\WCF;
use wcf\util\FileUtil;
/**
* Returns true if a route matches. Please bear in mind, that the
- * first route which is able to consume all path components is used,
+ * first route that is able to consume all path components is used,
* even if other routes may fit better. Route order is crucial!
*
+ * @param string $application application identifier
* @return boolean
*/
- public function matches() {
+ public function matches($application) {
foreach ($this->routes as $route) {
if ($this->requestHandler->isACPRequest() != $route->isACP()) {
continue;
}
- if ($route->matches(self::getPathInfo())) {
+ $match = false;
+ if ($route instanceof IRequestRoute) {
+ $match = $route->matches($application, self::getPathInfo());
+ }
+ else if ($route instanceof IRoute) {
+ // legacy route
+ $match = $route->matches(self::getPathInfo());
+ }
+
+ if ($match) {
$this->routeData = $route->getRouteData();
$this->isDefaultController = $this->routeData['isDefaultController'];
$this->setPattern('~
/?
(?:
- (?P<controller>[A-Za-z0-9\-]+)
+ (?P<controller>
+ (?:
+ [a-z][a-z0-9]+
+ (?:-[a-z][a-z0-9]+)*
+ )+
+ )
(?:
/
(?P<id>\d+)
* @subpackage system.request
* @category Community Framework
*/
-interface IRequestRoute extends IRoute {
+interface IRequestRoute {
+ /**
+ * Builds a link upon route components.
+ *
+ * @param array $components list of url components
+ * @return string
+ */
+ public function buildLink(array $components);
+
+ /**
+ * Returns true if current route can handle the build request.
+ *
+ * @param array $components list of url components
+ * @return boolean
+ */
+ public function canHandle(array $components);
+
+ /**
+ * Returns parsed route data.
+ *
+ * @return array
+ */
+ public function getRouteData();
+
+ /**
+ * Returns true if route applies for ACP.
+ *
+ * @return boolean
+ */
+ public function isACP();
+
+ /**
+ * Returns true if given request url matches this route.
+ *
+ * @param string $application application identifier
+ * @param string $requestURL request url
+ * @return boolean
+ */
+ public function matches($application, $requestURL);
+
/**
* Configures this route to handle either ACP or frontend requests.
*
--- /dev/null
+<?php
+namespace wcf\system\request\route;
+
+use wcf\system\exception\SystemException;
+use wcf\system\request\ControllerMap;
+use wcf\util\FileUtil;
+
+class LookupRequestRoute implements IRequestRoute {
+ /**
+ * @var ControllerMap
+ */
+ protected $controllerMap;
+
+ protected $routeData = [];
+
+ public function __construct(ControllerMap $controllerMap) {
+ $this->controllerMap = $controllerMap;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function matches($application, $requestURL) {
+ $requestURL = FileUtil::addLeadingSlash($requestURL);
+
+ if ($requestURL === '/') {
+ // ignore empty urls and let them be handled by regular routes
+ return false;
+ }
+
+ $regex = '~^
+ /
+ (?P<controller>.+?)
+ (?:
+ (?P<id>[0-9]+)
+ (?:
+ -
+ (?P<title>[^/]+)
+ )?
+ /
+ )?
+ $~x';
+
+ if (preg_match($regex, $requestURL, $matches)) {
+ if (!empty($matches['id'])) {
+ // check for static controller URLs
+ $this->routeData = $this->controllerMap->resolveCustomController($application, $matches['controller']);
+ }
+
+ if (empty($this->routeData)) {
+ // try to match the entire url
+ $this->routeData = $this->controllerMap->resolveCustomController($application, $requestURL);
+ }
+ }
+
+ return (!empty($this->routeData));
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getRouteData() {
+ return $this->getRouteData();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setIsACP($isACP) {
+ // lookups are not supported for ACP requests
+ }
+
+ /**
+ * @inheritDoc
+ * @throws SystemException
+ */
+ public function buildLink(array $components) {
+ throw new SystemException('LookupRequestRoute cannot build links, please verify capabilities by calling canHandle() first.');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function canHandle(array $components) {
+ // this route cannot build routes, it is a one-way resolver
+ return false;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function isACP() {
+ // lookups are not supported for ACP requests
+ return false;
+ }
+}
\ No newline at end of file