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. * {@inheritdoc}
  232. */
  233. public function blockStart($showControls = true, $return = false, $additionalClass = '')
  234. {
  235. $attr = $this->getBlockAttributes();
  236. $outerAttributes = [
  237. 'key' => $this->indices[$this->current] ?? null,
  238. ];
  239. $oAttr = HtmlUtils::assembleAttributeString($outerAttributes);
  240. $class = 'pimcore_block_entry';
  241. if (!empty($additionalClass)) {
  242. $class = sprintf('%s %s', $class, $additionalClass);
  243. }
  244. $html = '<div class="' . $class . '" ' . $oAttr . ' ' . $attr . '>';
  245. if ($showControls) {
  246. $html .= $this->blockControls(true);
  247. }
  248. if ($return) {
  249. return $html;
  250. }
  251. $this->outputEditmode($html);
  252. }
  253. /**
  254. * Custom position of button controls between blockStart -> blockEnd
  255. *
  256. * @param bool $return
  257. */
  258. public function blockControls($return = false)
  259. {
  260. $attr = $this->getBlockAttributes();
  261. $html = <<<EOT
  262. <div class="pimcore_block_buttons" $attr>
  263. <div class="pimcore_block_amount" $attr></div>
  264. <div class="pimcore_block_plus" $attr></div>
  265. <div class="pimcore_block_minus" $attr></div>
  266. <div class="pimcore_block_up" $attr></div>
  267. <div class="pimcore_block_down" $attr></div>
  268. <div class="pimcore_block_clear" $attr></div>
  269. </div>
  270. EOT;
  271. $this->current++;
  272. if ($return) {
  273. return $html;
  274. }
  275. $this->outputEditmode($html);
  276. }
  277. /**
  278. * {@inheritdoc}
  279. */
  280. public function blockEnd($return = false)
  281. {
  282. // close outer element
  283. $html = '</div>';
  284. if ($return) {
  285. return $html;
  286. }
  287. $this->outputEditmode($html);
  288. }
  289. /**
  290. * {@inheritdoc}
  291. */
  292. public function setConfig($config)
  293. {
  294. if (empty($config['limit'])) {
  295. $config['limit'] = 1000000;
  296. }
  297. $this->config = $config;
  298. if (($this->config['manual'] ?? false) === true) {
  299. $this->config['reload'] = true;
  300. }
  301. return $this;
  302. }
  303. /**
  304. * {@inheritdoc}
  305. */
  306. public function getCount()
  307. {
  308. return count($this->indices);
  309. }
  310. /**
  311. * {@inheritdoc}
  312. */
  313. public function getCurrent()
  314. {
  315. return $this->current - 1;
  316. }
  317. /**
  318. * {@inheritdoc}
  319. */
  320. public function getCurrentIndex()
  321. {
  322. return $this->indices[$this->getCurrent()] ?? 0;
  323. }
  324. /**
  325. * @return array
  326. */
  327. public function getIndices()
  328. {
  329. return $this->indices;
  330. }
  331. /**
  332. * If object was serialized, set the counter back to 0
  333. */
  334. public function __wakeup()
  335. {
  336. $this->current = 0;
  337. }
  338. /**
  339. * {@inheritdoc}
  340. */
  341. public function isEmpty()
  342. {
  343. return !(bool) count($this->indices);
  344. }
  345. /**
  346. * @return Block\Item[]
  347. */
  348. public function getElements()
  349. {
  350. $document = $this->getDocument();
  351. // https://github.com/pimcore/pimcore/issues/6629
  352. if (!$document instanceof Model\Document\PageSnippet) {
  353. return [];
  354. }
  355. $parentBlockNames = $this->getParentBlockNames();
  356. $parentBlockNames[] = $this->getName();
  357. $list = [];
  358. foreach ($this->getData() as $index) {
  359. $list[] = new Block\Item($document, $parentBlockNames, (int)$index);
  360. }
  361. return $list;
  362. }
  363. /**
  364. * @return string
  365. */
  366. private function getBlockAttributes(): string
  367. {
  368. $attributes = [
  369. 'data-name' => $this->getName(),
  370. 'data-real-name' => $this->getRealName(),
  371. ];
  372. return HtmlUtils::assembleAttributeString($attributes);
  373. }
  374. /**
  375. * @return bool
  376. */
  377. private function isIgnoreEditmodeIndices(): bool
  378. {
  379. $requestStack = \Pimcore::getContainer()->get('request_stack');
  380. $request = $requestStack->getCurrentRequest();
  381. if ($request === null) {
  382. return false;
  383. }
  384. return $request->get(self::ATTRIBUTE_IGNORE_EDITMODE_INDICES, false);
  385. }
  386. }