Source for file maintainer-defs.php

Documentation is available at maintainer-defs.php

  1. <?php
  2. /* ******************************************************************** */
  3. /* CATALYST PHP Source Code */
  4. /* -------------------------------------------------------------------- */
  5. /* This program is free software; you can redistribute it and/or modify */
  6. /* it under the terms of the GNU General Public License as published by */
  7. /* the Free Software Foundation; either version 2 of the License, or */
  8. /* (at your option) any later version. */
  9. /* */
  10. /* This program is distributed in the hope that it will be useful, */
  11. /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
  12. /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
  13. /* GNU General Public License for more details. */
  14. /* */
  15. /* You should have received a copy of the GNU General Public License */
  16. /* along with this program; if not, write to: */
  17. /* The Free Software Foundation, Inc., 59 Temple Place, Suite 330, */
  18. /* Boston, MA 02111-1307 USA */
  19. /* -------------------------------------------------------------------- */
  20. /* */
  21. /* Filename: maintainer-defs.php */
  22. /* Author: Paul Waite */
  23. /* Description: Classes which allow generic table maintenance UIs */
  24. /* to be built. */
  25. /* */
  26. /* ******************************************************************** */
  27. /** @package database */
  28. include_once("application.php");
  29. /** Form elements */
  30. ("form-defs.php");
  31. /** Button widgets */
  32. ("button-defs.php");
  33. /** Record maintainer classes */
  34. ("recmaint-defs.php");
  35.  
  36. // ----------------------------------------------------------------------
  37. // Add cases for each database type here..
  38.  
  39. if (isset($RESPONSE)) {
  40. switch ($RESPONSE->datasource->dbtype()) {
  41. case "postgres":
  42. include_once("pg-schema-defs.php");
  43. break;
  44. case "mysql":
  45. include_once("my-schema-defs.php");
  46. break;
  47. case "oracle":
  48. include_once("or-schema-defs.php");
  49. break;
  50. case "odbc":
  51. include_once("od-schema-defs.php");
  52. break;
  53. case "mssql_server":
  54. include_once("ss-schema-defs.php");
  55. break;
  56. default:
  57. include_once("pg-schema-defs.php");
  58. } // switch
  59. }
  60. else {
  61. include_once("pg-schema-defs.php");
  62. }
  63.  
  64. // Standard field widths
  65. $fullwidth = 600;
  66. $mostwidth = ceil($fullwidth * 0.67);
  67. $halfwidth = ceil($fullwidth * 0.50);
  68. $thirdwidth = ceil($fullwidth * 0.37);
  69. $quartwidth = ceil($fullwidth * 0.25);
  70. $fifthwidth = ceil($fullwidth * 0.2);
  71.  
  72. // ----------------------------------------------------------------------
  73. /**
  74. * Class comprising functionality which allows a database table to
  75. * be maintained through a user interface which allows the usual Add,
  76. * Modify, Delete options, but which gets just about all the info it
  77. * requires from the database schema itself. A dynamic maintainer.
  78. *
  79. * Example of usage: consider a table 'foo' with an integer key field
  80. * named 'bar', which comes from a sequence. It also has a field 'desc'
  81. * of type 'text', and a foreign key field 'user_id' of type 'text'
  82. * which refers to 'uuser.user_id'. For the sake of demonstration it
  83. * also has a field 'auth_code' which we only ever want to view, a
  84. * field called 'special' which we always want hidden, and a field
  85. * called 'blurb' which is a memofield of specific sizing.
  86. *
  87. * To maintain 'foo' you might then proceed as follows. Note that a lot
  88. * of methods have been used here for illustration, but in fact you
  89. * might easily use a lot less in real life.
  90. *
  91. * $maint = new maintainer("Foo Maintenance", "foo");
  92. * $maint->set_title("Setup Users");
  93. * $maint->set_fieldsequence("bar", "seq_bar_id");
  94. * $maint->set_labelfields("uuser", "full_name");
  95. * $maint->set_nonblankfields("full_name,user_type,email");
  96. * $maint->set_hiddenfields("special");
  97. * $maint->set_viewonlyfields("auth_code");
  98. * $maint->set_fieldlabel("auth_code", "Authorization code");
  99. * $maint->set_fieldsize("blurb", 300, 250);
  100. * $maint->set_datetimeformat("last_login", "M j H:i");
  101. * $maint->view_primary_keys();
  102. * $maint->view_record_filter();
  103. * ...
  104. * $RESPONSE->plugin("MAIN_CONTENT", $maint->render());
  105. * @package database
  106. */
  107. class maintainer extends HTMLObject {
  108. // Public
  109. /** The name of the database containing the table */
  110.  
  111. var $database = "";
  112. /** Table requiring maintenance (object) */
  113.  
  114. var $table;
  115. /** Name of form we will be using */
  116.  
  117. var $formname = "";
  118. /** Type of form we will be using: either "form" or "subform" */
  119.  
  120. var $formtype = "form";
  121.  
  122. // Private
  123. /** Database schema
  124. @access private */
  125. var $schema;
  126. /** If true, password field content is displayed
  127. @access private */
  128. var $view_passwords = false;
  129. /** If true, password field content is encrypted
  130. @access private */
  131. var $passwd_encryption = false;
  132. /** If true, primary keys are displayed
  133. @access private */
  134. var $view_pks = false;
  135. /** If true, status bar is displayed
  136. @access private */
  137. var $show_statusbar = true;
  138. /** True if record is valid
  139. @access private */
  140. var $recvalid = false;
  141. /** Current record/row
  142. @access private */
  143. var $current_row;
  144. /** Row count - total records in table
  145. @access private */
  146. var $rowcount = 0;
  147. /** Title of this maintenance page
  148. @access private */
  149. var $title = "";
  150. /** If true we auto-detect sequences for integer fields,
  151. named 'seq_{fieldname}'
  152. @access private */
  153. var $do_autosequence = true;
  154. /** If true we include a built-in record filter
  155. @access private */
  156. var $show_recfilter = false;
  157. /** Array of joined tables. Tables with a 1-to-1 link.
  158. @access private */
  159. var $joined_tables = array();
  160. /** Array of linked tables. Tables forming many-to-many link.
  161. @access private */
  162. var $linked_tables = array();
  163. /** Array of detail tables. Master-detail relationship.
  164. @access private */
  165. var $detail_tables = array();
  166. /** Array of disallowed button names eg:
  167. 'save', 'reset', 'add', 'remove', 'cancel', 'refresh'
  168. @access private */
  169. /** Array of messages to display at the top of the maintainer.
  170. Useful for per-field validation failed messages.
  171. @access private */
  172. var $msgs = array();
  173. var $hidden_buttons = array();
  174. /** True if maintainer has been activated
  175. @access private */
  176. var $activated = false;
  177. /** Maintainers form encoding type
  178. @access private */
  179. var $enctype = "";
  180. /** True if this maintainer is good to go
  181. @access private */
  182. var $valid = false;
  183.  
  184. // ....................................................................
  185. /**
  186. * Create a new maintainer.
  187. * @param string $title Title to display at top of this maintainer
  188. * @param string $tablename Name of main table to maintain
  189. * @param string $dbname Name of database table is to be found in
  190. */
  191. function maintainer($title, $tablename, $dbname="") {
  192. global $RESPONSE;
  193. if (isset($RESPONSE)) {
  194. if ($dbname == "") {
  195. $dbname = $RESPONSE->datasource->db_name_selected;
  196. }
  197. else {
  198. $RESPONSE->select_database($dbname);
  199. }
  200. }
  201. if ($title == "") {
  202. $title = ucwords(str_replace("_", " ", $this->tablename)) . " Maintenance";
  203. }
  204. $this->set_title($title);
  205. $this->tablename = $tablename;
  206. $this->database = $dbname;
  207.  
  208. if ($this->database != "") {
  209. $this->schema = new DB_schema($this->database);
  210. $this->schema->getsequences();
  211. $this->schema->getschema_table($this->tablename);
  212. $table = $this->schema->gettable($this->tablename);
  213. if (is_object($table)) {
  214. $this->table = $table;
  215. $this->valid = true;
  216. $this->formname = $this->tablename . "_fm";
  217. // Get all FK tables..
  218. foreach ($this->table->constraints as $con) {
  219. if ($con->type == "f") {
  220. $this->schema->getschema_table($con->fk_tablename);
  221. }
  222. } // foreach
  223. }
  224. }
  225. } // maintainer
  226. // ....................................................................
  227. /**
  228. * Activate the maintainer. This is not done in the constructor so
  229. * that the various maintainer setups can be called prior to doing
  230. * this POSTprocess and record manipulation etc. You can either call
  231. * this method yourself, or let the call to the render() method do it
  232. * for you.
  233. * @access private
  234. */
  235. function activate() {
  236. global $RESPONSE, $mode;
  237. global $recfilter_field, $recfilter_opr, $recfilter_val;
  238.  
  239. // initialise mode..
  240. $this->mode = $mode;
  241.  
  242. // Detect presence of field sequences in schema..
  243. if ($this->do_autosequence) {
  244. $this->autosequence();
  245. }
  246.  
  247. // First process any joined tables..
  248. if (count($this->joined_tables) > 0
  249. && ($mode == "add" || $mode == "remove")) {
  250. $this->activate_joins();
  251. }
  252.  
  253. // Process POST action..
  254. $this->POSTprocess();
  255.  
  256. debugbr("After POSTprocess mode is $this->mode", DBG_DEBUG);
  257.  
  258. // Get current record, if any..
  259. if ($this->mode != "add"
  260. && $this->mode != "adding"
  261. && $this->mode != "filter") {
  262. $keyfields = $this->keyfieldnames();
  263.  
  264. $Qrow = new dbselect($this->tablename);
  265. $Qrow->fieldlist("*");
  266. $wheres = array();
  267. $invalid = false;
  268. foreach ($keyfields as $fieldname) {
  269. $field = $this->table->fields[$fieldname];
  270. $postedvar = "recmaint_$fieldname";
  271. global $$postedvar;
  272. if (isset($$postedvar)) {
  273. switch ($field->generic_type()) {
  274. case "numeric":
  275. if ($$postedvar != "") {
  276. $wheres[] = "$fieldname=" . $$postedvar;
  277. }
  278. else {
  279. $invalid = true;
  280. }
  281. break;
  282. default:
  283. $wheres[] = "$fieldname='" . $$postedvar . "'";
  284. } // switch
  285. }
  286. else {
  287. $invalid = true;
  288. }
  289. }
  290. if (!$invalid && count($wheres) > 0) {
  291. $Qrow->where( implode(" AND ", $wheres) );
  292. $Qrow->execute();
  293. if ($Qrow->hasdata) {
  294. foreach ($this->table->fields as $field) {
  295. if (isset($Qrow->current_row[$field->name])) {
  296. switch ($field->generic_type()) {
  297. case "logical":
  298. $this->current_row[$field->name] = $Qrow->istrue($field->name);
  299. break;
  300. case "date":
  301. $dtfmt = (isset($field->datetimeformat) ? $field->datetimeformat : DISPLAY_DATE_ONLY);
  302. $dtval = datetime_to_displaydate($dtfmt, $Qrow->field($field->name));
  303. $this->current_row[$field->name] = $dtval;
  304. break;
  305. case "datetime":
  306. $dtfmt = (isset($field->datetimeformat) ? $field->datetimeformat : DISPLAY_TIMESTAMP_FORMAT);
  307. $dtval = datetime_to_displaydate($dtfmt, $Qrow->field($field->name));
  308. $this->current_row[$field->name] = $dtval;
  309. break;
  310. default:
  311. $this->current_row[$field->name] = $Qrow->field($field->name);
  312. } // switch
  313. }
  314. } // foreach
  315. $this->recvalid = true;
  316. }
  317. }
  318. // Get record count if required..
  319. if ($this->show_statusbar) {
  320. $q = "SELECT COUNT(*) as tot FROM $this->tablename";
  321. if (isset($recfilter_field) && $recfilter_field != "") {
  322. $q .= " WHERE $recfilter_field $recfilter_opr ";
  323. $Ffield = $this->table->fields[$recfilter_field];
  324. switch ($Ffield->generic_type) {
  325. case "numeric":
  326. $q .= $recfilter_val;
  327. break;
  328. case "logical":
  329. $recfilter_val = strtolower($recfilter_val);
  330. if ($recfilter_val == "t" || $recfilter_val == "1" || $recfilter_val == "true") {
  331. $q .= $RESPONSE->datasource->db_value_from_bool(true);
  332. }
  333. else {
  334. $q .= $RESPONSE->datasource->db_value_from_bool(false);
  335. }
  336. break;
  337. default:
  338. $q .= "'$recfilter_val'";
  339. } // switch
  340. }
  341. $rcQ = dbrecordset($q);
  342. if ($rcQ->hasdata) {
  343. $this->rowcount = $rcQ->field("tot");
  344. }
  345. }
  346. }
  347.  
  348. // Activate any joined tables too..
  349. if (count($this->joined_tables) > 0
  350. && $mode != "add"
  351. && $mode != "remove") {
  352. $this->activate_joins();
  353. }
  354.  
  355. // POST processing for any detail tables..
  356. if (count($this->detail_tables) > 0) {
  357. $keyvals = $this->get_keyvalues();
  358. foreach ($this->detail_tables as $tablename => $mastdet) {
  359. $mastdet->POSTprocess($this->formname, $keyvals);
  360. }
  361. }
  362.  
  363. // Filtering mode refresh requires clean slate..
  364. if ($this->mode == "filter") {
  365. $this->recvalid = false;
  366. $this->mode = "edit";
  367. }
  368. elseif ($this->mode == "adding") {
  369. $this->mode = "add";
  370. }
  371.  
  372. // Flag it as done..
  373. $this->activated = true;
  374.  
  375. } // activate
  376. // ....................................................................
  377. /** Activate joined tables
  378. * @access private
  379. */
  380. function activate_joins() {
  381. // Activate any joined tables too..
  382. if (count($this->joined_tables) > 0) {
  383. foreach ($this->joined_tables as $tablename => $Jmaint) {
  384. $Jmaint->recvalid = $this->recvalid;
  385. if ($this->recvalid) {
  386. foreach ($Jmaint->joinfields as $join) {
  387. $bits = explode("=", $join);
  388. $masterf = $bits[0];
  389. if (isset($bits[1])) $joinf= $bits[1];
  390. else $joinf = $masterf;
  391. $join_postedvar = "recmaint_" . $joinf;
  392. global $$join_postedvar;
  393. $$join_postedvar = $this->current_row[$masterf];
  394. }
  395. }
  396. // Activate joined table
  397. $Jmaint->activate();
  398. $this->joined_tables[$tablename] = $Jmaint;
  399. }
  400. }
  401. } // activate_joins
  402. // ....................................................................
  403. /**
  404. * Set the name of the form we should use. Sometimes this is useful
  405. * when other entities are using the same form.
  406. * @param string $formname The name of the maintainer form to use
  407. * @param string $type Form type: either "form" or "subform"
  408. */
  409. function set_formname($formname, $type="form") {
  410. if (trim($formname) != "" ) {
  411. $this->formname = trim($formname);
  412. }
  413. $this->formtype = $type;
  414. } // set_formname
  415. // ....................................................................
  416. /**
  417. * Specify the maintainers form encoding type. This will enable us to use
  418. * file upload fields within the maintainer.
  419. * @param string $enctype the encoding type the form is to use.
  420. * leave blank for stand form encoding.
  421. */
  422. function set_formenctype($enctype="") {
  423. if (trim($enctype) != "" ) {
  424. $this->enctype = trim($enctype);
  425. }
  426. } // set_formenctype
  427. // ....................................................................
  428. /**
  429. * Specify that the given field should be non-blank. This causes a check
  430. * to be made on form submit and if any field is empty (nullstring) then a
  431. * warning message is displayed and submit is prevented.
  432. * @param string $fieldnames Comma-delimited list of non-blank field names
  433. */
  434. function set_nonblankfields($fieldnames) {
  435. if (!is_array($fieldnames)) {
  436. $fieldnames = explode(",", $fieldnames);
  437. }
  438. foreach ($fieldnames as $fname) {
  439. if (isset($this->table->fields[$fname])) {
  440. $field = $this->table->fields[$fname];
  441. $field->nonblank = true;
  442. $this->table->fields[$fname] = $field;
  443. }
  444. }
  445. if (count($this->joined_tables) > 0) {
  446. foreach ($this->joined_tables as $tablename => $Jmaint) {
  447. $Jmaint->set_nonblankfields($fieldnames);
  448. $this->joined_tables[$tablename] = $Jmaint;
  449. }
  450. }
  451. if (count($this->detail_tables) > 0) {
  452. foreach ($this->detail_tables as $tablename => $mastdet) {
  453. $mastdet->DetailMaint->set_nonblankfields($fieldnames);
  454. $this->detail_tables[$tablename] = $mastdet;
  455. }
  456. }
  457. } // set_nonblankfields
  458. // ....................................................................
  459. /**
  460. * Specify that the given buttons should be hidden. BY default all the
  461. * usual buttons are available. This method allows you to list those
  462. * which should NOT be shown. Possible button names are:
  463. * 'save', 'reset', 'add', 'remove', 'cancel', 'refresh'.
  464. * @param mixed $buttonnames Array or delimited list of button names to hide
  465. * @param string $delim Delimiter - defaulted to ','
  466. */
  467. function set_hiddenbuttons($buttonnames, $delim=",") {
  468. if (!is_array($buttonnames)) {
  469. $buttonnames = explode($delim, $buttonnames);
  470. }
  471. $this->hidden_buttons = $buttonnames;
  472. if (count($this->detail_tables) > 0) {
  473. foreach ($this->detail_tables as $tablename => $mastdet) {
  474. $mastdet->DetailMaint->set_hiddenbuttons($buttonnames);
  475. $this->detail_tables[$tablename] = $mastdet;
  476. }
  477. }
  478. } // set_hiddenbuttons
  479. // ....................................................................
  480. /**
  481. * Specify that the given fields should be hidden, not editable. Value
  482. * will be submitted on POST (save) via hidden field in form.
  483. * @param string $fieldnames Comma-delimited list of field names to hide
  484. * @param string $delim Delimiter - defaulted to ','
  485. */
  486. function set_hiddenfields($fieldnames, $delim=",") {
  487. $this->set_disposition($fieldnames, "hidden", $delim);
  488. } // set_hiddenfields
  489. // ....................................................................
  490. /**
  491. * Specify that the given fields should be disabled, not editable. Field
  492. * is seen on screen, but is not modifiable.
  493. * @param string $fieldnames Comma-delimited list of field names to disable
  494. * @param string $delim Delimiter - defaulted to ','
  495. */
  496. function set_disabledfields($fieldnames, $delim=",") {
  497. $this->set_disposition($fieldnames, "disabled", $delim);
  498. } // set_disabledfields
  499. // ....................................................................
  500. /**
  501. * Specify that the given field should be omitted from the form
  502. * @param string $fieldnames Comma-delimited list of field names to omit
  503. * @param string $delim Delimiter - defaulted to ','
  504. */
  505. function set_omittedfields($fieldnames, $delim=",") {
  506. $this->set_disposition($fieldnames, "omitted", $delim);
  507. } // set_omittedfields
  508. // ....................................................................
  509. /**
  510. * Specify that the given field should be displayed on the form as text
  511. * (view-only) but will not be submitted with the form.
  512. * @param string $fieldnames Comma-delimited list of field names to view-only
  513. * @param string $delim Delimiter - defaulted to ','
  514. */
  515. function set_viewonlyfields($fieldnames, $delim=",") {
  516. $this->set_disposition($fieldnames, "viewonly", $delim);
  517. } // set_viewonlyfields
  518. // ....................................................................
  519. /**
  520. * Set the field disposition. This is an umbrella property of a field
  521. * which controls how it gets displayed (or not). Internal method.
  522. * @param string $fieldname Name of field to set disposition on
  523. * @param string $disposition Disposition of this field
  524. * @param string $delim Delimiter - defaulted to ','
  525. * @access private
  526. */
  527. function set_disposition($fieldnames, $disposition, $delim=",") {
  528. $fnames = explode($delim, $fieldnames);
  529. foreach ($fnames as $fname) {
  530. if (isset($this->table->fields[$fname])) {
  531. $field = $this->table->fields[$fname];
  532. $field->disposition = $disposition;
  533. $this->table->fields[$fname] = $field;
  534. }
  535. }
  536. if (count($this->joined_tables) > 0) {
  537. foreach ($this->joined_tables as $tablename => $Jmaint) {
  538. $Jmaint->set_disposition($fieldnames, $disposition, $delim);
  539. $this->joined_tables[$tablename] = $Jmaint;
  540. }
  541. }
  542. if (count($this->detail_tables) > 0) {
  543. foreach ($this->detail_tables as $tablename => $mastdet) {
  544. $mastdet->DetailMaint->set_disposition($fieldnames, $disposition, $delim);
  545. $this->detail_tables[$tablename] = $mastdet;
  546. }
  547. }
  548. } // set_disposition
  549. // ....................................................................
  550. /**
  551. * Use given user interface element for maintaining specified table field.
  552. * @param string $fieldname Name of field to use form field for
  553. * @param object $element Form user interface element to use
  554. */
  555. function set_formfieldwidget($fieldname, $element) {
  556. if (isset($this->table->fields[$fieldname])) {
  557. $field = $this->table->fields[$fieldname];
  558. $field->UIelement = $element;
  559. $this->table->fields[$fieldname] = $field;
  560. }
  561. if (count($this->joined_tables) > 0) {
  562. foreach ($this->joined_tables as $tablename => $Jmaint) {
  563. $Jmaint->set_formfieldwidget($fieldname, $element);
  564. $this->joined_tables[$tablename] = $Jmaint;
  565. }
  566. }
  567. if (count($this->detail_tables) > 0) {
  568. foreach ($this->detail_tables as $tablename => $mastdet) {
  569. $mastdet->DetailMaint->set_formfieldwidget($fieldname, $element);
  570. $this->detail_tables[$tablename] = $mastdet;
  571. }
  572. }
  573. } // set_formfieldwidget
  574. // ....................................................................
  575. /**
  576. * Sets the type of a text field. This is a generic type and the
  577. * possibilities are:
  578. * 'text' Standard text field
  579. * 'password' Rendered as a password field, and a confirm field
  580. * 'memo' Standard textarea widget
  581. * 'image' Text field which contains an image which is displayed
  582. * @param string $fieldname Name of field to set size of.
  583. * @param string $type Generic display type of the text field
  584. */
  585. function set_fieldtexttype($fieldname, $fieldtype) {
  586. if (isset($this->table->fields[$fieldname])) {
  587. $field = $this->table->fields[$fieldname];
  588. $field->fieldtype = $fieldtype;
  589. $this->table->fields[$fieldname] = $field;
  590. }
  591. if (count($this->joined_tables) > 0) {
  592. foreach ($this->joined_tables as $tablename => $Jmaint) {
  593. $Jmaint->set_fieldtexttype($fieldname, $fieldtype);
  594. $this->joined_tables[$tablename] = $Jmaint;
  595. }
  596. }
  597. if (count($this->detail_tables) > 0) {
  598. foreach ($this->detail_tables as $tablename => $mastdet) {
  599. $mastdet->DetailMaint->set_fieldtexttype($fieldname, $fieldtype);
  600. $this->detail_tables[$tablename] = $mastdet;
  601. }
  602. }
  603. } // set_fieldtexttype
  604. // ....................................................................
  605. /**
  606. * Sets the CSS style/class for a field.
  607. * @param string $fieldname Name of field to apply style/class to.
  608. * @param string $css Style setting, or CSS classname
  609. */
  610. function set_fieldcss($fieldname, $css) {
  611. if (isset($this->table->fields[$fieldname])) {
  612. $field = $this->table->fields[$fieldname];
  613. $field->css = $css;
  614. $this->table->fields[$fieldname] = $field;
  615. }
  616. if (count($this->joined_tables) > 0) {
  617. foreach ($this->joined_tables as $tablename => $Jmaint) {
  618. $Jmaint->set_fieldcss($fieldname, $css);
  619. $this->joined_tables[$tablename] = $Jmaint;
  620. }
  621. }
  622. if (count($this->detail_tables) > 0) {
  623. foreach ($this->detail_tables as $tablename => $mastdet) {
  624. $mastdet->DetailMaint->set_fieldcss($fieldname, $css);
  625. $this->detail_tables[$tablename] = $mastdet;
  626. }
  627. }
  628. } // set_fieldcss
  629. // ....................................................................
  630. /**
  631. * Sets the size of the field in pixels, width x height
  632. * @param string $fieldname Name of field to set size of.
  633. * @param string $pxwidth Width of field in pixels
  634. * @param string $pxheight Height of field in pixels
  635. */
  636. function set_fieldsize($fieldname, $pxwidth, $pxheight=0) {
  637. if (isset($this->table->fields[$fieldname])) {
  638. $field = $this->table->fields[$fieldname];
  639. if ($pxwidth > 0) $field->pxwidth = $pxwidth;
  640. if ($pxheight > 0) $field->pxheight = $pxheight;
  641. $this->table->fields[$fieldname] = $field;
  642. }
  643. if (count($this->joined_tables) > 0) {
  644. foreach ($this->joined_tables as $tablename => $Jmaint) {
  645. $Jmaint->set_fieldsize($fieldname, $pxwidth, $pxheight);
  646. $this->joined_tables[$tablename] = $Jmaint;
  647. }
  648. }
  649. if (count($this->detail_tables) > 0) {
  650. foreach ($this->detail_tables as $tablename => $mastdet) {
  651. $mastdet->DetailMaint->set_fieldsize($fieldname, $pxwidth, $pxheight);
  652. $this->detail_tables[$tablename] = $mastdet;
  653. }
  654. }
  655. } // set_fieldsize
  656. // ....................................................................
  657. /**
  658. * Sets the label of the field, which then takes the place of the
  659. * default naming which uses a proper-cased version of the field
  660. * name, with underscores replaced by spaces.
  661. * @param string $fieldname Name of field to set size of.
  662. * @param string $label Field label string to use.
  663. */
  664. function set_fieldlabel($fieldname, $label) {
  665. if (isset($this->table->fields[$fieldname])) {
  666. $field = $this->table->fields[$fieldname];
  667. $field->label = $label;
  668. $this->table->fields[$fieldname] = $field;
  669. }
  670. if (count($this->joined_tables) > 0) {
  671. foreach ($this->joined_tables as $tablename => $Jmaint) {
  672. $Jmaint->set_fieldlabel($fieldname, $label);
  673. $this->joined_tables[$tablename] = $Jmaint;
  674. }
  675. }
  676. if (count($this->detail_tables) > 0) {
  677. foreach ($this->detail_tables as $tablename => $mastdet) {
  678. $mastdet->DetailMaint->set_fieldlabel($fieldname, $label);
  679. $this->detail_tables[$tablename] = $mastdet;
  680. }
  681. }
  682. } // set_fieldlabel
  683. // ....................................................................
  684. /**
  685. * Associates a named sequence with a field. This is so we can create
  686. * new records using that sequence to populate the record field.
  687. * Notes: the maintainer will, as default, try to detect sequences for
  688. * integer fields. @see disable_autosequence method.
  689. * @param string $fieldname Name of field to link sequence to.
  690. * @param string $sequencename Name of sequence to link to this field.
  691. */
  692. function set_fieldsequence($fieldname, $sequencename) {
  693. if (isset($this->table->fields[$fieldname])) {
  694. $field = $this->table->fields[$fieldname];
  695. $field->sequencename = $sequencename;
  696. $this->table->fields[$fieldname] = $field;
  697. }
  698. if (count($this->joined_tables) > 0) {
  699. foreach ($this->joined_tables as $tablename => $Jmaint) {
  700. $Jmaint->set_fieldsequence($fieldname, $sequencename);
  701. $this->joined_tables[$tablename] = $Jmaint;
  702. }
  703. }
  704. if (count($this->detail_tables) > 0) {
  705. foreach ($this->detail_tables as $tablename => $mastdet) {
  706. $mastdet->DetailMaint->set_fieldsequence($fieldname, $sequencename);
  707. $this->detail_tables[$tablename] = $mastdet;
  708. }
  709. }
  710. } // set_fieldsequence
  711. // ....................................................................
  712. /**
  713. * Associates a function with the field which will be called when
  714. * data is POSTed to format the content. Only really useful for
  715. * text/memo/numeric fields. The function should accept a string
  716. * content parameter, and return the re-formatted string content.
  717. * @param string $fieldname Name of field to link sequence to.
  718. * @param string $funcname Name of function to re-format content
  719. */
  720. function set_fieldpostproc($fieldname, $funcname) {
  721. if (function_exists($funcname)) {
  722. if (isset($this->table->fields[$fieldname])) {
  723. $field = $this->table->fields[$fieldname];
  724. $field->postproc = $funcname;
  725. $this->table->fields[$fieldname] = $field;
  726. }
  727. if (count($this->joined_tables) > 0) {
  728. foreach ($this->joined_tables as $tablename => $Jmaint) {
  729. $Jmaint->set_fieldpostproc($fieldname, $funcname);
  730. $this->joined_tables[$tablename] = $Jmaint;
  731. }
  732. }
  733. if (count($this->detail_tables) > 0) {
  734. foreach ($this->detail_tables as $tablename => $mastdet) {
  735. $mastdet->DetailMaint->set_fieldpostproc($fieldname, $funcname);
  736. $this->detail_tables[$tablename] = $mastdet;
  737. }
  738. }
  739. }
  740. } // set_fieldpostproc
  741. // ....................................................................
  742. /**
  743. * Associates a function with the field which will be called when
  744. * data is displayed to format the content. Only really useful for
  745. * text/memo/numeric fields. The function should accept a string
  746. * content parameter, and return the re-formatted string content.
  747. * @param string $fieldname Name of field to link sequence to.
  748. * @param string $funcname Name of function to re-format content
  749. */
  750. function set_fielddisplayproc($fieldname, $funcname) {
  751. if (function_exists($funcname)) {
  752. if (isset($this->table->fields[$fieldname])) {
  753. $field = $this->table->fields[$fieldname];
  754. $field->displayproc = $funcname;
  755. $this->table->fields[$fieldname] = $field;
  756. }
  757. if (count($this->joined_tables) > 0) {
  758. foreach ($this->joined_tables as $tablename => $Jmaint) {
  759. $Jmaint->set_fielddisplayproc($fieldname, $funcname);
  760. $this->joined_tables[$tablename] = $Jmaint;
  761. }
  762. }
  763. if (count($this->detail_tables) > 0) {
  764. foreach ($this->detail_tables as $tablename => $mastdet) {
  765. $mastdet->DetailMaint->set_fielddisplayproc($fieldname, $funcname);
  766. $this->detail_tables[$tablename] = $mastdet;
  767. }
  768. }
  769. }
  770. } // set_fielddisplayproc
  771. // ....................................................................
  772. /**
  773. * Associates a function with the field which will be called when data
  774. * is POSTed to validate the content. The function should accept a string
  775. * content parameter, and return true (if valid) or false.
  776. * @param string $fieldname Name of field to link sequence to.
  777. * @param string $funcname Name of function to re-format content
  778. * @param string $failmsg Message to display if validation fails
  779. */
  780. function set_fieldvalidationproc($fieldname, $funcname, $failmsg) {
  781. if (function_exists($funcname)) {
  782. if (isset($this->table->fields[$fieldname])) {
  783. $field = $this->table->fields[$fieldname];
  784. $field->validationproc = $funcname;
  785. $field->validationfailmsg = $failmsg;
  786. $this->table->fields[$fieldname] = $field;
  787. }
  788. if (count($this->joined_tables) > 0) {
  789. foreach ($this->joined_tables as $tablename => $Jmaint) {
  790. $Jmaint->set_fieldvalidationproc($fieldname, $funcname, $failmsg);
  791. $this->joined_tables[$tablename] = $Jmaint;
  792. }
  793. }
  794. if (count($this->detail_tables) > 0) {
  795. foreach ($this->detail_tables as $tablename => $mastdet) {
  796. $mastdet->DetailMaint->set_fieldvalidationproc($fieldname, $funcname, $failmsg);
  797. $this->detail_tables[$tablename] = $mastdet;
  798. }
  799. }
  800. }
  801. } // set_fieldvalidationproc
  802. // ....................................................................
  803. /**
  804. * Associates a string of text 'blurb' with the field. This will
  805. * be presented just sitting below the field as explanatory text.
  806. * @param string $fieldname Name of field to link sequence to.
  807. * @param string $blurb Text string of info/blurb for this field
  808. */
  809. function set_fieldblurb($fieldname, $blurb) {
  810. if ($blurb != "") {
  811. if (isset($this->table->fields[$fieldname])) {
  812. $field = $this->table->fields[$fieldname];
  813. $field->blurb = $blurb;
  814. $this->table->fields[$fieldname] = $field;
  815. }
  816. if (count($this->joined_tables) > 0) {
  817. foreach ($this->joined_tables as $tablename => $Jmaint) {
  818. $Jmaint->set_fieldblurb($fieldname, $blurb);
  819. $this->joined_tables[$tablename] = $Jmaint;
  820. }
  821. }
  822. if (count($this->detail_tables) > 0) {
  823. foreach ($this->detail_tables as $tablename => $mastdet) {
  824. $mastdet->DetailMaint->set_fieldblurb($fieldname, $blurb);
  825. $this->detail_tables[$tablename] = $mastdet;
  826. }
  827. }
  828. }
  829. } // set_fieldblurb
  830. // ....................................................................
  831. /**
  832. * Associates a list of fieldnames on a table to use as the label
  833. * for a drop-down select reference. This is mainly so you can specify
  834. * meaningful label strings for drop-down selects on foreign keyed
  835. * fields, although it will work on any table, not just FKs.
  836. * Note: The list of field names should be comma-delimited.
  837. * @param string $tablename Name of foreign key table
  838. * @param string $labelfields Names of fields on this table for label
  839. */
  840. function set_labelfields($tablename, $labelfields) {
  841. if (!is_array($labelfields)) {
  842. $labelfields = explode(",", $labelfields);
  843. }
  844. $table = $this->schema->gettable($tablename);
  845. $table->labelfields = $labelfields;
  846. $this->schema->addtable($table);
  847. if (count($this->joined_tables) > 0) {
  848. foreach ($this->joined_tables as $jtablename => $Jmaint) {
  849. if ($jtablename == $tablename) {
  850. $Jmaint->set_labelfields($tablename, $labelfields);
  851. $this->joined_tables[$jtablename] = $Jmaint;
  852. }
  853. }
  854. }
  855. if (count($this->detail_tables) > 0) {
  856. foreach ($this->detail_tables as $dtablename => $mastdet) {
  857. if ($dtablename == $tablename) {
  858. $mastdet->detailtable->labelfields = $labelfields;
  859. $this->detail_tables[$dtablename] = $mastdet;
  860. }
  861. }
  862. }
  863. } // set_labelfields
  864. // ....................................................................
  865. /**
  866. * Associates a list of fieldnames on a table to use as the ordering
  867. * for a drop-down select reference. Note: The list of field names should
  868. * be comma-delimited.
  869. * @param string $tablename Name of foreign key table
  870. * @param string $labelfields Names of fields on this table for ordering
  871. */
  872. function set_orderfields($tablename, $orderfields) {
  873. if (!is_array($orderfields)) {
  874. $orderfields = explode(",", $orderfields);
  875. }
  876. $table = $this->schema->gettable($tablename);
  877. $table->orderfields = $orderfields;
  878. $this->schema->addtable($table);
  879. if (count($this->joined_tables) > 0) {
  880. foreach ($this->joined_tables as $jtablename => $Jmaint) {
  881. if ($jtablename == $tablename) {
  882. $Jmaint->set_orderfields($tablename, $orderfields);
  883. $this->joined_tables[$jtablename] = $Jmaint;
  884. }
  885. }
  886. }
  887. if (count($this->detail_tables) > 0) {
  888. foreach ($this->detail_tables as $dtablename => $mastdet) {
  889. if ($dtablename == $tablename) {
  890. $mastdet->detailtable->orderfields = $orderfields;
  891. $this->detail_tables[$dtablename] = $mastdet;
  892. }
  893. }
  894. }
  895. } // set_orderfields
  896. // ....................................................................
  897. /**
  898. * Sets a datetime format string for a specified field. This influences
  899. * the formatting of displayed dates and/or times in that field.
  900. * @param string $fieldname Name of field to link sequence to.
  901. * @param string $format Datetime format string eg: "d/m/Y H:i:s"
  902. */
  903. function set_datetimeformat($fieldname, $format) {
  904. if (isset($this->table->fields[$fieldname])) {
  905. $field = $this->table->fields[$fieldname];
  906. $field->datetimeformat = $format;
  907. $this->table->fields[$fieldname] = $field;
  908. }
  909. if (count($this->joined_tables) > 0) {
  910. foreach ($this->joined_tables as $tablename => $Jmaint) {
  911. $Jmaint->set_datetimeformat($fieldname, $format);
  912. $this->joined_tables[$tablename] = $Jmaint;
  913. }
  914. }
  915. if (count($this->detail_tables) > 0) {
  916. foreach ($this->detail_tables as $tablename => $mastdet) {
  917. $mastdet->DetailMaint->set_datetimeformat($fieldname, $format);
  918. $this->detail_tables[$tablename] = $mastdet;
  919. }
  920. }
  921. } // set_datetimeformat
  922. // ....................................................................
  923. /**
  924. * Restrict access. Use this method to restrict maintainer access
  925. * to the specified group membership. This will cause the RESPONSE to
  926. * be sent without any content.
  927. * @param string $grouplist Comma-delimited list of user groups to allow
  928. */
  929. function set_allowed_groups($grouplist) {
  930. global $RESPONSE;
  931. if (isset($RESPONSE) && !$RESPONSE->ismemberof_group_in($grouplist)) {
  932. $RESPONSE->send();
  933. exit;
  934. }
  935. } // allowed_groups
  936. // ....................................................................
  937. /**
  938. * Set the title of this maintainer. The default is derived from the
  939. * name of the maintained table, with 'Maintenance' appended. Otherwise
  940. * set your own title using this method.
  941. * @param string $title Title of this maintainer widget
  942. */
  943. function set_title($title) {
  944. $this->title = $title;
  945. } // set_title
  946. // ....................................................................
  947. /**
  948. * Specify that the maintainer should not auto-detect sequences which
  949. * pertain to fields on the table. The default action is to look for
  950. * sequences for all integer fields. This method allows you to turn
  951. * this feature off, in case it is getting in the way. You can then
  952. * use the set_fieldsequence() method
  953. * @see set_fieldsequence()
  954. * @see autosequence()
  955. */
  956. function disable_autosequence() {
  957. $this->do_autosequence = false;
  958. } // disable_autosequence
  959. // ....................................................................
  960. /**
  961. * Auto-detect sequences for integer fields. The technique is to assume
  962. * sequences are named after the field in the form: 'seq_{fieldname}'
  963. * and if so then this sequence is associated with the given field
  964. * named {fieldname}.
  965. */
  966. function autosequence() {
  967. foreach ($this->table->fields as $field) {
  968. if ($field->is_integer_class()) {
  969. $seqname = "seq_" . $field->name;
  970. if (isset($this->schema->sequences[$seqname])) {
  971. $this->set_fieldsequence($field->name, $seqname);
  972. }
  973. }
  974. }
  975. } // autosequence
  976. // ....................................................................
  977. /**
  978. * Specify whether the maintainer should show its status bar or not.
  979. * The initial default is that it is shown.
  980. * @param boolean $mode If true then hide statusbar, else show it
  981. */
  982. function hide_statusbar($mode=true) {
  983. $this->show_statusbar = $mode;
  984. } // hide_statusbar
  985. // ....................................................................
  986. /**
  987. * Associates a table with the maintained table. This is a table with
  988. * a 1-to-1 or 1-to-many relationship with the table being maintained.
  989. * We currently support the '1-to-1' link where the joined table data
  990. * is merged into the main table. This method will therefore cause
  991. * that joined table's data to be maintained alongside the main data,
  992. * as accessed via the join fields provided. The $joinfields should
  993. * be a comma-delimited string of the following form:
  994. * 'fieldA=fieldB,fieldX=fieldY'
  995. * Where the first field is the one in the table being maintained,
  996. * and the second the equivalent in the joined table. If only one
  997. * field is supplied, it is assumed to be identically named in both.
  998. * @param string $title Title of this linkage, will be used as a heading
  999. * @param string $tablename Name of foreign key table
  1000. * @param string $joinfields Pairs of fields joining the tables
  1001. */
  1002. function joined_table($title, $tablename, $joinfields) {
  1003. if (!is_array($joinfields)) {
  1004. $joinfields = explode(",", $joinfields);
  1005. }
  1006. $Jmaint = new maintainer($title, $tablename, $this->database);
  1007. $Jmaint->joinfields = $joinfields;
  1008. $Jmaint->hide_statusbar();
  1009. $this->joined_tables[$tablename] = $Jmaint;
  1010. } // joined_table
  1011. // ....................................................................
  1012. /**
  1013. * Associates a table with the maintained table via a link-table.
  1014. * This defines the standard threesome which makes up a many-to-many
  1015. * link, and where the middle link-table consists only of the key
  1016. * fields common to both main tables. This method will cause the link
  1017. * table to be maintained via either a group of checkboxes, or a
  1018. * multiple select dropdown menu (combo box).
  1019. * NB: This mechanism assumes that the field-naming follows the
  1020. * convention whereby the link-table key is composed of keyfields which
  1021. * are named identically to the keyfields in each of the linked
  1022. * tables (the maintained one and the linked one).
  1023. * @param string $title Title of this linkage, will be used as a heading
  1024. * @param string $linked_tablename Name of linked table
  1025. * @param string $link_tablename Name of table linking the two tables
  1026. * @param string $uistyle User interface style to use: "combo" or "checkbox"
  1027. * @param integer $uiperrow Maximum number of UI entities per row
  1028. */
  1029. function linked_table($title, $linked_tablename, $link_tablename, $uistyle="combo", $uiperrow=5) {
  1030. $this->schema->getschema_table($linked_tablename);
  1031. $this->schema->getschema_table($link_tablename);
  1032. $linked_table = $this->schema->gettable($linked_tablename);
  1033. $link_table = $this->schema->gettable($link_tablename);
  1034. $m2m = new many_to_many_link(
  1035. $title,
  1036. $this->table,
  1037. $link_table,
  1038. $linked_table,
  1039. $uistyle,
  1040. $uiperrow
  1041. );
  1042. $this->linked_tables[$linked_tablename] = $m2m;
  1043. } // linked_table
  1044. // ....................................................................
  1045. /**
  1046. * Associates a detail table with the maintained table. This defines
  1047. * the standard Master->Detail relationship where there are many detail
  1048. * records for each master record. This results in a special multi-record
  1049. * widget in which the detail records for the current master record can
  1050. * be maintained.
  1051. * @param string $title Title of this relationship, can be used as a heading
  1052. * @param string $detail_tablename Name of detail table
  1053. * @param string $orderby Comma-separated detail fields to order by
  1054. * @param integer $keywidth Optional width of key listbox in px
  1055. * @param integer $keyrows Optional number of key listbox rows
  1056. */
  1057. function detail_table($title, $detail_tablename, $orderby="", $keywidth=0, $keyrows=6) {
  1058. $this->schema->getschema_table($detail_tablename);
  1059. $DetailMaint = new maintainer("", $detail_tablename, $this->database);
  1060. $DetailMaint->recvalid = true;
  1061. $mastdet = new master_detail_link(
  1062. $title,
  1063. $this->table,
  1064. $DetailMaint->table,
  1065. $orderby,
  1066. $keywidth,
  1067. $keyrows
  1068. );
  1069. $mastdet->DetailMaint = $DetailMaint;
  1070. $this->detail_tables[$detail_tablename] = $mastdet;
  1071. } // detail_table
  1072. // ....................................................................
  1073. /**
  1074. * Allows primary key values to be viewed along with other data. It is
  1075. * sometimes useful to see this info in view-only mode.
  1076. * @param boolean $mode If true then primary keys are shown, else not
  1077. */
  1078. function view_primary_keys($mode = true) {
  1079. $this->view_pks = $mode;
  1080. } // view_primary_keys
  1081. // ....................................................................
  1082. /**
  1083. * Allows content of any password fields to be shown for reference. This
  1084. * is useful to reference screens where someone might need to be able
  1085. * to read passwords from the maintenance screen. Defaults to false.
  1086. * @param boolean $mode If true then passwords are shown, else not
  1087. */
  1088. function view_passwords($mode=true) {
  1089. $this->view_passwords = $mode;
  1090. } // view_passwords
  1091. // ....................................................................
  1092. /**
  1093. * Whether passwords are encrypted or not. If true then we just apply
  1094. * the standard MD5 algorithm to the content.
  1095. * @param boolean $mode Whether to enrypt passwords or not
  1096. */
  1097. function set_passwd_encryption($mode=true) {
  1098. $this->passwd_encryption = $mode;
  1099. } // passwd_encryption
  1100. // ....................................................................
  1101. /**
  1102. * Causes the filtering widgets to be viewed or not viewed. The filter
  1103. * widgets allow users to input rudimentary filtering criteria on a
  1104. * single field which they can select, in order to filter the recordset.
  1105. * @param boolean $mode Whether to show a record filter or not
  1106. */
  1107. function view_record_filter($mode=true) {
  1108. $this->show_recfilter = $mode;
  1109. } // view_record_filter
  1110. // ....................................................................
  1111. /** Return array of keyfield names
  1112. * @access private
  1113. */
  1114. function keyfieldnames() {
  1115. return $this->table->getkeyfieldnames();
  1116. } // keyfieldnames
  1117. // ....................................................................
  1118. /** Return array of non-keyfield names
  1119. * @access private
  1120. */
  1121. function nonkeyfieldnames() {
  1122. return $this->table->getnonkeyfieldnames();
  1123. } // nonkeyfieldnames
  1124. // ....................................................................
  1125. /** Acquire the keyvalues for the current record of the maintained
  1126. * table. This is used with linked and detail tables as the anchor key.
  1127. * @return array The keyvalues which define the current maintained record
  1128. * @access private
  1129. */
  1130. function get_keyvalues() {
  1131. $keyvals = array();
  1132. if ($this->recvalid) {
  1133. foreach ($this->table->fields as $field) {
  1134. if ($field->ispkey) {
  1135. $key = "$field->name=";
  1136. switch ($field->generic_type) {
  1137. case "logical":
  1138. $key .= $RESPONSE->datasource->db_value_from_bool($this->current_row[$field->name]);
  1139. break;
  1140. case "numeric":
  1141. $key .= $this->current_row[$field->name];
  1142. break;
  1143. default:
  1144. $key .= "'" . $this->current_row[$field->name] . "'";
  1145. } // switch
  1146. $keyvals[] = $key;
  1147. }
  1148. } // foreach
  1149. }
  1150. return $keyvals;
  1151. } // get_keyvalues
  1152. // ....................................................................
  1153. /** Return a sub-form for modifying/adding record data
  1154. * @return object The sub-form object created
  1155. * @access private
  1156. */
  1157. function edit_subform(&$save_button) {
  1158. global $LIBDIR;
  1159. // Standard field widths
  1160. global $fullwidth, $mostwidth, $halfwidth;
  1161. global $thirdwidth, $quartwidth, $fifthwidth;
  1162.  
  1163. $F = new subform();
  1164. $F->inherit_attributes($this);
  1165. $F->labelcss = "axfmlbl";
  1166.  
  1167. // FILTER: Display filter widgets if required..
  1168. if ($this->show_recfilter
  1169. && $this->mode != "add"
  1170. && !in_array("refresh", $this->hiddenbuttons)) {
  1171. global $recfilter_field, $recfilter_opr, $recfilter_val;
  1172. $SELfld = new form_combofield("recfilter_field", "", $recfilter_field);
  1173. $SELfld->setclass("axcombo");
  1174. $SELfld->additem("");
  1175. foreach ($this->table->fields as $field) {
  1176. $SELfld->additem($field->name);
  1177. }
  1178. $SELopr = new form_combofield("recfilter_opr", "", $recfilter_opr);
  1179. $SELopr->setclass("axcombo");
  1180. $SELopr->additem("=", "equals");
  1181. $SELopr->additem(">", "greater than");
  1182. $SELopr->additem("<", "less than");
  1183. $SELopr->additem("<>", "not equal");
  1184. $SELopr->additem("~*", "contains");
  1185.  
  1186. $TXTval = new form_textfield("recfilter_val", "", $recfilter_val);
  1187. $TXTval->setclass("axtxtbox");
  1188. $TXTval->setstyle("width:$quartwidth". "px;");
  1189. $refbtn = new form_imagebutton(
  1190. "_refresh", "Refresh", "",
  1191. "$LIBDIR/img/_refresh.gif",
  1192. "Refresh view",
  1193. 77, 15
  1194. );
  1195. $refbtn->set_onclick("return bclick('refresh')", SCRIPT_APPEND);
  1196. $Tf = new table("filter");
  1197. $Tf->td( $SELfld->render(), "border-right:0px none;" );
  1198. $Tf->td( $SELopr->render(), "border-right:0px none;" );
  1199. $Tf->td( $TXTval->render(), "border-right:0px none;" );
  1200. $Tf->td( $refbtn->render() );
  1201. $F->add_text( $Tf->render() );
  1202. } // filter
  1203.  
  1204. // PRIMARY KEYS: Primary key(s) only when adding a record..
  1205. if ($this->mode == "add" || $this->view_pks) {
  1206. $this->insert_key_formfields($F);
  1207. }
  1208.  
  1209. // DATA FIELDS: Non-primary key fields..
  1210. $this->insert_data_formfields($F);
  1211.  
  1212. // JOINED TABLES: Joined tables sub-forms..
  1213. if (count($this->joined_tables) > 0) {
  1214. foreach ($this->joined_tables as $tablename => $Jmaint) {
  1215. $F->add_separator($Jmaint->title);
  1216. $F->add( $Jmaint->edit_subform($dummyref) );
  1217. $this->joined_tables[$tablename] = $Jmaint;
  1218. }
  1219. } // joined tables
  1220.  
  1221. if ($this->mode != "add") {
  1222. // LINKED TABLES: Linked tables content..
  1223. if (count($this->linked_tables) > 0) {
  1224. $keyvals = $this->get_keyvalues();
  1225. foreach ($this->linked_tables as $tablename => $m2m) {
  1226. $F->add_separator($m2m->title);
  1227. $UIelement = $m2m->getUIelement(
  1228. $this->table->name,
  1229. implode(",", $keyvals),
  1230. $this->recvalid
  1231. );
  1232. if (is_subclass_of($UIelement, "form_field")) {
  1233. $F->add( $UIelement );
  1234. }
  1235. else {
  1236. $F->add_text( $UIelement->render() );
  1237. }
  1238. }
  1239. } // linked tables
  1240.  
  1241. // DETAIL TABLES: Master-detail tables content..
  1242. if (count($this->detail_tables) > 0) {
  1243. $keyvals = $this->get_keyvalues();
  1244. foreach ($this->detail_tables as $tablename => $mastdet) {
  1245. $F->add_separator($mastdet->title);
  1246. $UIelement = $mastdet->getUIelement(
  1247. implode(",", $keyvals),
  1248. $this->formname,
  1249. $this->recvalid,
  1250. $save_button
  1251. );
  1252. $F->add_text( $UIelement->render() );
  1253. }
  1254. } // detail tables
  1255. }
  1256.  
  1257. // Return the form object..
  1258. return $F;
  1259. } // edit_subform
  1260. // ....................................................................
  1261. /**
  1262. * Inserts form fields for table data fields into the given form. This
  1263. * inserts form elements for data fields only - no primary key fields.
  1264. * @param object $F Reference to a form object to insert form elements into
  1265. * @param string $prefix Prefix to add to name of form element
  1266. * @param string $except List of fields to omit, comma-delimited, or array
  1267. * @access private
  1268. */
  1269. function insert_data_formfields(&$F, $prefix="recmaint_", $except="") {
  1270. global $RESPONSE, $bevent;
  1271.  
  1272. if (!is_array($except)) {
  1273. $except = explode(",", $except);
  1274. }
  1275. foreach ($this->table->fields as $field) {
  1276. if (!in_array($field->name, $except)) {
  1277. if (!isset($field->disposition)) {
  1278. $field->disposition = "normal";
  1279. }
  1280. if (!$field->ispkey) {
  1281. $UIelement = $this->get_UIelement($field->name);
  1282. if ($UIelement !== false) {
  1283. if (!$this->recvalid) {
  1284. $UIelement->disabled = true;
  1285. }
  1286. $UIelement->name = $prefix . $field->name;
  1287. if (isset($field->label)) {
  1288. $UIelement->label = $field->label;
  1289. }
  1290. else {
  1291. $UIelement->label = ucwords(str_replace("_", " ", $field->name));
  1292. }
  1293. // Set field value..
  1294. if ($this->recvalid) {
  1295. switch ($field->generic_type()) {
  1296. case "logical":
  1297. $UIelement->checked = $RESPONSE->datasource->bool_from_db_value($this->current_row[$field->name]);
  1298. break;
  1299. default:
  1300. $UIelement->setvalue($this->current_row[$field->name]);
  1301. if (isset($field->displayproc)) {
  1302. $UIelement->value = call_user_func($field->displayproc, $UIelement->value);
  1303. }
  1304. // Never display passwords back to user..
  1305. if ($UIelement->type == "password") {
  1306. $password_value = $UIelement->value;
  1307. $UIelement->value = "";
  1308. }
  1309. } // switch
  1310. }
  1311. // Display according to disposition..
  1312. switch ($field->disposition) {
  1313. case "normal":
  1314. $F->add($UIelement);
  1315. if (isset($field->blurb)) {
  1316. $F->add_annotation("<span class=axyl_note>$field->blurb</span>");
  1317. }
  1318. break;
  1319. case "hidden":
  1320. $UInew = new form_hiddenfield(
  1321. $UIelement->name,
  1322. $UIelement->value
  1323. );
  1324. $F->add($UInew);
  1325. break;
  1326. case "disabled":
  1327. $UIelement->disabled = true;
  1328. $F->add($UIelement);
  1329. if (isset($field->blurb)) {
  1330. $F->add_annotation("<span class=axyl_note>$field->blurb</span>");
  1331. }
  1332. break;
  1333. case "viewonly":
  1334. $UInew = new form_displayonlyfield(
  1335. $UIelement->name,
  1336. $UIelement->label,
  1337. $UIelement->value
  1338. );
  1339. $F->add($UInew);
  1340. if (isset($field->blurb)) {
  1341. $F->add_annotation("<span class=axyl_note>$field->blurb</span>");
  1342. }
  1343. break;
  1344. case "omitted":
  1345. break;
  1346. } // switch
  1347.  
  1348. // Deal with password field. For password fields we never provide
  1349. // the existing password in the entry & confirm fields. Instead they
  1350. // can change the password by putting a new password into the blank
  1351. // fields. The View Password option is only useful for non-encrypted
  1352. // passwords..
  1353. if ($UIelement->type == "password" && $field->disposition == "normal") {
  1354. $UIviewpass = new form_displayonlyfield(
  1355. "viewonly_" . $field->name,
  1356. "Current password",
  1357. $password_value
  1358. );
  1359. $UIviewpass->setclass("axtxtbox");
  1360. $UIelement->name = "confirm_" . $field->name;
  1361. $UIelement->label = "Confirm password";
  1362. $F->add($UIelement);
  1363. if ($this->view_passwords) {
  1364. if ($this->passwd_encryption) {
  1365. // Axyl-encrypted passwords always have 'axenc_' prefix..
  1366. if (substr($UIviewpass->value, 0, 6) == "axenc_") {
  1367. $UIviewpass->value = "(encrypted)";
  1368. }
  1369. else {
  1370. $UIviewpass->value = "(plain text - please change)";
  1371. }
  1372. }
  1373. $F->add($UIviewpass);
  1374. }
  1375. }
  1376. } // UIelement valid
  1377. }
  1378. } // except
  1379. } // foreach
  1380. } // insert_data_formfields
  1381. // ....................................................................
  1382. /**
  1383. * Inserts form fields for table key fields into the given form. This
  1384. * inserts form elements for key fields only - no data fields.
  1385. * @param object Reference to a form object to insert form elements into
  1386. * @param string Prefix to add to name of form element
  1387. * @param string $except List of fields to omit, comma-delimited, or array
  1388. * @param bool $force_edit If true force keyfields to be editable
  1389. * @access private
  1390. */
  1391. function insert_key_formfields(&$F, $prefix="recmaint_", $except="", $force_edit=false) {
  1392. if (!is_array($except)) {
  1393. $except = explode(",", $except);
  1394. }
  1395. foreach ($this->table->fields as $field) {
  1396. if (!in_array($field->name, $except)) {
  1397. if ($field->ispkey) {
  1398. $UIelement = $this->get_UIelement($field->name);
  1399. if (!$this->recvalid) {
  1400. $UIelement->disabled = true;
  1401. }
  1402. $UIelement->setvalue($this->current_row[$field->name]);
  1403. if (isset($field->label)) {
  1404. $UIelement->label = $field->label . " (k)";
  1405. }
  1406. else {
  1407. $UIelement->label = ucwords(str_replace("_", " ", $field->name) . " (k)");
  1408. }
  1409. if ($this->mode == "add" || $force_edit) {
  1410. // Skip serialised fields, add all others..
  1411. if (!$field->is_serial_class()) {
  1412. $UIelement->name = $prefix . $field->name;
  1413. if (isset($field->sequencename)) {
  1414. $UIelement->editable = false;
  1415. }
  1416. $F->add($UIelement);
  1417. if (isset($field->blurb)) {
  1418. $F->add_annotation("<span class=axyl_note>$field->blurb</span>");
  1419. }
  1420. }
  1421. }
  1422. else {
  1423. $UIelement->name = "viewonly_" . $field->name;
  1424. $UIelement->disabled = true;
  1425. $F->add($UIelement);
  1426. if (isset($field->blurb)) {
  1427. $F->add_annotation("<span class=axyl_note>$field->blurb</span>");
  1428. }
  1429. }
  1430. }
  1431. } // except
  1432. } // foreach
  1433. } // insert_key_formfields
  1434. // ....................................................................
  1435. /**
  1436. * Returns the foreign key constraint object that the field is present
  1437. * in, or false if it isn't present in any.
  1438. * @param string $fieldname Name of field to check if part of constraint
  1439. * @return boolean
  1440. * @access private
  1441. */
  1442. function foreign_key_constraint($fieldname) {
  1443. $fkcon = false;
  1444. foreach ($this->table->constraints as $con) {
  1445. if ($con->type == "f"
  1446. && is_array($con->fieldnames)
  1447. && in_array($fieldname, $con->fieldnames)) {
  1448. $fkcon = $con;
  1449. break;
  1450. }
  1451. } // foreach
  1452. return $fkcon;
  1453. } // foreign_key_constraint
  1454. // ....................................................................
  1455. /**
  1456. * Returns true if the given field a join key for a joined table.
  1457. * @param string $fieldname Name of field to check if part of join key
  1458. * @return boolean
  1459. * @access private
  1460. */
  1461. function is_join_key($fieldname) {
  1462. $isjk = false;
  1463. foreach ($this->joined_tables as $tablename => $Jmaint) {
  1464. foreach ($Jmaint->joinfields as $join) {
  1465. $bits = explode("=", $join);
  1466. if ($fieldname == $bits[0]) {
  1467. $isjk = true;
  1468. break;
  1469. }
  1470. }
  1471. } // foreach
  1472. return $isjk;
  1473. } // is_join_key
  1474. // ....................................................................
  1475. /**
  1476. * Return the user interface element for maintaining specified table
  1477. * field. If one exists already for that field it is returned. If not,
  1478. * then the field is analysed and a UI element is created for it.
  1479. * @param string $fieldname Name of field to use form field for
  1480. * @access private
  1481. */
  1482. function get_UIelement($fieldname) {
  1483. // Standard field widths
  1484. global $fullwidth, $mostwidth, $halfwidth;
  1485. global $thirdwidth, $quartwidth, $fifthwidth, $RESPONSE;
  1486.  
  1487. $txt_width = $halfwidth; // Standard text field
  1488. $num_width = $quartwidth; // Numeric text field
  1489. $dti_width = $thirdwidth; // Date-time field
  1490. $mem_width = $halfwidth; // Memofield (textarea) width
  1491. $mem_height = $fifthwidth; // Memofield height
  1492.  
  1493. $UIelement = false;
  1494. if (isset($this->table->fields[$fieldname])) {
  1495. $field = $this->table->fields[$fieldname];
  1496. if (isset($field->UIelement)) {
  1497. $UIelement = $field->UIelement;
  1498. }
  1499. else {
  1500. // We have to determine UItype..
  1501. $UItype = "";
  1502. //var_dump($field, "<br>");
  1503. // First, check for foreign key reference..
  1504. $con = $this->foreign_key_constraint($field->name);
  1505. if (is_object($con)) {
  1506. $UItype = "foreignkey";
  1507. }
  1508.  
  1509. // Standard type if not foreign key..
  1510. if ($UItype == "") {
  1511. $UItype = $field->generic_type();
  1512. }
  1513.  
  1514. // Create the appropriate form UI element..
  1515. switch ($UItype) {
  1516. case "foreignkey":
  1517. if (!isset($this->joined_tables[$con->fk_tablename])) {
  1518. $UIelement = $this->getFKcombo($con->fk_tablename, $con->fk_fieldnames, true);
  1519. $UIelement->mandatory = $field->notnull;
  1520. $UIelement->setclass("axcombo");
  1521. }
  1522. elseif ($this->mode != "add") {
  1523. $UIelement = new form_hiddenfield();
  1524. }
  1525. break; // foreignkey
  1526.  
  1527. case "text":
  1528. if (isset($field->fieldtype)) {
  1529. $ftype = $field->fieldtype;
  1530. }
  1531. else {
  1532. $patt = "/desc|comment|blurb|memo|article|story|long/";
  1533. if (preg_match($patt, $field->name)) {
  1534. $ftype = "memo";
  1535. }
  1536. else {
  1537. $ftype = "text";
  1538. }
  1539. }
  1540. switch ($ftype) {
  1541. case "memo":
  1542. $w = (isset($field->pxwidth) ? $field->pxwidth : $mem_width);
  1543. $h = (isset($field->pxheight) ? $field->pxheight : $mem_height);
  1544. $UIelement = new form_memofield();
  1545. $UIelement->setclass("axmemo");
  1546. if (isset($$UIvarname)) {
  1547. $UIelement->setvalue($$UIvarname);
  1548. }
  1549. break;
  1550.  
  1551. case "text":
  1552. $w = (isset($field->pxwidth) ? $field->pxwidth : $txt_width);
  1553. $UIelement = new form_textfield();
  1554. $UIelement->setclass("axtxtbox");
  1555. break;
  1556.  
  1557. case "password":
  1558. $w = (isset($field->pxwidth) ? $field->pxwidth : $txt_width);
  1559. $UIelement = new form_passwordfield();
  1560. $UIelement->setclass("axtxtbox");
  1561. break;
  1562.  
  1563. case "image":
  1564. $w = (isset($field->pxwidth) ? $field->pxwidth : $txt_width);
  1565. $UIelement = new form_imagefield();
  1566. $UIelement->setclass("axtxtbox");
  1567. break;
  1568. } // switch
  1569.  
  1570. // Field sizing..
  1571. if (isset($w)) $UIelement->setstyle("width:$w" . "px;");
  1572. if (isset($h)) $UIelement->setstyle("height:$h" . "px;");
  1573.  
  1574. // Non-blank means the text cannot be nullstring..
  1575. $UIelement->mandatory = isset($field->nonblank);
  1576. break; // text
  1577.  
  1578. case "date":
  1579. case "datetime":
  1580. $UIelement = new form_textfield();
  1581. $UIelement->setclass("axdatetime");
  1582. // Field sizing..
  1583. $w = (isset($field->pxwidth) ? $field->pxwidth : $dti_width);
  1584. $UIelement->setstyle("width:$w" . "px;");
  1585. $UIelement->mandatory = $field->notnull;
  1586. break; // datetime
  1587.  
  1588. case "numeric":
  1589. $UIelement = new form_textfield();
  1590. $UIelement->setclass("axnumbox");
  1591. // Field sizing..
  1592. $w = (isset($field->pxwidth) ? $field->pxwidth : $num_width);
  1593. $UIelement->setstyle("width:$w" . "px;");
  1594. $UIelement->mandatory = $field->notnull;
  1595. break; // numeric
  1596.  
  1597. case "logical":
  1598. $UIelement = new form_checkbox();
  1599. $UIelement->setclass("axchkbox");
  1600.  
  1601. break; // logical
  1602. } // switch
  1603.  
  1604. // Stash new or changed UI element safely away..
  1605. if (is_object($UIelement)) {
  1606. // Apply any specifically requested CSS..
  1607. if (isset($field->css) && $field->css != "") {
  1608. $UIelement->setcss($field->css);
  1609. }
  1610. $field->UIelement = $UIelement;
  1611. $this->table->fields[$fieldname] = $field;
  1612. }
  1613. }
  1614. }
  1615. // Return user interface element..
  1616. return $UIelement;
  1617.  
  1618. } // get_UIelement
  1619. // ....................................................................
  1620. /**
  1621. * Return a SELECT form_combofield which is a dropdown for the given
  1622. * field on the given table. Usually this is for foreign key references,
  1623. * but in fact it is general enough to be used on any table, including
  1624. * the one being maintained (eg. used for key-field drop-down).
  1625. * @param string $fk_tablename Name of table to build select from
  1626. * @param string $fk_fieldnames Array of fieldnames to build select for
  1627. * @param mixed $fk_labelfields Array of fieldnames to use as label
  1628. * @param boolean $nullitem If true, a nullstring item will be the first item
  1629. * @param string $filtersql SQL string to add to query as a filter
  1630. * @access private
  1631. */
  1632. function getFKcombo($fk_tablename, $fk_fieldnames, $nullitem=false, $filtersql="") {
  1633. $fk_table = $this->schema->gettable($fk_tablename);
  1634. foreach ($fk_fieldnames as $fk_fieldname) {
  1635. $fk_fields[] = $fk_table->fields[$fk_fieldname];
  1636. }
  1637. // If no label fields specified, try to find one..
  1638. if (!isset($fk_table->labelfields)) {
  1639. $fk_labelfields[] = $fk_table->getlabelfield();
  1640. }
  1641. else {
  1642. $fk_labelfields = $fk_table->labelfields;
  1643. }
  1644. // If no order fields specified, try to find one..
  1645. if (!isset($fk_table->orderfields)) {
  1646. $fk_orderfields[] = $fk_table->getorderfield();
  1647. }
  1648. else {
  1649. $fk_orderfields = $fk_table->orderfields;
  1650. }
  1651. // Create combo field..
  1652. $UIelement = new form_combofield();
  1653. $UIelement->setclass("axcombo");
  1654. if ($nullitem) {
  1655. $UIelement->additem(NULLVALUE, "");
  1656. }
  1657. // Create query and get the UI data..
  1658. $UIdata = new dbselect($fk_table->name);
  1659. if ($filtersql != "") {
  1660. $UIdata->where($filtersql);
  1661. }
  1662. $UIdata->fieldlist($fk_fieldnames);
  1663. foreach ($fk_labelfields as $fk_labelfield) {
  1664. $UIdata->fieldlist($fk_labelfield);
  1665. }
  1666. if (count($fk_orderfields) > 0) {
  1667. $UIdata->orderby($fk_orderfields[0]);
  1668. }
  1669. else {
  1670. $UIdata->orderby($fk_fieldname);
  1671. }
  1672. $UIdata->execute();
  1673. if ($UIdata->hasdata) {
  1674. do {
  1675. $values = array();
  1676. foreach ($fk_fieldnames as $fk_fieldname) {
  1677. $values[] = $UIdata->field($fk_fieldname);
  1678. }
  1679. $value = implode(FIELD_DELIM, $values);
  1680. if (count($fk_labelfields) > 0) {
  1681. $labels = array();
  1682. foreach ($fk_labelfields as $fk_labelfield) {
  1683. $labels[] = $UIdata->field($fk_labelfield);
  1684. }
  1685. $label = implode(" ", $labels);
  1686. }
  1687. else {
  1688. $label = str_replace(FIELD_DELIM, "|", $value);
  1689. }
  1690. $UIelement->additem($value, $label);
  1691. } while ($UIdata->get_next());
  1692. }
  1693. // Return it..
  1694. return $UIelement;
  1695.  
  1696. } // getFKcombo
  1697. // ....................................................................
  1698. /** Get posted variable value by name.
  1699. * @param string $postedvar Name of POSTed form-field with value in it
  1700. * @param object $field Field which is the target of the POST action
  1701. * @return mixed FALSE if not defined, else the string value POSTed
  1702. * @access private
  1703. */
  1704. function get_posted_value($postedvar, $field) {
  1705. global $$postedvar;
  1706.  
  1707. switch ($field->generic_type()) {
  1708. case "logical":
  1709. $postedval = isset($$postedvar);
  1710. break;
  1711. case "date":
  1712. if (isset($$postedvar) && $$postedvar != "") {
  1713. $postedval = displaydate_to_date($$postedvar);
  1714. if(isset($field->postproc)) {
  1715. $postedval = call_user_func($field->postproc, $postedval);
  1716. }
  1717. }
  1718. else $postedval = NULLVALUE;
  1719. break;
  1720. case "datetime":
  1721. if (isset($$postedvar) && $$postedvar != "") {
  1722. $postedval = displaydate_to_datetime($$postedvar);
  1723. if(isset($field->postproc)) {
  1724. $postedval = call_user_func($field->postproc, $postedval);
  1725. }
  1726. }
  1727. else $postedval = NULLVALUE;
  1728. break;
  1729. default:
  1730. $postedval = $$postedvar;
  1731. if(isset($field->postproc)) {
  1732. $postedval = call_user_func($field->postproc, $postedval);
  1733. }
  1734. } // switch
  1735. return $postedval;
  1736. } // get_posted_value
  1737. // ....................................................................
  1738. /**
  1739. * Insert a new row for this table on the database using the values
  1740. * for fields as provided via a POST.
  1741. * @return boolean True if record was inserted else false
  1742. * @access private
  1743. */
  1744. function insert_row() {
  1745. $successful = true;
  1746. $query = new dbinsert($this->tablename);
  1747. $keyfields = $this->keyfieldnames();
  1748. foreach ($keyfields as $fieldname) {
  1749. $field = $this->table->fields[$fieldname];
  1750. $postedvar = "recmaint_$field->name";
  1751. $query->set($fieldname, $this->get_posted_value($postedvar, $field));
  1752. } // foreach
  1753. // Set the non-key field values..
  1754. $nonkeyfields = $this->nonkeyfieldnames();
  1755. foreach ($nonkeyfields as $fieldname) {
  1756. $field = $this->table->fields[$fieldname];
  1757. // Only include fields allowed to be updated..
  1758. if (!isset($field->disposition) ||
  1759. ($field->disposition == "normal" ||
  1760. $field->disposition == "hidden")) {
  1761. $postedvar = "recmaint_$fieldname";
  1762. $postedval = $this->get_posted_value($postedvar, $field);
  1763. // Apply any field validation..
  1764. if (isset($field->validationproc)) {
  1765. $valid = call_user_func($field->validationproc, $postedval);
  1766. if ($valid === false) {
  1767. $successful = false;
  1768. $this->msgs[] = $field->validationfailmsg;
  1769. }
  1770. }
  1771. switch ($field->fieldtype) {
  1772. case "password":
  1773. // Only non-nullstrings acceptable for passwords..
  1774. if ($postedval != "") {
  1775. if ($this->passwd_encryption) {
  1776. // Axyl-encrypted passwords always have 'axenc_' prefix..
  1777. $postedval = "axenc_" . md5($postedval);
  1778. }
  1779. $query->set($fieldname, $postedval);
  1780. }
  1781. break;
  1782. default:
  1783. $query->set($fieldname, $postedval);
  1784. } // switch
  1785. }
  1786. } // foreach
  1787. if ($successful) {
  1788. $successful = $query->execute();
  1789. }
  1790. return $successful;
  1791.  
  1792. } // insert_row
  1793. // ....................................................................
  1794. /**
  1795. * Update an existing row on the database using global variables
  1796. * provided by a POST.
  1797. * @return boolean True if record was updated else false
  1798. * @access private
  1799. */
  1800. function update_row() {
  1801. $successful = true;
  1802. $query = new dbupdate($this->tablename);
  1803. $keyfields = $this->keyfieldnames();
  1804. $keywheres = array();
  1805. foreach ($keyfields as $fieldname) {
  1806. $field = $this->table->fields[$fieldname];
  1807. $postedvar = "recmaint_$fieldname";
  1808. $postedval = $this->get_posted_value($postedvar, $field);
  1809. // Apply any field validation..
  1810. if (isset($field->validationproc)) {
  1811. $valid = call_user_func($field->validationproc, $postedval);
  1812. if ($valid === false) {
  1813. $this->msgs[] = $field->validationfailmsg;
  1814. $successful = false;
  1815. }
  1816. }
  1817. switch ($field->generic_type()) {
  1818. case "logical":
  1819. $wheres[] = ($postedval) ? "$fieldname=TRUE" : "$fieldname=FALSE";
  1820. break;
  1821. case "numeric":
  1822. if ($postedval) {
  1823. $keywheres[] = "$fieldname=$postedval";
  1824. }
  1825. break;
  1826. default:
  1827. if ($postedval) {
  1828. $keywheres[] = "$fieldname='$postedval'";
  1829. }
  1830. } // switch
  1831. } // foreach
  1832. if (count($keywheres) > 0) {
  1833. $query->where( implode(" AND ", $keywheres) );
  1834. }
  1835. // Set the non-key field values..
  1836. $nonkeyfields = $this->nonkeyfieldnames();
  1837. foreach ($nonkeyfields as $fieldname) {
  1838. $field = $this->table->fields[$fieldname];
  1839. // Only include fields allowed to be updated..
  1840. if (!isset($field->disposition) ||
  1841. ($field->disposition == "normal" ||
  1842. $field->disposition == "hidden")) {
  1843. $postedvar = "recmaint_$fieldname";
  1844. $postedval = $this->get_posted_value($postedvar, $field);
  1845. // Apply any field validation..
  1846. if (isset($field->validationproc)) {
  1847. $valid = call_user_func($field->validationproc, $postedval);
  1848. if ($valid === false) {
  1849. $this->msgs[] = $field->validationfailmsg;
  1850. $successful = false;
  1851. }
  1852. }
  1853. switch ($field->fieldtype) {
  1854. case "password":
  1855. // Only non-nullstrings acceptable for passwords..
  1856. if ($postedval != "") {
  1857. if ($this->passwd_encryption) {
  1858. // Axyl-encrypted passwords always have 'axenc_' prefix..
  1859. $postedval = "axenc_" . md5($postedval);
  1860. }
  1861. $query->set($fieldname, $postedval);
  1862. }
  1863. break;
  1864. default:
  1865. $query->set($fieldname, $postedval);
  1866. } // switch
  1867. }
  1868. } // foreach
  1869. if ($successful) {
  1870. $successful = $query->execute();
  1871. }
  1872.  
  1873. if ($successful) {
  1874. // Deal with changes to any linked-tables
  1875. if (count($this->linked_tables) > 0) {
  1876. foreach ($this->linked_tables as $m2m) {
  1877. $Q = new dbdelete($m2m->linktable->name);
  1878. $Q->where( implode(" AND ", $keywheres) );
  1879. $Q->execute();
  1880. $postedvar = "recmaint_" . $m2m->table1->name . "_" . $m2m->table2->name;
  1881. global $$postedvar;
  1882. if (isset($$postedvar)) {
  1883. foreach ($$postedvar as $key) {
  1884. $Q = new dbinsert($m2m->linktable->name);
  1885. $keyvalues = explode("|", $key);
  1886. $ix = 0;
  1887. // Posted keyfields of linked table..
  1888. foreach ($m2m->table2->fields as $field) {
  1889. if ($field->ispkey) {
  1890. $fieldname = $field->name;
  1891. $fieldvalue = $keyvalues[$ix++];
  1892. $Q->set($fieldname, $fieldvalue);
  1893. }
  1894. } // foreach keyfield
  1895. // Posted keyfields of main table..
  1896. $keyfields = $this->keyfieldnames();
  1897. foreach ($keyfields as $fieldname) {
  1898. $postedvar = "recmaint_" . $fieldname;
  1899. global $$postedvar;
  1900. if (isset($$postedvar)) {
  1901. $Q->set($fieldname, $$postedvar);
  1902. }
  1903. } // foreach
  1904. $successful = $Q->execute();
  1905. } // foreach posted key
  1906. }
  1907. } // foreach
  1908. }
  1909. }
  1910. return $successful;
  1911. } // update_row
  1912. // ....................................................................
  1913. /**
  1914. * Delete a row from the database, using key field information
  1915. * provided via a POST.
  1916. * @return boolean True if record was deleted else false
  1917. * @access private
  1918. */
  1919. function delete_row() {
  1920. $query = new dbdelete($this->tablename);
  1921. $keyfields = $this->keyfieldnames();
  1922. $wheres = array();
  1923. foreach ($keyfields as $fieldname) {
  1924. $field = $this->table->fields[$fieldname];
  1925. $postedvar = "recmaint_" . $fieldname;
  1926. global $$postedvar;
  1927. switch ($field->generic_type()) {
  1928. case "logical":
  1929. if (isset($$postedvar)) $wheres[] = "$fieldname=TRUE";
  1930. else $wheres[] = "$fieldname=FALSE";
  1931. break;
  1932. case "numeric":
  1933. if (isset($$postedvar)) {
  1934. $wheres[] = "$fieldname=" . $$postedvar;
  1935. }
  1936. break;
  1937. default:
  1938. if (isset($$postedvar)) {
  1939. $wheres[] = "$fieldname='" . $$postedvar . "'";
  1940. }
  1941. } // switch
  1942. } // foreach
  1943. if (count($wheres) > 0) {
  1944. $query->where( implode(" AND ", $wheres) );
  1945. }
  1946. return $query->execute();
  1947.  
  1948. } // delete_row
  1949. // ....................................................................
  1950. /**
  1951. * Just populate the class row data with default values or, if the
  1952. * field has a sequence, the next value of that sequence.
  1953. * @access private
  1954. */
  1955. function initialise_row() {
  1956. foreach ($this->table->fields as $field) {
  1957. if (isset($field->sequencename) && !$this->is_join_key($field->name)) {
  1958. $this->current_row[$field->name] =
  1959. get_next_sequencevalue(
  1960. $field->sequencename,
  1961. $this->table->name,
  1962. $field->name
  1963. );
  1964. }
  1965. else {
  1966. $dflt = $field->default;
  1967. if (substr($dflt, 0, 1) == "'" && substr($dflt, -1) == "'") {
  1968. $dflt = str_replace("'", "", $dflt);
  1969. }
  1970. $this->current_row[$field->name] = $dflt;
  1971. }
  1972. }
  1973. } // initialise_row
  1974. // ....................................................................
  1975. /** Process any POST action
  1976. * @access private
  1977. */
  1978. function POSTprocess() {
  1979. global $mode, $bevent;
  1980.  
  1981. debugbr("POST processing for $this->tablename", DBG_DEBUG);
  1982. debugbr("event: $bevent", DBG_DEBUG);
  1983.  
  1984. // Mode of operation..
  1985. if (!isset($mode)) $mode = "edit";
  1986. $this->mode = $mode;
  1987.  
  1988. switch ($bevent) {
  1989. // ADD BUTTON
  1990. case "add":
  1991. $this->initialise_row();
  1992. $this->recvalid = true;
  1993. $this->mode = "adding";
  1994. break;
  1995.  
  1996. // CANCEL BUTTON
  1997. case "cancel":
  1998. $this->mode = "edit";
  1999. break;
  2000.  
  2001. // SAVE BUTTON
  2002. case "update":
  2003. switch ($mode) {
  2004. case "edit":
  2005. $res = $this->update_row();
  2006. $this->mode = "edit";
  2007. break;
  2008.  
  2009. case "add":
  2010. $res = $this->insert_row();
  2011. $this->mode = "edit";
  2012. break;
  2013. } // switch
  2014. break;
  2015.  
  2016. // DELETE BUTTON
  2017. case "remove":
  2018. $res = $this->delete_row();
  2019. if ($res) {
  2020. $this->recvalid = false;
  2021. }
  2022. $this->mode = "edit";
  2023. break;
  2024.  
  2025. // REFRESH BUTTON
  2026. case "refresh":
  2027. $this->mode = "filter";
  2028. break;
  2029. } // switch
  2030.  
  2031. } // POSTprocess
  2032. // ....................................................................
  2033. /** Render the maintainer as HTML. Use the render() method rather than
  2034. * directly calling this method.
  2035. * @return string The HTML for this maintainer
  2036. */
  2037. function html() {
  2038. global $RESPONSE, $LIBDIR;
  2039. global $recfilter_field, $recfilter_opr, $recfilter_val;
  2040.  
  2041. $html = "";
  2042. if ($this->valid) {
  2043.  
  2044. // Activate if not already done..
  2045. if (!$this->activated) {
  2046. $this->activate();
  2047. }
  2048.  
  2049. // Put in some javascript to prevent accidental deletes. If you are
  2050. // not using the Axyl $RESPONSE object, then insert this code in
  2051. // some other way, to provide protection against accidental delete..
  2052. if (isset($RESPONSE)) {
  2053. $RESPONSE->body->add_script(
  2054. "function delWarn() {\n"
  2055. . " var msg=\"WARNING:\\n\\n\";\n"
  2056. . " msg+=\"Do you really want to delete this record?\\n\\n\";\n"
  2057. . " var rc=confirm(msg);\n"
  2058. . " if (rc) {bclick('remove');}\n"
  2059. . " else alert(\"Record survives to fight another day.\");\n"
  2060. . "}\n"
  2061. . "function bclick(ev) {\n"
  2062. . " var doit=true;\n"
  2063. . " if (ev=='update') doit=validate();\n"
  2064. . " if (doit) {\n"
  2065. . " document.forms." . $this->formname . ".bevent.value=ev;\n"
  2066. . " document.forms." . $this->formname . ".submit();\n"
  2067. . " }\n"
  2068. . "}\n"
  2069. );
  2070. }
  2071.  
  2072. // -----------------------------------------------------------------------
  2073. // SELECT MENU
  2074. $s = "";
  2075. if ($this->mode != "add") {
  2076. $s = "No keyfield";
  2077. $keyfields = $this->keyfieldnames();
  2078. if (count($keyfields) > 0) {
  2079. $hids = "";
  2080. foreach ($keyfields as $keyfieldname) {
  2081. $hid = new form_hiddenfield("recmaint_" . $keyfieldname);
  2082. if ($this->recvalid) {
  2083. $hid->setvalue($this->current_row[$keyfieldname]);
  2084. }
  2085. $hids .= $hid->render();
  2086. }
  2087. // Possible user-supplied filtering..
  2088. $filtersql = "";
  2089. if (isset($recfilter_field) && $recfilter_field != "") {
  2090. $q .= " $recfilter_field $recfilter_opr ";
  2091. $Ffield = $this->table->fields[$recfilter_field];
  2092. switch ($Ffield->generic_type) {
  2093. case "numeric":
  2094. $q .= $recfilter_val;
  2095. break;
  2096. case "logical":
  2097. $recfilter_val = strtolower($recfilter_val);
  2098. if ($recfilter_val == "t" || $recfilter_val == "true" || $recfilter_val == "1") {
  2099. $q .= $RESPONSE->datasource->db_value_from_bool(true);
  2100. }
  2101. else {
  2102. $q .= $RESPONSE->datasource->db_value_from_bool(false);
  2103. }
  2104. break;
  2105. default:
  2106. $q .= "'$recfilter_val'";
  2107. } // switch
  2108. $filtersql = $q;
  2109. }
  2110.  
  2111. $Sel_F = $this->getFKcombo($this->tablename, $keyfields, true, $filtersql);
  2112. if ($this->recvalid) {
  2113. $keyval = array();
  2114. foreach ($keyfields as $keyfieldname) {
  2115. $keyval[] = $this->current_row[$keyfieldname];
  2116. }
  2117. $Sel_F->setvalue(implode(FIELD_DELIM, $keyval));
  2118. }
  2119. $Sel_F->set_onchange("keynav_" . $this->tablename . "()");
  2120. $Tsel = new table("selector");
  2121. $Tsel->setpadding(2);
  2122. $Tsel->tr();
  2123.  
  2124. $Tsel->td("<b>Go to:</b>&nbsp;" . $Sel_F->render("sel_$this->tablename"), "axfg" );
  2125. $Tsel->td_alignment("right");
  2126. $s = $Tsel->render("sel_$this->tablename") . $hids;
  2127.  
  2128. // Javascript function to enable multi-part key navigation..
  2129. $js = "function keynav_" . $this->tablename . "() {\n";
  2130. $js .= " keycombo=eval('document.forms.$this->formname.sel_$this->tablename');\n";
  2131. $js .= " if (keycombo != null) {\n";
  2132. $js .= " ix = keycombo.selectedIndex;\n";
  2133. $js .= " if (ix != -1) {\n";
  2134. $js .= " keys = keycombo.options[ix].value.split('" . FIELD_DELIM . "');\n";
  2135. $ix = 0;
  2136. foreach ($keyfields as $keyfieldname) {
  2137. $js .= " document.forms.$this->formname.recmaint_$keyfieldname.value=keys[" . $ix . "];\n";
  2138. $ix += 1;
  2139. }
  2140. $js .= " document.forms.$this->formname.submit();\n";
  2141. $js .= " }\n";
  2142. $js .= " }\n";
  2143. $js .= "}\n";
  2144. // Insert javascript to navigate the recordset. If you are not using the
  2145. // Axyl $RESPONSE object, then insert this code in some other way..
  2146. if (isset($RESPONSE)) {
  2147. $RESPONSE->body->add_script($js);
  2148. }
  2149. }
  2150. else {
  2151. debugbr("maintainer: no keyfields found!", DBG_DEBUG);
  2152. }
  2153. }
  2154. $KEY_SELECT = $s;
  2155.  
  2156. // -----------------------------------------------------------------------
  2157. // BUTTONS and DETAILS
  2158.  
  2159. // CONTROL BUTTONS
  2160. $addbtn = new form_imagebutton(
  2161. "_add", "", "", "$LIBDIR/img/_add.gif", "Add new", 57, 15);
  2162. $canbtn = new form_imagebutton(
  2163. "_cancel", "", "", "$LIBDIR/img/_cancel.gif", "Cancel operation", 57, 15);
  2164. $savbtn = new form_imagebutton(
  2165. "_save", "", "", "$LIBDIR/img/_save.gif", "Save", 57, 15);
  2166. $rembtn = new form_imagebutton(
  2167. "_remove", "", "", "$LIBDIR/img/_remove.gif", "Remove", 57, 15);
  2168. $rstbtn = new form_imagebutton(
  2169. "_reset", "", "", "$LIBDIR/img/_reset.gif", "Reset values", 57, 15);
  2170.  
  2171. // On-click trapping..
  2172. $addbtn->set_onclick("bclick('add')", SCRIPT_APPEND);
  2173. $canbtn->set_onclick("bclick('cancel')", SCRIPT_APPEND);
  2174. $savbtn->set_onclick("bclick('update')", SCRIPT_APPEND);
  2175. $rembtn->set_onclick("delWarn()", SCRIPT_APPEND);
  2176. $rstbtn->set_onclick("document.forms.$this->formname.reset()");
  2177.  
  2178. // The maintainer edit form. Pass the save button so that sub-maintainers
  2179. // used by master-detail links can register this button..
  2180. $oform = $this->edit_subform($savbtn);
  2181.  
  2182. // Buttons display table..
  2183. $Tbtns = new table("buttons");
  2184. $Tbtns->setpadding(2);
  2185. $Tbtns->tr();
  2186. $Tbtns->td();
  2187.  
  2188. $savbtn_r = in_array("save", $this->hidden_buttons) ? "" : $savbtn->render();
  2189. $rstbtn_r = in_array("reset", $this->hidden_buttons) ? "" : $rstbtn->render();
  2190. $addbtn_r = in_array("add", $this->hidden_buttons) ? "" : $addbtn->render();
  2191. $rembtn_r = in_array("remove", $this->hidden_buttons) ? "" : $rembtn->render();
  2192. $canbtn_r = in_array("cancel", $this->hidden_buttons) ? "" : $canbtn->render();
  2193.  
  2194. if ($this->recvalid) {
  2195. $Tbtns->td_content( "&nbsp;" . $savbtn_r );
  2196. $Tbtns->td_content( "&nbsp;" . $rstbtn_r );
  2197. }
  2198. if ($this->mode != "add") {
  2199. $Tbtns->td_content( "&nbsp;" . $addbtn_r );
  2200. if ($this->recvalid) {
  2201. $Tbtns->td_content( "&nbsp;" . $rembtn_r );
  2202. }
  2203. }
  2204. else {
  2205. $Tbtns->td_content( "&nbsp;" . $canbtn_r );
  2206. }
  2207. $Tbtns->td_content("&nbsp;");
  2208. $Tbtns->td_alignment("right", "bottom");
  2209. $CONTROL_BUTTONS = $Tbtns->render();
  2210.  
  2211. // Install onsubmit processing..
  2212. $password_validation = false;
  2213. $mandatory_validation = false;
  2214. if ($this->recvalid) {
  2215. foreach ($oform->elements as $fel) {
  2216. if ($fel->type == "password") {
  2217. if (!$password_validation) {
  2218. $password_validation = true;
  2219. // Put in some javascript to check password fields agree, and that
  2220. // the password is of the minimum length required. If you are not
  2221. // using the Axyl $RESPONSE object, then insert this code in some
  2222. // other way.
  2223. if (isset($RESPONSE)) {
  2224. $RESPONSE->body->add_script(
  2225. "function checkpass() {\n"
  2226. . " var pfn='$fel->name';\n"
  2227. . " var cfn=pfn.replace(/^recmaint_/,'confirm_');\n"
  2228. . " var pfo=eval('document.forms.$this->formname.' + pfn);\n"
  2229. . " var cfo=eval('document.forms.$this->formname.' + cfn);\n"
  2230. . " var msg='';\n"
  2231. . " if (pfo != null && cfo != null) {\n"
  2232. . " if (pfo.value != cfo.value) {\n"
  2233. . " msg+='\\nThe password does not match your confirmation.\\n';\n"
  2234. . " }\n"
  2235. . " if (pfo.value.length < " . $RESPONSE->passwd_min_chars . ") {\n"
  2236. . " msg='\\nThe password must have more than " . $RESPONSE->passwd_min_chars . " characters.\\n';\n"
  2237. . " }\n"
  2238. . " if (msg != '') {\n"
  2239. . " alert(msg);\n"
  2240. . " return false;\n"
  2241. . " }\n"
  2242. . " }\n"
  2243. . " return true;\n"
  2244. . "}\n"
  2245. );
  2246. }
  2247. }
  2248. } // password
  2249.  
  2250. if (isset($fel->mandatory) && $fel->mandatory === true) {
  2251. if (!$mandatory_validation) {
  2252. $mandatory_validation = true;
  2253. // Put in some javascript to check mandatory fields. If you
  2254. // are not using the Axyl $RESPONSE object, then insert this
  2255. // code in some other way..
  2256. if (isset($RESPONSE)) {
  2257. $RESPONSE->body->add_script(
  2258. "function checkmand(fields,labels) {\n"
  2259. . " var fld=fields.split('|');\n"
  2260. . " var lbl=labels.split('|');\n"
  2261. . " var bad='';\n"
  2262. . " for (var ix=0; ix<fld.length; ix++) {\n"
  2263. . " var fn=fld[ix];\n"
  2264. . " var fob=eval('document.forms.$this->formname.' + fn);\n"
  2265. . " if (fob != null) {\n"
  2266. . " if (fob.value == '' || (fob.type.substr(0,6) == 'select' && fob.selectedIndex == -1)) {\n"
  2267. . " if (bad != '') bad += ', ';\n"
  2268. . " bad += lbl[ix];\n"
  2269. . " }\n"
  2270. . " }\n"
  2271. . " }\n"
  2272. . " if (bad != '') {\n"
  2273. . " msg='\\nThere are some mandatory fields which are not filled in.\\n';\n"
  2274. . " msg +='These are: ' + bad + '\\n\\n';\n"
  2275. . " msg+='Please correct, and try again.\\n';\n"
  2276. . " alert(msg);\n"
  2277. . " return false;\n"
  2278. . " }\n"
  2279. . " return true;\n"
  2280. . "}\n"
  2281. );
  2282. }
  2283. }
  2284. $mandfields[] = $fel->name;
  2285. $mandlabels[] = $fel->label;
  2286. } // mandatory
  2287. } // foreach form element
  2288. } // recvalid
  2289.  
  2290. // Details table..
  2291. $s = "";
  2292. $Tdetail = new table("details");
  2293. $Tdetail->setpadding(2);
  2294. $Tdetail->tr();
  2295. $Tdetail->td( $oform->render() );
  2296. $Tdetail->td_alignment("center", "top");
  2297. $s = $Tdetail->render();
  2298. $DETAILS = $s;
  2299.  
  2300. // -----------------------------------------------------------------------
  2301. // STATUSBAR
  2302. $s = "";
  2303. if ($this->show_statusbar) {
  2304. $Tstatus = new table("statusbar");
  2305. $Tstatus->setpadding(2);
  2306. $Tstatus->tr();
  2307. if ($this->recvalid) {
  2308. switch ($this->mode) {
  2309. case "edit" : $status = "Editing"; break;
  2310. case "adding" : $status = "Adding new record"; break;
  2311. case "add" : $status = "Creating new record"; break;
  2312. case "remove" : $status = "Deleting record"; break;
  2313. default : $status = "No record"; break;
  2314. } // switch
  2315. if (isset($recfilter_field) && $recfilter_field != "") {
  2316. $status .= " (filtered)";
  2317. }
  2318. $Tstatus->td( "Mode: $status", "axfg" );
  2319. $Tstatus->td_css("border-right:0px none");
  2320. }
  2321. else {
  2322. $Tstatus->td( "Select a record", "axfg" );
  2323. $Tstatus->td_css("border-right:0px none");
  2324. }
  2325.  
  2326. $Tstatus->td("Table: $this->tablename&nbsp;&nbsp;[$this->database]", "axfg" );
  2327. $Tstatus->td_css("border-right:0px none;border-left:0px none");
  2328. $Tstatus->td_alignment("center");
  2329.  
  2330. $Tstatus->td("Rows: $this->rowcount", "axfg" );
  2331. $Tstatus->td_css("border-left:0px none");
  2332. $Tstatus->td_alignment("right");
  2333.  
  2334. $Tstatus->set_width_profile("20%,60%,20%");
  2335. $s = $Tstatus->render();
  2336. }
  2337. $STATUSBAR = $s;
  2338.  
  2339. // -----------------------------------------------------------------------
  2340. // MAINT CONTENT
  2341. $T = new table("main");
  2342. $T->inherit_attributes($this);
  2343.  
  2344. $T->tr("axtitle");
  2345. $T->td($this->title, "axtitle");
  2346. $T->td_alignment("center");
  2347.  
  2348. $T->tr("axyl_rowstripe_dark");
  2349. $T->td($CONTROL_BUTTONS);
  2350. $T->td_alignment("right", "top");
  2351.  
  2352. $T->tr("axyl_rowstripe_lite");
  2353. $T->td($KEY_SELECT);
  2354. $T->td_alignment("right", "top");
  2355.  
  2356. // Avoid too many horizontal lines when no data..
  2357. if ($this->recvalid) {
  2358. $T->tr("");
  2359. $T->td("", "axsubhdg");
  2360. $T->td_height(3);
  2361. }
  2362.  
  2363. // Display any messages we have..
  2364. if (count($this->msgs) > 0) {
  2365. $T->tr("axerror");
  2366. $T->td(implode("<br>", $this->msgs), "axerror");
  2367. $T->td_css("padding-top:8px;padding-bottom:8px;");
  2368. $T->td_alignment("center");
  2369. }
  2370.  
  2371. $T->tr("axyl_rowstripe_dark");
  2372. $T->td($DETAILS);
  2373. $T->td_alignment("", "top");
  2374.  
  2375. $T->tr("axyl_rowstripe_lite");
  2376. $T->td($STATUSBAR);
  2377. $T->td_alignment("right", "top");
  2378.  
  2379. $T->tr();
  2380. $T->td("", "axfoot");
  2381. $T->td_height(3);
  2382.  
  2383. $MAINT_CONTENT = $T->render();
  2384.  
  2385. // -----------------------------------------------------------------------
  2386. // Put it all inside one form..
  2387. if ($this->formtype == "form") {
  2388. $F = new form($this->formname);
  2389. }
  2390. else {
  2391. $F = new subform($this->formname);
  2392. }
  2393.  
  2394. if ( trim($this->enctype) != "" ) {
  2395. $F->enctype = $this->enctype;
  2396. }
  2397.  
  2398. $F->setclass("axform");
  2399. $F->labelcss = "axfmlbl";
  2400.  
  2401. // Form validation..
  2402. $valJS = "function validate() {\n";
  2403. $valJS .= " var valid=true;\n";
  2404. if ($password_validation || $mandatory_validation) {
  2405. if ($password_validation) {
  2406. $valJS .= " if(valid) valid=checkpass();\n";
  2407. }
  2408. if ($mandatory_validation) {
  2409. $parms = "'" . implode("|",$mandfields) . "','" . implode("|",$mandlabels) . "'";
  2410. $valJS .= " if(valid) valid=checkmand($parms);\n";
  2411. }
  2412. }
  2413. $valJS .= " return valid;\n";
  2414. $valJS .= "}\n";
  2415. // If you are not using the Axyl $RESPONSE object, then insert
  2416. // this code in some other way..
  2417. if (isset($RESPONSE)) {
  2418. $RESPONSE->body->add_script($valJS);
  2419. }
  2420.  
  2421. $F->add_text($MAINT_CONTENT);
  2422. $F->add(new form_hiddenfield("mode", $this->mode));
  2423. $F->add(new form_hiddenfield("bevent"));
  2424.  
  2425. $F->inherit_attributes($this);
  2426. $html = $F->render();
  2427. }
  2428. else {
  2429. $html = "Invalid schema. Check database/table names.";
  2430. }
  2431. // Ensure default database restored..
  2432. if (isset($RESPONSE)) {
  2433. $RESPONSE->select_database();
  2434. }
  2435. // Return it all..
  2436. return $html;
  2437. } // html
  2438.  
  2439. } // maintainer class
  2440. // -----------------------------------------------------------------------
  2441.  
  2442. /**
  2443. * A class encapsulating the Many-to-Many relationship of three tables.
  2444. * The main purpose of this class is to provide functionality to return
  2445. * a user interface element which will maintain the relationship. This
  2446. * can be either a set of checkboxes in a table, or a multi-select
  2447. * dropdown menu.
  2448. * @package database
  2449. * @access private
  2450. */
  2451. class many_to_many_link extends HTMLObject {
  2452. // Public
  2453. /** Title of this linkage */
  2454.  
  2455. var $title = "";
  2456.  
  2457. // Private
  2458. /** First linked table
  2459. @access private */
  2460. var $table1;
  2461. /** The link-table
  2462. @access private */
  2463. var $linktable;
  2464. /** Second linked table
  2465. @access private */
  2466. var $table2;
  2467. /** Style of user inteface to use
  2468. @access private */
  2469. var $uistyle = "combo";
  2470. /** Max UI elements per row
  2471. @access private */
  2472. var $uiperrow = 5;
  2473. // ....................................................................
  2474. /**
  2475. * Define a many_to_many_link between three tables.
  2476. * @param string $title Title or name of this linkage for a heading
  2477. * @param object $table1 First linked table in the relationship
  2478. * @param object $linktable Link table, linking keys of both tables
  2479. * @param object $table2 Second linked table in the relationship
  2480. * @param string $uistyle Style of user interface: "combo" or "checkbox"
  2481. * @param integer $uiperrow Maximum UI elements per row
  2482. */
  2483. function many_to_many_link($title, $table1, $linktable, $table2, $uistyle="combo", $uiperrow=5) {
  2484. $this->title = $title;
  2485. $this->table1 = $table1;
  2486. $this->linktable = $linktable;
  2487. $this->table2 = $table2;
  2488. $this->uistyle = $uistyle;
  2489. $this->uiperrow = $uiperrow;
  2490. } // linked_table
  2491. // ....................................................................
  2492. /** Return a UI element with links selected. The table specified is
  2493. * the one we are anchoring to in the relationship and so this user
  2494. * interface element will list the linked records from the other table
  2495. * as linked by the link-table. The keyvalues are the ones which
  2496. * anchor the relationship to one record of $tablename and are provided
  2497. * as a string of the following format:
  2498. * "keyfieldname1='somtext',keyfieldname2=99" etc.
  2499. * @param string $tablename Name of anchoring table for this view
  2500. * @param string $keyvalues Anchoring key eg: "user_id='axyl'
  2501. * @param boolean $recvalid Whether record is valid or not
  2502. * @param string $uistyle Style of user interface element "combo" or "checkbox"
  2503. */
  2504. function getUIelement($tablename, $keyvalues, $recvalid=true, $uistyle="") {
  2505. if ($tablename == $this->table1->name) {
  2506. $table1 = $this->table1;
  2507. $table2 = $this->table2;
  2508. }
  2509. else {
  2510. $table1 = $this->table2;
  2511. $table2 = $this->table1;
  2512. }
  2513. if ($uistyle != "") {
  2514. $this->uistyle = $uistyle;
  2515. }
  2516. $possQ = $this->get_possible_links($table2->name, $labelfield);
  2517. if ($recvalid) {
  2518. $linkQ = $this->get_links_to($table1->name, $keyvalues, $labelfield);
  2519. }
  2520. $keyfields = $table2->getkeyfieldnames();
  2521. switch ($this->uistyle) {
  2522. case "checkbox":
  2523. // Checkboxes used generically below..
  2524. $chkbx = new form_checkbox("", "", $value="yes");
  2525. $chkbx->setclass("axchkbox");
  2526. // Build checked elements array..
  2527. $checked = array();
  2528. if ($linkQ->hasdata) {
  2529. $selvals = array();
  2530. do {
  2531. $keyvals = array();
  2532. foreach ($keyfields as $keyfield) {
  2533. $keyvals[] = $linkQ->field($keyfield);
  2534. }
  2535. $checked[] = implode("|", $keyvals);
  2536. } while ($linkQ->get_next());
  2537. }
  2538. $Tchk = new table($table1->name . "_" . $table2->name);
  2539. $Tchk->inherit_attributes($this);
  2540. $Tchk->setpadding(2);
  2541. if ($possQ->hasdata) {
  2542. $cols = $this->uiperrow; // Number of checkbox cell-pairs
  2543. $pct = number_format(floor(100/$cols), 0); // %width of each cell
  2544. $col = 0;
  2545. do {
  2546. // Start row if at first column..
  2547. if ($col == 0 ) $Tchk->tr();
  2548. // Render checkbox in a table..
  2549. $keyvals = array();
  2550. foreach ($keyfields as $keyfield) {
  2551. $keyvals[] = $possQ->field($keyfield);
  2552. }
  2553. $keyvalue = implode("|", $keyvals);
  2554. $label = $possQ->field($labelfield);
  2555. $chkbx->checked = in_array($keyvalue, $checked);
  2556. $chkbx->setvalue($keyvalue);
  2557.  
  2558. $Tc = new table();
  2559. $Tc->setstyle("border:0px none");
  2560. $Tc->td( $chkbx->render("recmaint_" . $table1->name . "_" . $table2->name . "[]") );
  2561. $Tc->td_alignment("", "top");
  2562. $Tc->td( $label, "axfmlbl" );
  2563. $Tc->td_alignment("", "top");
  2564. $Tc->set_width_profile("5%,95%");
  2565.  
  2566. $Tchk->td( $Tc->render() );
  2567. $Tchk->td_width("$pct%");
  2568. $Tchk->td_alignment("", "top");
  2569. // End row if at last column..
  2570. $col += 1;
  2571. if ($col == $cols) {
  2572. $col = 0;
  2573. }
  2574. } while ($possQ->get_next());
  2575. // Tidy up..
  2576. if ($col > 0) {
  2577. if ($col < $cols) {
  2578. $Tchk->td( "&nbsp;" );
  2579. $Tchk->td_width( (($cols - $col) * $pct) . "%" );
  2580. $Tchk->td_colspan( $cols - $col );
  2581. }
  2582. }
  2583. }
  2584. $UI = $Tchk;
  2585. break;
  2586.  
  2587. // Default is a combo-select..
  2588. default:
  2589. $UI = new form_combofield("recmaint_" . $table1->name . "_" . $table2->name);
  2590. $UI->inherit_attributes($this);
  2591. $UI->multiselect = true;
  2592. $UI->setclass("axlistbox");
  2593. $UI->set_size(10);
  2594. if ($possQ->hasdata) {
  2595. do {
  2596. $label = $possQ->field($labelfield);
  2597. $keyvals = array();
  2598. foreach ($keyfields as $keyfield) {
  2599. $keyvals[] = $possQ->field($keyfield);
  2600. }
  2601. $UI->additem(implode("|", $keyvals), $label);
  2602. } while ($possQ->get_next());
  2603. }
  2604. if ($linkQ->hasdata) {
  2605. $selvals = array();
  2606. do {
  2607. $keyvals = array();
  2608. foreach ($keyfields as $keyfield) {
  2609. $keyvals[] = $linkQ->field($keyfield);
  2610. }
  2611. $selvals[] = implode("|", $keyvals);
  2612. } while ($linkQ->get_next());
  2613. $UI->setvalue($selvals);
  2614. }
  2615. } // switch
  2616.  
  2617. // Return user interface element..
  2618. return (isset($UI) ? $UI : false);
  2619. } // getUIelement
  2620. // ....................................................................
  2621. /**
  2622. * Return an executed database query which has the current links
  2623. * to the given table in it. This query returns links which are held for
  2624. * a given key in table1, as defined by the values in the $keyvalues.
  2625. * The keyvalues are the ones which anchor the relationship to one
  2626. * record of $tablename and are provided as a string of the following
  2627. * format:
  2628. * "keyfieldname1='value1_text',keyfieldname2=value2_numeric" etc.
  2629. * @param string $tablename Table we want the links to refer to
  2630. * @param string $keyvalues Anchoring key eg: "user_id='axyl'"
  2631. * @param pointer Pointer to a field object for the label
  2632. * @return object The executed query
  2633. * @access private
  2634. */
  2635. function get_links_to($tablename, $keyvalues, &$labelfield) {
  2636. if ($tablename == $this->table1->name) {
  2637. $table1 = $this->table1;
  2638. $table2 = $this->table2;
  2639. }
  2640. else {
  2641. $table1 = $this->table2;
  2642. $table2 = $this->table1;
  2643. }
  2644. $linktable = $this->linktable;
  2645. $table1keyfields = $table1->getkeyfieldnames();
  2646. $table2keyfields = $table2->getkeyfieldnames();
  2647. $labelfield = $table2->getlabelfield();
  2648.  
  2649. // Build the linked tables query..
  2650. $Q = new dbselect();
  2651. $Q->fieldlist("*");
  2652. $Q->tables("$table1->name,$linktable->name,$table2->name");
  2653. $keywhere = "";
  2654. if ($keyvalues != "") {
  2655. $key_array = explode(",", $keyvalues);
  2656. foreach ($key_array as $keyclause) {
  2657. if ($keywhere != "") $keywhere .= " AND ";
  2658. $keywhere .= "$table1->name.$keyclause";
  2659. }
  2660. }
  2661. $link1 = "";
  2662. foreach ($table1keyfields as $fieldname) {
  2663. if ($link1 != "") $link1 .= " AND ";
  2664. $link1 .= "$table1->name.$fieldname = $linktable->name.$fieldname";
  2665. }
  2666. $link2 = "";
  2667. foreach ($table2keyfields as $fieldname) {
  2668. if ($link2 != "") $link2 .= " AND ";
  2669. $link2 .= "$table2->name.$fieldname = $linktable->name.$fieldname";
  2670. }
  2671. $where = "";
  2672. if ($keywhere != "") $where .= "$keywhere AND ";
  2673. $where .= "$link1 AND $link2";
  2674. $Q->where($where);
  2675. $orderby = "$table2->name.$labelfield";
  2676. $orderfield = $table2->getorderfield();
  2677. if ($orderfield != "") {
  2678. $orderby = $orderfield;
  2679. }
  2680. $Q->orderby($orderby);
  2681. $Q->execute();
  2682. return $Q;
  2683. } // get_links_to
  2684. // ....................................................................
  2685. /** Return an executed database query which has the given table content
  2686. * in it. This is intended for returning the complete possibilities for
  2687. * linking in the relationship.
  2688. * @param string $tablename Table we want the links to refer to
  2689. * @param array $keyvalues An associative array of keyfield name=value pairs
  2690. * @return object The executed query
  2691. * @access private
  2692. */
  2693. function get_possible_links($tablename, &$labelfield) {
  2694. if ($tablename == $this->table1->name) {
  2695. $table = $this->table1;
  2696. }
  2697. else {
  2698. $table = $this->table2;
  2699. }
  2700. $keyfields = $table->getkeyfieldnames();
  2701. // Find a likely label field in table..
  2702. $labelfield = $keyfields[0];
  2703. foreach ($table->fields as $field) {
  2704. if (!in_array($field->name, $keyfields)
  2705. && preg_match("/name|desc|label|title/i", $field->name)) {
  2706. $labelfield = $field->name;
  2707. break;
  2708. }
  2709. }
  2710. // Build the linked tables query..
  2711. $Q = new dbselect($table->name);
  2712. $Q->fieldlist("*");
  2713. $orderby = $labelfield;
  2714. $orderfield = $table->getorderfield();
  2715. if ($orderfield != "") {
  2716. $orderby = $orderfield;
  2717. }
  2718. $Q->orderby($orderby);
  2719. $Q->execute();
  2720. return $Q;
  2721. } // get_possible_links
  2722.  
  2723. } // many_to_many_link class
  2724. // -----------------------------------------------------------------------
  2725.  
  2726. /**
  2727. * This class encapsulates the functionality for maintaining a standard
  2728. * master - detail relationship. It provides a method for returning a
  2729. * maintenance widget for maintaining the detail records of the
  2730. * relationship, given an anchoring master table key.
  2731. * @package database
  2732. * @access private
  2733. */
  2734. class master_detail_link extends HTMLObject {
  2735. /** Title of this section */
  2736.  
  2737. var $title = "";
  2738. /** Master table in relationship */
  2739.  
  2740. var $mastertable = "";
  2741. /** Detail table in relationship */
  2742.  
  2743. var $detailtable = "";
  2744. /** Prefix to use for form fields etc. */
  2745.  
  2746. var $prefix = "detail_";
  2747. /** Detail fieldnames to order by (comma-separated). NB: if first
  2748. * field is of integer type, this will be maintained using the
  2749. * Up/Down buttons automatically. */
  2750.  
  2751. var $orderby = "";
  2752. /** Field to maintain with Up/Down ordering buttons */
  2753.  
  2754. var $orderfield = "";
  2755. /** Width of key combo in px */
  2756.  
  2757. var $keywidth = 0;
  2758. /** Height of key combo in px */
  2759.  
  2760. var $keyrows = 6;
  2761. /** Local maintainer for detail form field generation */
  2762.  
  2763. var $DetailMaint;
  2764. /** Mode of POST action */
  2765.  
  2766. var $mode = "";
  2767.  
  2768. // ....................................................................
  2769. /**
  2770. * Define a master-detail relationship. We expect the table objects to
  2771. * be provided for master and detail tables. The $orderby flag is used
  2772. * to order the detail records, and a non-null value will cause the
  2773. * user interface widget to display Up/Down buttons to allow the user
  2774. * to set the order.
  2775. * @param string $title Title of this relationship
  2776. * @param object $mastertable Master table in the relationship
  2777. * @param object $detailtable detail table in the relationship
  2778. * @param string $orderby Comma-separated list of fieldnames to order by
  2779. * @param integer $keywidth Optional width of key listbox in px
  2780. * @param integer $keyrows Optional number of key listbox rows
  2781. */
  2782. function master_detail_link($title, $mastertable, $detailtable, $orderby="", $keywidth=0, $keyrows=6) {
  2783. $this->title = $title;
  2784. $this->mastertable = $mastertable;
  2785. $this->detailtable = $detailtable;
  2786. $this->prefix = $this->detailtable->name . "_";
  2787. $this->orderby = $orderby;
  2788. $this->keywidth = $keywidth;
  2789. $this->keyrows = $keyrows;
  2790. if ($orderby != "") {
  2791. $ordflds = explode(",", $orderby);
  2792. foreach ($ordflds as $ordfld) {
  2793. $field = $detailtable->getfield($ordfld);
  2794. if (is_object($field) && $field->is_integer_class()) {
  2795. $this->orderfield = $ordfld;
  2796. break;
  2797. }
  2798. }
  2799. }
  2800. } // master_detail_link
  2801. // ....................................................................
  2802. /** Return a UI element containing all detail data. The masterkeyvalues
  2803. * are the ones which anchor the relationship to one record of $tablename
  2804. * and are provided as a string of the following format:
  2805. * "keyfieldname1='value1_text',keyfieldname2=value2_numeric" etc.
  2806. * The $formname is the name of the main enclosing form which submits
  2807. * the data in this detail records maintainer. The $bsave parameter
  2808. * is a reference to the button which will cause the form data to
  2809. * be submitted.
  2810. * @param string $masterkeyvalues Anchoring key eg: "user_id='axyl'"
  2811. * @param string $formname Name of form the element will be inside
  2812. * @param boolean $recvalid Whether the record is valid or not
  2813. * @param reference $bsave Pointer to main maintainer save button
  2814. */
  2815. function getUIelement($masterkeyvalues, $formname, $recvalid=true, &$bsave) {
  2816. global $RESPONSE, $LIBDIR;
  2817.  
  2818. $T = new table($this->prefix . "maint");
  2819. $T->inherit_attributes($this);
  2820.  
  2821. // ..................................................................
  2822. // KEYFIELD and RECORD MAINTAINER
  2823. // Detail table keys listbox
  2824. // Declared here so we can create the maintainer and register buttons
  2825. // before they are used in the form.
  2826. //
  2827. // This is the keyfield listbox which controls the maintainance
  2828. // process. It lists all records being maintained..
  2829. $comboname = $this->prefix . "keys";
  2830. $detailkeys_listbox = new form_combofield($comboname);
  2831. $detailkeys_listbox->setclass("axlistbox");
  2832. if ($this->keywidth > 0) {
  2833. $detailkeys_listbox->setstyle("width:" . $this->keywidth . "px;");
  2834. }
  2835. $detailkeys_listbox->size = $this->keyrows;
  2836.  
  2837. // Make a new record maintainer, and attach the buttons..
  2838. $maintainer = new recmaintainer($formname, $detailkeys_listbox, $this->prefix);
  2839. if (!$recvalid) {
  2840. $maintainer->display_disabled();
  2841. $detailkeys_listbox->disabled = true;
  2842. }
  2843.  
  2844. // Add onchange handling for detail keys field protection..
  2845. $detailkeys_listbox->set_onchange("checkProt('$formname','$comboname');", SCRIPT_APPEND);
  2846. if (!strstr($RESPONSE->body->script["javascript"], "checkProt(")) {
  2847. $RESPONSE->body->add_script(
  2848. "function checkProt(fm,comboname) {\n"
  2849. . " var combo = eval('document.forms.' + fm + '.' + comboname);\n"
  2850. . " if (combo != null) {\n"
  2851. . " ix=combo.selectedIndex;\n"
  2852. . " if (ix != -1) {\n"
  2853. . " nk = combo.options[ix].value.indexOf('NEW_');\n"
  2854. . " if (nk == 0) {protectPks(fm,false,comboname);}\n"
  2855. . " else {protectPks(fm,true,comboname);}\n"
  2856. . " }\n"
  2857. . " }\n"
  2858. . "}\n"
  2859. . "function protectPks(fm,mode,comboname) {\n"
  2860. . " var fmObj = eval('document.forms.' + fm);\n"
  2861. . " for (var i=0; i < fmObj.length; i++) {\n"
  2862. . " var e=fmObj.elements[i];\n"
  2863. . " if (e.id == comboname+'_fpkey') {\n"
  2864. . " if (e.readOnly != null) e.readOnly = mode;\n"
  2865. . " if (e.disabled != null) e.disabled = mode;\n"
  2866. . " }\n"
  2867. . " }\n"
  2868. . "}\n"
  2869. );
  2870. }
  2871.  
  2872. // Main buttons..
  2873. $bdel = new form_imagebutton("_ddel", "", "", "$LIBDIR/img/_delete.gif", "Delete", 57, 15);
  2874. $badd = new form_imagebutton("_dadd", "", "", "$LIBDIR/img/_add.gif", "Add new", 57, 15);
  2875. $brst = new form_imagebutton("_drst", "", "", "$LIBDIR/img/_reset.gif", "Reset", 57, 15);
  2876. $brst->set_onclick("document.forms.$formname.reset()");
  2877.  
  2878. // Register main buttons..
  2879. $maintainer->register_button("del", $bdel);
  2880. $maintainer->register_button("add", $badd);
  2881. $badd->set_onclick("checkProt('$formname','$comboname');", SCRIPT_APPEND);
  2882.  
  2883. $maintainer->register_button("reset", $brst);
  2884. // This button is the Save button defined for the external maintainer which
  2885. // contains this widget. It is used to hook into the store action, so that
  2886. // we can set up POST fields prior to submitting the form..
  2887. $maintainer->register_button("store", $bsave, SCRIPT_PREFIX);
  2888.  
  2889. // Implement ordering buttons if required..
  2890. if ($this->orderfield != "") {
  2891. $bup = new form_imagebutton("_dup", "", "", "$LIBDIR/img/_up.gif", "Move up", 57, 15);
  2892. $bdown = new form_imagebutton("_ddown", "", "", "$LIBDIR/img/_down.gif", "Move down", 57, 15);
  2893. $maintainer->register_button("up" , $bup);
  2894. $maintainer->register_button("down", $bdown);
  2895. }
  2896.  
  2897. // Generate query to populate the listbox..
  2898. $mastertable_name = $this->mastertable->name;
  2899. $detailtable_name = $this->detailtable->name;
  2900. $master_keyfields = $this->mastertable->getkeyfieldnames();
  2901. $detail_keyfields = $this->detailtable->getkeyfieldnames();
  2902.  
  2903. $Q = new dbselect();
  2904. $Q->tables("$mastertable_name,$detailtable_name");
  2905. $Q->fieldlist("*");
  2906.  
  2907. // Where clause
  2908. $whereclause = "";
  2909. $whereclauses = array();
  2910. if ($masterkeyvalues != "") {
  2911. $key_array = explode(",", $masterkeyvalues);
  2912. foreach ($key_array as $keyclause) {
  2913. if ($keyclause != "") {
  2914. $whereclauses[] = "$mastertable_name.$keyclause";
  2915. }
  2916. }
  2917. $whereclause = implode(" AND ", $whereclauses);
  2918. }
  2919. // Join
  2920. $joinclauses = array();
  2921. foreach ($master_keyfields as $fieldname) {
  2922. if ($fieldname != "") {
  2923. $joinclauses[] = "$mastertable_name.$fieldname = $detailtable_name.$fieldname";
  2924. }
  2925. }
  2926. $joinclause = implode(" AND ", $joinclauses);
  2927. $where = "";
  2928. if ($whereclause != "") $where .= $whereclause;
  2929. if ($where != "") $where .= " AND ";
  2930. if ($joinclause != "") $where .= $joinclause;
  2931. if ($where != "") {
  2932. $Q->where($where);
  2933. }
  2934. if ($this->orderby != "") {
  2935. $orderfields = explode(",", $this->orderby);
  2936. foreach ($orderfields as $orderfield) {
  2937. if ($orderfield != "") {
  2938. $Q->orderby("$detailtable_name.$orderfield");
  2939. }
  2940. }
  2941. }
  2942. $Q->execute();
  2943.  
  2944. // Populate key listbox..
  2945. if ($Q->hasdata) {
  2946. if (isset($this->detailtable->labelfields)) {
  2947. $labelfield = $this->detailtable->labelfields[0];
  2948. }
  2949. else {
  2950. $labelfield = $this->detailtable->getlabelfield();
  2951. }
  2952. do {
  2953. $keyids = array();
  2954. foreach ($detail_keyfields as $detailkeyfield) {
  2955. $keyids[] = $Q->field($detailkeyfield);
  2956. }
  2957. $keyidstr = implode("|", $keyids);
  2958. $detailkeys_listbox->additem($keyidstr, $Q->field($labelfield));
  2959.  
  2960. // Populate maintainer data. The maintainer add_record method
  2961. // requires an associative array keyed on listbox key id..
  2962. $rec = array();
  2963. foreach ($this->detailtable->fields as $field) {
  2964. if (!in_array($field->name, $master_keyfields)) {
  2965. $rec[$this->prefix . $field->name] = $Q->field($field->name);
  2966. }
  2967. }
  2968. $maintainer->add_record($keyidstr, $rec);
  2969.  
  2970. // Set listbox selected value to first item..
  2971. if ($detailkeys_listbox->value == "") {
  2972. $detailkeys_listbox->setvalue($keyidstr);
  2973. }
  2974. } while ($Q->get_next());
  2975. }
  2976.  
  2977. // Now set the defaults for each of the fields. These are
  2978. // necessary for when a new record is created..
  2979. $defaults = array();
  2980. foreach ($this->detailtable->fields as $field) {
  2981. if (!$field->ispkey) {
  2982. $defaults[$this->prefix . $field->name] = str_replace("'" , "", $field->default);
  2983. }
  2984. }
  2985. $maintainer->add_defaults($defaults);
  2986.  
  2987. $badd_r = in_array("add", $this->DetailMaint->hidden_buttons) ? "" : $badd->render();
  2988. $bdel_r = in_array("remove", $this->DetailMaint->hidden_buttons) ? "" : $bdel->render();
  2989.  
  2990. $T->tr();
  2991. $buttons = "";
  2992. if ($recvalid) {
  2993. if ($badd_r != "") $buttons .= $badd_r . "<br>";
  2994. if ($bdel_r != "") $buttons .= $bdel_r . "<br>";
  2995. if ($this->orderfield != "") {
  2996. $buttons .= $bup->render() . "<br>";
  2997. $buttons .= $bdown->render() . "<br>";
  2998. }
  2999. }
  3000. $T->td( $buttons );
  3001. $T->td_alignment("", "top");
  3002. $T->td( $detailkeys_listbox->render() );
  3003. $T->td_alignment("", "top");
  3004. $T->set_width_profile("35%,65%");
  3005.  
  3006. // Use maintainer to provide form fields for detail..
  3007. $FormContent = "";
  3008. $Fkeys = new subform();
  3009. $Fkeys->labelcss = "axfmlbl";
  3010. $this->DetailMaint->insert_key_formfields($Fkeys, $this->prefix, $master_keyfields, true);
  3011. if (isset($Fkeys->elements)) {
  3012. $elements = $Fkeys->elements;
  3013. $registered_elements = array();
  3014. foreach ($elements as $element) {
  3015. $element->editable = false;
  3016. $element->disabled = true;
  3017. if ($element->type != "textcontent" && $element->type != "annotation") {
  3018. $maintainer->register_field($element, "fpkey");
  3019. }
  3020. $registered_elements[] = $element;
  3021. }
  3022. $Fkeys->elements = $registered_elements;
  3023. }
  3024. $Fdata = new subform();
  3025. $Fdata->labelcss = "axfmlbl";
  3026. $this->DetailMaint->insert_data_formfields($Fdata, $this->prefix);
  3027. if (isset($Fdata->elements)) {
  3028. $elements = $Fdata->elements;
  3029. $registered_elements = array();
  3030. foreach ($elements as $element) {
  3031. if ($element->type != "textcontent" && $element->type != "annotation") {
  3032. $maintainer->register_field($element, "fdata");
  3033. }
  3034. $registered_elements[] = $element;
  3035. }
  3036. $Fdata->elements = $registered_elements;
  3037. }
  3038. $Fdata->elements = array_merge($Fkeys->elements, $Fdata->elements);
  3039. $T->tr();
  3040. $T->td( $Fdata->render() . $maintainer->render($this->detailtable->name) );
  3041. $T->td_colspan(2);
  3042.  
  3043. // Return the UI element..
  3044. return $T;
  3045. } // getUIelement
  3046. // ....................................................................
  3047. /**
  3048. * Return WHERE clause for detail table, given a bunch of keyvalues. The
  3049. * key values must contain both master and detail table keys.
  3050. * @param array $keyvals Key values delimited by "|"
  3051. */
  3052. function detailWhereClause($keyvals) {
  3053. $detail_keyfields = $this->detailtable->getkeyfieldnames();
  3054. $key_parts = explode("|", $keyvals);
  3055. $ix = 0;
  3056. $wheres = array();
  3057. foreach ($detail_keyfields as $keyfieldname) {
  3058. $keyval = $key_parts[$ix++];
  3059. $field = $this->detailtable->fields[$keyfieldname];
  3060. switch ($field->generic_type()) {
  3061. case "logical":
  3062. $wheres[] = ($keyval) ? "$keyfieldname=TRUE" : "$keyfieldname=FALSE";
  3063. break;
  3064. case "numeric":
  3065. $wheres[] = "$keyfieldname=$keyval";
  3066. break;
  3067. default:
  3068. $wheres[] = "$keyfieldname='$keyval'";
  3069. } // switch
  3070. } // foreach
  3071. return implode(" AND ", $wheres);
  3072. } // detailWhereClause
  3073. // ....................................................................
  3074. /**
  3075. * Process POST for the detail table. We have the anchor key passed in
  3076. * as an array of keys to the master record in format "fieldname='value'".
  3077. * @param array $masterkeyvalues Array of key values defining the master record
  3078. */
  3079. function POSTprocess($formname, $masterkeyvalues) {
  3080. global $mode, $bevent;
  3081.  
  3082. debug_trace($this);
  3083. $pfx = $this->detailtable->name;
  3084. $_form_var = $pfx . "_recmaintpost_form";
  3085. $_data_var = $pfx . "_recmaintpost_data";
  3086. $_flds_var = $pfx . "_recmaintpost_flds";
  3087. $_dels_var = $pfx . "_recmaintpost_dels";
  3088. $_order_var = $pfx . "_recmaintpost_order";
  3089. global $$_form_var;
  3090. global $$_data_var;
  3091. global $$_flds_var;
  3092. global $$_dels_var;
  3093. global $$_order_var;
  3094. $_recmaintpost_form = $$_form_var;
  3095. $_recmaintpost_data = $$_data_var;
  3096. $_recmaintpost_flds = $$_flds_var;
  3097. $_recmaintpost_dels = $$_dels_var;
  3098. $_recmaintpost_order = $$_order_var;
  3099.  
  3100. if (isset($_recmaintpost_form) && $_recmaintpost_form == $formname) {
  3101. $mastertable_name = $this->mastertable->name;
  3102. $detailtable_name = $this->detailtable->name;
  3103. $master_keyfields = $this->mastertable->getkeyfieldnames();
  3104. $detail_keyfields = $this->detailtable->getkeyfieldnames();
  3105.  
  3106. switch ($bevent) {
  3107. case "update":
  3108. // Deal with deletes..
  3109. if (isset($_recmaintpost_dels) && $_recmaintpost_dels != "") {
  3110. $delkeys = explode(FIELD_DELIM, $_recmaintpost_dels);
  3111. foreach ($delkeys as $delkey) {
  3112. $Q = new dbdelete($detailtable_name);
  3113. $Q->where($this->detailWhereClause($delkey));
  3114. $Q->execute();
  3115. }
  3116. }
  3117. // Detail record updates and adds..
  3118. if (isset($_recmaintpost_data) && $_recmaintpost_data != "") {
  3119. $update_recs = explode(RECORD_DELIM, $_recmaintpost_data);
  3120. foreach ($update_recs as $update_rec) {
  3121. $update_values = explode(FIELD_DELIM, $update_rec);
  3122. $update_key = array_shift($update_values);
  3123. $update_fields = explode(",", $_recmaintpost_flds);
  3124. // Cater for new creations. These are always assigned
  3125. // a placeholder ID beginning "NEW_"..
  3126. if (strstr($update_key, "NEW_")) {
  3127. $replkeyvals = array();
  3128. $upQ = new dbinsert($detailtable_name);
  3129. foreach ($masterkeyvalues as $masterkey) {
  3130. $keybits = explode("=", $masterkey);
  3131. $fieldname = $keybits[0];
  3132. $value = $keybits[1];
  3133. if (substr($value, 0, 1) == "'") {
  3134. $value = substr($value, 1);
  3135. }
  3136. if (substr($value, -1) == "'") {
  3137. $value = substr($value, 0, -1);
  3138. }
  3139. $upQ->set($fieldname, $value);
  3140. $replkeyvals[] = $value;
  3141. }
  3142. foreach ($detail_keyfields as $keyfieldname) {
  3143. if (!in_array($keyfieldname, $master_keyfields)) {
  3144. $field = $this->DetailMaint->table->fields[$keyfieldname];
  3145. if ($field->sequencename != "") {
  3146. $value = get_next_sequencevalue(
  3147. $field->sequencename,
  3148. $this->DetailMaint->table->name,
  3149. $field->name
  3150. );
  3151. }
  3152. else {
  3153. $value = array_shift($update_values);
  3154. if (isset($field->postproc)) {
  3155. $value = call_user_func($field->postproc, $value);
  3156. }
  3157. }
  3158. $upQ->set($keyfieldname, $value);
  3159. $replkeyvals[] = $value;
  3160. }
  3161. }
  3162. // Fix up potential re-ordering id..
  3163. if (isset($_recmaintpost_order) && count($replkeyvals) > 0) {
  3164. $_recmaintpost_order = str_replace($update_key, implode("|", $replkeyvals), $_recmaintpost_order);
  3165. }
  3166. }
  3167. // Standard update/save..
  3168. else {
  3169. $upQ = new dbupdate($detailtable_name);
  3170. $upQ->where($this->detailWhereClause($update_key));
  3171. // Shift irrelevant keyfield stuff out of the way..
  3172. $pkcount = count($detail_keyfields) - count($master_keyfields);
  3173. for ($i=0; $i < $pkcount; $i++) {
  3174. $dummy = array_shift($update_values);
  3175. }
  3176. }
  3177. // Add in the data fields..
  3178. $detail_datafields = $this->DetailMaint->table->getnonkeyfieldnames();
  3179. foreach ($detail_datafields as $datafieldname) {
  3180. $field = $this->DetailMaint->table->fields[$fieldname];
  3181. $value = array_shift($update_values);
  3182. if(isset($field->postproc)) {
  3183. $value = call_user_func($field->postproc, $value);
  3184. }
  3185. $upQ->set($datafieldname, $value);
  3186. }
  3187. $upQ->execute();
  3188. } // foreach detail rec
  3189. }
  3190.  
  3191. // Do any requested re-ordering..
  3192. if ($this->orderfield != "" && isset($_recmaintpost_order) && $_recmaintpost_order != "") {
  3193. $ord = 1;
  3194. $ordkeylist = explode(FIELD_DELIM, $_recmaintpost_order);
  3195. foreach ($ordkeylist as $ordkeyvals) {
  3196. $Oup = new dbupdate($detailtable_name);
  3197. $Oup->where($this->detailWhereClause($ordkeyvals));
  3198. $Oup->set($this->orderfield, $ord);
  3199. $Oup->execute();
  3200. $ord += 1;
  3201. }
  3202. }
  3203. // Drop through to viewing..
  3204. $this->mode = "viewing";
  3205. break;
  3206. } // switch
  3207. }
  3208. debug_trace();
  3209. } // POSTprocess
  3210.  
  3211.  
  3212.  
  3213. } // master_detail_link class
  3214. // -----------------------------------------------------------------------
  3215.  
  3216. ?>

Documentation generated by phpDocumentor 1.3.0RC3