com.lokorin.lokorin.lib
[ class tree: com.lokorin.lokorin.lib ] [ index: com.lokorin.lokorin.lib ] [ all elements ]

Source for file lib_admin_panel.php

Documentation is available at lib_admin_panel.php

  1. <?php
  2. /*
  3. * Lokorin.com
  4. * Copyright 2004-2006
  5. * Licensed under the GNU LGPL. See COPYING for full terms.
  6. */
  7. /**
  8. * A library containing admin panel functionality. The library can create
  9. * simplified and customized panels which can be used to alter database
  10. * tables. The library is integrated with the administration log and update
  11. * libraries.
  12. * @author Andreas Launila
  13. * @version $Revision: 1.17 $
  14. * @package com.lokorin.lokorin.lib
  15. * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  16. */
  17.  
  18. /**
  19. * For access to common constants and functions.
  20. */
  21. require_once('common.php');
  22. doInclude('admin_auth');
  23. doInclude('lib_forms');
  24. doInclude('lib_tables');
  25. doInclude('lib_displayer');
  26. CssHandler::getInstance()->includeCss('admin_panel');
  27.  
  28. /**
  29. * Adds an entry to the admin log, storing the action for later examination.
  30. * @param string $table The name of the table that the action affects.
  31. * @param string $module The name of the module that performed the action, or
  32. * alternativly a description of the action.
  33. * @param string $comment An optional comment to add additional information to
  34. * the log message.
  35. * @access private
  36. */
  37. function logAdminAction($table, $module, $comment = '') {
  38. global $db;
  39. $vars = array(
  40. 'table' => $table,
  41. 'admin_module' => $module,
  42. 'url' => SELF,
  43. 'comment' => $comment,
  44. 'user' => $_SESSION['user'],
  45. 'timestamp' => time(),
  46. 'ip' => $_SERVER['REMOTE_ADDR']);
  47. $db->queryInsert(TABLE_ADMIN_LOGS, $vars);
  48. }
  49.  
  50. /**
  51. * This function is notified whenever a module performs a table altering
  52. * action. Everything that should be notified of admin activity should
  53. * hook in here.
  54. * @param string $table The name of the table that the action affects.
  55. * @param string $module The name of the module class that performed the
  56. * action, or alternativly a description of the action.
  57. * @param string $sqlSelectThe sql selector (if there is any) that specifies
  58. * the row that was altered.
  59. * @access private
  60. */
  61. function logAdminActivity($table, $module, $sqlSelector = '') {
  62. $module = strtolower($module); //To avoid errors between PHP 4.3 and 5 .
  63. logAdminAction($table, $module, $sqlSelector);
  64. //Make sure the redirects and aliases reflect the change.
  65. doInclude('lib_redirects');
  66. buildUrlRedirects();
  67. buildUrlAliases();
  68. //Notify the sitemaps of the table changes.
  69. doInclude('lib_sitemap');
  70. notifyTableChanged($table);
  71. //Delete all directory cached contents to force it to be regenerated.
  72. doInclude('lib_cache');
  73. deleteAllCachedContents();
  74. }
  75.  
  76. /*
  77. * Common callbacks start.
  78. */
  79. /**
  80. * Formats a timestamp to the number of elapsed days since the point in time
  81. * which the timestamp represents. This includes special cases like 'Never',
  82. * 'Today' and 'Yesterday'.
  83. * @param integer $timestamp The unix timestamp to be converted.
  84. * @return string A string indicating the number of elapsed days.
  85. * @access public
  86. */
  87. function formatTimestampDays($timestamp) {
  88. if($timestamp == 0) {
  89. return 'Never';
  90. } else {
  91. $days = round((time() - $timestamp) / (3600*24));
  92. if($days == 0) {
  93. return 'Today';
  94. } else if($days == 1) {
  95. return 'Yesterday';
  96. } else {
  97. return $days.' days ago';
  98. }
  99. }
  100. }
  101.  
  102. /**
  103. * Formats a timestamp into a date representation. As a special case timestamp
  104. * equal to 0 get the representation 'Never'.
  105. * @param integer $timestamp The unix timestamp to be converted.
  106. * @return string A date representation.
  107. * @access public
  108. */
  109. function formatTimestampDate($timestamp) {
  110. if($timestamp == 0) {
  111. return 'Never';
  112. } else {
  113. return date(DATE_SHORTER, $timestamp);
  114. }
  115. }
  116.  
  117. /**
  118. * Formats a timestamp into a date representation complete with hours and
  119. * minutes. As a special case timestamp equal to 0 get the representation
  120. * 'Never'.
  121. * @param integer $timestamp The unix timestamp to be converted.
  122. * @return string A date/time representation.
  123. * @access public
  124. */
  125. function formatTimestampTime($timestamp) {
  126. if($timestamp == 0) {
  127. return 'Never';
  128. } else {
  129. return date(DATE_TIME, $timestamp);
  130. }
  131. }
  132.  
  133. /**
  134. * Converts a boolean value to either 'Yes' or 'No'. 'Yes' is used if the
  135. * value is a boolean true, 'No' is used in all other cases.
  136. * @param boolean $boolVal The boolean value that should be converted.
  137. * @return string 'Yes' or 'No'.
  138. * @access public
  139. */
  140. function convertBoolToYn($boolVal) {
  141. if($boolVal == 1) {
  142. return 'Yes';
  143. } else {
  144. return 'No';
  145. }
  146. }
  147. /*
  148. * Common callbacks end.
  149. */
  150.  
  151.  
  152. /**
  153. * Describes an module with admin functionality. The module defines actions
  154. * that it's able perform and performs the action when needed. This class
  155. * should be concidered abstract.
  156. * @author Andreas Launila
  157. * @package com.lokorin.lokorin.lib
  158. */
  159. abstract class AdminModule {
  160. /**
  161. * Contains all actions that the module handles. The key is the
  162. * action identifier and the value is the corresponding AdminAction
  163. * instance.
  164. * @var array
  165. */
  166. private $actions;
  167.  
  168. /**
  169. * Constructor for AdminModule.
  170. * @param array $definedActions An array containing AdminAction describing
  171. * all actions handled by the module.
  172. */
  173. public function __construct($definedActions) {
  174. if(!is_array($definedActions)) {
  175. $definedActions = array();
  176. }
  177. //Build the actions array so that the correct keys are given.
  178. $actions = array();
  179. foreach($definedActions as $action) {
  180. $actions[$action->getAction()] = $action;
  181. }
  182. $this->actions = $actions;
  183.  
  184. }
  185.  
  186. /**
  187. * Gets the output that the module wants to diaply.
  188. * @param string $action The action that triggered the output.
  189. * @return string Output defined by the module.
  190. */
  191. abstract public function getOutput($action);
  192.  
  193. /**
  194. * Checks if an action can be handled by the module.
  195. * @param string $action The action to be checked.
  196. * @return boolean True if the action can be handled by the module,
  197. * false otherwise.
  198. */
  199. public function handlesAction($action) {
  200. return in_array($action, array_keys($this->actions));
  201. }
  202.  
  203. /**
  204. * Gets all admin actions handled by the module.
  205. * @return array An array containing all admin actions that the module
  206. * handles.
  207. */
  208. public function getAdminActions() {
  209. return $this->actions;
  210. }
  211.  
  212. /**
  213. * Override this function if the module should be linked to once in the
  214. * panel.
  215. * @return string An expression that should be used with eval() or an
  216. * empty string if none is defined.
  217. */
  218. public function getGlobalLinkPattern() {
  219. return '';
  220. }
  221.  
  222. /**
  223. * Override this function if the module should be linked to once for every
  224. * entry displayed in the panel's overview. The pattern can use fieldnames
  225. * as $row['foo'] (where foo is the fieldname).
  226. * @return string An expression that should be used with eval() or an empty
  227. * string if none is defined.
  228. */
  229. public function getEntryLinkPattern() {
  230. return '';
  231. }
  232. /**
  233. * Notifies everything that should be notified of admin activity. This
  234. * function should be called whenever a module alters a table.
  235. * @param string $table The name of the table that the action affects.
  236. * @param string $sqlSelector The sql selector (if there is any) that
  237. * specifies the row that was altered.
  238. */
  239. protected function logActivity($table, $sqlSelector = '') {
  240. logAdminActivity($table, get_class($this), $sqlSelector);
  241. }
  242. }
  243.  
  244. /**
  245. * Describes a table field with a connected value in $_REQUEST. The table
  246. * field tries to capture it's connected value if possible.
  247. * @author Andreas Launila
  248. * @package com.lokorin.lokorin.lib
  249. */
  250. class TableField {
  251. /**
  252. * The name used to identify field in $_REQUEST.
  253. * @var string
  254. */
  255. private $requestName;
  256. /**
  257. * The name that should be displayed to the user.
  258. * @var string
  259. */
  260. private $displayName;
  261. /**
  262. * The name of the corresponding field in the table.
  263. * @var string
  264. */
  265. private $tableField;
  266. /**
  267. * The value of the field (used for different things, e.g. identification).
  268. * @var string
  269. */
  270. private $value;
  271.  
  272. /**
  273. * Constructor for TableField.
  274. * @param string $requestName The name used to identify field in $_REQUEST.
  275. * @param string $displayName The name to display to the user.
  276. * @param string $tableField The corresponding field in the table.
  277. * @param string $value The value of the field.
  278. */
  279. public function __construct($requestName, $displayName, $tableField,
  280. $value = false) {
  281. $this->requestName = $requestName;
  282. $this->displayName = $displayName;
  283. $this->tableField = $tableField;
  284. if(($value !== false) || !isset($_REQUEST[$requestName])) {
  285. $this->value = $value;
  286. }
  287. }
  288.  
  289. /**
  290. * Gets the request name of the field.
  291. * @return string An $_REQUEST name.
  292. */
  293. public function getRequestName() {
  294. return $this->requestName;
  295. }
  296.  
  297. /**
  298. * Gets the display name of the field.
  299. * @return string A name.
  300. */
  301. public function getDisplayName() {
  302. return $this->displayName;
  303. }
  304.  
  305. /**
  306. * Gets the name of the table field connected to this instance.
  307. * @return string A table field name.
  308. */
  309. public function getTableField() {
  310. return $this->tableField;
  311. }
  312.  
  313. /**
  314. * Gets the value of the field.
  315. * @return string A value.
  316. */
  317. public function getValue() {
  318. if((strlen($this->value) == 0) &&
  319. isset($_REQUEST[$this->requestName])) {
  320. $this->value = $_REQUEST[$this->requestName];
  321. }
  322. return $this->value;
  323. }
  324.  
  325. /**
  326. * Checks if the field has a value set or not.
  327. * @return boolean True if the field has a value, false otherwise.
  328. */
  329. public function hasValue() {
  330. return $this->value !== false;
  331. }
  332.  
  333. /**
  334. * Sets a new value for the field.
  335. * @param $newVal The new value to assign.
  336. */
  337. public function setValue($newVal) {
  338. $this->value = $newVal;
  339. }
  340. }
  341.  
  342. /**
  343. * Describes an admin action, an identifier for something that can be done by
  344. * the admin panel.
  345. * @author Andreas Launila
  346. * @package com.lokorin.lokorin.lib
  347. */
  348. class AdminAction {
  349. /**
  350. * The action value that triggers the action.
  351. * @var string
  352. */
  353. private $actionIdentifier;
  354. /**
  355. * The name that describes the action to the user.
  356. * @var string
  357. * @access private
  358. */
  359. private $displayName;
  360.  
  361. /**
  362. * Constructor for AdminAction.
  363. * @param string $actionId The action value that identifies the action.
  364. * @param string $displayName The name that describes the action to the
  365. * user.
  366. */
  367. public function __construct($actionId, $displayName) {
  368. $this->actionIdentifier = $actionId;
  369. $this->displayName = $displayName;
  370. }
  371.  
  372. /**
  373. * Gets the action value identifing the action.
  374. * @return string An action value.
  375. */
  376. public function getAction() {
  377. return $this->actionIdentifier;
  378. }
  379.  
  380. /**
  381. * Gets the name that describes the action.
  382. * @return string An describing name.
  383. */
  384. public function getDisplayName() {
  385. return $this->displayName;
  386. }
  387. }
  388.  
  389. /**
  390. * Describes a module that deletes rows from a table.
  391. * @author Andreas Launila
  392. * @package com.lokorin.lokorin.lib
  393. */
  394. class DeleteModule extends AdminModule {
  395. /**
  396. * The name of the table that the module should delete rows from.
  397. * @var string
  398. */
  399. private $table;
  400. /**
  401. * Contains the TableField instances that should be used to identify which
  402. * row that should be deleted.
  403. * @var array
  404. */
  405. private $fields;
  406. /**
  407. * If a confirmation should be asked for or not. True if a confirmation
  408. * should be asked for, false otherwise.
  409. * @var boolen
  410. */
  411. private $doConfirmation;
  412.  
  413. /**
  414. * Constructor for DeleteModule.
  415. * @param string $table The table that should be worked against.
  416. * @param array $identifierFields The fields that identify which row that
  417. * should be deleted.
  418. * @param boolean $requestConfimation If a confimation should be asked
  419. * for or not.
  420. */
  421. public function __construct($table, $identifierFields, $requestConfirmation = true) {
  422. parent::__construct(array(
  423. new AdminAction('remove', 'Remove'),
  424. new AdminAction('do_remove', 'Remove')
  425. ));
  426. $this->table = $table;
  427. $this->fields = $identifierFields;
  428. $this->doConfirmation = $requestConfirmation;
  429. }
  430.  
  431. /**
  432. * @see AdminModule.getOutput($action)
  433. */
  434. public function getOutput($action) {
  435. if($action == 'remove') {
  436. if($this->doConfirmation) {
  437. return $this->processConfirm();
  438. } else {
  439. return $this->processRemove();
  440. }
  441. } else if($action == 'do_remove') {
  442. return $this->processRemove();
  443. }
  444. }
  445.  
  446. /**
  447. * Creates a confirmation dialog.
  448. * @return string A confirmation dialog.
  449. */
  450. private function processConfirm() {
  451. $queryString = '';
  452. $header = new Header();
  453. $body = array();
  454. foreach($this->fields as $field) {
  455. $header->add($field->getDisplayName());
  456. $body[] = $field->getValue();
  457. $queryString .= '&amp;'.$field->getRequestName().'='.$field->getValue();
  458. }
  459. $table = new Table();
  460. $table->setHeader($header);
  461. $table->addRow($body);
  462. return 'Are you sure that you want to delete the following entry?'.$table->getTable().
  463. '<a href="'.SELF.'?action=do_remove'.$queryString.'" class="adminAction">Yes</a>' . "\n" .
  464. '<a href="'.SELF.'" class="adminAction">No</a>';
  465.  
  466. }
  467.  
  468. /**
  469. * Removes an entry and creates a confirmation message.
  470. * @return string An confirmation or error message.
  471. */
  472. private function processRemove() {
  473. //Make sure that all identifier fields have been set.
  474. $valid = true;
  475. foreach($this->fields as $field) {
  476. if(!$field->hasValue()) {
  477. $valid = false;
  478. break;
  479. }
  480. }
  481. if(!$valid) {
  482. return 'Invalid input, some identifier fields were not set.';
  483. }
  484.  
  485. //Perform the deletion
  486. global $db;
  487. //Build the match parameters.
  488. $matchFields = array();
  489. foreach($this->fields as $field) {
  490. $matchFields[$field->getTableField()] = $field->getValue();
  491. }
  492. $sqlSel = $db->buildSqlSelector($matchFields);
  493. $result = $db->queryDelete($this->table, $sqlSel);
  494. $this->logActivity($this->table, $sqlSel);
  495. $idField = $this->fields[0];
  496. $output = 'The entry was successfully removed.';
  497. doInclude('lib_redirects');
  498. doDelayedRedirect(SELF, 2);
  499. return $output;
  500. }
  501.  
  502. /**
  503. * @see AdminModule.getEntryLinkPattern()
  504. */
  505. public function getEntryLinkPattern() {
  506. $fields = '';
  507. foreach($this->fields as $field) {
  508. $fields .= "&amp;".$field->getRequestName().
  509. "=\$row['".$field->getTableField()."']";
  510. }
  511. return '<a href="'.SELF.'?action=remove'.$fields.
  512. '" class="adminAction">Remove</a>';
  513. }
  514. }
  515.  
  516. /**
  517. * Describes a module that edits a specified row in a table.
  518. * @author Andreas Launila
  519. * @package com.lokorin.lokorin.lib
  520. */
  521. class EditModule extends AdminModule {
  522. /**
  523. * The name of table that contains the row that should be edited.
  524. * @var string
  525. */
  526. private $table;
  527. /**
  528. * Contains the fields, represented by TableField instances, that should
  529. * be edited.
  530. * @var array
  531. */
  532. private $fields;
  533. /**
  534. * Contains the form fields, represented by Field instances, that should
  535. * be used to override the request name contained in the key.
  536. * @var array
  537. */
  538. private $formFields;
  539. /**
  540. * Contains the fields that identify the rows that should be edited. The
  541. * key is the name of the table field and the corresponding value is the
  542. * corresponding form field that should be used for the table field.
  543. * @var array
  544. */
  545. private $idFields;
  546.  
  547. /**
  548. * Constructor for EditModule.
  549. * @param string $table The table that should be edited.
  550. * @param array $fieldsToEdit An array of TableField with the fields that
  551. * should be edited.
  552. * @param array $idFields An array of Tablefield that identifies which
  553. * entry that should be edited.
  554. * @param array $formFields An array of Field (from lib_form_generator.php)
  555. * with the connected request name as field name.
  556. */
  557. public function __construct($table, $fieldsToEdit, $idFields, $formFields = array()) {
  558. parent::__construct(array(
  559. new AdminAction('edit', 'Edit'),
  560. new AdminAction('do_edit', 'Perform edit')
  561. ));
  562. $this->table = $table;
  563. $this->fields = $fieldsToEdit;
  564. //Build the id fields.
  565. $this->idFields = array();
  566. foreach($idFields as $field) {
  567. $this->idFields[$field->getTableField()] =
  568. new Hidden('editid_'.$field->getRequestName());
  569. }
  570. //Build the form fields (making the key the request name).
  571. $fields = array();
  572. foreach($formFields as $field) {
  573. $fields[$field->getName()] = $field;
  574. }
  575. $this->formFields = $fields;
  576.  
  577. }
  578.  
  579. /**
  580. * @see AdminModule.getOutput($action)
  581. */
  582. public function getOutput($action) {
  583. if($action == 'edit') {
  584. return $this->getEditDialog();
  585. } else if($action == 'do_edit') {
  586. return $this->processEdit();
  587. }
  588. }
  589.  
  590. /**
  591. * Gets the corresponding edit dialog.
  592. * @return string An edit dialog.
  593. */
  594. private function getEditDialog() {
  595. global $db;
  596.  
  597. $sqlFields = array();
  598. foreach($this->idFields as $tableField => $formField) {
  599. $sqlFields[$tableField] = $formField->getValue();
  600. }
  601. $rows = $db->querySelectAll($this->table,
  602. $db->buildSqlSelector($sqlFields)." LIMIT 1");
  603. $row = $rows[0];
  604. //Make the form
  605. $form = new Form('Edit', 'post', SELF.'?action=do_edit');
  606. foreach($this->fields as $field) {
  607. if(isset($row[$field->getTableField()])) {
  608. $field->setValue($row[$field->getTableField()]);
  609. }
  610. if(isset($this->formFields[$field->getRequestName()])) {
  611. $formField = $this->formFields[$field->getRequestName()];
  612. } else {
  613. $formField = new TextField($field->getRequestName());
  614. }
  615. $formField->setValue($field->getValue());
  616. $form->addField($formField, $field->getDisplayName());
  617. }
  618. $form->addField(new SubmitButton('edit', 'Edit'));
  619. //Add the hidden identifiers.
  620. foreach($this->idFields as $field) {
  621. $form->addField($field);
  622. }
  623.  
  624. return $form->getForm();
  625. }
  626.  
  627. /**
  628. * Processes an edit request and returns the result or an error message.
  629. * @return An confirmation message or an error.
  630. */
  631. private function processEdit() {
  632. global $db;
  633.  
  634. $vars = array();
  635. foreach($this->fields as $field) {
  636. $vars[$field->getTableField()] = $field->getValue();
  637. }
  638.  
  639. $sqlFields = array();
  640. foreach($this->idFields as $tableField => $formField) {
  641. $sqlFields[$tableField] = $formField->getValue();
  642. }
  643. $sqlSelector = $db->buildSqlSelector($sqlFields);
  644. $db->queryUpdate($this->table, $vars, $sqlSelector);
  645. $this->logActivity($this->table, $sqlSelector);
  646. $output = 'The entry was successfully edited.';
  647. doInclude('lib_redirects');
  648. doDelayedRedirect(SELF, 2);
  649. return $output;
  650. }
  651.  
  652. /**
  653. * @see AdminModule.getEntryLinkPattern()
  654. */
  655. public function getEntryLinkPattern() {
  656. $fields = '';
  657. foreach($this->idFields as $tableField => $formField) {
  658. $fields .= "&amp;".$formField->getName()."=\$row['".$tableField."']";
  659. }
  660. return '<a href="'.SELF.'?action=edit'.$fields.'" class="adminAction">Edit</a>';
  661. }
  662. }
  663.  
  664. /**
  665. * Describes a module for adding new rows to a table.
  666. * @author Andreas Launila
  667. * @package com.lokorin.lokorin.lib
  668. */
  669. class AddModule extends AdminModule {
  670. /**
  671. * The name of the table that the module should add rows to.
  672. * @var string
  673. */
  674. private $table;
  675. /**
  676. * Contains the fields (represented by TableFiled instances) that should
  677. * be set when adding a new module.
  678. * @var array
  679. */
  680. private $fields;
  681. /**
  682. * Contains the form fields, represented by Field instances, that should
  683. * be used to override the request name contained in the key.
  684. * @var array
  685. */
  686. private $formFields;
  687.  
  688. /**
  689. * Constructor for AddModule.
  690. * @param string $table The table that entries should be added to.
  691. * @param array $fieldsToAdd An array of TableField with the fields that
  692. * should be added.
  693. * @param array $formFields An array of Field (from lib_form_generator.php)
  694. * with the connected request name as field name.
  695. */
  696. public function __construct($table, $fieldsToAdd, $formFields = array()) {
  697. parent::__construct(array(
  698. new AdminAction('add', 'New entry'),
  699. new AdminAction('do_add', 'Add')
  700. ));
  701. $this->table = $table;
  702. $this->fields = $fieldsToAdd;
  703. //Build the form fields (making the key the request name).
  704. $fields = array();
  705. foreach($formFields as $field) {
  706. $fields[$field->getName()] = $field;
  707. }
  708. $this->formFields = $fields;
  709. }
  710.  
  711. /**
  712. * @see AdminModule.getOutput($action)
  713. */
  714. public function getOutput($action) {
  715. if($action == 'add') {
  716. return $this->getAddDialog();
  717. } else if($action == 'do_add') {
  718. return $this->processAdd();
  719. }
  720. }
  721.  
  722. /**
  723. * Gets the corresponding add dialog.
  724. * @return string An add dialog.
  725. */
  726. private function getAddDialog() {
  727. $form = new Form('Add a new entry', 'post', SELF.'?action=do_add');
  728. foreach($this->fields as $field) {
  729. if(isset($this->formFields[$field->getRequestName()])) {
  730. $fieldToAdd = $this->formFields[$field->getRequestName()];
  731. } else {
  732. $fieldToAdd = new TextField($field->getRequestName());
  733. }
  734. if($field->hasValue()) {
  735. $fieldToAdd->setValue($field->getValue());
  736. }
  737. $form->addField($fieldToAdd, $field->getDisplayName());
  738. }
  739. $form->addField(new SubmitButton('add', 'Add'));
  740. return $form->getForm();
  741. }
  742.  
  743. /**
  744. * Processes an add request and returns the result or an error message.
  745. * @return An confirmation message or an error.
  746. */
  747. private function processAdd() {
  748. global $db;
  749.  
  750. $vars = array();
  751. foreach($this->fields as $field) {
  752. $vars[$field->getTableField()] = $field->getValue();
  753. }
  754.  
  755. $db->queryInsert($this->table, $vars);
  756. $this->logActivity($this->table, $db->getLastInsertId());
  757. $output = 'The entry was successfully added.';
  758. doInclude('lib_redirects');
  759. doDelayedRedirect(SELF, 2);
  760. return $output;
  761. }
  762.  
  763. /**
  764. * @see AdminModule.getGlobalLinkPattern()
  765. */
  766. public function getGlobalLinkPattern() {
  767. return '<a href="'.SELF.'?action=add" ' .
  768. 'class="adminAction">New Entry</a>';
  769. }
  770. }
  771.  
  772. /**
  773. * Describes a module that uses callbacks to process events.
  774. * @author Andreas Launila
  775. * @package com.lokorin.lokorin.lib
  776. */
  777. class CallbackModule extends AdminModule {
  778. /**
  779. * Contains all the callbacks, action value as key, the callback as value.
  780. * @var array
  781. */
  782. private $callbacks;
  783. /**
  784. * The global link pattern to be used by the module.
  785. * @var string
  786. */
  787. private $globalLink;
  788. /**
  789. * The entry link pattern to be returned by the module.
  790. * @var string
  791. */
  792. private $entryLink;
  793.  
  794. /**
  795. * Constructor for CallbackModule.
  796. * @param array $callbacks An array of the callbacks to be used,
  797. * corresponding action value as key, callback as value.
  798. * @param string $globalLink The global link pattern (see AdminModule.getGlobalLinkPattern()
  799. * that the module should use.
  800. * @param string $entryLink The entry link pattern (see AdminModule.getEntryLinkPattern()
  801. * that the module should use.
  802. */
  803. public function __construct($callbacks, $globalLink = '', $entryLink = '') {
  804. $actions = array();
  805. foreach($callbacks as $action => $callback) {
  806. $actions[] = new AdminAction($action, $action);
  807. }
  808. parent::__construct($actions);
  809.  
  810. $this->callbacks = $callbacks;
  811. $this->globalLink = $globalLink;
  812. $this->entryLink = $entryLink;
  813. }
  814.  
  815. /**
  816. * @see AdminModule.getOutput($action)
  817. */
  818. public function getOutput($action) {
  819. if(isset($this->callbacks[$action])) {
  820. return $this->callbacks[$action]();
  821. } else {
  822. return 'Unmapped action ('.$action.').';
  823. }
  824. }
  825.  
  826. /**
  827. * @see AdminModule.getGlobalLinkPattern()
  828. */
  829. public function getGlobalLinkPattern() {
  830. return $this->globalLink;
  831. }
  832.  
  833. /**
  834. * @see AdminModule.getEntryLinkPattern()
  835. */
  836. public function getEntryLinkPattern() {
  837. return $this->entryLink;
  838. }
  839. }
  840.  
  841. /**
  842. * Describes a module that shows read only contents of a specified row.
  843. * @author Andreas Launila
  844. * @package com.lokorin.lokorin.lib
  845. */
  846. class DetailsModule extends AdminModule {
  847. /**
  848. * The table that should be read from.
  849. * @var string
  850. */
  851. private $table;
  852. /**
  853. * Contains TableField instances representing the fields that should be
  854. * viewed in the detailed overview.
  855. * @var array
  856. */
  857. private $fieldsToView;
  858. /**
  859. * Contains the fields (as TableField instances) that identify which row
  860. * that should be viewed.
  861. * @var array
  862. */
  863. private $idFields;
  864. /**
  865. * Contains all format callbacks with the corresponding table field as key
  866. * and the callback as value.
  867. * @var array
  868. */
  869. private $callbacks;
  870.  
  871. /**
  872. * Constructor for DetailsModule.
  873. * @param string $table The table to read from.
  874. * @param array $fieldsToView The TableField instances that should be shown
  875. * in the detailed version.
  876. * @param array $idFields The Tablefield instances that identifies which
  877. * row that contains the specified entry.
  878. * @param array $formatCallbacks An optional set of callbacks that should
  879. * be used to format output (i.e. timestamps). The key should be the
  880. * corresponding table field.
  881. */
  882. public function __construct($table, $fieldsToView, $idFields,
  883. $formatCallbacks = array()) {
  884. parent::__construct(array(
  885. new AdminAction('details', 'Details')
  886. ));
  887.  
  888. $this->table = $table;
  889. $this->fieldsToView = $fieldsToView;
  890. $this->idFields = $idFields;
  891. $this->callbacks = $formatCallbacks;
  892. }
  893.  
  894. /**
  895. * @see AdminModule.getOutput($action)
  896. */
  897. public function getOutput($action) {
  898. if($action != 'details') {
  899. return '';
  900. }
  901.  
  902. global $db;
  903.  
  904. $sqlFields = array();
  905. foreach($this->idFields as $field) {
  906. $sqlFields[$field->getTableField()] = $field->getValue();
  907. }
  908. $rows = $db->querySelectAll($this->table,
  909. $db->buildSqlSelector($sqlFields));
  910. $row = $rows[0];
  911.  
  912. $output = '<ul>';
  913. foreach($this->fieldsToView as $field) {
  914. $value = $row[$field->getTableField()];
  915. if(isset($this->callbacks[$field->getTableField()])) {
  916. $value = $this->callbacks[$field->getTableField()]($value);
  917. }
  918. $output .= '<li><i>'.$field->getDisplayName().'</i>: '.
  919. htmlspecialchars($value).'</li>';
  920. }
  921. $output .= '</ul>';
  922. return $output;
  923. }
  924.  
  925. /**
  926. * @see AdminModule.getEntryLinkPattern()
  927. */
  928. public function getEntryLinkPattern() {
  929. $fields = '';
  930. foreach($this->idFields as $field) {
  931. $fields .= "&amp;".$field->getRequestName()."=\$row['".$field->getTableField()."']";
  932. }
  933. return '<a href="'.SELF.'?action=details'.$fields.
  934. '" class="adminAction">Details</a>';
  935. }
  936. }
  937.  
  938.  
  939. /**
  940. * Describes a module that sets specified fields to specified values when
  941. * requested.
  942. * @author Andreas Launila
  943. * @package com.lokorin.lokorin.lib
  944. */
  945. class UpdateModule extends AdminModule {
  946. /**
  947. * The name of the table which rows should be updated.
  948. * @var string
  949. */
  950. private $table;
  951. /**
  952. * Contains the fields (represented by TableField instances) and the values
  953. * that they should be set to.
  954. * @var array
  955. */
  956. private $fieldsToSet;
  957. /**
  958. * Contains the fields (represented by TableFiled instances) that identify
  959. * which row that should be set.
  960. * @var array
  961. */
  962. private $idFields;
  963. /**
  964. * The name for the link that should represent the module.
  965. * @var string
  966. */
  967. private $linkName;
  968.  
  969. /**
  970. * Constructor for UpdateModule.
  971. * @param string $table The table to read from.
  972. * @param array $fieldsToSet The TableField instances that should be set
  973. * (along with the value that should be used).
  974. * @param array $idFields The Tablefield instances that identifies which
  975. * row that should be set.
  976. * @param string $linkName An optional name for the link that will be
  977. * representing the action e.g. 'Mark as investigated'.
  978. */
  979. public function __construct($table, $fieldsToSet, $idFields, $linkName = 'Update') {
  980. parent::__construct(array(
  981. new AdminAction('update', 'Update')
  982. ));
  983.  
  984. $this->table = $table;
  985. $this->fieldsToSet = $fieldsToSet;
  986. $this->idFields = $idFields;
  987. $this->linkName = $linkName;
  988. }
  989.  
  990. /**
  991. * @see AdminModule.getOutput($action)
  992. */
  993. public function getOutput($action) {
  994. if($action != 'update') {
  995. return '';
  996. }
  997.  
  998. global $db;
  999. $sqlFields = array();
  1000. foreach($this->idFields as $field) {
  1001. $sqlFields[$field->getTableField()] = $field->getValue();
  1002. }
  1003.  
  1004. $vars = array();
  1005. foreach($this->fieldsToSet as $field) {
  1006. $vars[$field->getTableField()] = $field->getValue();
  1007. }
  1008.  
  1009. $db->queryUpdate($this->table, $vars, $db->buildSqlSelector($sqlFields));
  1010.  
  1011. return '';
  1012. }
  1013.  
  1014. /**
  1015. * @see AdminModule.getEntryLinkPattern()
  1016. */
  1017. public function getEntryLinkPattern() {
  1018. $fields = '';
  1019. foreach($this->idFields as $field) {
  1020. $fields .= "&amp;".$field->getRequestName()."=\$row['".$field->getTableField()."']";
  1021. }
  1022. return '<a href="'.SELF.'?action=update'.$fields.
  1023. '" class="adminAction">'.$this->linkName.'</a>';
  1024. }
  1025. }
  1026.  
  1027.  
  1028. /**
  1029. * Describes a panel itself. The panel itself is an module that shows an
  1030. * overview of a table and the available modules.
  1031. * @author Andreas Launila
  1032. * @package com.lokorin.lokorin.lib
  1033. */
  1034. class AdminPanel extends AdminModule {
  1035. /**
  1036. * Contains all the modules (AdminModule instances) that belong to the
  1037. * panel.
  1038. * @var array
  1039. */
  1040. private $modules;
  1041. /**
  1042. * The action that should be used if no other action is declared (in
  1043. * $_REQUEST).
  1044. * @var string
  1045. */
  1046. private $defaultAction;
  1047. /**
  1048. * The name of the table that the panel should view.
  1049. * @var string
  1050. */
  1051. private $tabel;
  1052. /**
  1053. * Contains the fields (represented by TableField instances) that should
  1054. * be displayed.
  1055. * @var array
  1056. */
  1057. private $fields;
  1058. /**
  1059. * Contains callbacks that should be used to properly format fields. Key
  1060. * is the tables fieldname, value is the callback. If no callback for a
  1061. * field is found then the field is directly outputted without any
  1062. * preprocessing.
  1063. * @var array
  1064. */
  1065. private $formatCallbacks;
  1066. /**
  1067. * The appendage to the view query. This could for instance be a match
  1068. * statement or a order apppendage.
  1069. * @var string
  1070. */
  1071. private $queryAppendage;
  1072. /**
  1073. * The number of items that should be displayed in each page when viewing
  1074. * the table overview.
  1075. * @var integer
  1076. */
  1077. private $itemsPerPage;
  1078. /**
  1079. * The table displayer that should be used to view the the contents of the
  1080. * viewe database.
  1081. * @var AbstractTableDisplayer
  1082. */
  1083. private $displayer;
  1084.  
  1085. /**
  1086. * Constructor for AdminPanel.
  1087. * @param string $table The table that should be overviewed to.
  1088. * @param array $fieldsToView An array of TableField with the fields that
  1089. * should be viewed.
  1090. * @param string $queryAppendage An optional condition on what and how to
  1091. * show the sql result. The condition is appended to the end of the
  1092. * query.
  1093. * @param string $defaultAction The action value that should be used if no
  1094. * other action is declared. If no value is specified then the first
  1095. * module added to the panel will be used.
  1096. * @param array $formatCallbacks An array of Field (from lib_form_generator.php)
  1097. * with the connected table field as name.
  1098. */
  1099. public function __construct($table, $fieldsToView, $queryAppendage = '',
  1100. $defaultAction = '', $formatCallbacks = array()) {
  1101. parent::__construct(array(
  1102. new AdminAction('view', 'Overview')
  1103. ));
  1104. $this->table = $table;
  1105. $this->fields = $fieldsToView;
  1106. $this->queryAppendage = $queryAppendage;
  1107. $this->formatCallbacks = $formatCallbacks;
  1108. $this->defaultAction = $defaultAction;
  1109. $this->itemsPerPage = 20;
  1110.  
  1111. $this->displayer = new TableDisplayer(
  1112. $this->table, $this->itemsPerPage, $this->queryAppendage);
  1113.  
  1114. $this->addModule($this);
  1115. }
  1116.  
  1117. /**
  1118. * Sets the number of items that should be displayed in each page when
  1119. * viewing the table overview.
  1120. * @param integer $newValue The number of items that should be displayed
  1121. * per page.
  1122. */
  1123. public function setItemsPerPage($newValue) {
  1124. $this->displayer->setRowsPerPage($newValue);
  1125. }
  1126. /**
  1127. * Sets a new table displayer that should be used by the panel to view
  1128. * databses with.
  1129. * @param AbstractTableDisplayer $newTableDisplayerToUse the new table
  1130. * displayer that should be used.
  1131. */
  1132. public function setTableDisplayer(AbstractTableDisplayer $newTableDisplayerToUse) {
  1133. $this->displayer = $newTableDisplayerToUse;
  1134. }
  1135.  
  1136. /**
  1137. * Adds an additional module to the panel.
  1138. * @param AdminModule $module The module that should be added.
  1139. */
  1140. public function addModule(AdminModule $module) {
  1141. $this->modules[] = $module;
  1142. //Update the default action if none is set.
  1143. if($this->defaultAction == '') {
  1144. $actions = $module->getAdminActions();
  1145. if(sizeof($actions) > 0) {
  1146. $action = current($actions);
  1147. $this->defaultAction = $action->getAction();
  1148. }
  1149. }
  1150. }
  1151.  
  1152. /**
  1153. * Gets the panel in its HTML form.
  1154. * @return string An admin panel.
  1155. */
  1156. public function getPanel() {
  1157. if(isset($_REQUEST['action'])) {
  1158. $action = $_REQUEST['action'];
  1159. } else {
  1160. $action = $this->defaultAction;
  1161. }
  1162.  
  1163. /*
  1164. * This is a necessary because of how PHP handles function calls. It
  1165. * does not accept calling the same class through a pointer for some
  1166. * strange reason.
  1167. */
  1168. if($action == 'view') {
  1169. return $this->getOutput($action);
  1170. }
  1171.  
  1172. //Go through all modules.
  1173. foreach($this->modules as $module) {
  1174. if($module->handlesAction($action)) {
  1175. $output = $module->getOutput($action);
  1176. //If the module produced output then show it, otherwise display
  1177. //the default action.
  1178. if($output != '') {
  1179. return $output;
  1180. } else {
  1181. $_REQUEST['action'] = $this->defaultAction;
  1182. return $this->getPanel();
  1183. }
  1184. }
  1185. }
  1186.  
  1187. return 'No module was found for the specified action.';
  1188. }
  1189.  
  1190. /**
  1191. * @see AdminModule.getOutput($action)
  1192. */
  1193. public function getOutput($action) {
  1194. if($action != 'view') {
  1195. return '';
  1196. }
  1197.  
  1198. global $db;
  1199. $displayer = $this->displayer;
  1200. $menuBar = $displayer->getPageMenuBar();
  1201. $output = $menuBar;
  1202. $fieldNames = array();
  1203. foreach($this->fields as $field) {
  1204. $fieldNames[] = $field->getTableField();
  1205. }
  1206.  
  1207. //Check what modules that want to be added where.
  1208. $entryLinkPatterns = array();
  1209. foreach($this->modules as $module) {
  1210. if($module->getEntryLinkPattern() != '') {
  1211. //Add to the per entry list.
  1212. $entryLinkPatterns[] = $module->getEntryLinkPattern();
  1213. }
  1214. if($module->getGlobalLinkPattern() != '') {
  1215. //Add the link directly.
  1216. $output .= ' '.$module->getGlobalLinkPattern();
  1217. }
  1218. }
  1219. $rows = $displayer->getPageRows();
  1220. if(sizeof($rows) > 0) {
  1221. //Construct the table header.
  1222. $header = new Header();
  1223. foreach($this->fields as $field) {
  1224. $header->add($field->getDisplayName());
  1225. }
  1226. if(sizeof($entryLinkPatterns) > 0) {
  1227. $header->add('Admin operations');
  1228. }
  1229. //Construct the table.
  1230. $table = new Table();
  1231. $table->setHeader($header);
  1232. foreach($rows as $row) {
  1233. //Output the designated fields.
  1234. $tableRow = array();
  1235. foreach($this->fields as $field) {
  1236. $name = $field->getTableField();
  1237. $value = $row[$name];
  1238. if(isset($this->formatCallbacks[$name])) {
  1239. $value = $this->formatCallbacks[$name]($value);
  1240. }
  1241. $tableRow[] = htmlspecialchars($value);
  1242. }
  1243. if(sizeof($entryLinkPatterns) > 0) {
  1244. //Add the modules that want to be added for each entry.
  1245. $cell = '';
  1246. foreach($entryLinkPatterns as $pattern) {
  1247. foreach($row as $name => $value) {
  1248. //Horribly inefficent method, couldn't get any other method to work though.
  1249. $pattern = str_replace("\$row['".$name."']", $row[$name], $pattern);
  1250. }
  1251. $cell .= ' '.$pattern;
  1252. }
  1253. $tableRow[] = $cell;
  1254. }
  1255. $table->addRow($tableRow);
  1256. }
  1257. return $output.$table->getTable().$menuBar;
  1258. } else {
  1259. return $output.'No entries found.';
  1260. }
  1261. }
  1262. }
  1263.  
  1264. ?>

Documentation generated on Sun, 16 Apr 2006 21:03:07 +0200 by phpDocumentor 1.3.0RC4