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

/plugins/jojo_core/classes/Jojo/Table.php

http://jojocms.googlecode.com/
PHP | 582 lines | 425 code | 69 blank | 88 comment | 110 complexity | f66ec02cfeca9300bc39242050e940ba MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, LGPL-2.0, CC-BY-SA-3.0, MIT
  1. <?php
  2. /**
  3. * Jojo CMS
  4. * ================
  5. *
  6. * Copyright 2007-2008 Harvey Kane <code@ragepank.com>
  7. * Copyright 2007-2008 Michael Holt <code@gardyneholt.co.nz>
  8. * Copyright 2007 Melanie Schulz <mel@gardyneholt.co.nz>
  9. *
  10. * See the enclosed file license.txt for license information (LGPL). If you
  11. * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
  12. *
  13. * @author Harvey Kane <code@ragepank.com>
  14. * @author Michael Cochrane <mikec@jojocms.org>
  15. * @author Melanie Schulz <mel@gardyneholt.co.nz>
  16. * @license http://www.fsf.org/copyleft/lgpl.html GNU Lesser General Public License
  17. * @link http://www.jojocms.org JojoCMS
  18. * @package jojo_core
  19. */
  20. class Jojo_Table {
  21. /* The name of the database table */
  22. private $table;
  23. /* The id of the current record */
  24. private $currentrecord;
  25. /* The options for the table data for this table */
  26. private $tabledata;
  27. /* Object of type Jojo_Field representing each field in this table */
  28. private $fields;
  29. /* cached value of the first permissions field in table */
  30. private $permsfield;
  31. /* Constructor */
  32. function __construct($table, $id = -1, $index = '')
  33. {
  34. $this->table = $table;
  35. /* Get info about this table */
  36. $rows = Jojo::selectQuery("SELECT * FROM {tabledata} WHERE td_name = ?", array($table));
  37. if (isset($rows[0])) {
  38. foreach($rows[0] as $k => $v) {
  39. $k = str_replace('td_', '', $k);
  40. $this->setOption($k, $v);
  41. }
  42. }
  43. /* Ensure options have values */
  44. if (!$this->getOption('displayfield')) {
  45. $this->setOption('displayfield', $this->getOption('primarykey'));
  46. }
  47. if (!$this->getOption('displayname')) {
  48. $this->setOption('displayname', $this->getOption('name'));
  49. }
  50. /* Get details about the fields in this table */
  51. $fieldlist = Jojo::selectQuery("SELECT * FROM {fielddata} WHERE fd_table = ? ORDER BY fd_order, fielddataid", array($table));
  52. $this->fields = array();
  53. foreach ($fieldlist as $field) {
  54. $this->fields[$field['fd_field']] = Jojo_Field::factory($field['fd_type'], $field, $this);
  55. if (!isset($this->permsfield) && ($field['fd_type'] == 'permissions')) {
  56. $this->permsfield = $field['fd_field'];
  57. }
  58. }
  59. if ($id > -1) {
  60. $this->getRecord($id);
  61. }
  62. }
  63. /**
  64. * Retrieve specific record in the database
  65. */
  66. function getRecord($id)
  67. {
  68. /* Create the query */
  69. $this->currentrecord = $id;
  70. if ($this->getOption('displayfield')) {
  71. $query = sprintf("SELECT *, %s AS DISPLAYFIELDVALUE FROM {%s} WHERE `%s` = ? LIMIT 1", $this->getOption('displayfield'), $this->table, $this->getOption('primarykey'));
  72. } else {
  73. $query = sprintf("SELECT * FROM {%s} WHERE `%s` = ? LIMIT 1", $this->table, $this->getOption('primarykey'));
  74. }
  75. /* Fetch the record from the database */
  76. $fieldvalues = Jojo::selectRow($query, array($id));
  77. /* Set all the fields to their values */
  78. if ($fieldvalues) {
  79. foreach ($fieldvalues as $k => $v) {
  80. if ($k == 'DISPLAYFIELDVALUE') {
  81. $this->setOption('displayvalue', $v);
  82. } else {
  83. $this->fields[$k]->setValueFromDB($v);
  84. }
  85. }
  86. }
  87. }
  88. /**
  89. * Return the ID of the current record
  90. */
  91. function getRecordID()
  92. {
  93. return $this->currentrecord;
  94. }
  95. /* Return the name of this table */
  96. function getTableName()
  97. {
  98. return $this->table;
  99. }
  100. /* Return the name of the first permissions field */
  101. function getPermsField()
  102. {
  103. if (isset($this->permsfield)) return $this->permsfield;
  104. foreach ($this->fields as $fname => $field) {
  105. //if (strtolower(get_class($field)) == 'jojo_field_permissions') {
  106. if ($field->fd_type == 'permissions') {
  107. $this->permsfield = $fname;
  108. return $fname;
  109. }
  110. }
  111. return false;
  112. }
  113. /* Return the Default Permissions for this table */
  114. function getDefaultPermissions()
  115. {
  116. static $cache;
  117. if (!is_array($cache)) {
  118. preg_match_all("/([a-zA-Z]+)\.([a-zA-Z]+)[\s=]+([01])+/", $this->getOption('defaultpermissions'), $parts);
  119. $cache = array();
  120. foreach ($parts[0] as $k => $v) {
  121. $cache[$parts[1][$k]][$parts[2][$k]] = ($parts[3][$k] == 1);
  122. }
  123. }
  124. return $cache;
  125. }
  126. /* Return HTML view of the current record in this table */
  127. function getHTML($mode = 'view')
  128. {
  129. $html = '';
  130. $fieldHTML = array();
  131. foreach($this->fields as $fieldname => $field) {
  132. $fieldHTML[$fieldname] = array(
  133. 'name' => $field->getDisplayName(),
  134. 'tabname' => $field->getOption('tabname'),
  135. 'showlabel' => $field->getOption('showlabel'),
  136. 'html' => $field->getHTML($mode),
  137. 'js' => $field->getJS($mode),
  138. 'value' => $field->getValue(),
  139. 'error' => $field->getError(),
  140. 'type' => $field->getOption('type'),
  141. 'required' => $field->getOption('required'),
  142. 'flags' => $field->getOption('flags'),
  143. 'privacy' => $field->getPrivacy(),
  144. );
  145. }
  146. return $fieldHTML;
  147. }
  148. /* Return HTML view of the current record in this table, for a single field only */
  149. function getFieldHTML($fieldname, $mode = 'view')
  150. {
  151. $field = $this->fields[$fieldname];
  152. $fieldHTML = array(
  153. 'name' => $field->getDisplayName(),
  154. 'tabname' => $field->getOption('tabname'),
  155. 'showlabel' => $field->getOption('showlabel'),
  156. 'html' => $field->getHTML($mode),
  157. 'error' => $field->getError(),
  158. 'type' => $field->getOption('type'),
  159. 'required' => $field->getOption('required'),
  160. );
  161. return $fieldHTML;
  162. }
  163. /* Return an array of all the field names */
  164. function getFieldNames()
  165. {
  166. $names = array();
  167. foreach($this->fields as $fieldname => $field) {
  168. $names[] = $fieldname;
  169. }
  170. return $names;
  171. }
  172. /* Check all the fields and ensure there are no errors with any of them */
  173. function fieldErrors()
  174. {
  175. $errors = array();
  176. foreach($this->fields as $fieldname => $field) {
  177. $valid = $field->validate();
  178. if ($valid !== true) {
  179. $errors[$fieldname] = sprintf('%s: %s', $field->getDisplayName(), $valid);
  180. }
  181. }
  182. if (count($errors) > 0) {
  183. return $errors;
  184. }
  185. return false;
  186. }
  187. /* Save this record to the database */
  188. function saveRecord()
  189. {
  190. $result = '';
  191. /* For tables with varchar based primary keys */
  192. $newrecord = true;
  193. if ($this->currentrecord > 0) {
  194. $newrecord = false;
  195. } else {
  196. $sqltype = Jojo::getMySQLType($this->getTableName(), $this->getOption('primarykey'));
  197. if (strpos($sqltype,'varchar') !== false && $this->currentrecord != '') {
  198. $newrecord = false;
  199. }
  200. }
  201. /* Create SQL query */
  202. if (!$newrecord) {
  203. $query = "UPDATE {" . $this->table . "} SET ";
  204. } else {
  205. $query = "INSERT INTO {" . $this->table . "} SET ";
  206. }
  207. /* Add all the values */
  208. $values = array();
  209. foreach ($this->fields as $fieldname => $f) {
  210. $value = $this->fields[$fieldname]->getValue();
  211. switch ($this->fields[$fieldname]->getOption('type')) {
  212. case 'readonly':
  213. break;
  214. case 'permissions':
  215. /* permissions fields are stared as an array, but must be converted to text for the SQL */
  216. if (is_array($value)) {
  217. $strvalue = '';
  218. foreach ($value as $k => $v) {
  219. $strvalue .= "$k = $v\n";
  220. }
  221. } else {
  222. $strvalue = $value;
  223. }
  224. $query .= sprintf(' `%s` = ?,', $fieldname);
  225. $values[] = $strvalue;
  226. break;
  227. default:
  228. $query .= sprintf(' `%s` = ?,', $fieldname);
  229. $values[] = $value;
  230. break;
  231. }
  232. }
  233. $query = rtrim($query, ', ');
  234. if (!$newrecord) {
  235. /* Update record */
  236. $query .= " WHERE `" . $this->getOption('primarykey') . "`= ? LIMIT 1";
  237. $values[] = $this->currentrecord;
  238. $res = Jojo::updateQuery($query, $values);
  239. if ($res === false) {
  240. return "An error occured updating the record.";
  241. }
  242. } else {
  243. /* Insert new record */
  244. $this->currentrecord = Jojo::insertQuery($query, $values);
  245. }
  246. /* Output result to the user */
  247. if (!$newrecord) {
  248. $result = $this->getOption('displayname') . " updated.";
  249. } else {
  250. $result = "A new " . $this->getOption('displayname') . " has been added. ID#" . $this->currentrecord;
  251. }
  252. /* Run any post-save procedures */
  253. foreach ($this->fields as $k => $f) {
  254. $this->fields[$k]->afterSave();
  255. }
  256. return $result;
  257. }
  258. function deleteRecord()
  259. {
  260. global $_USERID;
  261. /* Create the query */
  262. $query = sprintf("DELETE FROM {%s} WHERE `%s`= ? LIMIT 1",
  263. $this->table,
  264. $this->getOption('primarykey')
  265. );
  266. $values = array($this->currentrecord);
  267. /* Delete the record */
  268. if (Jojo::deleteQuery($query, $values)) {
  269. /* Run any post-delete procedures */
  270. $res = true;
  271. foreach ($this->fields as $f) {
  272. $res = $res && $f->onDelete();
  273. }
  274. return $res;
  275. }
  276. return false;
  277. }
  278. /* Set the value of an option for this table */
  279. function setOption($option, $value)
  280. {
  281. $this->tabledata[$option] = $value;
  282. }
  283. /* Return the value of an option for this table from table data */
  284. function getOption($option)
  285. {
  286. return isset($this->tabledata[$option]) ? $this->tabledata[$option] : false;
  287. }
  288. /**
  289. * Return a field by name
  290. */
  291. function getField($field)
  292. {
  293. return (isset($this->fields[$field])) ? $this->fields[$field] : false;
  294. }
  295. /**
  296. * Return the first field of a matching type
  297. */
  298. function getFieldByType($type)
  299. {
  300. foreach ($this->fields as $f) {
  301. if ($f instanceof $type) {
  302. return $f;
  303. }
  304. }
  305. return false;
  306. }
  307. /* Set the value of a field */
  308. function setFieldValue($field, $value)
  309. {
  310. if (isset($this->fields[$field])) {
  311. $this->fields[$field]->setPostData($value);
  312. return $this->fields[$field]->setValue($value);
  313. }
  314. return false;
  315. }
  316. /* Get the value of a field */
  317. function getFieldValue($field)
  318. {
  319. if (isset($this->fields[$field])) {
  320. return $this->fields[$field]->getValue();
  321. }
  322. return false;
  323. }
  324. /* Return an instance of table */
  325. static function &singleton($table = null)
  326. {
  327. static $instances;
  328. if (!isset($instances)) {
  329. $instances = array();
  330. }
  331. if (empty($instances[$table])) {
  332. $instances[$table] = new Jojo_table($table);
  333. }
  334. return $instances[$table];
  335. }
  336. /* DTree with ajax needs to be treated carefully, because it returns script, not HTML */
  337. function createlist($menutype = "tree", $ajax = false, $prefix = 'edit', $selectednode=false)
  338. {
  339. if ($menutype == "auto" && ($this->getOption('parentfield') || $this->getOption('group1'))) {
  340. $menutype = "tree";
  341. } elseif ($menutype == "auto") {
  342. $menutype = 'list';
  343. }
  344. if ($menutype == 'tree') {
  345. Jojo::assignTemplateVar('draggable', ($this->getOption('group1') || $this->getOption('parentfield')) && $this->getFieldByType('Jojo_Field_Order'));
  346. Jojo::assignTemplateVar('table', $this->table);
  347. if (Jojo::fieldExists($this->table, 'language')) {
  348. Jojo::assignTemplateVar('table_languages', Jojo::selectAssoc('SELECT DISTINCT l.tag, l.description FROM {tag_language} l WHERE l.tag IN (SELECT DISTINCT language FROM {' . $this->table . '}) OR l.tag IN (SELECT language FROM {site_language}) ORDER BY l.description'));
  349. }
  350. Jojo::assignTemplateVar('displayname', $this->getOption('displayname'));
  351. return Jojo::fetchTemplate('admin/edit-ajaxtree.tpl');
  352. } elseif ($menutype == 'list' || $menutype == 'recursivePath' || $menutype == 'array') {
  353. global $_USERGROUPS;
  354. $idfield = $this->getOption('primarykey');
  355. $displayfield = Jojo::either($this->getOption('displayfield'), $this->getOption('primarykey'));
  356. $parentfield = Jojo::either($this->getOption('parentfield'), $this->getOption('group1'), "'0'");
  357. $categorytable = $this->getOption('categorytable');
  358. $categoryfield = Jojo::either($this->getOption('categoryfield'), $this->getOption('group1'), "'0'");
  359. $orderbyfield = $this->getOption('orderbyfields');
  360. $group1field = $this->getOption('group1');
  361. $group2field = $this->getOption('group2');
  362. $golivefield = $this->getOption('golivefield');
  363. $expiryfield = $this->getOption('expiryfield');
  364. $activefield = $this->getOption('activefield');
  365. //filter results
  366. $datafilter = Jojo::either($this->getOption('filterby'), '1');
  367. $groupownerfilter = '';
  368. if ($this->getOption('groupowner') != '') {
  369. $groupownerfilter = ' AND ( ';
  370. foreach ($_USERGROUPS as $g) {
  371. $groupownerfilter .= "(" . $this->getOption('groupowner') . "='" . $g . "') OR";
  372. }
  373. $groupownerfilter .= " " . $this->getOption('groupowner') . "='' )";
  374. }
  375. if (is_array($_USERGROUPS) && in_array('admin', $_USERGROUPS)) {
  376. $groupownerfilter = '';
  377. }
  378. $rolloverfield = Jojo::either($this->getOption('rolloverfield'), "''");
  379. //this will be used for output
  380. $html = "";
  381. // - this is used to pull down the main groupings - takes an extra query
  382. if ($this->getOption('group2') != "") {
  383. // Done in main loop below, no extra query needed.
  384. } elseif ($this->getOption('group1') != "") {
  385. //Layer1 represents table structure where the first level is the grouping, then individual records underneath
  386. $layer1 = Jojo::selectQuery("SELECT " . $this->getOption('group1') . " FROM {" . $this->table . "} GROUP BY " . $this->getOption('group1') . " ORDER BY " . $this->getOption('group1') . "");
  387. }
  388. //Main query
  389. $query = "SELECT $idfield AS id, $displayfield AS display, $parentfield AS parent, $categoryfield AS categoryfield, $rolloverfield AS rollover "
  390. . Jojo::onlyIf($group1field, ", " . $group1field . " AS group1")
  391. . Jojo::onlyIf($group2field, ", " . $group2field . " AS group2")
  392. . Jojo::onlyIf($golivefield, ", " . $golivefield . " AS golive")
  393. . Jojo::onlyIf($expiryfield, ", " . $expiryfield . " AS expiry")
  394. . Jojo::onlyIf($activefield, ", " . $activefield . " AS active")
  395. . " FROM {" . $this->table . "} WHERE $datafilter " . $groupownerfilter . " ORDER BY " . Jojo::onlyIf($group1field, " " . $group1field . ", ")
  396. . Jojo::onlyIf($orderbyfield, " " . $orderbyfield . ", ")
  397. . " display";
  398. $records = Jojo::selectQuery($query);
  399. //get the TABLEDATA options for the category
  400. $catidfield = '';
  401. $catoptions = Jojo::selectQuery("SELECT * FROM {tabledata} WHERE td_name = ? LIMIT 1", array($categorytable));
  402. if (isset($catoptions[0])) {
  403. $catidfield = $catoptions[0]['td_primarykey'];
  404. $catdisplayfield = Jojo::either($catoptions[0]['td_displayfield'], $catoptions[0]['td_primarykey']);
  405. $catparentfield = Jojo::either($catoptions[0]['td_parentfield'], $catoptions[0]['td_group1'], "'0'");
  406. $catorderbyfield = $catoptions[0]['td_orderbyfields'];
  407. $catrolloverfield = Jojo::either($catoptions[0]['td_rolloverfield'], "'0'");
  408. $catgroupownerfilter = '';
  409. if ($catoptions[0]['td_groupowner'] != '') {
  410. $catgroupownerfilter = ' AND ( ';
  411. foreach ($_USERGROUPS as $g) {
  412. $catgroupownerfilter .= "(" . $catoptions[0]['td_groupowner'] . "='" . $g . "') OR";
  413. }
  414. $catgroupownerfilter .= " " . $catoptions[0]['td_groupowner'] . "='' )";
  415. }
  416. if (is_array($_USERGROUPS) && in_array('admin', $_USERGROUPS)) {
  417. $catgroupownerfilter = '';
  418. }
  419. }
  420. /* Category query */
  421. if ($catidfield != '') {
  422. //TODO: Make this query a join so it hides empty categories (if this would be useful)
  423. $query = "SELECT $catidfield AS id, $catdisplayfield AS display, $catparentfield AS parent, $catrolloverfield AS rollover
  424. FROM {" . $categorytable . "}
  425. WHERE 1 " . $catgroupownerfilter . Jojo::onlyif($catorderbyfield, "ORDER BY " . $catorderbyfield . "");
  426. $catrecords = Jojo::selectQuery($query);
  427. }
  428. $tree = new hktree($this->table);
  429. /* Add group 1 categories to HKTree */
  430. $groups = $this->_transpose($records);
  431. if (isset($groups['group1']) && is_array($groups['group1'])) {
  432. $groups['group1'] = array_unique($groups['group1']);
  433. foreach ($groups['group1'] as $group1) {
  434. $status = 'folder';
  435. $tree->addnode('c' . $group1, 0, $group1, '', '', "setH1('Loading...');frajax('load', '" . $this->table . "',this.value); return false;", '', '', '', $status);
  436. }
  437. }
  438. /* Add categories to HKTree */
  439. if (isset($catrecords) && is_array($catrecords)) {
  440. for ($i = 0; $i < count($catrecords); $i++) {
  441. $status = 'folder';
  442. $tree->addnode('c' . $catrecords[$i]['id'], 0, $catrecords[$i]['display'], '', '', '', '', '', '', $status);
  443. }
  444. }
  445. /* Add items to HKTree */
  446. for ($i = 0; $i < count($records); $i++) {
  447. if (($records[$i]['categoryfield'] != "") and ($records[$i]['categoryfield'] != "0")) {
  448. $parent = "c" . $records[$i]['categoryfield'];
  449. } elseif (isset($records[$i]['group1']) && $records[$i]['group1'] != '') {
  450. $parent = "'" . strtolower('c' . $records[$i]['group1']) . "'";
  451. } elseif ($records[$i]['parent'] != "") {
  452. $parent = "'" . strtolower($records[$i]['parent']) . "'";
  453. } else {
  454. $parent = "0";
  455. }
  456. $link = $prefix . "/" . $this->table . "/" . $records[$i]['id'] . "/";
  457. if (!$golivefield && !$expiryfield) {
  458. $status = '';
  459. } elseif ($golivefield && $expiryfield) {
  460. $now = strtotime('now');
  461. if (($now < $records[$i]['golive']) || (($now > $records[$i]['expiry']) && ($records[$i]['expiry'] > 0))) {
  462. $status = 'expired';
  463. } else {
  464. $status = '';
  465. }
  466. } else {
  467. $status = '';
  468. }
  469. if (!empty($activefield) && (($records[$i]['active'] == 'no') || ($records[$i]['active'] == 'inactive') ||($records[$i]['active'] == '0') || ($records[$i]['active'] === 0) || ($records[$i]['active'] == ''))) {
  470. $status = 'expired';
  471. }
  472. /* check permissions */
  473. global $_USERGROUPS;
  474. $perms = new Jojo_Permissions();
  475. $perms->getPermissions($this->table, $records[$i]['id']);
  476. /*
  477. A quick hack so that the list type will show record in the list after a frajax save.
  478. Permissions are only checked on 'page' records, not article / faq / anything else records.
  479. */
  480. if (($this->table != 'page') || $perms->hasPerm($_USERGROUPS, 'view')) {
  481. /* add the node */
  482. $tree->addnode($records[$i]['id'], $parent, $records[$i]['display'], $link, '', "frajax('load', '" . $this->table . "', '" . $records[$i]['id'] . "'); return false;", '', $records[$i]['rollover'],'',$status);
  483. }
  484. }
  485. if ($selectednode !== false) $tree->selected = $selectednode;
  486. /* output the tree structure */
  487. if ($menutype == 'recursivePath') {
  488. return $tree->recursivePath();
  489. } elseif ($menutype == 'array') {
  490. return $tree->printout_array();
  491. } else {
  492. $html .= "<form id=\"jump\" name=\"jump\" method=\"post\" action=\"" . $prefix . "/" . $this->table . "/\">\n<select name=\"jumpid\" size=\"25\" style=\"width: 100%\" onchange=\"frajax('load', '" . $this->table . "',this.value); return false;\">\n";
  493. $html .= $tree->printout_select(10, $this->currentrecord);
  494. $html .= "</select>";
  495. $html .= "</form>\n";
  496. }
  497. return $html;
  498. } else {
  499. return "Unknown menu type selected, select a valid menu type in tabledata.";
  500. }
  501. }
  502. /* Converts the format of a 2D array from $arr[a][b] to $arr[b][a] - used for sorting the array*/
  503. function _transpose($arr)
  504. {
  505. $newarr = array();
  506. foreach ($arr as $keyx => $valx) {
  507. foreach ($valx as $keyy => $valy) {
  508. $newarr[$keyy][$keyx] = $valy;
  509. }
  510. }
  511. return $newarr;
  512. }
  513. }