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