예제 #1
0
 /**
  * {@inheritdoc}
  */
 public function getTitle(Request $request, Route $route)
 {
     $route_title = NULL;
     // A dynamic title takes priority. Route::getDefault() returns NULL if the
     // named default is not set.  By testing the value directly, we also avoid
     // trying to use empty values.
     if ($callback = $route->getDefault('_title_callback')) {
         $callable = $this->controllerResolver->getControllerFromDefinition($callback);
         $arguments = $this->controllerResolver->getArguments($request, $callable);
         $route_title = call_user_func_array($callable, $arguments);
     } elseif ($title = $route->getDefault('_title')) {
         $options = array();
         if ($context = $route->getDefault('_title_context')) {
             $options['context'] = $context;
         }
         $args = array();
         if ($raw_parameters = $request->attributes->get('_raw_variables')) {
             foreach ($raw_parameters->all() as $key => $value) {
                 $args['@' . $key] = $value;
                 $args['%' . $key] = $value;
             }
         }
         if ($title_arguments = $route->getDefault('_title_arguments')) {
             $args = array_merge($args, (array) $title_arguments);
         }
         // Fall back to a static string from the route.
         $route_title = $this->t($title, $args, $options);
     }
     return $route_title;
 }
예제 #2
0
 /**
  * {@inheritdoc}
  */
 public function collect(Request $request, Response $response, \Exception $exception = NULL)
 {
     parent::collect($request, $response, $exception);
     $controller = $this->controllerResolver->getController($request);
     $this->data['controller'] = $this->getMethodData($controller[0], $controller[1]);
     $this->data['access_check'] = $this->accessCheck;
 }
예제 #3
0
 /**
  * Checks access for the account and route using the custom access checker.
  *
  * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
  *   The route match object to be checked.
  * @param \Drupal\Core\Session\AccountInterface $account
  *   The account being checked.
  *
  * @return \Drupal\Core\Access\AccessResultInterface
  *   The access result.
  */
 public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account)
 {
     $callable = $this->controllerResolver->getControllerFromDefinition($route->getRequirement('_custom_access'));
     $arguments_resolver = $this->argumentsResolverFactory->getArgumentsResolver($route_match, $account);
     $arguments = $arguments_resolver->getArguments($callable);
     return call_user_func_array($callable, $arguments);
 }
 /**
  * Returns the result of invoking the sub-controller.
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The request object.
  * @param mixed $controller_definition
  *   A controller definition string, or a callable object/closure.
  *
  * @return mixed
  *   The result of invoking the controller. Render arrays, strings, HtmlPage,
  *   and HtmlFragment objects are possible.
  */
 public function getContentResult(Request $request, $controller_definition)
 {
     if ($controller_definition instanceof \Closure) {
         $callable = $controller_definition;
     } else {
         $callable = $this->controllerResolver->getControllerFromDefinition($controller_definition);
     }
     $arguments = $this->controllerResolver->getArguments($request, $callable);
     $page_content = call_user_func_array($callable, $arguments);
     return $page_content;
 }
 /**
  * Sets a response given a (main content) render array.
  *
  * @param \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event
  *   The event to process.
  */
 public function onViewRenderArray(GetResponseForControllerResultEvent $event)
 {
     $request = $event->getRequest();
     $result = $event->getControllerResult();
     // Render the controller result into a response if it's a render array.
     if (is_array($result) && ($request->query->has(static::WRAPPER_FORMAT) || $request->getRequestFormat() == 'html')) {
         $wrapper = $request->query->get(static::WRAPPER_FORMAT, 'html');
         // Fall back to HTML if the requested wrapper envelope is not available.
         $wrapper = isset($this->mainContentRenderers[$wrapper]) ? $wrapper : 'html';
         $renderer = $this->classResolver->getInstanceFromDefinition($this->mainContentRenderers[$wrapper]);
         $event->setResponse($renderer->renderResponse($result, $request, $this->routeMatch));
     }
 }
예제 #6
0
 /**
  * Tests a YAML file containing both static permissions and a callback.
  */
 public function testPermissionsYamlStaticAndCallback()
 {
     vfsStreamWrapper::register();
     $root = new vfsStreamDirectory('modules');
     vfsStreamWrapper::setRoot($root);
     $this->moduleHandler = $this->getMock('Drupal\\Core\\Extension\\ModuleHandlerInterface');
     $this->moduleHandler->expects($this->once())->method('getModuleDirectories')->willReturn(array('module_a' => vfsStream::url('modules/module_a')));
     $url = vfsStream::url('modules');
     mkdir($url . '/module_a');
     file_put_contents($url . '/module_a/module_a.permissions.yml', "'access module a':\n  title: 'Access A'\n  description: 'bla bla'\npermission_callbacks:\n  - 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription'\n");
     $modules = array('module_a');
     $extensions = array('module_a' => $this->mockModuleExtension('module_a', 'Module a'));
     $this->moduleHandler->expects($this->any())->method('getImplementations')->with('permission')->willReturn(array());
     $this->moduleHandler->expects($this->any())->method('getModuleList')->willReturn(array_flip($modules));
     $this->controllerResolver->expects($this->once())->method('getControllerFromDefinition')->with('Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription')->willReturn(array(new TestPermissionCallbacks(), 'titleDescription'));
     $this->permissionHandler = new TestPermissionHandler($this->moduleHandler, $this->stringTranslation, $this->controllerResolver);
     // Setup system_rebuild_module_data().
     $this->permissionHandler->setSystemRebuildModuleData($extensions);
     $actual_permissions = $this->permissionHandler->getPermissions();
     $this->assertCount(2, $actual_permissions);
     $this->assertEquals($actual_permissions['access module a']['title'], 'Access A');
     $this->assertEquals($actual_permissions['access module a']['provider'], 'module_a');
     $this->assertEquals($actual_permissions['access module a']['description'], 'bla bla');
     $this->assertEquals($actual_permissions['access module b']['title'], 'Access B');
     $this->assertEquals($actual_permissions['access module b']['provider'], 'module_a');
     $this->assertEquals($actual_permissions['access module b']['description'], 'bla bla');
 }
