2 /**********************************************************************
3 Copyright (C) FrontAccounting, LLC.
4 Released under the terms of the GNU General Public License, GPL,
5 as published by the Free Software Foundation, either version 3
6 of the License, or (at your option) any later version.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ***********************************************************************/
13 include_once $path_to_root.'/includes/db/class.data_set.inc';
19 function text($value, $name='', $opts=array())
21 $text = array('label', 'size'=>"", 'max'=>"", 'title'=>false, 'labparams'=>"", 'post_label'=>"", 'inparams'=>"");
22 $opts = array_merge($text, $opts);
27 call_user_func_array('text_cells', $opts);
32 . use __construct in base class to avoid need for implicit call to parent constructor.
33 . use private instead _* function name convention
36 // User input-output conversions.
40 var $data; // data in php format
41 var $fields; // input fields format descriptions
42 var $errors = array();
46 function __construct($name)
51 function error($msg, $context=null)
55 $context = count($this->errors);
56 $this->errors[$context] = $msg;
61 Input/output formatters - convert values between php/user domains.
63 function _format_input($value, $fmt)
67 $this->dec = get_qty_dec($value);
72 return user_numeric($value);
74 return user_numeric($value)/100;
82 // Returns formatted value
84 function _format_output($value, $fmt)
88 return price_format($value);
90 return number_format2($value, $this->dec);
92 return number_format2($value);
94 return percent_format($value*100);
96 $this->dec = get_qty_dec($value); // retrieve dec for use in following qty fields
104 * Returns html element for given field
105 * @mode - true for edit, false for read-only
107 function _format_cells($value, $fmt, $mode)
109 $value = $this->_format_output($fmt);
111 // available formatters with parameters
113 'email_cell' => array('label', 'params'=>'', 'id'=>null),
114 'amount_cell' => array('label'=>null, 'bold'=>false, 'params'=>'', 'id'=>null),
115 'text_cells' => array('label', 'name', 'value'=>null, 'size'=>"", 'max'=>"", 'title'=>false,
116 'labparams'=>"", 'post_label'=>"", 'inparams'=>"")
118 // format functions used in various modes
120 // field format => (view [,edit])
121 '' => array('label_cell', 'text_cells'), // default
122 'price' => array('label_cell', 'amount_cell'),
134 * PHP->user format values convertion.
137 function set_output($data=null, &$output=null)
142 if (!isset($output)) {
144 $prefix = $this->name;
148 foreach($this->fields as $name => $fmt) {
153 } elseif (!is_array($fmt))
154 $fmt = array('fmt' => $fmt);
156 $post = $prefix.(isset($fmt['post']) ? $fmt['post'] : $name);
158 $fld = isset($fmt['fld']) ? $fmt['fld'] : $name;
160 if (is_object($this->data))
161 $value = isset($this->data->$fld) ? $this->data->$fld : @$fmt['dflt'];
163 $value = isset($this->data[$fld]) ? $this->data[$fld] : @$fmt['dflt'];
165 $output[$post] = $this->_format_output($value, @$fmt['fmt']);
171 * User->php format values convertion.
172 * $input - data in user format
173 * $all - return also null values for non-existing input field.
175 function get_input($input=null, $all=false)
180 if ($this->name) // strip view name prefix
181 foreach($input as $postkey=>$postval )
183 if (strpos($postkey, $this->name) === 0)
185 $id = substr($postkey, strlen($this->name));
186 $input[$id] = $postval;
188 unset($input[$postkey]);
192 foreach ($this->fields as $name => $fmt) {
193 if (is_int($name)) { // direct string passed: this is name of field
195 $fmt = array(); // default format
196 } elseif (!is_array($fmt))
197 $fmt = array('fmt' => $fmt);
198 $post = isset($fmt['post']) ? $fmt['post'] : $name; // input name (default to field name)
199 $fld = isset($fmt['fld']) ? $fmt['fld'] : $name; // input value (default to field name)
201 // if ($all || array_key_exists($post, $input))
203 $value = $this->_format_input(@$input[$post], @$fmt['fmt']);
205 // if (is_array($data))
206 if ($all || isset($value))
207 $data[$fld] = $value;
209 // $data->$fld = $value;
216 //--------------------------------------------------------
218 // Return data formatted according to field descriptions.
220 function get_fields_views($input=null, $mode=null)
227 foreach ($this->fields as $name => $fmt) {
232 $post = isset($fmt['post']) ? $fmt['post'] : $name;
233 $fld = isset($fmt['fld']) ? $fmt['fld'] : $name;
235 $value = $this->_format_cells(@$input[$post], @$fmt['fmt'], $mode);
237 $view[$fld] = $value;
245 // Template for simple table editors
248 class simple_crud_view extends user_view {
255 var $_none = ''; // selector value when no item is selected
256 var $pre_handlers; // control buttons and related methods called before view display
261 var $options = array(
262 'delete' => true, // true or message for successfull action.
269 var $display_both = false; //when set to true both list and editor are displayed all the time (eventually set in sub classes)
272 function __construct($name, $data_set = null, $options=array())
274 parent::__construct($name);
276 $this->options = array_merge($this->options, $options);
278 $this->views[''] = 'list_view'; // default view
280 if ($this->options['update'])
281 $this->_add_action('Edit', '_edit', _('Edit'), _('Edit document line'), ICON_EDIT, '',
284 if ($this->options['delete'])
285 $this->_add_action('Delete', '_delete', _('Delete'), _('Remove line from document'), ICON_DELETE, '',
288 if ($this->options['update'])
289 $this->_add_action('UPDATE', '_update', _('Update'), _('Submit changes'), ICON_UPDATE, 'default',
292 $this->_add_action('RESET', '_cancel', _('Cancel'), _('Cancel changes'), ICON_ESCAPE, 'cancel',
295 if ($this->options['insert'])
296 $this->_add_action('ADD', '_add', _('Add'), _('Add new'), ICON_ADD, 'default',
299 if ($this->options['insert'])
300 $this->_add_action('NEW', '_add', _('New'), _('Add new'), ICON_ADD, 'default',
303 if ($this->options['clone'])
304 $this->_add_action('CLONE', '_cloning', _('Clone'), _('Clone'), ICON_ADD, '',
307 $this->data_set = $data_set;
308 $this->fields = $data_set->fields;
310 // $this->_prev_status();
313 function _add_action($name, $handler, $but_value=null, $but_title=false, $but_icon=false, $aspect='', $view=null)
315 $this->pre_handlers[$name] = $handler;
318 $this->tool_buttons[$name] = array($but_value, $but_title, $but_icon, $aspect);
321 $this->_add_mode($name, $view);
324 function _add_mode($name, $view)
326 $this->views[$name] = $view;
329 function _prev_status()
331 // Restore previous mode/key (obsolete for views stored in session)
333 $mod = get_post($this->name.'Mode', $this->Mode);
335 if (is_array($mod)) {
337 $this->selected_id = $val!==null ? @quoted_printable_decode($val) : $this->_none;
341 $this->selected_id = $this->_none;
347 function _check_mode()
351 $mod = '';//$this->Mode;
352 // Detect action (mode change)
353 foreach (array_keys($this->pre_handlers) as $m) { // check button controls
355 if (isset($_POST[$this->name.$m])) {
356 unset($_POST['_focus']); // focus on first form entry
357 $Ajax->activate($this->name.'_div');
358 $Ajax->activate($this->name.'_controls');
359 $val = is_array($_POST[$this->name.$m]) ? key($_POST[$this->name.$m]) : null;
360 $this->prev_id = $this->selected_id;
361 $this->selected_id = $val!==null ? @quoted_printable_decode($val) : $this->_none;
365 if (!$mod && $_SERVER['REQUEST_METHOD'] == 'GET') // initialize on every GET
371 function display_error()
373 $this->errors = array_merge($this->data_set->errors, $this->errors);
374 $firsterr = reset($this->errors);
375 $field = key($this->errors);
377 if (!is_numeric($field))
378 set_focus($this->name.$field);
379 display_error($firsterr);
381 $this->errors = array(); // clean up to prevent false errors on object reuse
384 // Set record for edition
386 function _edit($mode)
388 if ($this->selected_id != $this->prev_id || $mode != $this->Mode) { // get record for edition
389 $this->data = $this->data_set->get($this->selected_id !== $this->_none ? $this->selected_id : null);
392 // if ($this->Mode != $mode) {
396 // $this->display_error();
401 // Update record after edition
403 function _update($mode)
405 if (!$this->options['update'])
408 $this->data = $this->get_input();
409 if ($this->data_set->update_check($this->selected_id, $this->data) && $this->data_set->update($this->selected_id, $this->data)) {
410 $this->selected_id = $this->_none;
411 $this->Mode = 'RESET';
413 if (is_string($this->options['update']))
414 display_notification($this->options['update']);
418 $this->display_error();
427 if (!$this->options['insert'])
430 if ($mode == 'ADD') {
431 $this->data = $this->get_input();
432 if ($this->data_set->insert_check($this->data) && ($this->data_set->insert($this->data) !== false)) {
434 if (is_string($this->options['insert']))
435 display_notification($this->options['insert']);
436 $this->Mode = 'RESET';
440 $this->display_error();
442 $this->data = $this->data_set->get();
447 // Delete selected record
451 if (!$this->options['delete'])
454 if ($this->data_set->delete_check($this->selected_id) && $this->data_set->delete($this->selected_id))
456 if (is_string($this->options['delete']))
457 display_notification($this->options['delete']);
459 $this->display_error();
463 // Return to listing view
468 $this->selected_id = $this->_none;
469 if ($this->display_both)
471 $this->data = $this->data_set->get();
475 if (!$this->display_both)
476 $Ajax->activate($this->name.'_div');
477 $this->Mode = 'RESET';
480 // Clone record for new edition
484 if (!$this->options['clone'])
487 $this->_edit('Edit');
488 $this->selected_id = $this->_none;
491 Generate form controls
494 function _record_controls($list_view = false)
496 $clone = $this->options['clone'] && $this->selected_id != $this->_none;
498 div_start($this->name.'_controls');
502 $this->action_button('NEW');
504 if ($this->Mode == 'NEW' || $this->selected_id==$this->_none)
506 $this->action_button('ADD');
507 $this->action_button('RESET');
509 $this->action_button('UPDATE', $this->selected_id);
510 if ($clone && $this->display_both)
511 $this->action_button('CLONE', $this->selected_id);
512 $this->action_button('RESET');
520 //===========================================================================
525 // Submit buttons for form actions
527 function action_button($action, $selected_id=null)
529 list($value, $title, $icon, $aspect) = $this->tool_buttons[$action];
530 submit($this->name.$action.(isset($selected_id) ? "[$selected_id]" : ''), $value, true, $title, $aspect, $icon);
534 // Tool button for grid line actions.
536 function tool_button($name, $selected_id=null, $params='')
538 $b = $this->tool_buttons[$name];
540 return "<td align='center' $params>"
541 .button( "{$this->name}$name"
542 .($selected_id === null || $selected_id === $this->_none ? '': "[$selected_id]"),
543 $b[0], $b[1], $b[2], $b[3])."</td>";
546 // Main function - display current CRUD editor content
548 function show($Mode=null)
551 $Mode = $this->_check_mode(true);
553 div_start($this->name.'_div');
555 if (array_key_exists($Mode, $this->pre_handlers)) {
556 $fun = $this->pre_handlers[$Mode];
558 call_user_func($fun, $Mode);
563 if (isset($this->views[$this->Mode]))
564 $this->{$this->views[$this->Mode]}($Mode);
566 $this->{$this->views['']}($Mode); // default view
568 // this is needed only when we use temporary crud object together with ajax screen updates
569 hidden($this->name.'Mode'.'['.$this->selected_id.']', $this->Mode);
574 // Optional things like focus set performed on edition cancel.
581 // Show database content in pager/table
582 // parameter $Mode contains new mode on change, or '' otherwise
584 function list_view($Mode) {
585 display_notification(__FUNCTION__. ' is not defined...');
589 // Show record editor screen content
590 // parameter $Mode contains new mode on change, or '' otherwise
592 function editor_view($Mode) {
593 display_notification(__FUNCTION__. ' is not defined...');
597 class selector_crud_view extends simple_crud_view {
599 function __construct($name, $data_set = null, $options=array())
601 $this->display_both = true;
602 parent::__construct($name, $data_set, $options);
605 function _check_mode()
609 // list controls lookup
610 $prev_mode = $this->Mode;
613 $list = $this->name.'_id';
614 $this->prev_id = $this->selected_id;
615 $this->selected_id = get_post($list);
616 if (list_updated($list)) {
617 $Ajax->activate($this->name);
618 $Ajax->activate($this->name.'_controls');
619 $mod = $this->selected_id == $this->_none ? 'RESET' : 'Edit';
621 $mod = simple_crud_view::_check_mode();
624 if ($mod != $prev_mode) {
625 $Ajax->activate($this->name.'_controls');
627 if (get_post('_show_inactive_update'))
629 $Ajax->activate($this->name.'_id');
632 $_POST[$list] = $this->selected_id;
640 $_POST[$this->name.'_id'] = $this->selected_id = $this->_none;
641 $Ajax->activate($this->name.'_id');
646 // Template for line table editors
649 class table_crud_view extends simple_crud_view {
653 function __construct($name, &$data_set = null, $options=array())
655 $this->display_both = true;
656 parent::__construct($name, $data_set, $options);
660 * Returns selector string from data or current selected_id
663 function curr_id($data=null)
666 return $data ? implode('_', array_intersect_key($data, array_fill_keys($this->key, true))) : null; // php 5.2.0
668 return $this->selected_id;
672 * Show single line from record table
675 function show_record_row($line_no, $record=array())
678 $id = $this->curr_id($record); // extract key
681 $this->set_output($line, $view);
683 $editables = array();
684 if ($this->options['edit'] && $this->Mode=='Edit' && $this->selected_id==$id)
686 $editables = $this->data_set->edit_status($key);
688 $ctrls = $this->get_field_views($record, $editables);
689 alt_table_row_color($k);
690 foreach($ctrls as $key => $fld)
697 if ($this->options['edit']) {
699 $this->tool_button($this->selected_id==$id ? 'UPDATE' : 'Edit', $id);
702 if ($this->options['delete']$this->selected_id==$id) {
704 $this->tool_button( ? 'Cancel' : 'Delete', $id);
709 if ($this->options['insert'])
711 alt_table_row_color($k);
712 $this->new_line($line_no, $line);
717 function show_list_headers()
721 // Main function - display current CRUD table content
723 function show($Mode=null)
726 $Mode = $this->_check_mode(true);
727 div_start($this->name.'_div');
729 if (array_key_exists($Mode, $this->pre_handlers))
731 $fun = $this->pre_handlers[$Mode];
734 if (is_object($fun[0]))
735 $fun[0]->$fun[1]($Mode);
741 display_heading($this->title);
743 start_table(TABLESTYLE, "width=95%");
744 $this->show_list_headers();
746 foreach($this->data_set->get_all() as $line_no => $line) {
747 $this->show_record_row($line_no, $line);
749 $this->show_record_row();
752 hidden($this->name.'Mode'.'['.$this->selected_id.']', $this->Mode);