Source for file maintainer-defs.php

Documentation is available at maintainer-defs.php

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

Documentation generated by phpDocumentor 1.3.0RC3