예제 #7
0
 /**
  * {@inheritdoc}
  */
 public function rebuild()
 {
     if ($this->building) {
         throw new \RuntimeException('Recursive router rebuild detected.');
     }
     if (!$this->lock->acquire('router_rebuild')) {
         // Wait for another request that is already doing this work.
         // We choose to block here since otherwise the routes might not be
         // available, resulting in a 404.
         $this->lock->wait('router_rebuild');
         return FALSE;
     }
     $this->building = TRUE;
     $collection = new RouteCollection();
     foreach ($this->getRouteDefinitions() as $routes) {
         // The top-level 'routes_callback' is a list of methods in controller
         // syntax, see \Drupal\Core\Controller\ControllerResolver. These methods
         // should return a set of \Symfony\Component\Routing\Route objects, either
         // in an associative array keyed by the route name, which will be iterated
         // over and added to the collection for this provider, or as a new
         // \Symfony\Component\Routing\RouteCollection object, which will be added
         // to the collection.
         if (isset($routes['route_callbacks'])) {
             foreach ($routes['route_callbacks'] as $route_callback) {
                 $callback = $this->controllerResolver->getControllerFromDefinition($route_callback);
                 if ($callback_routes = call_user_func($callback)) {
                     // If a RouteCollection is returned, add the whole collection.
                     if ($callback_routes instanceof RouteCollection) {
                         $collection->addCollection($callback_routes);
                     } else {
                         foreach ($callback_routes as $name => $callback_route) {
                             $collection->add($name, $callback_route);
                         }
                     }
                 }
             }
             unset($routes['route_callbacks']);
         }
         foreach ($routes as $name => $route_info) {
             $route_info += array('defaults' => array(), 'requirements' => array(), 'options' => array());
             $route = new Route($route_info['path'], $route_info['defaults'], $route_info['requirements'], $route_info['options']);
             $collection->add($name, $route);
         }
     }
     // DYNAMIC is supposed to be used to add new routes based upon all the
     // static defined ones.
     $this->dispatcher->dispatch(RoutingEvents::DYNAMIC, new RouteBuildEvent($collection));
     // ALTER is the final step to alter all the existing routes. We cannot stop
     // people from adding new routes here, but we define two separate steps to
     // make it clear.
     $this->dispatcher->dispatch(RoutingEvents::ALTER, new RouteBuildEvent($collection));
     $this->checkProvider->setChecks($collection);
     $this->dumper->addRoutes($collection);
     $this->dumper->dump();
     $this->lock->release('router_rebuild');
     $this->dispatcher->dispatch(RoutingEvents::FINISHED, new Event());
     $this->building = FALSE;
     $this->rebuildNeeded = FALSE;
     return TRUE;
 }
예제 #8
0
 /**
  * {@inheritdoc}
  */
 public function getTitle(LocalTaskInterface $local_task)
 {
     $controller = array($local_task, 'getTitle');
     $request = $this->requestStack->getCurrentRequest();
     $arguments = $this->controllerResolver->getArguments($request, $controller);
     return call_user_func_array($controller, $arguments);
 }
 /**
  * Tests the rebuild with routes provided by a callback.
  *
  * @see \Drupal\Core\Routing\RouteBuilder::rebuild()
  */
 public function testRebuildWithProviderBasedRoutes()
 {
     $this->lock->expects($this->once())->method('acquire')->with('router_rebuild')->will($this->returnValue(TRUE));
     $this->yamlDiscovery->expects($this->once())->method('findAll')->will($this->returnValue(array('test_module' => array('route_callbacks' => array('\\Drupal\\Tests\\Core\\Routing\\TestRouteSubscriber::routesFromArray', 'test_module.route_service:routesFromCollection')))));
     $container = new ContainerBuilder();
     $container->set('test_module.route_service', new TestRouteSubscriber());
     $this->controllerResolver->expects($this->any())->method('getControllerFromDefinition')->will($this->returnCallback(function ($controller) use($container) {
         $count = substr_count($controller, ':');
         if ($count == 1) {
             list($service, $method) = explode(':', $controller, 2);
             $object = $container->get($service);
         } else {
             list($class, $method) = explode('::', $controller, 2);
             $object = new $class();
         }
         return array($object, $method);
     }));
     $route_collection_filled = new RouteCollection();
     $route_collection_filled->add('test_route.1', new Route('/test-route/1'));
     $route_collection_filled->add('test_route.2', new Route('/test-route/2'));
     $route_build_event = new RouteBuildEvent($route_collection_filled);
     // Ensure that the alter routes events are fired.
     $this->dispatcher->expects($this->at(0))->method('dispatch')->with(RoutingEvents::DYNAMIC, $route_build_event);
     $this->dispatcher->expects($this->at(1))->method('dispatch')->with(RoutingEvents::ALTER, $route_build_event);
     // Ensure that the routes are set to the dumper and dumped.
     $this->dumper->expects($this->at(0))->method('addRoutes')->with($route_collection_filled);
     $this->dumper->expects($this->at(1))->method('dump');
     $this->assertTrue($this->routeBuilder->rebuild());
 }
