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

Documentation generated by phpDocumentor 1.3.0RC3