annotate core/modules/rest/src/Plugin/ResourceBase.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 1fec387a4317
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\rest\Plugin;
Chris@0 4
Chris@0 5 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
Chris@0 6 use Drupal\Core\Plugin\PluginBase;
Chris@0 7 use Psr\Log\LoggerInterface;
Chris@0 8 use Symfony\Component\DependencyInjection\ContainerInterface;
Chris@0 9 use Symfony\Component\Routing\Route;
Chris@0 10 use Symfony\Component\Routing\RouteCollection;
Chris@0 11
Chris@0 12 /**
Chris@0 13 * Common base class for resource plugins.
Chris@0 14 *
Chris@0 15 * Note that this base class' implementation of the permissions() method
Chris@0 16 * generates a permission for every method for a resource. If your resource
Chris@0 17 * already has its own access control mechanism, you should opt out from this
Chris@0 18 * default permissions() method by overriding it.
Chris@0 19 *
Chris@0 20 * @see \Drupal\rest\Annotation\RestResource
Chris@0 21 * @see \Drupal\rest\Plugin\Type\ResourcePluginManager
Chris@0 22 * @see \Drupal\rest\Plugin\ResourceInterface
Chris@0 23 * @see plugin_api
Chris@0 24 *
Chris@0 25 * @ingroup third_party
Chris@0 26 */
Chris@0 27 abstract class ResourceBase extends PluginBase implements ContainerFactoryPluginInterface, ResourceInterface {
Chris@0 28
Chris@0 29 /**
Chris@0 30 * The available serialization formats.
Chris@0 31 *
Chris@0 32 * @var array
Chris@0 33 */
Chris@0 34 protected $serializerFormats = [];
Chris@0 35
Chris@0 36 /**
Chris@0 37 * A logger instance.
Chris@0 38 *
Chris@0 39 * @var \Psr\Log\LoggerInterface
Chris@0 40 */
Chris@0 41 protected $logger;
Chris@0 42
Chris@0 43 /**
Chris@0 44 * Constructs a Drupal\rest\Plugin\ResourceBase object.
Chris@0 45 *
Chris@0 46 * @param array $configuration
Chris@0 47 * A configuration array containing information about the plugin instance.
Chris@0 48 * @param string $plugin_id
Chris@0 49 * The plugin_id for the plugin instance.
Chris@0 50 * @param mixed $plugin_definition
Chris@0 51 * The plugin implementation definition.
Chris@0 52 * @param array $serializer_formats
Chris@0 53 * The available serialization formats.
Chris@0 54 * @param \Psr\Log\LoggerInterface $logger
Chris@0 55 * A logger instance.
Chris@0 56 */
Chris@0 57 public function __construct(array $configuration, $plugin_id, $plugin_definition, array $serializer_formats, LoggerInterface $logger) {
Chris@0 58 parent::__construct($configuration, $plugin_id, $plugin_definition);
Chris@0 59 $this->serializerFormats = $serializer_formats;
Chris@0 60 $this->logger = $logger;
Chris@0 61 }
Chris@0 62
Chris@0 63 /**
Chris@0 64 * {@inheritdoc}
Chris@0 65 */
Chris@0 66 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
Chris@0 67 return new static(
Chris@0 68 $configuration,
Chris@0 69 $plugin_id,
Chris@0 70 $plugin_definition,
Chris@0 71 $container->getParameter('serializer.formats'),
Chris@0 72 $container->get('logger.factory')->get('rest')
Chris@0 73 );
Chris@0 74 }
Chris@0 75
Chris@0 76 /**
Chris@0 77 * Implements ResourceInterface::permissions().
Chris@0 78 *
Chris@0 79 * Every plugin operation method gets its own user permission. Example:
Chris@0 80 * "restful delete entity:node" with the title "Access DELETE on Node
Chris@0 81 * resource".
Chris@0 82 */
Chris@0 83 public function permissions() {
Chris@0 84 $permissions = [];
Chris@0 85 $definition = $this->getPluginDefinition();
Chris@0 86 foreach ($this->availableMethods() as $method) {
Chris@0 87 $lowered_method = strtolower($method);
Chris@0 88 $permissions["restful $lowered_method $this->pluginId"] = [
Chris@0 89 'title' => $this->t('Access @method on %label resource', ['@method' => $method, '%label' => $definition['label']]),
Chris@0 90 ];
Chris@0 91 }
Chris@0 92 return $permissions;
Chris@0 93 }
Chris@0 94
Chris@0 95 /**
Chris@0 96 * {@inheritdoc}
Chris@0 97 */
Chris@0 98 public function routes() {
Chris@0 99 $collection = new RouteCollection();
Chris@0 100
Chris@0 101 $definition = $this->getPluginDefinition();
Chris@0 102 $canonical_path = isset($definition['uri_paths']['canonical']) ? $definition['uri_paths']['canonical'] : '/' . strtr($this->pluginId, ':', '/') . '/{id}';
Chris@0 103 $create_path = isset($definition['uri_paths']['create']) ? $definition['uri_paths']['create'] : '/' . strtr($this->pluginId, ':', '/');
Chris@0 104 // BC: the REST module originally created the POST URL for a resource by
Chris@0 105 // reading the 'https://www.drupal.org/link-relations/create' URI path from
Chris@0 106 // the plugin annotation. For consistency with entity type definitions, that
Chris@0 107 // then changed to reading the 'create' URI path. For any REST Resource
Chris@0 108 // plugins that were using the old mechanism, we continue to support that.
Chris@0 109 if (!isset($definition['uri_paths']['create']) && isset($definition['uri_paths']['https://www.drupal.org/link-relations/create'])) {
Chris@0 110 $create_path = $definition['uri_paths']['https://www.drupal.org/link-relations/create'];
Chris@0 111 }
Chris@0 112
Chris@0 113 $route_name = strtr($this->pluginId, ':', '.');
Chris@0 114
Chris@0 115 $methods = $this->availableMethods();
Chris@0 116 foreach ($methods as $method) {
Chris@0 117 $route = $this->getBaseRoute($canonical_path, $method);
Chris@0 118
Chris@0 119 switch ($method) {
Chris@0 120 case 'POST':
Chris@0 121 $route->setPath($create_path);
Chris@0 122 $collection->add("$route_name.$method", $route);
Chris@0 123 break;
Chris@0 124
Chris@0 125 case 'GET':
Chris@0 126 case 'HEAD':
Chris@0 127 // Restrict GET and HEAD requests to the media type specified in the
Chris@0 128 // HTTP Accept headers.
Chris@0 129 foreach ($this->serializerFormats as $format_name) {
Chris@0 130 // Expose one route per available format.
Chris@0 131 $format_route = clone $route;
Chris@0 132 $format_route->addRequirements(['_format' => $format_name]);
Chris@0 133 $collection->add("$route_name.$method.$format_name", $format_route);
Chris@0 134 }
Chris@0 135 break;
Chris@0 136
Chris@0 137 default:
Chris@0 138 $collection->add("$route_name.$method", $route);
Chris@0 139 break;
Chris@0 140 }
Chris@0 141 }
Chris@0 142
Chris@0 143 return $collection;
Chris@0 144 }
Chris@0 145
Chris@0 146 /**
Chris@0 147 * Provides predefined HTTP request methods.
Chris@0 148 *
Chris@0 149 * Plugins can override this method to provide additional custom request
Chris@0 150 * methods.
Chris@0 151 *
Chris@0 152 * @return array
Chris@0 153 * The list of allowed HTTP request method strings.
Chris@0 154 */
Chris@0 155 protected function requestMethods() {
Chris@0 156 return [
Chris@0 157 'HEAD',
Chris@0 158 'GET',
Chris@0 159 'POST',
Chris@0 160 'PUT',
Chris@0 161 'DELETE',
Chris@0 162 'TRACE',
Chris@0 163 'OPTIONS',
Chris@0 164 'CONNECT',
Chris@0 165 'PATCH',
Chris@0 166 ];
Chris@0 167 }
Chris@0 168
Chris@0 169 /**
Chris@0 170 * {@inheritdoc}
Chris@0 171 */
Chris@0 172 public function availableMethods() {
Chris@0 173 $methods = $this->requestMethods();
Chris@0 174 $available = [];
Chris@0 175 foreach ($methods as $method) {
Chris@0 176 // Only expose methods where the HTTP request method exists on the plugin.
Chris@0 177 if (method_exists($this, strtolower($method))) {
Chris@0 178 $available[] = $method;
Chris@0 179 }
Chris@0 180 }
Chris@0 181 return $available;
Chris@0 182 }
Chris@0 183
Chris@0 184 /**
Chris@0 185 * Gets the base route for a particular method.
Chris@0 186 *
Chris@0 187 * @param string $canonical_path
Chris@0 188 * The canonical path for the resource.
Chris@0 189 * @param string $method
Chris@0 190 * The HTTP method to be used for the route.
Chris@0 191 *
Chris@0 192 * @return \Symfony\Component\Routing\Route
Chris@0 193 * The created base route.
Chris@0 194 */
Chris@0 195 protected function getBaseRoute($canonical_path, $method) {
Chris@0 196 return new Route($canonical_path, [
Chris@0 197 '_controller' => 'Drupal\rest\RequestHandler::handle',
Chris@0 198 ],
Chris@0 199 $this->getBaseRouteRequirements($method),
Chris@0 200 [],
Chris@0 201 '',
Chris@0 202 [],
Chris@0 203 // The HTTP method is a requirement for this route.
Chris@0 204 [$method]
Chris@0 205 );
Chris@0 206 }
Chris@0 207
Chris@0 208 /**
Chris@0 209 * Gets the base route requirements for a particular method.
Chris@0 210 *
Chris@0 211 * @param $method
Chris@0 212 * The HTTP method to be used for the route.
Chris@0 213 *
Chris@0 214 * @return array
Chris@0 215 * An array of requirements for parameters.
Chris@0 216 */
Chris@0 217 protected function getBaseRouteRequirements($method) {
Chris@0 218 $lower_method = strtolower($method);
Chris@0 219 // Every route MUST have requirements that result in the access manager
Chris@0 220 // having access checks to check. If it does not, the route is made
Chris@0 221 // inaccessible. So, we default to granting access to everyone. If a
Chris@0 222 // permission exists, then we add that below. The access manager requires
Chris@0 223 // that ALL access checks must grant access, so this still results in
Chris@0 224 // correct behavior.
Chris@0 225 $requirements = [
Chris@0 226 '_access' => 'TRUE',
Chris@0 227 ];
Chris@0 228
Chris@0 229 // Only specify route requirements if the default permission exists. For any
Chris@0 230 // more advanced route definition, resource plugins extending this base
Chris@0 231 // class must override this method.
Chris@0 232 $permission = "restful $lower_method $this->pluginId";
Chris@0 233 if (isset($this->permissions()[$permission])) {
Chris@0 234 $requirements['_permission'] = $permission;
Chris@0 235 }
Chris@0 236
Chris@0 237 return $requirements;
Chris@0 238 }
Chris@0 239
Chris@0 240 }