예제 #10
0
 /**
  * Invokes the form and returns the result.
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The request object.
  *
  * @return array
  *   The render array that results from invoking the controller.
  */
 public function getContentResult(Request $request)
 {
     $form_object = $this->getFormObject($request, $this->formDefinition);
     // Add the form and form_state to trick the getArguments method of the
     // controller resolver.
     $form_state = array();
     $request->attributes->set('form', array());
     $request->attributes->set('form_state', $form_state);
     $args = $this->controllerResolver->getArguments($request, array($form_object, 'buildForm'));
     $request->attributes->remove('form');
     $request->attributes->remove('form_state');
     // Remove $form and $form_state from the arguments, and re-index them.
     unset($args[0], $args[1]);
     $form_state['build_info']['args'] = array_values($args);
     return $this->formBuilder->buildForm($form_object, $form_state);
 }
 /**
  * Sets a response given a (main content) render array.
  *
  * @param \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event
  *   The event to process.
  */
 public function onViewRenderArray(GetResponseForControllerResultEvent $event)
 {
     $request = $event->getRequest();
     $result = $event->getControllerResult();
     $format = $request->getRequestFormat();
     // Render the controller result into a response if it's a render array.
     if (is_array($result)) {
         if (isset($this->mainContentRenderers[$format])) {
             $renderer = $this->classResolver->getInstanceFromDefinition($this->mainContentRenderers[$format]);
             $event->setResponse($renderer->renderResponse($result, $request, $this->routeMatch));
         } else {
             $supported_formats = array_keys($this->mainContentRenderers);
             $supported_mimetypes = array_map([$request, 'getMimeType'], $supported_formats);
             $event->setResponse(new JsonResponse(['message' => 'Not Acceptable.', 'supported_mime_types' => $supported_mimetypes], 406));
         }
     }
 }
예제 #12
0
 /**
  * Invokes the form and returns the result.
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The request object.
  * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
  *   The route match.
  *
  * @return array
  *   The render array that results from invoking the controller.
  */
 public function getContentResult(Request $request, RouteMatchInterface $route_match)
 {
     $form_arg = $this->getFormArgument($route_match);
     $form_object = $this->getFormObject($route_match, $form_arg);
     // Add the form and form_state to trick the getArguments method of the
     // controller resolver.
     $form_state = new FormState();
     $request->attributes->set('form', []);
     $request->attributes->set('form_state', $form_state);
     $args = $this->controllerResolver->getArguments($request, [$form_object, 'buildForm']);
     $request->attributes->remove('form');
     $request->attributes->remove('form_state');
     // Remove $form and $form_state from the arguments, and re-index them.
     unset($args[0], $args[1]);
     $form_state->addBuildInfo('args', array_values($args));
     return $this->formBuilder->buildForm($form_object, $form_state);
 }
예제 #13
0
 /**
  * Test the access method.
  */
 public function testAccess()
 {
     $request = new Request(array());
     $this->controllerResolver->expects($this->at(0))->method('getControllerFromDefinition')->with('\\Drupal\\Tests\\Core\\Access\\TestController::accessDeny')->will($this->returnValue(array(new TestController(), 'accessDeny')));
     $this->argumentsResolver->expects($this->at(0))->method('getArguments')->will($this->returnValue(array()));
     $this->controllerResolver->expects($this->at(1))->method('getControllerFromDefinition')->with('\\Drupal\\Tests\\Core\\Access\\TestController::accessAllow')->will($this->returnValue(array(new TestController(), 'accessAllow')));
     $this->argumentsResolver->expects($this->at(1))->method('getArguments')->will($this->returnValue(array()));
     $this->controllerResolver->expects($this->at(2))->method('getControllerFromDefinition')->with('\\Drupal\\Tests\\Core\\Access\\TestController::accessParameter')->will($this->returnValue(array(new TestController(), 'accessParameter')));
     $this->argumentsResolver->expects($this->at(2))->method('getArguments')->will($this->returnValue(array('parameter' => 'TRUE')));
     $route = new Route('/test-route', array(), array('_custom_access' => '\\Drupal\\Tests\\Core\\Access\\TestController::accessDeny'));
     $account = $this->getMock('Drupal\\Core\\Session\\AccountInterface');
     $this->assertSame(AccessInterface::DENY, $this->accessChecker->access($route, $request, $account));
     $route = new Route('/test-route', array(), array('_custom_access' => '\\Drupal\\Tests\\Core\\Access\\TestController::accessAllow'));
     $this->assertSame(AccessInterface::ALLOW, $this->accessChecker->access($route, $request, $account));
     $route = new Route('/test-route', array('parameter' => 'TRUE'), array('_custom_access' => '\\Drupal\\Tests\\Core\\Access\\TestController::accessParameter'));
     $this->assertSame(AccessInterface::ALLOW, $this->accessChecker->access($route, $request, $account));
 }
