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

/concrete/core/models/block_types.php

https://bitbucket.org/selfeky/xclusivescardwebsite
PHP | 1056 lines | 627 code | 149 blank | 280 comment | 105 complexity | c0f0e4e2cb23e200084fd840700a6847 MD5 | raw file
  1. <?php
  2. defined('C5_EXECUTE') or die("Access Denied.");
  3. /**
  4. * Contains the blocktype object, the block type list (which is just a wrapper for querying the system for block types, and the block type
  5. * DB wrapper for ADODB.
  6. * @package Blocks
  7. * @author Andrew Embler <andrew@concrete5.org>
  8. * @category Concrete
  9. * @copyright Copyright (c) 2003-2008 Concrete5. (http://www.concrete5.org)
  10. * @license http://www.concrete5.org/license/ MIT License
  11. *
  12. */
  13. /**
  14. *
  15. * The block type list object holds types of blocks, takes care of querying the file system for newly available
  16. * @author Andrew Embler <andrew@concrete5.org>
  17. * @copyright Copyright (c) 2003-2008 Concrete5. (http://www.concrete5.org)
  18. * @license http://www.concrete5.org/license/ MIT License
  19. * @package Blocks
  20. * @category Concrete
  21. */
  22. class Concrete5_Model_BlockTypeList extends Object {
  23. /**
  24. * array of BlockType objects - should likely be considered a protected property
  25. * use getBlockTypeList instead of accessing this property directly
  26. * @see BlockTypeList::getBlockTypeList()
  27. * @var BlockType[] $btArray
  28. */
  29. public $btArray = array();
  30. /**
  31. * Gets an array of BlockTypes for a given Package
  32. * @param Package $pkg
  33. * @return BlockType[]
  34. */
  35. public static function getByPackage($pkg) {
  36. $db = Loader::db();
  37. $r = $db->Execute("select btID from BlockTypes where pkgID = ?", $pkg->getPackageID());
  38. $blockTypes = array();
  39. while ($row = $r->FetchRow()) {
  40. $blockTypes[] = BlockType::getByID($row['btID']);
  41. }
  42. return $blockTypes;
  43. }
  44. /**
  45. * @todo comment this one
  46. * @param string $xml
  47. * @return void
  48. */
  49. public static function exportList($xml) {
  50. $attribs = self::getInstalledList();
  51. $nxml = $xml->addChild('blocktypes');
  52. foreach($attribs as $bt) {
  53. $type = $nxml->addChild('blocktype');
  54. $type->addAttribute('handle', $bt->getBlockTypeHandle());
  55. $type->addAttribute('package', $bt->getPackageHandle());
  56. }
  57. }
  58. /**
  59. * returns an array of Block Types used in the concrete5 Dashboard
  60. * @param Area $ap
  61. * @return BlockType[]
  62. */
  63. public static function getDashboardBlockTypes() {
  64. $db = Loader::db();
  65. $btIDs = $db->GetCol('select btID from BlockTypes where btHandle like "dashboard_%" order by btDisplayOrder asc, btID asc');
  66. $blockTypes = array();
  67. foreach($btIDs as $btID) {
  68. $blockTypes[] = BlockType::getByID($btID);
  69. }
  70. return $blockTypes;
  71. }
  72. /**
  73. * BlockTypeList class constructor
  74. * @param array $allowedBlocks array of allowed BlockType id's if you'd like to limit the list to just those
  75. * @return BlockTypeList
  76. */
  77. function __construct($allowedBlocks = null) {
  78. $db = Loader::db();
  79. $this->btArray = array();
  80. $q = "select btID from BlockTypes where btIsInternal = 0 ";
  81. if ($allowedBlocks != null) {
  82. $q .= ' and btID in (' . implode(',', $allowedBlocks) . ') ';
  83. }
  84. $q .= ' order by btDisplayOrder asc, btName asc, btID asc';
  85. $r = $db->query($q);
  86. if ($r) {
  87. while ($row = $r->fetchRow()) {
  88. $bt = BlockType::getByID($row['btID']);
  89. if (is_object($bt)) {
  90. $this->btArray[] = $bt;
  91. }
  92. }
  93. $r->free();
  94. }
  95. return $this;
  96. }
  97. /**
  98. * gets the array of BlockType objects
  99. * @return BlockType[]
  100. * @see BlockTypeList::getInstalledList()
  101. */
  102. public function getBlockTypeList() {
  103. return $this->btArray;
  104. }
  105. /**
  106. * Gets a list of block types that are not installed, used to get blocks that can be installed
  107. * This function only surveys the web/blocks directory - it's not looking at the package level.
  108. * @return BlockType[]
  109. */
  110. public static function getAvailableList() {
  111. $env = Environment::get();
  112. $env->clearOverrideCache();
  113. $blocktypes = array();
  114. $dir = DIR_FILES_BLOCK_TYPES;
  115. $db = Loader::db();
  116. $btHandles = $db->GetCol("select btHandle from BlockTypes order by btDisplayOrder asc, btName asc, btID asc");
  117. $aDir = array();
  118. if (is_dir($dir)) {
  119. $currentLocale = Localization::activeLocale();
  120. if ($currentLocale != 'en_US') {
  121. Localization::changeLocale('en_US');
  122. }
  123. $handle = opendir($dir);
  124. while(($file = readdir($handle)) !== false) {
  125. if (strpos($file, '.') === false) {
  126. $fdir = $dir . '/' . $file;
  127. if (is_dir($fdir) && !in_array($file, $btHandles) && file_exists($fdir . '/' . FILENAME_BLOCK_CONTROLLER)) {
  128. $bt = new BlockType;
  129. $bt->btHandle = $file;
  130. $class = $bt->getBlockTypeClass();
  131. require_once($fdir . '/' . FILENAME_BLOCK_CONTROLLER);
  132. if (!class_exists($class)) {
  133. continue;
  134. }
  135. $bta = new $class;
  136. $bt->btName = $bta->getBlockTypeName();
  137. $bt->btDescription = $bta->getBlockTypeDescription();
  138. $bt->hasCustomViewTemplate = file_exists(DIR_FILES_BLOCK_TYPES . '/' . $file . '/' . FILENAME_BLOCK_VIEW);
  139. $bt->hasCustomEditTemplate = file_exists(DIR_FILES_BLOCK_TYPES . '/' . $file . '/' . FILENAME_BLOCK_EDIT);
  140. $bt->hasCustomAddTemplate = file_exists(DIR_FILES_BLOCK_TYPES . '/' . $file . '/' . FILENAME_BLOCK_ADD);
  141. $bt->installed = false;
  142. $bt->btID = null;
  143. $blocktypes[] = $bt;
  144. }
  145. }
  146. }
  147. if ($currentLocale != 'en_US') {
  148. Localization::changeLocale($currentLocale);
  149. }
  150. }
  151. return $blocktypes;
  152. }
  153. /**
  154. * gets a list of installed BlockTypes
  155. * @return BlockType[]
  156. */
  157. public static function getInstalledList() {
  158. $db = Loader::db();
  159. $r = $db->query("select btID from BlockTypes order by btDisplayOrder asc, btName asc, btID asc");
  160. $btArray = array();
  161. while ($row = $r->fetchRow()) {
  162. $bt = BlockType::getByID($row['btID']);
  163. if (is_object($bt)) {
  164. $btArray[] = $bt;
  165. }
  166. }
  167. return $btArray;
  168. }
  169. /**
  170. * Gets a list of installed BlockTypes
  171. * - could be defined as static
  172. * @todo we have three duplicate functions getBlockTypeArray, getInstalledList, getBlockTypeList
  173. * @return BlockType[]
  174. */
  175. public function getBlockTypeArray() {
  176. $db = Loader::db();
  177. $q = "select btID from BlockTypes order by btDisplayOrder asc, btName asc, btID asc";
  178. $r = $db->query($q);
  179. $btArray = array();
  180. if ($r) {
  181. while ($row = $r->fetchRow()) {
  182. $bt = BlockType::getByID($row['btID']);
  183. if (is_object($bt)) {
  184. $btArray[] = $bt;
  185. }
  186. }
  187. $r->free();
  188. }
  189. return $btArray;
  190. }
  191. /**
  192. * gets the form post action for the current block type given the area
  193. * @param Area $a
  194. * @return string
  195. */
  196. public function getBlockTypeAddAction(&$a) {
  197. $step = ($_REQUEST['step']) ? '&step=' . $_REQUEST['step'] : '';
  198. $arHandle = urlencode($a->getAreaHandle());
  199. $c = $a->getAreaCollectionObject();
  200. $cID = $c->getCollectionID();
  201. $valt = Loader::helper('validation/token');
  202. $str = DIR_REL . "/" . DISPATCHER_FILENAME . "?cID={$cID}&amp;areaName={$arHandle}&amp;mode=edit&amp;btask=add" . $step . '&' . $valt->getParameter();
  203. return $str;
  204. }
  205. /**
  206. * gets the form post action for the current block type given the area
  207. * @param Area $a
  208. * @return string
  209. */
  210. public function getBlockTypeAliasAction(&$a) {
  211. $step = ($_REQUEST['step']) ? '&step=' . $_REQUEST['step'] : '';
  212. $arHandle = urlencode($a->getAreaHandle());
  213. $c = $a->getAreaCollectionObject();
  214. $cID = $c->getCollectionID();
  215. $str = DIR_REL . "/" . DISPATCHER_FILENAME . "?cID={$cID}&amp;areaName={$arHandle}&amp;mode=edit&amp;btask=alias" . $step . '&' . $valt->getParameter();
  216. return $str;
  217. }
  218. public static function resetBlockTypeDisplayOrder($column = 'btID') {
  219. $db = Loader::db();
  220. $stmt = $db->Prepare("UPDATE BlockTypes SET btDisplayOrder = ? WHERE btID = ?");
  221. $btDisplayOrder = 1;
  222. $blockTypes = $db->GetArray("SELECT btID, btHandle, btIsInternal FROM BlockTypes ORDER BY {$column} ASC");
  223. foreach ($blockTypes as $bt) {
  224. if ($bt['btIsInternal']) {
  225. $db->Execute($stmt, array(0, $bt['btID']));
  226. } else {
  227. $db->Execute($stmt, array($btDisplayOrder, $bt['btID']));
  228. $btDisplayOrder++;
  229. }
  230. }
  231. }
  232. }
  233. /**
  234. *
  235. * @access private
  236. */
  237. class Concrete5_Model_BlockTypeDB extends ADOdb_Active_Record {
  238. public $_table = 'BlockTypes';
  239. }
  240. /**
  241. *
  242. * Any type of content that can be added to pages is represented as a type of block, and thereby a block type object.
  243. * @package Blocks
  244. * @author Andrew Embler <andrew@concrete5.org>
  245. * @license http://www.concrete5.org/license/ MIT License
  246. * @package Blocks
  247. * @category Concrete
  248. */
  249. class Concrete5_Model_BlockType extends Object {
  250. /**
  251. * @var array $addBTUArray
  252. */
  253. public $addBTUArray = array();
  254. /**
  255. * @var array $addBTGArray
  256. */
  257. public $addBTGArray = array();
  258. /**
  259. * @var BlockTypeController
  260. */
  261. public $controller;
  262. /**
  263. * Gets the BlockType object for the given Block Type Handle
  264. * ex:
  265. * <code><?php
  266. * $bt = BlockType::getByHandle('content'); // returns the BlockType object for the core Content block
  267. * ?></code>
  268. * @param string $handle
  269. * @return BlockType|false
  270. */
  271. public static function getByHandle($handle) {
  272. $bt = CacheLocal::getEntry('blocktype', $handle);
  273. if ($bt === -1) {
  274. return false;
  275. }
  276. if (is_object($bt)) {
  277. $bt->controller = Loader::controller($bt);
  278. return $bt;
  279. }
  280. $bt = BlockType::get('btHandle = ?', array($handle));
  281. if (is_object($bt)) {
  282. CacheLocal::set('blocktype', $handle, $bt);
  283. $bt->controller = Loader::controller($bt);
  284. return $bt;
  285. }
  286. CacheLocal::set('blocktype', $handle, -1);
  287. return false;
  288. }
  289. /**
  290. * Gets the BlockType for a given Block Type ID
  291. * @param int $btID
  292. * @return BlockType
  293. */
  294. public static function getByID($btID) {
  295. $bt = CacheLocal::getEntry('blocktype', $btID);
  296. if ($bt === -1) {
  297. return false;
  298. } else if (!is_object($bt)) {
  299. $where = 'btID = ?';
  300. $bt = BlockType::get($where, array($btID));
  301. if (is_object($bt)) {
  302. CacheLocal::set('blocktype', $btID, $bt);
  303. } else {
  304. CacheLocal::set('blocktype', $btID, -1);
  305. }
  306. }
  307. $bt->controller = Loader::controller($bt);
  308. return $bt;
  309. }
  310. /**
  311. * internal method to query the BlockTypes table and get a BlockType object
  312. * @param string
  313. * @param array
  314. */
  315. protected static function get($where, $properties) {
  316. $db = Loader::db();
  317. $q = "select btID, btName, btDescription, btHandle, pkgID, btActiveWhenAdded, btIsInternal, btCopyWhenPropagate, btIncludeAll, btDisplayOrder, btInterfaceWidth, btInterfaceHeight from BlockTypes where {$where}";
  318. $r = $db->query($q, $properties);
  319. if ($r->numRows() > 0) {
  320. $row = $r->fetchRow();
  321. $bt = new BlockType;
  322. $bt->setPropertiesFromArray($row);
  323. return $bt;
  324. }
  325. }
  326. /**
  327. * if a the current BlockType is Internal or not - meaning one of the core built-in concrete5 blocks
  328. * @access private
  329. * @return boolean
  330. */
  331. function isBlockTypeInternal() {return $this->btIsInternal;}
  332. /**
  333. * Returns true if the block type is internal (and therefore cannot be removed) a core block
  334. * @return boolean
  335. */
  336. public function isInternalBlockType() {
  337. return $this->btIsInternal;
  338. }
  339. /**
  340. * Returns true if the block type ships with concrete5 by checking to see if it's in the concrete/blocks/ directory
  341. * @deprecated
  342. */
  343. public function isCoreBlockType() {
  344. return is_dir(DIR_FILES_BLOCK_TYPES_CORE . '/' . $this->getBlockTypeHandle());
  345. }
  346. /**
  347. * Determines if the block type has templates available
  348. * @return boolean
  349. */
  350. public function hasAddTemplate() {
  351. $bv = new BlockView();
  352. $bv->setBlockObject($this);
  353. $path = $bv->getBlockPath(FILENAME_BLOCK_ADD);
  354. if (file_exists($path . '/' . FILENAME_BLOCK_ADD)) {
  355. return true;
  356. }
  357. return false;
  358. }
  359. /**
  360. * returns the width in pixels that the block type's editing dialog will open in
  361. * @return int
  362. */
  363. public function getBlockTypeInterfaceWidth() {return $this->btInterfaceWidth;}
  364. /**
  365. * returns the height in pixels that the block type's editing dialog will open in
  366. * @return int
  367. */
  368. public function getBlockTypeInterfaceHeight() {return $this->btInterfaceHeight;}
  369. /**
  370. * returns the id of the BlockType's package if it's in a package
  371. * @return int
  372. */
  373. public function getPackageID() {return $this->pkgID;}
  374. /**
  375. * returns the handle of the BlockType's package if it's in a package
  376. * @return string
  377. */
  378. public function getPackageHandle() {
  379. return PackageList::getHandle($this->pkgID);
  380. }
  381. /**
  382. * determines if a user or group can add a block of the current BlockType
  383. * @param UserInfo|Group $obj
  384. * @return boolean
  385. */
  386. public function canAddBlock($obj) {
  387. switch(strtolower(get_class($obj))) {
  388. case 'group':
  389. return in_array($obj->getGroupID(), $this->addBTGArray);
  390. break;
  391. case 'userinfo':
  392. return in_array($obj->getUserID(), $this->addBTUArray);
  393. break;
  394. }
  395. }
  396. /**
  397. * Returns the number of unique instances of this block throughout the entire site
  398. * note - this count could include blocks in areas that are no longer rendered by the theme
  399. * @param boolean specify true if you only want to see the number of blocks in active pages
  400. * @return int
  401. */
  402. public function getCount($ignoreUnapprovedVersions = false) {
  403. $db = Loader::db();
  404. if ($ignoreUnapprovedVersions) {
  405. $count = $db->GetOne("SELECT count(btID) FROM Blocks b
  406. WHERE btID=?
  407. AND EXISTS (
  408. SELECT 1 FROM CollectionVersionBlocks cvb
  409. INNER JOIN CollectionVersions cv ON cv.cID=cvb.cID AND cv.cvID=cvb.cvID
  410. WHERE b.bID=cvb.bID AND cv.cvIsApproved=1
  411. )", array($this->btID));
  412. }
  413. else {
  414. $count = $db->GetOne("SELECT count(btID) FROM Blocks WHERE btID = ?", array($this->btID));
  415. }
  416. return $count;
  417. }
  418. /**
  419. * Not a permissions call. Actually checks to see whether this block is not an internal one.
  420. * @return boolean
  421. */
  422. public function canUnInstall() {
  423. /*$cnt = $this->getCount();
  424. if ($cnt > 0 || $this->isBlockTypeInternal()) {
  425. return false;
  426. }*/
  427. return (!$this->isBlockTypeInternal());
  428. }
  429. /**
  430. * gets the BlockTypes description text
  431. * @return string
  432. */
  433. function getBlockTypeDescription() {
  434. return $this->btDescription;
  435. }
  436. /**
  437. * Gets the custom templates available for the current BlockType
  438. * @return array an array of strings
  439. */
  440. function getBlockTypeCustomTemplates() {
  441. $btHandle = $this->getBlockTypeHandle();
  442. $pkgHandle = $this->getPackageHandle();
  443. $templates = array();
  444. $fh = Loader::helper('file');
  445. if (file_exists(DIR_FILES_BLOCK_TYPES . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES)) {
  446. $templates = array_merge($templates, $fh->getDirectoryContents(DIR_FILES_BLOCK_TYPES . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES));
  447. }
  448. /*
  449. if ($pkgHandle != null) {
  450. if (is_dir(DIR_PACKAGES . '/' . $pkgHandle)) {
  451. $templates = array_merge($templates, $fh->getDirectoryContents(DIR_PACKAGES . "/{$pkgHandle}/" . DIRNAME_BLOCKS . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES));
  452. } else {
  453. $templates = array_merge($templates, $fh->getDirectoryContents(DIR_PACKAGES_CORE . "/{$pkgHandle}/" . DIRNAME_BLOCKS . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES));
  454. }
  455. }
  456. */
  457. // NOW, we check to see if this btHandle has any custom templates that have been installed as separate packages
  458. $pl = PackageList::get();
  459. $packages = $pl->getPackages();
  460. foreach($packages as $pkg) {
  461. $d = (is_dir(DIR_PACKAGES . '/' . $pkg->getPackageHandle())) ? DIR_PACKAGES . '/'. $pkg->getPackageHandle() : DIR_PACKAGES_CORE . '/'. $pkg->getPackageHandle();
  462. if (is_dir($d . '/' . DIRNAME_BLOCKS . '/' . $btHandle . '/' . DIRNAME_BLOCK_TEMPLATES)) {
  463. $templates = array_merge($templates, $fh->getDirectoryContents($d . '/' . DIRNAME_BLOCKS . '/' . $btHandle . '/' . DIRNAME_BLOCK_TEMPLATES));
  464. }
  465. }
  466. if (file_exists(DIR_FILES_BLOCK_TYPES_CORE . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES)) {
  467. $templates = array_merge($templates, $fh->getDirectoryContents(DIR_FILES_BLOCK_TYPES_CORE . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES));
  468. }
  469. $templates = array_unique($templates);
  470. return $templates;
  471. }
  472. /**
  473. * gets the available composer templates
  474. * used for editing instances of the BlockType while in the composer ui in the dashboard
  475. * @return array array of strings
  476. */
  477. function getBlockTypeComposerTemplates() {
  478. $btHandle = $this->getBlockTypeHandle();
  479. $pkgHandle = $this->getPackageHandle();
  480. $templates = array();
  481. $fh = Loader::helper('file');
  482. if (file_exists(DIR_FILES_BLOCK_TYPES . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES_COMPOSER)) {
  483. $templates = array_merge($templates, $fh->getDirectoryContents(DIR_FILES_BLOCK_TYPES . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES_COMPOSER));
  484. }
  485. if (file_exists(DIR_FILES_BLOCK_TYPES_CORE . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES_COMPOSER)) {
  486. $templates = array_merge($templates, $fh->getDirectoryContents(DIR_FILES_BLOCK_TYPES_CORE . "/{$btHandle}/" . DIRNAME_BLOCK_TEMPLATES_COMPOSER));
  487. }
  488. $templates = array_unique($templates);
  489. return $templates;
  490. }
  491. function setBlockTypeDisplayOrder($displayOrder) {
  492. $db = Loader::db();
  493. $displayOrder = intval($displayOrder); //in case displayOrder came from a string (so ADODB escapes it properly)
  494. $sql = "UPDATE BlockTypes SET btDisplayOrder = btDisplayOrder - 1 WHERE btDisplayOrder > ?";
  495. $vals = array($this->btDisplayOrder);
  496. $db->Execute($sql, $vals);
  497. $sql = "UPDATE BlockTypes SET btDisplayOrder = btDisplayOrder + 1 WHERE btDisplayOrder >= ?";
  498. $vals = array($displayOrder);
  499. $db->Execute($sql, $vals);
  500. $sql = "UPDATE BlockTypes SET btDisplayOrder = ? WHERE btID = ?";
  501. $vals = array($displayOrder, $this->btID);
  502. $db->Execute($sql, $vals);
  503. // now we remove the block type from cache
  504. $ca = new Cache();
  505. $ca->delete('blockTypeByID', $this->btID);
  506. $ca->delete('blockTypeByHandle', $this->btHandle);
  507. $ca->delete('blockTypeList', false);
  508. }
  509. /**
  510. * installs a new BlockType from a package,
  511. * typicaly called from a package controller's install() method durring package installation
  512. * @todo Documentation how is the btID used, if you want to reserve/specify a btID??
  513. * @param string $btHandle the block Type's handle
  514. * @param Package $pkg
  515. * @param int $btID if it's an existing block type
  516. * @return void|string error message
  517. */
  518. public function installBlockTypeFromPackage($btHandle, $pkg, $btID = 0) {
  519. $dir1 = DIR_PACKAGES . '/' . $pkg->getPackageHandle() . '/' . DIRNAME_BLOCKS;
  520. $dir2 = DIR_PACKAGES_CORE . '/' . $pkg->getPackageHandle() . '/' . DIRNAME_BLOCKS;
  521. if (file_exists($dir1)) {
  522. $dir = $dir1;
  523. $dirDbXml = $dir;
  524. } else {
  525. $dir = $dir2;
  526. $dirDbXml = $dir;
  527. }
  528. // now we check to see if it's been overridden in the site root and if so we do it there
  529. if ($btID > 0) {
  530. // this is only necessary when it's an existing refresh
  531. if (file_exists(DIR_FILES_BLOCK_TYPES . '/' . $btHandle . '/' . FILENAME_BLOCK_CONTROLLER)) {
  532. $dir = DIR_FILES_BLOCK_TYPES;
  533. }
  534. if (file_exists(DIR_FILES_BLOCK_TYPES . '/' . $btHandle . '/' . FILENAME_BLOCK_DB)) {
  535. $dirDbXml = DIR_FILES_BLOCK_TYPES;
  536. }
  537. }
  538. $bt = new BlockType;
  539. $bt->btHandle = $btHandle;
  540. $bt->pkgHandle = $pkg->getPackageHandle();
  541. $bt->pkgID = $pkg->getPackageID();
  542. return BlockType::doInstallBlockType($btHandle, $bt, $dir, $btID, $dirDbXml);
  543. }
  544. /**
  545. * refreshes the BlockType's database schema throws an Exception if error
  546. * @return void
  547. */
  548. public function refresh() {
  549. if ($this->getPackageID() > 0) {
  550. $pkg = Package::getByID($this->getPackageID());
  551. $resp = BlockType::installBlockTypeFromPackage($this->getBlockTypeHandle(), $pkg, $this->getBlockTypeID());
  552. if ($resp != '') {
  553. throw new Exception($resp);
  554. }
  555. } else {
  556. $resp = BlockType::installBlockType($this->getBlockTypeHandle(), $this->getBlockTypeID());
  557. if ($resp != '') {
  558. throw new Exception($resp);
  559. }
  560. }
  561. }
  562. /**
  563. * installs a core or root level BlockType (from /blocks or /concrete/blocks, not a package)
  564. * should likely be a static method
  565. * @param string $btHandle
  566. * @param int $btID btID if it's an existing block type
  567. */
  568. public function installBlockType($btHandle, $btID = 0) {
  569. if ($btID == 0) {
  570. // then we don't allow one to already exist
  571. $db = Loader::db();
  572. $cnt = $db->GetOne("select btID from BlockTypes where btHandle = ?", array($btHandle));
  573. if ($cnt > 0) {
  574. return false;
  575. }
  576. }
  577. if (file_exists(DIR_FILES_BLOCK_TYPES . '/' . $btHandle . '/' . FILENAME_BLOCK_CONTROLLER)) {
  578. $dir = DIR_FILES_BLOCK_TYPES;
  579. } else {
  580. $dir = DIR_FILES_BLOCK_TYPES_CORE;
  581. }
  582. if (file_exists(DIR_FILES_BLOCK_TYPES . '/' . $btHandle . '/' . FILENAME_BLOCK_DB)) {
  583. $dirDbXml = DIR_FILES_BLOCK_TYPES;
  584. } else {
  585. $dirDbXml = DIR_FILES_BLOCK_TYPES_CORE;
  586. }
  587. $bt = new BlockType;
  588. $bt->btHandle = $btHandle;
  589. $bt->pkgHandle = null;
  590. $bt->pkgID = 0;
  591. return BlockType::doInstallBlockType($btHandle, $bt, $dir, $btID, $dirDbXml);
  592. }
  593. /**
  594. * Renders a particular view of a block type, using the public $controller variable as the block type's controller
  595. * @param string template 'view' for the default
  596. * @return void
  597. */
  598. public function render($view = 'view') {
  599. $bv = new BlockView();
  600. $bv->setController($this->controller);
  601. $bv->render($this, $view);
  602. }
  603. /**
  604. * get's the block type controller
  605. * @return BlockTypeController
  606. */
  607. public function getController() {
  608. return $this->controller;
  609. }
  610. /**
  611. * installs a block type
  612. * @param string $btHandle
  613. * @param BlockType $bt
  614. * @param string $dir
  615. * @param int $btID
  616. * @param string $dirDbXml
  617. */
  618. protected function doInstallBlockType($btHandle, $bt, $dir, $btID = 0, $dirDbXml) {
  619. $db = Loader::db();
  620. $env = Environment::get();
  621. $env->clearOverrideCache();
  622. if (file_exists($dir . '/' . $btHandle . '/' . FILENAME_BLOCK_CONTROLLER)) {
  623. $class = $bt->getBlockTypeClass();
  624. $path = $dirDbXml . '/' . $btHandle;
  625. if (!class_exists($class)) {
  626. require_once($dir . '/' . $btHandle . '/' . FILENAME_BLOCK_CONTROLLER);
  627. }
  628. if (!class_exists($class)) {
  629. throw new Exception(t("%s not found. Please check that the block controller file contains the correct class name.", $class));
  630. }
  631. $bta = new $class;
  632. //Attempt to run the subclass methods (install schema from db.xml, etc.)
  633. $r = $bta->install($path);
  634. //Validate
  635. if ($r === false) {
  636. return t('Error: Block Type cannot be installed because no db.xml file can be found. Either create a db.xml file for this block type, or remove the $btTable variable from its controller.');
  637. } else if (!$r->result && $r->message) {
  638. return $r->message;
  639. } else if (!$r->result && !$r->message) {
  640. return t('Error: Block Type cannot be installed due to an unknown database error. Please check that the db.xml file for this block type is formatted correctly.');
  641. } else if ($message = BlockType::validateInstalledDatabaseTable($bta->getBlockTypeDatabaseTable())) {
  642. $db->Execute('DROP TABLE IF EXISTS ' . $bta->getBlockTypeDatabaseTable());
  643. return $message;
  644. }
  645. $currentLocale = Localization::activeLocale();
  646. if ($currentLocale != 'en_US') {
  647. // Prevent the database records being stored in wrong language
  648. Localization::changeLocale('en_US');
  649. }
  650. //Install the block
  651. $btd = new BlockTypeDB();
  652. $btd->btHandle = $btHandle;
  653. $btd->btName = $bta->getBlockTypeName();
  654. $btd->btDescription = $bta->getBlockTypeDescription();
  655. $btd->btActiveWhenAdded = $bta->isActiveWhenAdded();
  656. $btd->btCopyWhenPropagate = $bta->isCopiedWhenPropagated();
  657. $btd->btIncludeAll = $bta->includeAll();
  658. $btd->btIsInternal = $bta->isBlockTypeInternal();
  659. $btd->btInterfaceHeight = $bta->getInterfaceHeight();
  660. $btd->btInterfaceWidth = $bta->getInterfaceWidth();
  661. $btd->pkgID = $bt->getPackageID();
  662. if ($currentLocale != 'en_US') {
  663. Localization::changeLocale($currentLocale);
  664. }
  665. if ($btID > 0) {
  666. $btd->btID = $btID;
  667. $btDisplayOrder = $db->GetOne('select btDisplayOrder from BlockTypes where btID = ?', array($btID));
  668. if (!$btDisplayOrder) {
  669. $btDisplayOrder = 0;
  670. }
  671. $btd->btDisplayOrder = $btDisplayOrder;
  672. $r = $btd->Replace();
  673. } else {
  674. if ($bta->isBlockTypeInternal()) {
  675. $btd->btDisplayOrder = 0;
  676. } else {
  677. $btMax = $db->GetOne('select max(btDisplayOrder) from BlockTypes');
  678. if ($btMax < 1 && $btMax !== '0') {
  679. $btd->btDisplayOrder = 0;
  680. } else {
  681. $btd->btDisplayOrder = $btMax + 1;
  682. }
  683. }
  684. $r = $btd->save();
  685. }
  686. // now we remove the block type from cache
  687. $ca = new Cache();
  688. $ca->delete('blockTypeByID', $btID);
  689. $ca->delete('blockTypeByHandle', $btHandle);
  690. $ca->delete('blockTypeList', false);
  691. if (!$r) {
  692. return $db->ErrorMsg();
  693. }
  694. } else {
  695. return t("No block found with the handle %s.", $btHandle);
  696. }
  697. }
  698. /**
  699. * Internal helper function for doInstallBlockType().
  700. *
  701. * Checks if the database table with the given name
  702. * has at least 2 fields and one of those is called "btID".
  703. *
  704. * If valid, we return an empty string.
  705. * If not valid, we return an error message.
  706. * If passed an empty string, we return an empty string.
  707. */
  708. private function validateInstalledDatabaseTable($btTable) {
  709. $message = '';
  710. if (!empty($btTable)) {
  711. $fields = Loader::db()->MetaColumnNames($btTable);
  712. if (count($fields) < 2) {
  713. $message = t('Error: Block Type table must contain at least two fields.');
  714. } else if (!in_array('bID', $fields)) {
  715. $message = t('Error: Block Type table must contain a "bID" primary key field.');
  716. }
  717. }
  718. return $message;
  719. }
  720. /*
  721. * Returns a path to where the block type's files are located.
  722. * @access public
  723. * @return string $path
  724. */
  725. public function getBlockTypePath() {
  726. if ($this->getPackageID() > 0) {
  727. $pkgHandle = $this->getPackageHandle();
  728. $dirp = (is_dir(DIR_PACKAGES . '/' . $pkgHandle)) ? DIR_PACKAGES : DIR_PACKAGES_CORE;
  729. $dir = $dirp . '/' . $pkgHandle . '/' . DIRNAME_BLOCKS . '/' . $this->getBlockTypeHandle();
  730. } else {
  731. if (is_dir(DIR_FILES_BLOCK_TYPES . '/' . $this->getBlockTypeHandle())) {
  732. $dir = DIR_FILES_BLOCK_TYPES . '/' . $this->getBlockTypeHandle();
  733. } else {
  734. $dir = DIR_FILES_BLOCK_TYPES_CORE . '/' . $this->getBlockTypeHandle();
  735. }
  736. }
  737. return $dir;
  738. }
  739. /** @todo Continue documenting from here down **/
  740. /*
  741. * @access private
  742. *
  743. */
  744. protected function _getClass() {
  745. $btHandle = $this->btHandle;
  746. $pkgHandle = $this->getPackageHandle();
  747. $env = Environment::get();
  748. require_once($env->getPath(DIRNAME_BLOCKS . '/' . $this->btHandle . '/' . FILENAME_BLOCK_CONTROLLER, $pkgHandle));
  749. // takes the handle and performs some magic to get the class;
  750. $btHandle = $this->getBlockTypeHandle();
  751. // split by underscores or dashes
  752. $words = preg_split('/\_|\-/', $btHandle);
  753. for ($i = 0; $i < count($words); $i++) {
  754. $words[$i] = ucfirst($words[$i]);
  755. }
  756. $class = implode('', $words);
  757. $class = $class . 'BlockController';
  758. return $class;
  759. }
  760. public function inc($file, $args = array()) {
  761. extract($args);
  762. $bt = $this;
  763. global $c;
  764. global $a;
  765. $env = Environment::get();
  766. include($env->getPath(DIRNAME_BLOCKS . '/' . $this->getBlockTypeHandle() . '/' . $file, $this->getPackageHandle()));
  767. }
  768. public function getBlockTypeClass() {
  769. return $this->_getClass();
  770. }
  771. /**
  772. * Deprecated -- use getBlockTypeClass() instead.
  773. */
  774. public function getBlockTypeClassFromHandle() {
  775. return $this->getBlockTypeClass();
  776. }
  777. /**
  778. * Removes the block type. Also removes instances of content.
  779. */
  780. public function delete() {
  781. $db = Loader::db();
  782. $r = $db->Execute('select cID, cvID, b.bID, arHandle from CollectionVersionBlocks cvb inner join Blocks b on b.bID = cvb.bID where btID = ?', array($this->getBlockTypeID()));
  783. while ($row = $r->FetchRow()) {
  784. $nc = Page::getByID($row['cID'], $row['cvID']);
  785. $b = Block::getByID($row['bID'], $nc, $row['arHandle']);
  786. if (is_object($b)) {
  787. $b->deleteBlock();
  788. }
  789. }
  790. $ca = new Cache();
  791. $ca->delete('blockTypeByID', $this->btID);
  792. $ca->delete('blockTypeByHandle', $this->btHandle);
  793. $ca->delete('blockTypeList', false);
  794. $db->Execute("delete from BlockTypes where btID = ?", array($this->btID));
  795. //Remove gaps in display order numbering (to avoid future sorting errors)
  796. BlockTypeList::resetBlockTypeDisplayOrder('btDisplayOrder');
  797. }
  798. /**
  799. * Allows block types to be updated
  800. * @param array $data
  801. */
  802. public function update($data) {
  803. $db = Loader::db();
  804. $btHandle = $this->btHandle;
  805. $btName = $this->btName;
  806. $btDescription = $this->btDescription;
  807. if (isset($data['btHandle'])) {
  808. $btHandle = $data['btHandle'];
  809. }
  810. if (isset($data['btName'])) {
  811. $btName = $data['btName'];
  812. }
  813. if (isset($data['btDescription'])) {
  814. $btDescription = $data['btDescription'];
  815. }
  816. $db->Execute('update BlockTypes set btHandle = ?, btName = ?, btDescription = ? where btID = ?', array($btHandle, $btName, $btDescription, $this->btID));
  817. // now we remove the block type from cache
  818. $ca = new Cache();
  819. $ca->delete('blockTypeByID', $this->btID);
  820. $ca->delete('blockTypeByHandle', $btHandle);
  821. $ca->delete('blockTypeList', false);
  822. }
  823. /*
  824. * Adds a block to the system without adding it to a collection.
  825. * Passes page and area data along if it is available, however.
  826. */
  827. public function add($data, $c = false, $a = false) {
  828. $db = Loader::db();
  829. $u = new User();
  830. if (isset($data['uID'])) {
  831. $uID = $data['uID'];
  832. } else {
  833. $uID = $u->getUserID();
  834. }
  835. $btID = $this->btID;
  836. $dh = Loader::helper('date');
  837. $bDate = $dh->getSystemDateTime();
  838. $bIsActive = ($this->btActiveWhenAdded == 1) ? 1 : 0;
  839. $v = array($_POST['bName'], $bDate, $bDate, $bIsActive, $btID, $uID);
  840. $q = "insert into Blocks (bName, bDateAdded, bDateModified, bIsActive, btID, uID) values (?, ?, ?, ?, ?, ?)";
  841. $r = $db->prepare($q);
  842. $res = $db->execute($r, $v);
  843. $bIDnew = $db->Insert_ID();
  844. // we get the block object for the block we just added
  845. if ($res) {
  846. $nb = Block::getByID($bIDnew);
  847. $btHandle = $this->getBlockTypeHandle();
  848. $class = $this->getBlockTypeClass();
  849. $bc = new $class($nb);
  850. if (is_object($c)) {
  851. $bc->setCollectionObject($c);
  852. }
  853. $bc->save($data);
  854. // the previous version of the block above is cached without the values
  855. $nb->refreshCache();
  856. return Block::getByID($bIDnew);
  857. }
  858. }
  859. function getBlockTypeID() {
  860. return $this->btID;
  861. }
  862. function getBlockTypeHandle() {
  863. return $this->btHandle;
  864. }
  865. // getBlockAddAction vs. getBlockTypeAddAction() - The difference is very simple. We call getBlockTypeAddAction() to grab the
  866. // action properties for the form that presents the drop-down select menu for selecting which type of block to add. We call the other
  867. // function when we've already chosen a type to add, and we're interested in actually adding the block - content completed - to the database
  868. function getBlockAddAction(&$a, $alternateHandler = null) {
  869. // Note: This is fugly, since we're just grabbing query string variables, but oh well. Not _everything_ can be object oriented
  870. $btID = $this->btID;
  871. $step = ($_REQUEST['step']) ? '&step=' . $_REQUEST['step'] : '';
  872. $c = $a->getAreaCollectionObject();
  873. $cID = $c->getCollectionID();
  874. $arHandle = urlencode($a->getAreaHandle());
  875. $valt = Loader::helper('validation/token');
  876. if ($alternateHandler) {
  877. $str = $alternateHandler . "?cID={$cID}&arHandle={$arHandle}&btID={$btID}&mode=edit" . $step . '&' . $valt->getParameter();
  878. } else {
  879. $str = DIR_REL . "/" . DISPATCHER_FILENAME . "?cID={$cID}&arHandle={$arHandle}&btID={$btID}&mode=edit" . $step . '&' . $valt->getParameter();
  880. }
  881. return $str;
  882. }
  883. function getBlockTypeName() {
  884. return $this->btName;
  885. }
  886. function isInstalled() {
  887. return $this->installed;
  888. }
  889. function getBlockTypeActiveWhenAdded() {
  890. return $this->btActiveWhenAdded;
  891. }
  892. function isCopiedWhenPropagated() {
  893. return $this->btCopyWhenPropagate;
  894. }
  895. function includeAll() {
  896. return $this->btIncludeAll;
  897. }
  898. function hasCustomEditTemplate() {
  899. return $this->hasCustomEditTemplate;
  900. }
  901. function hasCustomViewTemplate() {
  902. return $this->hasCustomViewTemplate;
  903. }
  904. function hasCustomAddTemplate() {
  905. return $this->hasCustomAddTemplate;
  906. }
  907. }