PageRenderTime 23ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/concrete/src/Entity/Block/BlockType/BlockType.php

http://github.com/concrete5/concrete5
PHP | 692 lines | 408 code | 73 blank | 211 comment | 32 complexity | dd549c9df97388bc252092a0db64b05b MD5 | raw file
Possible License(s): MIT, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. <?php
  2. namespace Concrete\Core\Entity\Block\BlockType;
  3. use Concrete\Core\Block\Block;
  4. use BlockTypeSet;
  5. use Concrete\Block\CoreStackDisplay\Controller;
  6. use Concrete\Core\Block\BlockType\BlockTypeList;
  7. use Concrete\Core\Block\View\BlockView;
  8. use Concrete\Core\Database\Schema\Schema;
  9. use Concrete\Core\Filesystem\TemplateFile;
  10. use Concrete\Core\Package\PackageList;
  11. use Concrete\Core\Support\Facade\Application;
  12. use Concrete\Core\Support\Facade\Facade;
  13. use Core;
  14. use Database as DB;
  15. use Environment;
  16. use Loader;
  17. use Localization;
  18. use Package;
  19. use Page;
  20. use Concrete\Core\User\User;
  21. use Doctrine\ORM\Mapping as ORM;
  22. use Concrete\Core\Database\Connection\Connection;
  23. /**
  24. * @ORM\Entity
  25. * @ORM\Table(name="BlockTypes")
  26. */
  27. class BlockType
  28. {
  29. public $controller;
  30. /**
  31. * @ORM\Column(type="boolean")
  32. */
  33. protected $btIgnorePageThemeGridFrameworkContainer = false;
  34. /**
  35. * @ORM\Id @ORM\Column(type="integer")
  36. * @ORM\GeneratedValue
  37. */
  38. protected $btID;
  39. /**
  40. * @ORM\Column(type="string", length=128)
  41. */
  42. protected $btHandle;
  43. /**
  44. * @ORM\Column(type="string", length=128)
  45. */
  46. protected $btName;
  47. /**
  48. * @ORM\Column(type="text")
  49. */
  50. protected $btDescription;
  51. /**
  52. * @ORM\Column(type="boolean")
  53. */
  54. protected $btCopyWhenPropagate = false;
  55. /**
  56. * @ORM\Column(type="boolean")
  57. */
  58. protected $btIncludeAll = false;
  59. /**
  60. * @ORM\Column(type="boolean")
  61. */
  62. protected $btIsInternal = false;
  63. /**
  64. * @ORM\Column(type="boolean")
  65. */
  66. protected $btSupportsInlineEdit = false;
  67. /**
  68. * @ORM\Column(type="boolean")
  69. */
  70. protected $btSupportsInlineAdd = false;
  71. /**
  72. * @ORM\Column(type="integer")
  73. */
  74. protected $btDisplayOrder = 0;
  75. /**
  76. * @ORM\Column(type="integer")
  77. */
  78. protected $btInterfaceHeight;
  79. /**
  80. * @ORM\Column(type="integer")
  81. */
  82. protected $btInterfaceWidth;
  83. /**
  84. * @ORM\Column(type="integer", options={"unsigned": true})
  85. */
  86. protected $pkgID = 0;
  87. public function getBlockTypeInSetName()
  88. {
  89. if ($this->controller) {
  90. return $this->controller->getBlockTypeInSetName();
  91. }
  92. }
  93. /**
  94. * Sets the Ignore Page Theme Gride Framework Container.
  95. */
  96. public function setBlockTypeIgnorePageThemeGridFrameworkContainer($btIgnorePageThemeGridFrameworkContainer)
  97. {
  98. $this->btIgnorePageThemeGridFrameworkContainer = $btIgnorePageThemeGridFrameworkContainer;
  99. }
  100. /**
  101. * Sets the block type handle.
  102. */
  103. public function setBlockTypeName($btName)
  104. {
  105. $this->btName = $btName;
  106. }
  107. /**
  108. * Sets the block type description.
  109. */
  110. public function setBlockTypeDescription($btDescription)
  111. {
  112. $this->btDescription = $btDescription;
  113. }
  114. /**
  115. * Sets the block type handle.
  116. */
  117. public function setBlockTypeHandle($btHandle)
  118. {
  119. $this->btHandle = $btHandle;
  120. }
  121. public function setPackageID($pkgID)
  122. {
  123. $this->pkgID = $pkgID;
  124. }
  125. /**
  126. * Determines if the block type has templates available.
  127. *
  128. * @return bool
  129. */
  130. public function hasAddTemplate()
  131. {
  132. $bv = new BlockView($this);
  133. $path = $bv->getBlockPath(FILENAME_BLOCK_ADD);
  134. if (file_exists($path . '/' . FILENAME_BLOCK_ADD)) {
  135. return true;
  136. }
  137. return false;
  138. }
  139. /**
  140. * gets the available composer templates
  141. * used for editing instances of the BlockType while in the composer ui in the dashboard.
  142. *
  143. * @return TemplateFile[]
  144. */
  145. public function getBlockTypeComposerTemplates()
  146. {
  147. $btHandle = $this->getBlockTypeHandle();
  148. $files = array();
  149. $fh = Loader::helper('file');
  150. $dir = DIR_FILES_BLOCK_TYPES . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES_COMPOSER;
  151. if (is_dir($dir)) {
  152. $files = array_merge($files, $fh->getDirectoryContents($dir));
  153. }
  154. foreach (PackageList::get()->getPackages() as $pkg) {
  155. $dir =
  156. (is_dir(DIR_PACKAGES . '/' . $pkg->getPackageHandle()) ? DIR_PACKAGES : DIR_PACKAGES_CORE)
  157. . '/' . $pkg->getPackageHandle() . '/' . DIRNAME_BLOCKS . '/' . $btHandle . '/' . DIRNAME_BLOCK_TEMPLATES_COMPOSER;
  158. if (is_dir($dir)) {
  159. $files = array_merge($files, $fh->getDirectoryContents($dir));
  160. }
  161. }
  162. $dir = DIR_FILES_BLOCK_TYPES_CORE . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES_COMPOSER;
  163. if (file_exists($dir)) {
  164. $files = array_merge($files, $fh->getDirectoryContents($dir));
  165. }
  166. $templates = array();
  167. foreach (array_unique($files) as $file) {
  168. $templates[] = new TemplateFile($this, $file);
  169. }
  170. return TemplateFile::sortTemplateFileList($templates);
  171. }
  172. /**
  173. * @return string
  174. */
  175. public function getBlockTypeHandle()
  176. {
  177. return $this->btHandle;
  178. }
  179. /**
  180. * if a the current BlockType supports inline edit or not.
  181. *
  182. * @return bool
  183. */
  184. public function supportsInlineEdit()
  185. {
  186. return $this->btSupportsInlineEdit;
  187. }
  188. /**
  189. * if a the current BlockType supports inline add or not.
  190. *
  191. * @return bool
  192. */
  193. public function supportsInlineAdd()
  194. {
  195. return $this->btSupportsInlineAdd;
  196. }
  197. /**
  198. * Returns true if the block type is internal (and therefore cannot be removed) a core block.
  199. *
  200. * @return bool
  201. */
  202. public function isInternalBlockType()
  203. {
  204. return $this->btIsInternal;
  205. }
  206. /**
  207. * returns the width in pixels that the block type's editing dialog will open in.
  208. *
  209. * @return int
  210. */
  211. public function getBlockTypeInterfaceWidth()
  212. {
  213. return $this->btInterfaceWidth;
  214. }
  215. /**
  216. * returns the height in pixels that the block type's editing dialog will open in.
  217. *
  218. * @return int
  219. */
  220. public function getBlockTypeInterfaceHeight()
  221. {
  222. return $this->btInterfaceHeight;
  223. }
  224. /**
  225. * If true, container classes will not be wrapped around this block type in edit mode (if the
  226. * theme in question supports a grid framework.
  227. *
  228. * @return bool
  229. */
  230. public function ignorePageThemeGridFrameworkContainer()
  231. {
  232. return $this->btIgnorePageThemeGridFrameworkContainer;
  233. }
  234. /**
  235. * returns the id of the BlockType's package if it's in a package.
  236. *
  237. * @return int
  238. */
  239. public function getPackageID()
  240. {
  241. return $this->pkgID;
  242. }
  243. /**
  244. * gets the BlockTypes description text.
  245. *
  246. * @return string
  247. */
  248. public function getBlockTypeDescription()
  249. {
  250. return $this->btDescription;
  251. }
  252. /**
  253. * @return string
  254. */
  255. public function getBlockTypeName()
  256. {
  257. return $this->btName;
  258. }
  259. /**
  260. * @return bool
  261. */
  262. public function isCopiedWhenPropagated()
  263. {
  264. return $this->btCopyWhenPropagate;
  265. }
  266. /**
  267. * If true, this block is not versioned on a page – it is included as is on all versions of the page, even when updated.
  268. *
  269. * @return bool
  270. */
  271. public function includeAll()
  272. {
  273. return $this->btIncludeAll;
  274. }
  275. /**
  276. * @deprecated
  277. */
  278. public function getBlockTypeClassFromHandle()
  279. {
  280. return $this->getBlockTypeClass();
  281. }
  282. /**
  283. * Returns the class for the current block type.
  284. */
  285. public function getBlockTypeClass()
  286. {
  287. return \Concrete\Core\Block\BlockType\BlockType::getBlockTypeMappedClass($this->getBlockTypeHandle(), $this->getPackageHandle());
  288. }
  289. /**
  290. * returns the handle of the BlockType's package if it's in a package.
  291. *
  292. * @return string
  293. */
  294. public function getPackageHandle()
  295. {
  296. return \Concrete\Core\Package\PackageList::getHandle($this->pkgID);
  297. }
  298. /**
  299. * Returns an array of all BlockTypeSet objects that this block is in.
  300. *
  301. * @return BlockTypeSet[]
  302. */
  303. public function getBlockTypeSets()
  304. {
  305. $db = Loader::db();
  306. $list = array();
  307. $r = $db->Execute(
  308. 'select btsID from BlockTypeSetBlockTypes where btID = ? order by displayOrder asc',
  309. array($this->getBlockTypeID()));
  310. while ($row = $r->FetchRow()) {
  311. $list[] = BlockTypeSet::getByID($row['btsID']);
  312. }
  313. $r->Close();
  314. return $list;
  315. }
  316. /**
  317. * @return int
  318. */
  319. public function getBlockTypeID()
  320. {
  321. return $this->btID;
  322. }
  323. /**
  324. * Returns the number of unique instances of this block throughout the entire site
  325. * note - this count could include blocks in areas that are no longer rendered by the theme.
  326. *
  327. * @param bool specify true if you only want to see the number of blocks in active pages
  328. *
  329. * @return int
  330. */
  331. public function getCount($ignoreUnapprovedVersions = false)
  332. {
  333. $app = Application::getFacadeApplication();
  334. $db = $app->make(Connection::class);
  335. $now = $app->make('date')->getOverridableNow();
  336. if ($ignoreUnapprovedVersions) {
  337. $count = $db->GetOne(<<<'EOT'
  338. SELECT
  339. count(btID)
  340. FROM
  341. Blocks b
  342. INNER JOIN CollectionVersionBlocks cvb
  343. ON b.bID=cvb.bID
  344. INNER JOIN CollectionVersions cv
  345. ON cvb.cID=cv.cID AND cvb.cvID=cv.cvID AND cv.cvIsApproved=1 AND (cv.cvPublishDate IS NULL OR cv.cvPublishDate <= ?) AND (cv.cvPublishEndDate IS NULL OR cv.cvPublishEndDate >= ?)
  346. WHERE
  347. b.btID = ?
  348. EOT
  349. ,
  350. [$now, $now, $this->btID]
  351. );
  352. } else {
  353. $count = $db->GetOne("SELECT count(btID) FROM Blocks WHERE btID = ?", array($this->btID));
  354. }
  355. return $count;
  356. }
  357. /**
  358. * Not a permissions call. Actually checks to see whether this block is not an internal one.
  359. *
  360. * @return bool
  361. */
  362. public function canUnInstall()
  363. {
  364. return !$this->isBlockTypeInternal();
  365. }
  366. /**
  367. * if a the current BlockType is Internal or not - meaning one of the core built-in concrete5 blocks.
  368. *
  369. * @return bool
  370. */
  371. public function isBlockTypeInternal()
  372. {
  373. return $this->btIsInternal;
  374. }
  375. /**
  376. * Renders a particular view of a block type, using the public $controller variable as the block type's controller.
  377. *
  378. * @param string template 'view' for the default
  379. */
  380. public function render($view = 'view')
  381. {
  382. $bv = new BlockView($this);
  383. $bv->render($view);
  384. }
  385. /**
  386. * get's the block type controller.
  387. *
  388. * @return BlockTypeController
  389. */
  390. public function getController()
  391. {
  392. return $this->controller;
  393. }
  394. /**
  395. * Gets the custom templates available for the current BlockType.
  396. *
  397. * @return TemplateFile[]
  398. */
  399. public function getBlockTypeCustomTemplates(Block $b)
  400. {
  401. $btHandle = $this->getBlockTypeHandle();
  402. $fh = Loader::helper('file');
  403. $files = array();
  404. $dir = DIR_FILES_BLOCK_TYPES . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES;
  405. if (is_dir($dir)) {
  406. $files = array_merge($files, $fh->getDirectoryContents($dir));
  407. }
  408. // Next, check the current theme.
  409. $c = $b->getBlockCollectionObject();
  410. if (is_object($c)) {
  411. $theme = $c->getCollectionThemeObject();
  412. if (is_object($theme)) {
  413. $dir = DIR_FILES_THEMES . "/" . $theme->getThemeHandle() . "/" . DIRNAME_BLOCKS . "/" . $btHandle . "/" . DIRNAME_BLOCK_TEMPLATES;
  414. if (is_dir($dir)) {
  415. $files = array_merge($files, $fh->getDirectoryContents($dir));
  416. }
  417. if ($theme->getPackageHandle()) {
  418. $dir =
  419. (is_dir(DIR_PACKAGES . '/' . $theme->getPackageHandle()) ? DIR_PACKAGES : DIR_PACKAGES_CORE)
  420. . '/' . $theme->getPackageHandle() . '/' . DIRNAME_THEMES . '/' . $theme->getThemeHandle() . '/' . DIRNAME_BLOCKS . '/' . $btHandle . '/' . DIRNAME_BLOCK_TEMPLATES;
  421. if (is_dir($dir)) {
  422. $files = array_merge($files, $fh->getDirectoryContents($dir));
  423. }
  424. }
  425. $dir = DIR_FILES_THEMES_CORE . "/" . $theme->getThemeHandle() . "/" . DIRNAME_BLOCKS . "/" . $btHandle . "/" . DIRNAME_BLOCK_TEMPLATES;
  426. if (is_dir($dir)) {
  427. $files = array_merge($files, $fh->getDirectoryContents($dir));
  428. }
  429. }
  430. }
  431. // NOW, we check to see if this btHandle has any custom templates that have been installed as separate packages
  432. foreach (PackageList::get()->getPackages() as $pkg) {
  433. $dir =
  434. (is_dir(DIR_PACKAGES . '/' . $pkg->getPackageHandle()) ? DIR_PACKAGES : DIR_PACKAGES_CORE)
  435. . '/' . $pkg->getPackageHandle() . '/' . DIRNAME_BLOCKS . '/' . $btHandle . '/' . DIRNAME_BLOCK_TEMPLATES;
  436. if (is_dir($dir)) {
  437. $files = array_merge($files, $fh->getDirectoryContents($dir));
  438. }
  439. }
  440. $dir = DIR_FILES_BLOCK_TYPES_CORE . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES;
  441. if (is_dir($dir)) {
  442. $files = array_merge($files, $fh->getDirectoryContents($dir));
  443. }
  444. $templates = array();
  445. foreach (array_unique($files) as $file) {
  446. $templates[] = new TemplateFile($this, $file);
  447. }
  448. return TemplateFile::sortTemplateFileList($templates);
  449. }
  450. /**
  451. * @private
  452. */
  453. public function setBlockTypeDisplayOrder($displayOrder)
  454. {
  455. $db = Loader::db();
  456. $displayOrder = intval($displayOrder); //in case displayOrder came from a string (so ADODB escapes it properly)
  457. $sql = "UPDATE BlockTypes SET btDisplayOrder = btDisplayOrder - 1 WHERE btDisplayOrder > ?";
  458. $vals = array($this->btDisplayOrder);
  459. $db->Execute($sql, $vals);
  460. $sql = "UPDATE BlockTypes SET btDisplayOrder = btDisplayOrder + 1 WHERE btDisplayOrder >= ?";
  461. $vals = array($displayOrder);
  462. $db->Execute($sql, $vals);
  463. $sql = "UPDATE BlockTypes SET btDisplayOrder = ? WHERE btID = ?";
  464. $vals = array($displayOrder, $this->btID);
  465. $db->Execute($sql, $vals);
  466. // now we remove the block type from cache
  467. /** @var \Concrete\Core\Cache\Cache $cache */
  468. $cache = Core::make('cache');
  469. $cache->delete('blockTypeByID/' . $this->btID);
  470. $cache->delete('blockTypeByHandle/' . $this->btHandle);
  471. $cache->delete('blockTypeList');
  472. }
  473. /**
  474. * Get the display order of this block type when it's not assigned to any block type set.
  475. *
  476. * @return int
  477. */
  478. public function getBlockTypeDisplayOrder()
  479. {
  480. return $this->btDisplayOrder;
  481. }
  482. /**
  483. * refreshes the BlockType's database schema throws an Exception if error.
  484. */
  485. public function refresh()
  486. {
  487. $app = Facade::getFacadeApplication();
  488. $db = $app->make('database')->connection();
  489. $pkgHandle = false;
  490. if ($this->pkgID > 0) {
  491. $pkgHandle = $this->getPackageHandle();
  492. }
  493. $class = \Concrete\Core\Block\BlockType\BlockType::getBlockTypeMappedClass($this->btHandle, $pkgHandle);
  494. $bta = $app->build($class);
  495. $this->loadFromController($bta);
  496. $em = \ORM::entityManager();
  497. $em->persist($this);
  498. $em->flush();
  499. $env = Environment::get();
  500. $r = $env->getRecord(DIRNAME_BLOCKS . '/' . $this->btHandle . '/' . FILENAME_BLOCK_DB, $this->getPackageHandle());
  501. if ($r->exists()) {
  502. $parser = Schema::getSchemaParser(simplexml_load_file($r->file));
  503. $parser->setIgnoreExistingTables(false);
  504. $toSchema = $parser->parse($db);
  505. $fromSchema = $db->getSchemaManager()->createSchema();
  506. $comparator = new \Doctrine\DBAL\Schema\Comparator();
  507. $schemaDiff = $comparator->compare($fromSchema, $toSchema);
  508. $saveQueries = $schemaDiff->toSaveSql($db->getDatabasePlatform());
  509. foreach ($saveQueries as $query) {
  510. $db->query($query);
  511. }
  512. }
  513. }
  514. public function loadFromController($bta)
  515. {
  516. $this->btName = $bta->getBlockTypeName();
  517. $this->btDescription = $bta->getBlockTypeDescription();
  518. $this->btCopyWhenPropagate = $bta->isCopiedWhenPropagated();
  519. $this->btIncludeAll = $bta->includeAll();
  520. $this->btIsInternal = $bta->isBlockTypeInternal();
  521. $this->btSupportsInlineEdit = $bta->supportsInlineEdit();
  522. $this->btSupportsInlineAdd = $bta->supportsInlineAdd();
  523. $this->btIgnorePageThemeGridFrameworkContainer = $bta->ignorePageThemeGridFrameworkContainer();
  524. $this->btInterfaceHeight = $bta->getInterfaceHeight();
  525. $this->btInterfaceWidth = $bta->getInterfaceWidth();
  526. }
  527. /**
  528. * Removes the block type. Also removes instances of content.
  529. */
  530. public function delete()
  531. {
  532. $db = Loader::db();
  533. $r = $db->Execute(
  534. 'select cID, cvID, b.bID, arHandle
  535. from CollectionVersionBlocks cvb
  536. inner join Blocks b on b.bID = cvb.bID
  537. where btID = ?
  538. union
  539. select cID, cvID, cvb.bID, arHandle
  540. from CollectionVersionBlocks cvb
  541. inner join btCoreScrapbookDisplay btCSD on cvb.bID = btCSD.bID
  542. inner join Blocks b on b.bID = btCSD.bOriginalID
  543. where btID = ?',
  544. array($this->getBlockTypeID(), $this->getBlockTypeID()));
  545. while ($row = $r->FetchRow()) {
  546. $nc = Page::getByID($row['cID'], $row['cvID']);
  547. if (!is_object($nc) || $nc->isError()) {
  548. continue;
  549. }
  550. $b = Block::getByID($row['bID'], $nc, $row['arHandle']);
  551. if (is_object($b)) {
  552. $b->deleteBlock();
  553. }
  554. }
  555. $em = \ORM::entityManager();
  556. $em->remove($this);
  557. $em->flush();
  558. //Remove gaps in display order numbering (to avoid future sorting errors)
  559. BlockTypeList::resetBlockTypeDisplayOrder('btDisplayOrder');
  560. }
  561. /**
  562. * Adds a block to the system without adding it to a collection.
  563. * Passes page and area data along if it is available, however.
  564. *
  565. * @param mixed $data
  566. * @param bool|\Collection $c
  567. * @param bool|\Area $a
  568. *
  569. * @return bool|\Concrete\Core\Block\Block
  570. */
  571. public function add($data, $c = false, $a = false)
  572. {
  573. $app = Facade::getFacadeApplication();
  574. $db = $app->make('database')->connection();
  575. $u = $app->make(User::class);
  576. if (isset($data['uID'])) {
  577. $uID = $data['uID'];
  578. } else {
  579. $uID = $u->getUserID();
  580. }
  581. $bName = '';
  582. if (isset($data['bName'])) {
  583. $bName = $data['bName'];
  584. }
  585. $btID = $this->btID;
  586. $dh = $app->make('helper/date');
  587. $bDate = $dh->getOverridableNow();
  588. $bIsActive = (isset($this->btActiveWhenAdded) && $this->btActiveWhenAdded == 1) ? 1 : 0;
  589. $v = array($bName, $bDate, $bDate, $bIsActive, $btID, $uID);
  590. $q = "insert into Blocks (bName, bDateAdded, bDateModified, bIsActive, btID, uID) values (?, ?, ?, ?, ?, ?)";
  591. $res = $db->executeQuery($q, $v);
  592. // we get the block object for the block we just added
  593. if ($res) {
  594. $bIDnew = $db->lastInsertId();
  595. $nb = Block::getByID($bIDnew);
  596. if (is_object($c)) {
  597. $nb->setBlockCollectionObject($c);
  598. }
  599. if (is_object($a)) {
  600. $nb->setBlockAreaObject($a);
  601. }
  602. $class = $this->getBlockTypeClass();
  603. $bc = $app->build($class, [$nb]);
  604. $bc->save($data);
  605. return Block::getByID($bIDnew);
  606. }
  607. }
  608. /**
  609. * Loads controller.
  610. */
  611. public function loadController()
  612. {
  613. $class = $this->getBlockTypeClass();
  614. /** @var Controller controller */
  615. if ($class) {
  616. $this->controller = Facade::getFacadeApplication()->build($class, [$this]);
  617. }
  618. }
  619. }