예제 #14
0
 /**
  * Tests a dynamic title.
  *
  * @see \Drupal\Core\Controller\TitleResolver::getTitle()
  */
 public function testDynamicTitle()
 {
     $request = new Request();
     $route = new Route('/test-route', array('_title' => 'static title', '_title_callback' => 'Drupal\\Tests\\Core\\Controller\\TitleCallback::example'));
     $callable = array(new TitleCallback(), 'example');
     $this->controllerResolver->expects($this->once())->method('getControllerFromDefinition')->with('Drupal\\Tests\\Core\\Controller\\TitleCallback::example')->will($this->returnValue($callable));
     $this->controllerResolver->expects($this->once())->method('getArguments')->with($request, $callable)->will($this->returnValue(array('example')));
     $this->assertEquals('test example', $this->titleResolver->getTitle($request, $route));
 }
 /**
  * Ensures bubbleable metadata from early rendering is not lost.
  *
  * @param \Symfony\Component\HttpKernel\Event\FilterControllerEvent $event
  *   The controller event.
  */
 public function onController(FilterControllerEvent $event)
 {
     $controller = $event->getController();
     // See \Symfony\Component\HttpKernel\HttpKernel::handleRaw().
     $arguments = $this->controllerResolver->getArguments($event->getRequest(), $controller);
     $event->setController(function () use($controller, $arguments) {
         return $this->wrapControllerExecutionInRenderContext($controller, $arguments);
     });
 }
예제 #16
0
 /**
  * Creates a controller instance using route defaults.
  *
  * By design we cannot support all possible routes, but just the ones which
  * use the defaults provided by core, which are _content, _controller
  * and _form.
  *
  * @param array $defaults
  *   The default values provided by the route.
  *
  * @return array|null
  *   Returns the controller instance if it is possible to instantiate it, NULL
  */
 protected function getController(array $defaults)
 {
     $controller = NULL;
     if (isset($defaults['_content'])) {
         $controller = $this->controllerResolver->getControllerFromDefinition($defaults['_content']);
     }
     if (isset($defaults['_controller'])) {
         $controller = $this->controllerResolver->getControllerFromDefinition($defaults['_controller']);
     }
     if (isset($defaults['_form'])) {
         $form_arg = $defaults['_form'];
         // Check if the class exists first as the class resolver will throw an
         // exception if it doesn't. This also means a service cannot be used here.
         if (class_exists($form_arg)) {
             $controller = array($this->classResolver->getInstanceFromDefinition($form_arg), 'buildForm');
         }
     }
     return $controller;
 }
예제 #17
0
 /**
  * Sets a response given a (main content) render array.
  *
  * @param \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event
  *   The event to process.
  */
 public function onViewRenderArray(GetResponseForControllerResultEvent $event)
 {
     $request = $event->getRequest();
     $result = $event->getControllerResult();
     // Render the controller result into a response if it's a render array.
     if (is_array($result) && ($request->query->has(static::WRAPPER_FORMAT) || $request->getRequestFormat() == 'html')) {
         $wrapper = $request->query->get(static::WRAPPER_FORMAT, 'html');
         // Fall back to HTML if the requested wrapper envelope is not available.
         $wrapper = isset($this->mainContentRenderers[$wrapper]) ? $wrapper : 'html';
         $renderer = $this->classResolver->getInstanceFromDefinition($this->mainContentRenderers[$wrapper]);
         $response = $renderer->renderResponse($result, $request, $this->routeMatch);
         // The main content render array is rendered into a different Response
         // object, depending on the specified wrapper format.
         if ($response instanceof CacheableResponseInterface) {
             $main_content_view_subscriber_cacheability = (new CacheableMetadata())->setCacheContexts(['url.query_args:' . static::WRAPPER_FORMAT]);
             $response->addCacheableDependency($main_content_view_subscriber_cacheability);
         }
         $event->setResponse($response);
     }
 }
