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

Documentation generated by phpDocumentor 1.3.0RC3