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