예제 #18
0
 /**
  * Test the access method.
  */
 public function testAccess()
 {
     $route_match = $this->getMock('Drupal\\Core\\Routing\\RouteMatchInterface');
     $this->controllerResolver->expects($this->at(0))->method('getControllerFromDefinition')->with('\\Drupal\\Tests\\Core\\Access\\TestController::accessDeny')->will($this->returnValue(array(new TestController(), 'accessDeny')));
     $resolver0 = $this->getMock('Drupal\\Component\\Utility\\ArgumentsResolverInterface');
     $resolver0->expects($this->once())->method('getArguments')->will($this->returnValue(array()));
     $this->argumentsResolverFactory->expects($this->at(0))->method('getArgumentsResolver')->will($this->returnValue($resolver0));
     $this->controllerResolver->expects($this->at(1))->method('getControllerFromDefinition')->with('\\Drupal\\Tests\\Core\\Access\\TestController::accessAllow')->will($this->returnValue(array(new TestController(), 'accessAllow')));
     $resolver1 = $this->getMock('Drupal\\Component\\Utility\\ArgumentsResolverInterface');
     $resolver1->expects($this->once())->method('getArguments')->will($this->returnValue(array()));
     $this->argumentsResolverFactory->expects($this->at(1))->method('getArgumentsResolver')->will($this->returnValue($resolver1));
     $this->controllerResolver->expects($this->at(2))->method('getControllerFromDefinition')->with('\\Drupal\\Tests\\Core\\Access\\TestController::accessParameter')->will($this->returnValue(array(new TestController(), 'accessParameter')));
     $resolver2 = $this->getMock('Drupal\\Component\\Utility\\ArgumentsResolverInterface');
     $resolver2->expects($this->once())->method('getArguments')->will($this->returnValue(array('parameter' => 'TRUE')));
     $this->argumentsResolverFactory->expects($this->at(2))->method('getArgumentsResolver')->will($this->returnValue($resolver2));
     $route = new Route('/test-route', array(), array('_custom_access' => '\\Drupal\\Tests\\Core\\Access\\TestController::accessDeny'));
     $account = $this->getMock('Drupal\\Core\\Session\\AccountInterface');
     $this->assertEquals(AccessResult::neutral(), $this->accessChecker->access($route, $route_match, $account));
     $route = new Route('/test-route', array(), array('_custom_access' => '\\Drupal\\Tests\\Core\\Access\\TestController::accessAllow'));
     $this->assertEquals(AccessResult::allowed(), $this->accessChecker->access($route, $route_match, $account));
     $route = new Route('/test-route', array('parameter' => 'TRUE'), array('_custom_access' => '\\Drupal\\Tests\\Core\\Access\\TestController::accessParameter'));
     $this->assertEquals(AccessResult::allowed(), $this->accessChecker->access($route, $route_match, $account));
 }
예제 #19
0
 /**
  * {@inheritdoc}
  */
 public function generateCachePlaceholder($callback, array &$context)
 {
     if (is_string($callback) && strpos($callback, '::') === FALSE) {
         $callable = $this->controllerResolver->getControllerFromDefinition($callback);
     } else {
         $callable = $callback;
     }
     if (!is_callable($callable)) {
         throw new \InvalidArgumentException('$callable must be a callable function or of the form service_id:method.');
     }
     // Generate a unique token if one is not already provided.
     $context += ['token' => Crypt::randomBytesBase64(55)];
     return '<drupal-render-cache-placeholder callback="' . $callback . '" token="' . $context['token'] . '"></drupal-render-cache-placeholder>';
 }
예제 #20
0
 /**
  * {@inheritdoc}
  */
 public function transform(array $tree, array $manipulators)
 {
     foreach ($manipulators as $manipulator) {
         $callable = $manipulator['callable'];
         $callable = $this->controllerResolver->getControllerFromDefinition($callable);
         // Prepare the arguments for the menu tree manipulator callable; the first
         // argument is always the menu link tree.
         if (isset($manipulator['args'])) {
             array_unshift($manipulator['args'], $tree);
             $tree = call_user_func_array($callable, $manipulator['args']);
         } else {
             $tree = call_user_func($callable, $tree);
         }
     }
     return $tree;
 }
 /**
  * @covers \Drupal\Core\Menu\LocalActionManager::getActionsForRoute()
  *
  * @dataProvider getActionsForRouteProvider
  */
 public function testGetActionsForRoute($route_appears, array $plugin_definitions, array $expected_actions)
 {
     $this->discovery->expects($this->any())->method('getDefinitions')->will($this->returnValue($plugin_definitions));
     $map = array();
     foreach ($plugin_definitions as $plugin_id => $plugin_definition) {
         $plugin = $this->getMock('Drupal\\Core\\Menu\\LocalActionInterface');
         $plugin->expects($this->any())->method('getRouteName')->will($this->returnValue($plugin_definition['route_name']));
         $plugin->expects($this->any())->method('getRouteParameters')->will($this->returnValue(isset($plugin_definition['route_parameters']) ? $plugin_definition['route_parameters'] : array()));
         $plugin->expects($this->any())->method('getTitle')->will($this->returnValue($plugin_definition['title']));
         $this->controllerResolver->expects($this->any())->method('getArguments')->with($this->request, array($plugin, 'getTitle'))->will($this->returnValue(array()));
         $plugin->expects($this->any())->method('getWeight')->will($this->returnValue($plugin_definition['weight']));
         $this->controllerResolver->expects($this->any())->method('getArguments')->with($this->request, array($plugin, 'getTitle'))->will($this->returnValue(array()));
         $map[] = array($plugin_id, array(), $plugin);
     }
     $this->factory->expects($this->any())->method('createInstance')->will($this->returnValueMap($map));
     $this->assertEquals($expected_actions, $this->localActionManager->getActionsForRoute($route_appears));
 }
