vendor/pimcore/pimcore/models/Document/Editable/Block.php line 25

Open in your IDE?
  1. <?php
  2. /**
  3. * Pimcore
  4. *
  5. * This source file is available under two different licenses:
  6. * - GNU General Public License version 3 (GPLv3)
  7. * - Pimcore Commercial License (PCL)
  8. * Full copyright and license information is available in
  9. * LICENSE.md which is distributed with this source code.
  10. *
  11. * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12. * @license http://www.pimcore.org/license GPLv3 and PCL
  13. */
  14. namespace Pimcore\Model\Document\Editable;
  15. use Pimcore\Document\Editable\Block\BlockName;
  16. use Pimcore\Model;
  17. use Pimcore\Tool\HtmlUtils;
  18. /**
  19. * @method \Pimcore\Model\Document\Editable\Dao getDao()
  20. */
  21. class Block extends Model\Document\Editable implements BlockInterface
  22. {
  23. /**
  24. * @internal
  25. */
  26. const ATTRIBUTE_IGNORE_EDITMODE_INDICES = '_block_ignore_extra_editmode_indices';
  27. /**
  28. * Contains an array of indices, which represent the order of the elements in the block
  29. *
  30. * @internal
  31. *
  32. * @var array
  33. */
  34. protected $indices = [];
  35. /**
  36. * Current step of the block while iteration
  37. *
  38. * @internal
  39. *
  40. * @var int
  41. */
  42. protected $current = 0;
  43. /**
  44. * {@inheritdoc}
  45. */
  46. public function getType()
  47. {
  48. return 'block';
  49. }
  50. /**
  51. * {@inheritdoc}
  52. */
  53. public function getData()
  54. {
  55. return $this->indices;
  56. }
  57. /**
  58. * {@inheritdoc}
  59. */
  60. public function admin()
  61. {
  62. // nothing to do
  63. return '';
  64. }
  65. /**
  66. * {@inheritdoc}
  67. */
  68. public function frontend()
  69. {
  70. // nothing to do
  71. return '';
  72. }
  73. /**
  74. * {@inheritdoc}
  75. */
  76. public function setDataFromResource($data)
  77. {
  78. $this->indices = \Pimcore\Tool\Serialize::unserialize($data);
  79. return $this;
  80. }
  81. /**
  82. * {@inheritdoc}
  83. */
  84. public function setDataFromEditmode($data)
  85. {
  86. $this->indices = $data;
  87. return $this;
  88. }
  89. /**
  90. * @internal
  91. *
  92. * @return $this
  93. */
  94. protected function setDefault()
  95. {
  96. if (empty($this->indices) && isset($this->config['default']) && $this->config['default']) {
  97. for ($i = 0; $i < (int)$this->config['default']; $i++) {
  98. $this->indices[$i] = $i + 1;
  99. }
  100. }
  101. return $this;
  102. }
  103. /**
  104. * {@inheritdoc}
  105. */
  106. public function getIterator()
  107. {
  108. while ($this->loop()) {
  109. yield $this->getCurrentIndex();
  110. }
  111. if ($this->getEditmode() && !$this->isIgnoreEditmodeIndices()) {
  112. // yeah, I know the following is f******* crazy :D
  113. $this->current = 0;
  114. $indicesBackup = $this->indices;
  115. $this->indices[0] = 1000000;
  116. $this->getBlockState()->pushBlock(BlockName::createFromEditable($this));
  117. $this->blockConstruct();
  118. $blockStartHtml = $this->blockStart(true, true);
  119. ob_start();
  120. $editableDefCollector = $this->getEditableDefinitionCollector();
  121. $editableDefCollector->stashPush();
  122. yield $this->getCurrentIndex() + 1;
  123. $blockEndHtml = $this->blockEnd(true);
  124. $this->blockDestruct();
  125. $blockState = $this->getBlockState();
  126. if ($blockState->hasBlocks()) {
  127. $blockState->popBlock();
  128. }
  129. $templateEditableDefinitions = $editableDefCollector->getDefinitions();
  130. $editableDefCollector->stashPull();
  131. $this->config['template'] = [
  132. 'html' => $blockStartHtml . ob_get_clean() . $blockEndHtml,
  133. 'editables' => $templateEditableDefinitions,
  134. ];
  135. $editableDefCollector->add($this);
  136. $this->indices = $indicesBackup;
  137. }
  138. }
  139. /**
  140. * @internal
  141. *
  142. * @return bool
  143. */
  144. public function loop()
  145. {
  146. $manual = false;
  147. if (($this->config['manual'] ?? false) == true) {
  148. $manual = true;
  149. }
  150. $this->setDefault();
  151. if ($this->current > 0) {
  152. if (!$manual) {
  153. $this->blockDestruct();
  154. $this->blockEnd();
  155. }
  156. } else {
  157. if (!$manual) {
  158. $this->start();
  159. }
  160. }
  161. if ($this->current < count($this->indices) && $this->current < $this->config['limit']) {
  162. if (!$manual) {
  163. $this->blockConstruct();
  164. $this->blockStart();
  165. }
  166. return true;
  167. } else {
  168. if (!$manual) {
  169. $this->end();
  170. }
  171. return false;
  172. }
  173. }
  174. /**
  175. * {@inheritdoc}
  176. */
  177. protected function getEditmodeElementAttributes(): array
  178. {
  179. $attributes = parent::getEditmodeElementAttributes();
  180. $attributes = array_merge($attributes, [
  181. 'name' => $this->getName(),
  182. 'type' => $this->getType(),
  183. ]);
  184. return $attributes;
  185. }
  186. /**
  187. * {@inheritdoc}
  188. */
  189. public function start()
  190. {
  191. // set name suffix for the whole block element, this will be added to all child elements of the block
  192. $this->getBlockState()->pushBlock(BlockName::createFromEditable($this));
  193. $attributes = $this->getEditmodeElementAttributes();
  194. $attributeString = HtmlUtils::assembleAttributeString($attributes);
  195. $this->outputEditmode('<div ' . $attributeString . '>');
  196. return $this;
  197. }
  198. /**
  199. * {@inheritdoc}
  200. */
  201. public function end()
  202. {
  203. $this->current = 0;
  204. // remove the current block which was set by $this->start()
  205. $blockState = $this->getBlockState();
  206. if ($blockState->hasBlocks()) {
  207. $blockState->popBlock();
  208. }
  209. $this->outputEditmode('</div>');
  210. }
  211. /**
  212. * {@inheritdoc}
  213. */
  214. public function blockConstruct()
  215. {
  216. // set the current block suffix for the child elements (0, 1, 3, ...)
  217. // this will be removed in blockDestruct
  218. $this->getBlockState()->pushIndex($this->indices[$this->current] ?? 0);
  219. }
  220. /**
  221. * {@inheritdoc}
  222. */
  223. public function blockDestruct()
  224. {
  225. $blockState = $this->getBlockState();
  226. if ($blockState->hasIndexes()) {
  227. $blockState->popIndex();
  228. }
  229. }
  230. /**
  231. * @param bool $showControls
  232. * @param bool $return
  233. * @param string $additionalClass
  234. */
  235. public function blockStart($showControls = true, $return = false, $additionalClass = '')
  236. {
  237. $attr = $this->getBlockAttributes();
  238. $outerAttributes = [
  239. 'key' => $this->indices[$this->current] ?? null,
  240. ];
  241. $oAttr = HtmlUtils::assembleAttributeString($outerAttributes);
  242. $class = 'pimcore_block_entry';
  243. if (!empty($additionalClass)) {
  244. $class = sprintf('%s %s', $class, $additionalClass);
  245. }
  246. $html = '<div class="' . $class . '" ' . $oAttr . ' ' . $attr . '>';
  247. if ($showControls) {
  248. $html .= $this->blockControls(true);
  249. }
  250. if ($return) {
  251. return $html;
  252. }
  253. $this->outputEditmode($html);
  254. }
  255. /**
  256. * Custom position of button controls between blockStart -> blockEnd
  257. *
  258. * @param bool $return
  259. */
  260. public function blockControls($return = false)
  261. {
  262. $attr = $this->getBlockAttributes();
  263. $html = <<<EOT
  264. <div class="pimcore_block_buttons" $attr>
  265. <div class="pimcore_block_amount" $attr></div>
  266. <div class="pimcore_block_plus" $attr></div>
  267. <div class="pimcore_block_minus" $attr></div>
  268. <div class="pimcore_block_up" $attr></div>
  269. <div class="pimcore_block_down" $attr></div>
  270. <div class="pimcore_block_clear" $attr></div>
  271. </div>
  272. EOT;
  273. $this->current++;
  274. if ($return) {
  275. return $html;
  276. }
  277. $this->outputEditmode($html);
  278. }
  279. /**
  280. * @param bool $return
  281. */
  282. public function blockEnd($return = false)
  283. {
  284. // close outer element
  285. $html = '</div>';
  286. if ($return) {
  287. return $html;
  288. }
  289. $this->outputEditmode($html);
  290. }
  291. /**
  292. * {@inheritdoc}
  293. */
  294. public function setConfig($config)
  295. {
  296. if (empty($config['limit'])) {
  297. $config['limit'] = 1000000;
  298. }
  299. $this->config = $config;
  300. if (($this->config['manual'] ?? false) === true) {
  301. $this->config['reload'] = true;
  302. }
  303. return $this;
  304. }
  305. /**
  306. * {@inheritdoc}
  307. */
  308. public function getCount()
  309. {
  310. return count($this->indices);
  311. }
  312. /**
  313. * {@inheritdoc}
  314. */
  315. public function getCurrent()
  316. {
  317. return $this->current - 1;
  318. }
  319. /**
  320. * {@inheritdoc}
  321. */
  322. public function getCurrentIndex()
  323. {
  324. return $this->indices[$this->getCurrent()] ?? 0;
  325. }
  326. /**
  327. * @return array
  328. */
  329. public function getIndices()
  330. {
  331. return $this->indices;
  332. }
  333. /**
  334. * If object was serialized, set the counter back to 0
  335. */
  336. public function __wakeup()
  337. {
  338. $this->current = 0;
  339. }
  340. /**
  341. * {@inheritdoc}
  342. */
  343. public function isEmpty()
  344. {
  345. return !(bool) count($this->indices);
  346. }
  347. /**
  348. * @return Block\Item[]
  349. */
  350. public function getElements()
  351. {
  352. $document = $this->getDocument();
  353. // https://github.com/pimcore/pimcore/issues/6629
  354. if (!$document instanceof Model\Document\PageSnippet) {
  355. return [];
  356. }
  357. $parentBlockNames = $this->getParentBlockNames();
  358. $parentBlockNames[] = $this->getName();
  359. $list = [];
  360. foreach ($this->getData() as $index) {
  361. $list[] = new Block\Item($document, $parentBlockNames, (int)$index);
  362. }
  363. return $list;
  364. }
  365. /**
  366. * @return string
  367. */
  368. private function getBlockAttributes(): string
  369. {
  370. $attributes = [
  371. 'data-name' => $this->getName(),
  372. 'data-real-name' => $this->getRealName(),
  373. ];
  374. return HtmlUtils::assembleAttributeString($attributes);
  375. }
  376. /**
  377. * @return bool
  378. */
  379. private function isIgnoreEditmodeIndices(): bool
  380. {
  381. $requestStack = \Pimcore::getContainer()->get('request_stack');
  382. $request = $requestStack->getCurrentRequest();
  383. if ($request === null) {
  384. return false;
  385. }
  386. return $request->get(self::ATTRIBUTE_IGNORE_EDITMODE_INDICES, false);
  387. }
  388. }