ContextManager.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. <?php
  2. namespace Drupal\context;
  3. use Drupal\context\Entity\Context;
  4. use Drupal\context\Plugin\ContextReaction\Blocks;
  5. use Drupal\Core\Entity\Query\QueryFactory;
  6. use Drupal\Core\Entity\EntityManagerInterface;
  7. use Drupal\Core\Entity\EntityFormBuilderInterface;
  8. use Drupal\Core\Plugin\ContextAwarePluginInterface;
  9. use Drupal\Core\Condition\ConditionPluginCollection;
  10. use Drupal\Component\Plugin\Exception\ContextException;
  11. use Drupal\Core\Condition\ConditionAccessResolverTrait;
  12. use Drupal\Core\Plugin\Context\ContextHandlerInterface;
  13. use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
  14. use Drupal\Core\StringTranslation\StringTranslationTrait;
  15. use Drupal\Core\Theme\ThemeManagerInterface;
  16. /**
  17. * This is the manager service for the context module and should not be
  18. * confused with the built in contexts in Drupal.
  19. */
  20. class ContextManager {
  21. use ConditionAccessResolverTrait;
  22. use StringTranslationTrait;
  23. /**
  24. * @var \Drupal\Core\Entity\Query\QueryFactory
  25. */
  26. protected $entityQuery;
  27. /**
  28. * @var \Drupal\Core\Entity\EntityManagerInterface
  29. */
  30. protected $entityManager;
  31. /**
  32. * @var \Drupal\Core\Plugin\Context\ContextRepositoryInterface
  33. */
  34. protected $contextRepository;
  35. /**
  36. * @var \Drupal\Core\Plugin\Context\ContextHandlerInterface
  37. */
  38. protected $contextHandler;
  39. /**
  40. * If the context conditions has been evaluated then this is set to TRUE
  41. * otherwise FALSE.
  42. *
  43. * @var bool
  44. */
  45. protected $contextConditionsEvaluated = FALSE;
  46. /**
  47. * An array of contexts that have been evaluated and are active.
  48. *
  49. * @var array
  50. */
  51. protected $activeContexts = [];
  52. /**
  53. * @var \Drupal\Core\Entity\EntityFormBuilderInterface
  54. */
  55. private $entityFormBuilder;
  56. /**
  57. * @var \Drupal\Core\Theme\ThemeManagerInterface;
  58. */
  59. protected $themeManager;
  60. /**
  61. * Construct.
  62. *
  63. * @param QueryFactory $entityQuery
  64. * The Drupal entity query service.
  65. *
  66. * @param EntityManagerInterface $entityManager
  67. * The Drupal entity manager service.
  68. *
  69. * @param ContextRepositoryInterface $contextRepository
  70. * The drupal context repository service.
  71. *
  72. * @param ContextHandlerInterface $contextHandler
  73. * The Drupal context handler service.
  74. *
  75. * @param ThemeManagerInterface $themeManager
  76. * The Drupal theme manager service.
  77. *
  78. * @param \Drupal\Core\Entity\EntityFormBuilderInterface $entityFormBuilder
  79. */
  80. function __construct(
  81. QueryFactory $entityQuery,
  82. EntityManagerInterface $entityManager,
  83. ContextRepositoryInterface $contextRepository,
  84. ContextHandlerInterface $contextHandler,
  85. EntityFormBuilderInterface $entityFormBuilder,
  86. ThemeManagerInterface $themeManager
  87. )
  88. {
  89. $this->entityQuery = $entityQuery;
  90. $this->entityManager = $entityManager;
  91. $this->contextRepository = $contextRepository;
  92. $this->contextHandler = $contextHandler;
  93. $this->entityFormBuilder = $entityFormBuilder;
  94. $this->themeManager = $themeManager;
  95. }
  96. /**
  97. * Get all contexts.
  98. *
  99. * @return Context[]
  100. */
  101. public function getContexts() {
  102. $contextIds = $this->entityQuery
  103. ->get('context')
  104. ->execute();
  105. $contexts = $this->entityManager
  106. ->getStorage('context')
  107. ->loadMultiple($contextIds);
  108. // Sort the contexts by their weight.
  109. uasort($contexts, [$this, 'sortContextsByWeight']);
  110. return $contexts;
  111. }
  112. /**
  113. * Get all contexts sorted by their group and sorted by their weight inside
  114. * of each group.
  115. *
  116. * @return array
  117. */
  118. public function getContextsByGroup() {
  119. $contexts = $this->getContexts();
  120. $groups = [];
  121. // Add each context to their respective groups.
  122. foreach ($contexts as $context_id => $context) {
  123. $group = $context->getGroup();
  124. if ($group === Context::CONTEXT_GROUP_NONE) {
  125. $group = 'not_grouped';
  126. }
  127. $groups[$group][$context_id] = $context;
  128. }
  129. return $groups;
  130. }
  131. /**
  132. * Check to validate that the context name does not already exist.
  133. *
  134. * @param string $name
  135. * The machine name of the context to validate.
  136. *
  137. * @return bool
  138. */
  139. public function contextExists($name) {
  140. $entity = $this->entityQuery->get('context')
  141. ->condition('name', $name)
  142. ->execute();
  143. return (bool) $entity;
  144. }
  145. /**
  146. * Check to see if context conditions has been evaluated.
  147. *
  148. * @return bool
  149. */
  150. public function conditionsHasBeenEvaluated() {
  151. return $this->contextConditionsEvaluated;
  152. }
  153. /**
  154. * Get the evaluated and active contexts.
  155. *
  156. * @return \Drupal\context\ContextInterface[]
  157. */
  158. public function getActiveContexts() {
  159. if ($this->conditionsHasBeenEvaluated()) {
  160. return $this->activeContexts;
  161. }
  162. $this->evaluateContexts();
  163. return $this->activeContexts;
  164. }
  165. /**
  166. * Evaluate all context conditions.
  167. */
  168. public function evaluateContexts() {
  169. /** @var \Drupal\context\ContextInterface $context */
  170. foreach ($this->getContexts() as $context) {
  171. if ($this->evaluateContextConditions($context) && !$context->disabled()) {
  172. $this->activeContexts[] = $context;
  173. }
  174. }
  175. $this->contextConditionsEvaluated = TRUE;
  176. }
  177. /**
  178. * Get all active reactions or reactions of a certain type.
  179. *
  180. * @param string $reactionType
  181. * Either the reaction class name or the id of the reaction type to get.
  182. *
  183. * @return ContextReactionInterface[]
  184. */
  185. public function getActiveReactions($reactionType = NULL) {
  186. $reactions = [];
  187. foreach ($this->getActiveContexts() as $context) {
  188. // If no reaction type has been specified then add all reactions and
  189. // continue to the next context.
  190. if (is_null($reactionType)) {
  191. foreach ($context->getReactions() as $reaction) {
  192. // Only return block reaction if there is a block applied to the current theme.
  193. if ($reaction instanceof Blocks) {
  194. $blocks = $reaction->getBlocks();
  195. $current_theme = $this->getCurrentTheme();
  196. foreach ($blocks as $block) {
  197. if ($block->getConfiguration()['theme'] == $current_theme) {
  198. $reactions[] = $reaction;
  199. break;
  200. }
  201. }
  202. }
  203. else {
  204. $reactions[] = $reaction;
  205. }
  206. }
  207. continue;
  208. }
  209. $contextReactions = $context->getReactions();
  210. // Filter the reactions based on the reaction type.
  211. foreach ($contextReactions as $reaction) {
  212. if (class_exists($reactionType) && $reaction instanceof $reactionType) {
  213. $reactions[] = $reaction;
  214. continue;
  215. }
  216. if ($reaction->getPluginId() === $reactionType) {
  217. $reactions[] = $reaction;
  218. continue;
  219. }
  220. }
  221. }
  222. return $reactions;
  223. }
  224. /**
  225. * Evaluate a contexts conditions.
  226. *
  227. * @param ContextInterface $context
  228. * The context to evaluate conditions for.
  229. *
  230. * @return bool
  231. */
  232. public function evaluateContextConditions(ContextInterface $context) {
  233. $conditions = $context->getConditions();
  234. // Apply context to any context aware conditions.
  235. $this->applyContexts($conditions);
  236. // Set the logic to use when validating the conditions.
  237. $logic = $context->requiresAllConditions()
  238. ? 'and'
  239. : 'or';
  240. // Of there are no conditions then the context will be
  241. // applied as a site wide context.
  242. if (!count($conditions)) {
  243. $logic = 'and';
  244. }
  245. return $this->resolveConditions($conditions, $logic);
  246. }
  247. /**
  248. * Apply context to all the context aware conditions in the collection.
  249. *
  250. * @param ConditionPluginCollection $conditions
  251. * A collection of conditions to apply context to.
  252. *
  253. * @return bool
  254. */
  255. protected function applyContexts(ConditionPluginCollection &$conditions) {
  256. foreach ($conditions as $condition) {
  257. if ($condition instanceof ContextAwarePluginInterface) {
  258. try {
  259. $contexts = $this->contextRepository->getRuntimeContexts(array_values($condition->getContextMapping()));
  260. $this->contextHandler->applyContextMapping($condition, $contexts);
  261. }
  262. catch (ContextException $e) {
  263. return FALSE;
  264. }
  265. }
  266. }
  267. return TRUE;
  268. }
  269. /**
  270. * Get a rendered form for the context.
  271. * @param \Drupal\context\ContextInterface $context
  272. * @param string $formType
  273. * @param array $form_state_additions
  274. * @return array
  275. */
  276. public function getForm(ContextInterface $context, $formType = 'edit', array $form_state_additions = array()) {
  277. return $this->entityFormBuilder->getForm($context, $formType, $form_state_additions);
  278. }
  279. /**
  280. * Sorts an array of context entities by their weight.
  281. *
  282. * Callback for uasort().
  283. *
  284. * @param ContextInterface $a
  285. * First item for comparison.
  286. *
  287. * @param ContextInterface $b
  288. * Second item for comparison.
  289. *
  290. * @return int
  291. * The comparison result for uasort().
  292. */
  293. public function sortContextsByWeight(ContextInterface $a, ContextInterface $b) {
  294. if ($a->getWeight() == $b->getWeight()) {
  295. return 0;
  296. }
  297. return ($a->getWeight() < $b->getWeight()) ? -1 : 1;
  298. }
  299. /**
  300. * Get current active theme.
  301. *
  302. * @return string
  303. * Current active theme name.
  304. */
  305. private function getCurrentTheme() {
  306. return $this->themeManager->getActiveTheme()->getName();
  307. }
  308. }