AjaxBasePageNegotiator.php 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. <?php
  2. namespace Drupal\Core\Theme;
  3. use Drupal\Core\Access\CsrfTokenGenerator;
  4. use Drupal\Core\Config\ConfigFactoryInterface;
  5. use Drupal\Core\Routing\RouteMatchInterface;
  6. use Symfony\Component\HttpFoundation\RequestStack;
  7. /**
  8. * Defines a theme negotiator that deals with the active theme on ajax requests.
  9. *
  10. * Many different pages can invoke an Ajax request to a generic Ajax path. It is
  11. * almost always desired for an Ajax response to be rendered using the same
  12. * theme as the base page, because most themes are built with the assumption
  13. * that they control the entire page, so if the CSS for two themes are both
  14. * loaded for a given page, they may conflict with each other. For example,
  15. * Bartik is Drupal's default theme, and Seven is Drupal's default
  16. * administration theme. Depending on whether the "Use the administration theme
  17. * when editing or creating content" checkbox is checked, the node edit form may
  18. * be displayed in either theme, but the Ajax response to the Field module's
  19. * "Add another item" button should be rendered using the same theme as the rest
  20. * of the page.
  21. */
  22. class AjaxBasePageNegotiator implements ThemeNegotiatorInterface {
  23. /**
  24. * The CSRF token generator.
  25. *
  26. * @var \Drupal\Core\Access\CsrfTokenGenerator
  27. */
  28. protected $csrfGenerator;
  29. /**
  30. * The config factory.
  31. *
  32. * @var \Drupal\Core\Config\ConfigFactoryInterface
  33. */
  34. protected $configFactory;
  35. /**
  36. * The request stack.
  37. *
  38. * @var \Symfony\Component\HttpFoundation\RequestStack
  39. */
  40. protected $requestStack;
  41. /**
  42. * Constructs a new AjaxBasePageNegotiator.
  43. *
  44. * @param \Drupal\Core\Access\CsrfTokenGenerator $token_generator
  45. * The CSRF token generator.
  46. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
  47. * The config factory.
  48. * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
  49. * The request stack used to retrieve the current request.
  50. */
  51. public function __construct(CsrfTokenGenerator $token_generator, ConfigFactoryInterface $config_factory, RequestStack $request_stack) {
  52. $this->csrfGenerator = $token_generator;
  53. $this->configFactory = $config_factory;
  54. $this->requestStack = $request_stack;
  55. }
  56. /**
  57. * {@inheritdoc}
  58. */
  59. public function applies(RouteMatchInterface $route_match) {
  60. $ajax_page_state = $this->requestStack->getCurrentRequest()->request->get('ajax_page_state');
  61. return !empty($ajax_page_state['theme']) && isset($ajax_page_state['theme_token']);
  62. }
  63. /**
  64. * {@inheritdoc}
  65. */
  66. public function determineActiveTheme(RouteMatchInterface $route_match) {
  67. $ajax_page_state = $this->requestStack->getCurrentRequest()->request->get('ajax_page_state');
  68. $theme = $ajax_page_state['theme'];
  69. $token = $ajax_page_state['theme_token'];
  70. // Prevent a request forgery from giving a person access to a theme they
  71. // shouldn't be otherwise allowed to see. However, since everyone is
  72. // allowed to see the default theme, token validation isn't required for
  73. // that, and bypassing it allows most use-cases to work even when accessed
  74. // from the page cache.
  75. if ($theme === $this->configFactory->get('system.theme')->get('default') || $this->csrfGenerator->validate($token, $theme)) {
  76. return $theme;
  77. }
  78. }
  79. }