예제 #22
0
 /**
  * Builds all permissions provided by .permissions.yml files.
  *
  * @return array[]
  *   Each return permission is an array with the following keys:
  *   - title: The title of the permission.
  *   - description: The description of the permission, defaults to NULL.
  *   - provider: The provider of the permission.
  */
 protected function buildPermissionsYaml()
 {
     $all_permissions = array();
     $all_callback_permissions = array();
     foreach ($this->getYamlDiscovery()->findAll() as $provider => $permissions) {
         // The top-level 'permissions_callback' is a list of methods in controller
         // syntax, see \Drupal\Core\Controller\ControllerResolver. These methods
         // should return an array of permissions in the same structure.
         if (isset($permissions['permission_callbacks'])) {
             foreach ($permissions['permission_callbacks'] as $permission_callback) {
                 $callback = $this->controllerResolver->getControllerFromDefinition($permission_callback);
                 if ($callback_permissions = call_user_func($callback)) {
                     // Add any callback permissions to the array of permissions. Any
                     // defaults can then get processed below.
                     foreach ($callback_permissions as $name => $callback_permission) {
                         if (!is_array($callback_permission)) {
                             $callback_permission = array('title' => $callback_permission);
                         }
                         $callback_permission += array('description' => NULL);
                         $callback_permission['provider'] = $provider;
                         $all_callback_permissions[$name] = $callback_permission;
                     }
                 }
             }
             unset($permissions['permission_callbacks']);
         }
         foreach ($permissions as &$permission) {
             if (!is_array($permission)) {
                 $permission = array('title' => $permission);
             }
             $permission['title'] = $this->t($permission['title']);
             $permission['description'] = isset($permission['description']) ? $this->t($permission['description']) : NULL;
             $permission['provider'] = $provider;
         }
         $all_permissions += $permissions;
     }
     return $all_permissions + $all_callback_permissions;
 }
 /**
  * Setups the controller resolver to return the given controller definition.
  *
  * @param string $controller_definition
  *   The definition of a controller
  */
 protected function setupControllerResolver($controller_definition)
 {
     $controller = $controller_definition;
     list($class, $method) = explode('::', $controller);
     $this->controllerResolver->expects($this->atLeastOnce())->method('getControllerFromDefinition')->with($controller_definition)->will($this->returnValue(array(new $class(), $method)));
 }
