Provides RESTful access to database objects.
authorKiv4h <jeffrey.reichardt@googlemail.com>
Fri, 1 Jun 2012 17:38:34 +0000 (19:38 +0200)
committerKiv4h <jeffrey.reichardt@googlemail.com>
Fri, 1 Jun 2012 17:38:34 +0000 (19:38 +0200)
com.woltlab.wcf/option.xml
wcfsetup/install/files/lib/action/APIAction.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/rest/response/IRESTfulResponse.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/request/RouteHandler.class.php

index 00b1e2d44fcca233adfdcb6a64b3070db33e2e63..daf92b2c2dd307f28ef94372dc700979edbfed6d 100644 (file)
                                <optiontype>boolean</optiontype>
                                <!-- TODO: change to '0' (production mode) later -->
                                <defaultvalue>1</defaultvalue>
+                       </option>       
+                       
+                       <option name="module_api_access">
+                               <categoryname>module.system</categoryname>
+                               <optiontype>boolean</optiontype>
+                               <!-- TODO: change to '0' (production mode) later -->
+                               <defaultvalue>1</defaultvalue>
                        </option>
                        
                        <!-- general.page -->
diff --git a/wcfsetup/install/files/lib/action/APIAction.class.php b/wcfsetup/install/files/lib/action/APIAction.class.php
new file mode 100644 (file)
index 0000000..e8b3707
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+namespace wcf\action;
+use wcf\data\DatabaseObject;
+use wcf\system\api\rest\response\IRESTfulResponse;
+use wcf\system\exception\AJAXException;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\request\RouteHandler;
+use wcf\system\WCF;
+
+/**
+ * This action provides RESTful access to database objects.
+ *
+ * @author             Jeffrey Reichardt
+ * @copyright  2001-2012 WoltLab GmbH
+ * @license            GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage action
+ * @category   Community Framework
+ */
+final class APIAction extends AbstractAjaxAction {
+       /**
+        * needed modules to execute this action
+        * @var array<string>
+        */
+       public $neededModules = array('MODULE_API_ACCESS');
+       
+       /**
+        * @see wcf\action\IAction::execute()
+        */
+       public function execute() {
+               parent::execute();
+               
+               $routeData = RouteHandler::getInstance()->getRouteData();
+               
+               if (!isset($routeData['className']) || !isset($routeData['id'])) {
+                       throw new IllegalLinkException();
+               }               
+                       
+               // validate class name
+               if (!preg_match('~^[a-z0-9_]+$~i', $routeData['className'])) {
+                       throw new AJAXException("Illegal class name '".$routeData['className']."'");
+               }
+               
+               //get class data
+               $classData = $this->getClassData($routeData['className']);
+               
+               if ($classData === null) {
+                       throw new AJAXException("unable to find class for controller '".$routeData['className']."'");
+               }
+               else if (!class_exists($classData['className'])) {
+                       throw new AJAXException("unable to find class '".$classData['className']."'");
+               }               
+               
+               //create object
+               $object = new $classData['className']($routeData['id']);
+               
+               if (!$object || !($object instanceof IRESTfulResponse)) {
+                       throw new AJAXException("unable to create object of '".$routeData['className']."'");
+               }
+               
+               $this->data = $this->prune($object);
+               
+               if(empty($this->data)) {
+                       throw new AJAXException("no results");
+               }
+               
+               $this->executed();
+       }
+       
+       /**
+        * @see wcf\action\AbstractAction::executed()
+        */
+       protected function executed() {
+               $this->sendJsonResponse($this->data);
+       }
+       
+       /**
+        * Checks fields, prunes given array and returns it
+        *
+        * @return array
+        */
+       protected function prune(DatabaseObject $object) {
+               $prunedArray = array();
+       
+               foreach($object->getResponseFields() as $fieldName) {
+                       if ($object->$fieldName) {
+                               $prunedArray[$fieldName] = $object->$fieldName;
+                       }
+               }
+               
+               return $prunedArray;
+       }
+       
+       /**
+        * Tries to find class and returns class name and controller.
+        *
+        * @return array<string>
+        */
+       protected function getClassData($controller, $application = 'wcf') {
+               $className = $application.'\\data\\'.$controller.'\\'.ucfirst($controller);
+               if ($application != 'wcf' && !class_exists($className)) {
+                       $className = 'wcf\\data\\'.$controller.'\\'.ucfirst($controller);
+               }
+               if (!class_exists($className)) {
+                       return null;
+               }
+               
+               // check whether the class is abstract
+               $reflectionClass = new \ReflectionClass($className);
+               if ($reflectionClass->isAbstract()) {
+                       return null;
+               }
+               
+               return array(
+                       'className' => $className,
+                       'controller' => $controller
+               );
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/api/rest/response/IRESTfulResponse.class.php b/wcfsetup/install/files/lib/system/api/rest/response/IRESTfulResponse.class.php
new file mode 100644 (file)
index 0000000..ba1088b
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\system\api\rest\response;
+
+/**
+ * Interface for all rest'able database objects.
+ *
+ * @author             Jeffrey Reichardt
+ * @copyright  2001-2012 WoltLab GmbH
+ * @license            GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.api.rest.response
+ * @category   Community Framework
+ */
+interface IRESTfulResponse {
+       /**
+        * Returns a list of fields for responsing data.
+        *
+        * @return array<string>
+        */
+       public function getResponseFields();
+}
index bf06ab0707bd7597cc1c2fc155322d629762137e..d3fe7d7f47020a2282f54fd832c4e1c7fdee9abd 100644 (file)
@@ -69,6 +69,15 @@ class RouteHandler extends SingletonFactory {
                $acpRoute->setParameterOption('id', null, '\d+', true);
                $this->addRoute($acpRoute);
                
+               if (MODULE_API_ACCESS) {
+                       $apiRoute = new Route('api');
+                       $apiRoute->setSchema('/{controller}/{className}-{id}');
+                       $apiRoute->setParameterOption('controller', 'API');
+                       $apiRoute->setParameterOption('className', null, '\w+');
+                       $apiRoute->setParameterOption('id', null, '\d+');
+                       $this->addRoute($apiRoute);
+               }
+               
                $defaultRoute = new Route('default');
                $defaultRoute->setSchema('/{controller}/{id}');
                $defaultRoute->setParameterOption('controller', 'Index', null, true);