PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/concrete/blocks/core_stack_display/controller.php

https://github.com/concrete5/concrete5
PHP | 405 lines | 232 code | 49 blank | 124 comment | 33 complexity | 645d03ed72fdb99e29cd8d3f9b7145d5 MD5 | raw file
  1. <?php
  2. namespace Concrete\Block\CoreStackDisplay;
  3. use Concrete\Core\Block\BlockController;
  4. use Concrete\Core\Multilingual\Page\Section\Section;
  5. use Concrete\Core\Page\Page;
  6. use Concrete\Core\Page\Stack\Stack;
  7. use Concrete\Core\Permission\Checker;
  8. use Concrete\Core\Statistics\UsageTracker\TrackableInterface;
  9. /**
  10. * The controller for the stack display block. This is an internal proxy block that is inserted when a stack's contents are displayed in a page.
  11. *
  12. * @package Blocks
  13. * @subpackage Core Stack Display
  14. *
  15. * @author Andrew Embler <andrew@concretecms.org>
  16. * @copyright Copyright (c) 2003-2022 concretecms. (http://www.concretecms.org)
  17. * @license http://www.concretecms.org/license/ MIT License
  18. */
  19. class Controller extends BlockController implements TrackableInterface
  20. {
  21. /**
  22. * @var int|null
  23. */
  24. public $stID;
  25. /**
  26. * @var bool
  27. */
  28. protected $btCacheBlockRecord = true;
  29. /**
  30. * @var string
  31. */
  32. protected $btTable = 'btCoreStackDisplay';
  33. /**
  34. * @var bool
  35. */
  36. protected $btIsInternal = true;
  37. /**
  38. * @var bool
  39. */
  40. protected $btCacheSettingsInitialized = false;
  41. /**
  42. * @var int|null
  43. */
  44. protected $stIDNeutral;
  45. /**
  46. * @var int|null
  47. */
  48. protected $bOriginalID;
  49. /**
  50. * @return string
  51. */
  52. public function getBlockTypeDescription()
  53. {
  54. return t('Proxy block for stacks added through the UI.');
  55. }
  56. /**
  57. * @return string
  58. */
  59. public function getBlockTypeName()
  60. {
  61. return t('Stack Display');
  62. }
  63. /**
  64. * @return int
  65. */
  66. public function getOriginalBlockID()
  67. {
  68. return $this->bOriginalID;
  69. }
  70. /**
  71. * @return string
  72. */
  73. public function getSearchableContent()
  74. {
  75. $searchableContent = '';
  76. $stack = Stack::getByID($this->stID);
  77. if (is_object($stack)) {
  78. $blocks = $stack->getBlocks();
  79. if (!empty($blocks)) {
  80. foreach ($blocks as $block) {
  81. if (method_exists($block->instance, 'getSearchableContent')) {
  82. $searchableContent .= $block->instance->getSearchableContent();
  83. }
  84. }
  85. }
  86. }
  87. return $searchableContent;
  88. }
  89. /**
  90. * @param \SimpleXMLElement $blockNode
  91. * @param Page $page
  92. *
  93. * @return array<string, mixed>
  94. */
  95. public function getImportData($blockNode, $page)
  96. {
  97. $args = [];
  98. $content = (string) $blockNode->stack;
  99. $stack = Stack::getByName($content);
  100. $args['stID'] = 0;
  101. if (is_object($stack)) {
  102. $args['stID'] = $stack->getCollectionID();
  103. }
  104. return $args;
  105. }
  106. /**
  107. * @param string $method
  108. * @param mixed[] $parameters
  109. *
  110. * @return bool
  111. */
  112. public function isValidControllerTask($method, $parameters = [])
  113. {
  114. $b = $this->findBlockForAction($method, $parameters);
  115. return !empty($b);
  116. }
  117. /**
  118. * @param string $action
  119. * @param mixed[] $parameters
  120. *
  121. * @return mixed|void
  122. */
  123. public function runAction($action, $parameters = [])
  124. {
  125. parent::runAction($action, $parameters); // handles on_page_view
  126. $b = $this->findBlockForAction($action, $parameters);
  127. if (empty($b)) {
  128. return;
  129. }
  130. $controller = $b->getController();
  131. return $controller->runAction($action, $parameters);
  132. }
  133. /**
  134. * @param string $outputContent
  135. *
  136. * @return void
  137. */
  138. public function registerViewAssets($outputContent = '')
  139. {
  140. $stack = $this->getStack(true);
  141. if ($stack === null) {
  142. return;
  143. }
  144. $blocks = $stack->getBlocks();
  145. foreach ($blocks as $b) {
  146. /** @var BlockController|null $controller */
  147. $controller = $b->getController();
  148. if ($controller) {
  149. /** this always returns void */
  150. $controller->registerViewAssets($outputContent);
  151. }
  152. }
  153. }
  154. /**
  155. * @param string $method
  156. * @param array<string, mixed> $parameters
  157. *
  158. * @return \Concrete\Core\Block\Block|null
  159. */
  160. public function findBlockForAction($method, $parameters)
  161. {
  162. $stack = $this->getStack(true);
  163. if ($stack === null) {
  164. return null;
  165. }
  166. $blocks = $stack->getBlocks();
  167. foreach ($blocks as $b) {
  168. $controller = $b->getController();
  169. if ($controller->isValidControllerTask($method, $parameters)) {
  170. return $b;
  171. }
  172. }
  173. return null;
  174. }
  175. /**
  176. * @param \SimpleXMLElement $blockNode
  177. *
  178. * @return void
  179. */
  180. public function export(\SimpleXMLElement $blockNode)
  181. {
  182. $stack = $this->getStack(false);
  183. if ($stack !== null) {
  184. $cnode = $blockNode->addChild('stack');
  185. $node = dom_import_simplexml($cnode);
  186. $no = $node->ownerDocument;
  187. $node->appendChild($no->createCDataSection($stack->getCollectionName()));
  188. }
  189. }
  190. /**
  191. * @param Page $page
  192. *
  193. * @return mixed|void
  194. */
  195. public function on_page_view($page)
  196. {
  197. $stack = $this->getStack(true);
  198. if ($stack === null) {
  199. return;
  200. }
  201. $p = new Checker($stack);
  202. /** @phpstan-ignore-next-line */
  203. if ($p->canViewPage()) {
  204. $blocks = $stack->getBlocks();
  205. foreach ($blocks as $b) {
  206. $bp = new Checker($b);
  207. /** @phpstan-ignore-next-line */
  208. if ($bp->canViewBlock()) {
  209. $btc = $b->getController();
  210. if (get_class($btc) !== 'Controller') {
  211. $btc->outputAutoHeaderItems();
  212. }
  213. $csr = $b->getCustomStyle();
  214. if (is_object($csr)) {
  215. $css = $csr->getCSS();
  216. if ($css !== '') {
  217. $styleHeader = $csr->getStyleWrapper($css);
  218. $btc->addHeaderItem($styleHeader);
  219. }
  220. }
  221. $btc->runAction('on_page_view', [$page]);
  222. }
  223. }
  224. }
  225. }
  226. /**
  227. * @return bool
  228. */
  229. public function cacheBlockOutput()
  230. {
  231. $this->setupCacheSettings();
  232. return $this->btCacheBlockOutput;
  233. }
  234. /**
  235. * @return bool
  236. */
  237. public function cacheBlockOutputOnPost()
  238. {
  239. $this->setupCacheSettings();
  240. return $this->btCacheBlockOutputOnPost;
  241. }
  242. /**
  243. * @return int
  244. */
  245. public function getBlockTypeCacheOutputLifetime()
  246. {
  247. $this->setupCacheSettings();
  248. return $this->btCacheBlockOutputLifetime;
  249. }
  250. /**
  251. * @return int|null
  252. */
  253. public function getStackID()
  254. {
  255. return $this->stID;
  256. }
  257. /**
  258. * @param array<string,mixed> $args
  259. * @return void
  260. */
  261. public function save($args)
  262. {
  263. parent::save($args);
  264. $this->stID = $args['stID'];
  265. }
  266. /**
  267. * @throws \Illuminate\Contracts\Container\BindingResolutionException|\Exception
  268. *
  269. * @return void
  270. */
  271. protected function load()
  272. {
  273. parent::load();
  274. $this->set('stIDNeutral', null);
  275. /** @var Stack|false $stack */
  276. $stack = Stack::getByID($this->stID);
  277. if ($stack && $stack->isNeutralStack()) {
  278. $detector = app('multilingual/detector');
  279. // @var \Concrete\Core\Multilingual\Service\Detector $detector
  280. if ($detector->isEnabled()) {
  281. /** @var Section|false $section */
  282. $section = Section::getCurrentSection();
  283. if ($section) {
  284. $localized = $stack->getLocalizedStack($section);
  285. if ($localized) {
  286. $this->stIDNeutral = $this->stID;
  287. $this->stID = $localized->getCollectionID();
  288. $this->set('stIDNeutral', $this->stIDNeutral);
  289. $this->set('stID', $this->stID);
  290. }
  291. }
  292. }
  293. }
  294. }
  295. /**
  296. * Returns the Stack instance (if found).
  297. *
  298. * @param bool $localized set to true to look for a localized version of the stack (if not found return the neutral version)
  299. *
  300. * @return Stack|null
  301. */
  302. protected function getStack($localized)
  303. {
  304. if ($this->stIDNeutral === null || $localized) {
  305. $result = Stack::getByID($this->stID);
  306. } else {
  307. $result = Stack::getByID($this->stIDNeutral);
  308. }
  309. /** @var Stack|null $result */
  310. return $result;
  311. }
  312. /**
  313. * @return void
  314. */
  315. protected function setupCacheSettings()
  316. {
  317. if ($this->btCacheSettingsInitialized || Page::getCurrentPage()->isEditMode()) {
  318. return;
  319. }
  320. $this->btCacheSettingsInitialized = true;
  321. //Block cache settings are only as good as the weakest cached item inside. So loop through and check.
  322. $btCacheBlockOutput = true;
  323. $btCacheBlockOutputOnPost = true;
  324. $btCacheBlockOutputLifetime = 0;
  325. $stack = $this->getStack(true);
  326. if ($stack === null) {
  327. return;
  328. }
  329. $p = new Checker($stack);
  330. /** @phpstan-ignore-next-line */
  331. if ($p->canViewPage()) {
  332. $blocks = $stack->getBlocks();
  333. foreach ($blocks as $b) {
  334. if ($b->overrideAreaPermissions()) {
  335. $btCacheBlockOutput = false;
  336. $btCacheBlockOutputOnPost = false;
  337. $btCacheBlockOutputLifetime = 0;
  338. break;
  339. }
  340. $btCacheBlockOutput = $b->cacheBlockOutput();
  341. $btCacheBlockOutputOnPost = $btCacheBlockOutputOnPost && $b->cacheBlockOutputOnPost();
  342. //As soon as we find something which cannot be cached, entire block cannot be cached, so stop checking.
  343. if (!$btCacheBlockOutput) {
  344. return;
  345. }
  346. $expires = $b->getBlockOutputCacheLifetime();
  347. if ($expires && $btCacheBlockOutputLifetime < $expires) {
  348. $btCacheBlockOutputLifetime = $expires;
  349. }
  350. }
  351. }
  352. $this->btCacheBlockOutput = $btCacheBlockOutput;
  353. $this->btCacheBlockOutputOnPost = $btCacheBlockOutputOnPost;
  354. $this->btCacheBlockOutputLifetime = $btCacheBlockOutputLifetime;
  355. }
  356. }