예제 #24
0
 /**
  * See the docs for ::render().
  */
 protected function doRender(&$elements, $is_root_call = FALSE)
 {
     if (!isset($elements['#access']) && isset($elements['#access_callback'])) {
         if (is_string($elements['#access_callback']) && strpos($elements['#access_callback'], '::') === FALSE) {
             $elements['#access_callback'] = $this->controllerResolver->getControllerFromDefinition($elements['#access_callback']);
         }
         $elements['#access'] = call_user_func($elements['#access_callback'], $elements);
     }
     // Early-return nothing if user does not have access.
     if (empty($elements) || isset($elements['#access']) && !$elements['#access']) {
         return '';
     }
     // Do not print elements twice.
     if (!empty($elements['#printed'])) {
         return '';
     }
     if (!isset(static::$stack)) {
         static::$stack = new \SplStack();
     }
     static::$stack->push(new BubbleableMetadata());
     // Set the bubbleable rendering metadata that has configurable defaults, if:
     // - this is the root call, to ensure that the final render array definitely
     //   has these configurable defaults, even when no subtree is render cached.
     // - this is a render cacheable subtree, to ensure that the cached data has
     //   the configurable defaults (which may affect the ID and invalidation).
     if ($is_root_call || isset($elements['#cache']['keys'])) {
         $required_cache_contexts = $this->rendererConfig['required_cache_contexts'];
         if (isset($elements['#cache']['contexts'])) {
             $elements['#cache']['contexts'] = Cache::mergeContexts($elements['#cache']['contexts'], $required_cache_contexts);
         } else {
             $elements['#cache']['contexts'] = $required_cache_contexts;
         }
     }
     // Try to fetch the prerendered element from cache, replace any placeholders
     // and return the final markup.
     if (isset($elements['#cache']['keys'])) {
         $cached_element = $this->renderCache->get($elements);
         if ($cached_element !== FALSE) {
             $elements = $cached_element;
             // Only when we're in a root (non-recursive) Renderer::render() call,
             // placeholders must be processed, to prevent breaking the render cache
             // in case of nested elements with #cache set.
             if ($is_root_call) {
                 $this->replacePlaceholders($elements);
             }
             // Mark the element markup as safe. If we have cached children, we need
             // to mark them as safe too. The parent markup contains the child
             // markup, so if the parent markup is safe, then the markup of the
             // individual children must be safe as well.
             $elements['#markup'] = SafeMarkup::set($elements['#markup']);
             if (!empty($elements['#cache_properties'])) {
                 foreach (Element::children($cached_element) as $key) {
                     SafeMarkup::set($cached_element[$key]['#markup']);
                 }
             }
             // The render cache item contains all the bubbleable rendering metadata
             // for the subtree.
             $this->updateStack($elements);
             // Render cache hit, so rendering is finished, all necessary info
             // collected!
             $this->bubbleStack();
             return $elements['#markup'];
         }
     }
     // Two-tier caching: track pre-bubbling elements' #cache for later
     // comparison.
     // @see \Drupal\Core\Render\RenderCacheInterface::get()
     // @see \Drupal\Core\Render\RenderCacheInterface::set()
     $pre_bubbling_elements = [];
     $pre_bubbling_elements['#cache'] = isset($elements['#cache']) ? $elements['#cache'] : [];
     // If the default values for this element have not been loaded yet, populate
     // them.
     if (isset($elements['#type']) && empty($elements['#defaults_loaded'])) {
         $elements += $this->elementInfo->getInfo($elements['#type']);
     }
     // First validate the usage of #lazy_builder; both of the next if-statements
     // use it if available.
     if (isset($elements['#lazy_builder'])) {
         // @todo Convert to assertions once https://www.drupal.org/node/2408013
         //   lands.
         if (!is_array($elements['#lazy_builder'])) {
             throw new \DomainException('The #lazy_builder property must have an array as a value.');
         }
         if (count($elements['#lazy_builder']) !== 2) {
             throw new \DomainException('The #lazy_builder property must have an array as a value, containing two values: the callback, and the arguments for the callback.');
         }
         if (count($elements['#lazy_builder'][1]) !== count(array_filter($elements['#lazy_builder'][1], function ($v) {
             return is_null($v) || is_scalar($v);
         }))) {
             throw new \DomainException("A #lazy_builder callback's context may only contain scalar values or NULL.");
         }
         $children = Element::children($elements);
         if ($children) {
             throw new \DomainException(sprintf('When a #lazy_builder callback is specified, no children can exist; all children must be generated by the #lazy_builder callback. You specified the following children: %s.', implode(', ', $children)));
         }
         $supported_keys = ['#lazy_builder', '#cache', '#create_placeholder', '#weight', '#printed'];
         $unsupported_keys = array_diff(array_keys($elements), $supported_keys);
         if (count($unsupported_keys)) {
             throw new \DomainException(sprintf('When a #lazy_builder callback is specified, no properties can exist; all properties must be generated by the #lazy_builder callback. You specified the following properties: %s.', implode(', ', $unsupported_keys)));
         }
     }
     // If instructed to create a placeholder, and a #lazy_builder callback is
     // present (without such a callback, it would be impossible to replace the
     // placeholder), replace the current element with a placeholder.
     if (isset($elements['#create_placeholder']) && $elements['#create_placeholder'] === TRUE) {
         if (!isset($elements['#lazy_builder'])) {
             throw new \LogicException('When #create_placeholder is set, a #lazy_builder callback must be present as well.');
         }
         $elements = $this->createPlaceholder($elements);
     }
     // Build the element if it is still empty.
     if (isset($elements['#lazy_builder'])) {
         $callable = $elements['#lazy_builder'][0];
         $args = $elements['#lazy_builder'][1];
         if (is_string($callable) && strpos($callable, '::') === FALSE) {
             $callable = $this->controllerResolver->getControllerFromDefinition($callable);
         }
         $new_elements = call_user_func_array($callable, $args);
         // Retain the original cacheability metadata, plus cache keys.
         CacheableMetadata::createFromRenderArray($elements)->merge(CacheableMetadata::createFromRenderArray($new_elements))->applyTo($new_elements);
         if (isset($elements['#cache']['keys'])) {
             $new_elements['#cache']['keys'] = $elements['#cache']['keys'];
         }
         $elements = $new_elements;
         $elements['#lazy_builder_built'] = TRUE;
     }
     // Make any final changes to the element before it is rendered. This means
     // that the $element or the children can be altered or corrected before the
     // element is rendered into the final text.
     if (isset($elements['#pre_render'])) {
         foreach ($elements['#pre_render'] as $callable) {
             if (is_string($callable) && strpos($callable, '::') === FALSE) {
                 $callable = $this->controllerResolver->getControllerFromDefinition($callable);
             }
             $elements = call_user_func($callable, $elements);
         }
     }
     // Defaults for bubbleable rendering metadata.
     $elements['#cache']['tags'] = isset($elements['#cache']['tags']) ? $elements['#cache']['tags'] : array();
     $elements['#cache']['max-age'] = isset($elements['#cache']['max-age']) ? $elements['#cache']['max-age'] : Cache::PERMANENT;
     $elements['#attached'] = isset($elements['#attached']) ? $elements['#attached'] : array();
     // Allow #pre_render to abort rendering.
     if (!empty($elements['#printed'])) {
         // The #printed element contains all the bubbleable rendering metadata for
         // the subtree.
         $this->updateStack($elements);
         // #printed, so rendering is finished, all necessary info collected!
         $this->bubbleStack();
         return '';
     }
     // Add any JavaScript state information associated with the element.
     if (!empty($elements['#states'])) {
         drupal_process_states($elements);
     }
     // Get the children of the element, sorted by weight.
     $children = Element::children($elements, TRUE);
     // Initialize this element's #children, unless a #pre_render callback
     // already preset #children.
     if (!isset($elements['#children'])) {
         $elements['#children'] = '';
     }
     if (isset($elements['#markup'])) {
         // @todo Decide how to support non-HTML in the render API in
         //   https://www.drupal.org/node/2501313.
         $elements['#markup'] = SafeMarkup::checkAdminXss($elements['#markup']);
     }
     // Assume that if #theme is set it represents an implemented hook.
     $theme_is_implemented = isset($elements['#theme']);
     // Check the elements for insecure HTML and pass through sanitization.
     if (isset($elements)) {
         $markup_keys = array('#description', '#field_prefix', '#field_suffix');
         foreach ($markup_keys as $key) {
             if (!empty($elements[$key]) && is_scalar($elements[$key])) {
                 $elements[$key] = SafeMarkup::checkAdminXss($elements[$key]);
             }
         }
     }
     // Call the element's #theme function if it is set. Then any children of the
     // element have to be rendered there. If the internal #render_children
     // property is set, do not call the #theme function to prevent infinite
     // recursion.
     if ($theme_is_implemented && !isset($elements['#render_children'])) {
         $elements['#children'] = $this->theme->render($elements['#theme'], $elements);
         // If ThemeManagerInterface::render() returns FALSE this means that the
         // hook in #theme was not found in the registry and so we need to update
         // our flag accordingly. This is common for theme suggestions.
         $theme_is_implemented = $elements['#children'] !== FALSE;
     }
     // If #theme is not implemented or #render_children is set and the element
     // has an empty #children attribute, render the children now. This is the
     // same process as Renderer::render() but is inlined for speed.
     if ((!$theme_is_implemented || isset($elements['#render_children'])) && empty($elements['#children'])) {
         foreach ($children as $key) {
             $elements['#children'] .= $this->doRender($elements[$key]);
         }
         $elements['#children'] = SafeMarkup::set($elements['#children']);
     }
     // If #theme is not implemented and the element has raw #markup as a
     // fallback, prepend the content in #markup to #children. In this case
     // #children will contain whatever is provided by #pre_render prepended to
     // what is rendered recursively above. If #theme is implemented then it is
     // the responsibility of that theme implementation to render #markup if
     // required. Eventually #theme_wrappers will expect both #markup and
     // #children to be a single string as #children.
     if (!$theme_is_implemented && isset($elements['#markup'])) {
         $elements['#children'] = SafeMarkup::set($elements['#markup'] . $elements['#children']);
     }
     // Let the theme functions in #theme_wrappers add markup around the rendered
     // children.
     // #states and #attached have to be processed before #theme_wrappers,
     // because the #type 'page' render array from drupal_prepare_page() would
     // render the $page and wrap it into the html.html.twig template without the
     // attached assets otherwise.
     // If the internal #render_children property is set, do not call the
     // #theme_wrappers function(s) to prevent infinite recursion.
     if (isset($elements['#theme_wrappers']) && !isset($elements['#render_children'])) {
         foreach ($elements['#theme_wrappers'] as $key => $value) {
             // If the value of a #theme_wrappers item is an array then the theme
             // hook is found in the key of the item and the value contains attribute
             // overrides. Attribute overrides replace key/value pairs in $elements
             // for only this ThemeManagerInterface::render() call. This allows
             // #theme hooks and #theme_wrappers hooks to share variable names
             // without conflict or ambiguity.
             $wrapper_elements = $elements;
             if (is_string($key)) {
                 $wrapper_hook = $key;
                 foreach ($value as $attribute => $override) {
                     $wrapper_elements[$attribute] = $override;
                 }
             } else {
                 $wrapper_hook = $value;
             }
             $elements['#children'] = $this->theme->render($wrapper_hook, $wrapper_elements);
         }
     }
     // Filter the outputted content and make any last changes before the content
     // is sent to the browser. The changes are made on $content which allows the
     // outputted text to be filtered.
     if (isset($elements['#post_render'])) {
         foreach ($elements['#post_render'] as $callable) {
             if (is_string($callable) && strpos($callable, '::') === FALSE) {
                 $callable = $this->controllerResolver->getControllerFromDefinition($callable);
             }
             $elements['#children'] = call_user_func($callable, $elements['#children'], $elements);
         }
     }
     // We store the resulting output in $elements['#markup'], to be consistent
     // with how render cached output gets stored. This ensures that placeholder
     // replacement logic gets the same data to work with, no matter if #cache is
     // disabled, #cache is enabled, there is a cache hit or miss.
     $prefix = isset($elements['#prefix']) ? SafeMarkup::checkAdminXss($elements['#prefix']) : '';
     $suffix = isset($elements['#suffix']) ? SafeMarkup::checkAdminXss($elements['#suffix']) : '';
     $elements['#markup'] = $prefix . $elements['#children'] . $suffix;
     // We've rendered this element (and its subtree!), now update the stack.
     $this->updateStack($elements);
     // Cache the processed element if both $pre_bubbling_elements and $elements
     // have the metadata necessary to generate a cache ID.
     if (isset($pre_bubbling_elements['#cache']['keys']) && isset($elements['#cache']['keys'])) {
         if ($pre_bubbling_elements['#cache']['keys'] !== $elements['#cache']['keys']) {
             throw new \LogicException('Cache keys may not be changed after initial setup. Use the contexts property instead to bubble additional metadata.');
         }
         $this->renderCache->set($elements, $pre_bubbling_elements);
     }
     // Only when we're in a root (non-recursive) Renderer::render() call,
     // placeholders must be processed, to prevent breaking the render cache in
     // case of nested elements with #cache set.
     //
     // By running them here, we ensure that:
     // - they run when #cache is disabled,
     // - they run when #cache is enabled and there is a cache miss.
     // Only the case of a cache hit when #cache is enabled, is not handled here,
     // that is handled earlier in Renderer::render().
     if ($is_root_call) {
         $this->replacePlaceholders($elements);
         if (static::$stack->count() !== 1) {
             throw new \LogicException('A stray drupal_render() invocation with $is_root_call = TRUE is causing bubbling of attached assets to break.');
         }
     }
     // Rendering is finished, all necessary info collected!
     $this->bubbleStack();
     $elements['#printed'] = TRUE;
     $elements['#markup'] = SafeMarkup::set($elements['#markup']);
     return $elements['#markup'];
 }