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;
76 return isset($value) ? 1 : 0;
84 // Returns formatted value
86 function _format_output($value, $fmt)
90 return price_format($value);
92 return number_format2($value, $this->dec);
94 return number_format2($value);
96 return percent_format($value*100);
98 return !empty($value);
100 $this->dec = get_qty_dec($value); // retrieve dec for use in following qty fields
108 * Returns html element for given field
109 * @mode - true for edit, false for read-only
111 function _format_cells($value, $fmt, $mode)
113 $value = $this->_format_output($fmt);
115 // available formatters with parameters
117 'email_cell' => array('label', 'params'=>'', 'id'=>null),
118 'amount_cell' => array('label'=>null, 'bold'=>false, 'params'=>'', 'id'=>null),
119 'text_cells' => array('label', 'name', 'value'=>null, 'size'=>"", 'max'=>"", 'title'=>false,
120 'labparams'=>"", 'post_label'=>"", 'inparams'=>"")
122 // format functions used in various modes
124 // field format => (view [,edit])
125 '' => array('label_cell', 'text_cells'), // default
126 'price' => array('label_cell', 'amount_cell'),
138 * PHP->user format values convertion.
141 function set_output($data=null, &$output=null)
146 if (!isset($output)) {
148 $prefix = $this->name;
152 foreach($this->fields as $name => $fmt) {
157 } elseif (!is_array($fmt))
158 $fmt = array('fmt' => $fmt);
160 $post = $prefix.(isset($fmt['post']) ? $fmt['post'] : $name);
162 $fld = isset($fmt['fld']) ? $fmt['fld'] : $name;
164 if (is_object($this->data))
165 $value = isset($this->data->$fld) ? $this->data->$fld : @$fmt['dflt'];
167 $value = isset($this->data[$fld]) ? $this->data[$fld] : @$fmt['dflt'];
170 $output[$post] = $this->_format_output($value, @$fmt['fmt']);
176 * User->php format values convertion.
177 * $input - data in user format
178 * $all - return also null values for non-existing input field.
180 function get_input($input=null, $all=false)
185 if ($this->name) // strip view name prefix
186 foreach($input as $postkey=>$postval )
188 if (strpos($postkey, $this->name) === 0)
190 $id = substr($postkey, strlen($this->name));
191 $input[$id] = $postval;
193 unset($input[$postkey]);
197 foreach ($this->fields as $name => $fmt) {
198 if (is_int($name)) { // direct string passed: this is name of field
200 $fmt = array(); // default format
201 } elseif (!is_array($fmt))
202 $fmt = array('fmt' => $fmt);
203 $post = isset($fmt['post']) ? $fmt['post'] : $name; // input name (default to field name)
204 $fld = isset($fmt['fld']) ? $fmt['fld'] : $name; // input value (default to field name)
207 // if ($all || array_key_exists($post, $input))
209 if (@$fmt['fmt'] == 'check')
210 $value = @$input[$post] ? 1 : 0;
212 $value = $this->_format_input(@$input[$post], @$fmt['fmt']);
214 // if (is_array($data))
215 if ($all || isset($value))
216 $data[$fld] = $value;
218 // $data->$fld = $value;
225 //--------------------------------------------------------
227 // Return data formatted according to field descriptions.
229 function get_fields_views($input=null, $mode=null)
236 foreach ($this->fields as $name => $fmt) {
241 $post = isset($fmt['post']) ? $fmt['post'] : $name;
242 $fld = isset($fmt['fld']) ? $fmt['fld'] : $name;
244 $value = $this->_format_cells(@$input[$post], @$fmt['fmt'], $mode);
246 $view[$fld] = $value;
254 // Template for simple table editors
257 class simple_crud_view extends user_view {
264 var $_none = ''; // selector value when no item is selected
265 var $pre_handlers; // control buttons and related methods called before view display
270 var $options = array(
271 'delete' => true, // true or message for successfull action.
278 var $display_both = false; //when set to true both list and editor are displayed all the time (eventually set in sub classes)
281 function __construct($name, $data_set = null, $options=array())
283 parent::__construct($name);
285 $this->options = array_merge($this->options, $options);
287 $this->views[''] = 'list_view'; // default view
289 if ($this->options['update'])
290 $this->_add_action('Edit', '_edit', _('Edit'), _('Edit document line'), ICON_EDIT, '',
293 if ($this->options['delete'])
294 $this->_add_action('Delete', '_delete', _('Delete'), _('Remove line from document'), ICON_DELETE, '',
297 if ($this->options['update'])
298 $this->_add_action('UPDATE', '_update', _('Update'), _('Submit changes'), ICON_UPDATE, 'default',
301 $this->_add_action('RESET', '_cancel', _('Cancel'), _('Cancel changes'), ICON_ESCAPE, 'cancel',
304 if ($this->options['insert'])
305 $this->_add_action('ADD', '_add', _('Add'), _('Add new'), ICON_ADD, 'default',
308 if ($this->options['insert'])
309 $this->_add_action('NEW', '_add', _('New'), _('Add new'), ICON_ADD, 'default',
312 if ($this->options['clone'])
313 $this->_add_action('CLONE', '_cloning', _('Clone'), _('Clone'), ICON_ADD, '',
316 $this->data_set = $data_set;
317 $this->fields = $data_set->fields;
319 // $this->_prev_status();
322 function _add_action($name, $handler, $but_value=null, $but_title=false, $but_icon=false, $aspect='', $view=null)
324 $this->pre_handlers[$name] = $handler;
327 $this->tool_buttons[$name] = array($but_value, $but_title, $but_icon, $aspect);
330 $this->_add_mode($name, $view);
333 function _add_mode($name, $view)
335 $this->views[$name] = $view;
338 function _prev_status()
340 // Restore previous mode/key (obsolete for views stored in session)
342 $mod = get_post($this->name.'Mode', $this->Mode);
344 if (is_array($mod)) {
346 $this->selected_id = $val!==null ? @quoted_printable_decode($val) : $this->_none;
350 $this->selected_id = $this->_none;
356 function _check_mode()
360 $mod = '';//$this->Mode;
361 // Detect action (mode change)
362 foreach (array_keys($this->pre_handlers) as $m) { // check button controls
364 if (isset($_POST[$this->name.$m])) {
365 unset($_POST['_focus']); // focus on first form entry
366 $Ajax->activate($this->name.'_div');
367 $Ajax->activate($this->name.'_controls');
368 $val = is_array($_POST[$this->name.$m]) ? key($_POST[$this->name.$m]) : null;
369 $this->prev_id = $this->selected_id;
370 $this->selected_id = $val!==null ? @quoted_printable_decode($val) : $this->_none;
374 if (!$mod && $_SERVER['REQUEST_METHOD'] == 'GET') // initialize on every GET
380 function display_error()
382 $this->errors = array_merge($this->data_set->errors, $this->errors);
383 $firsterr = reset($this->errors);
384 $field = key($this->errors);
386 if (!is_numeric($field))
387 set_focus($this->name.$field);
388 display_error($firsterr);
390 $this->errors = array(); // clean up to prevent false errors on object reuse
393 // Set record for edition
395 function _edit($mode)
397 if ($this->selected_id != $this->prev_id || $mode != $this->Mode) { // get record for edition
398 $this->data = $this->data_set->get($this->selected_id !== $this->_none ? $this->selected_id : null);
401 // if ($this->Mode != $mode) {
405 // $this->display_error();
410 // Update record after edition
412 function _update($mode)
414 if (!$this->options['update'])
417 $this->data = $this->get_input();
418 if ($this->data_set->update_check($this->selected_id, $this->data) && $this->data_set->update($this->selected_id, $this->data)) {
419 $this->selected_id = $this->_none;
420 $this->Mode = 'RESET';
422 if (is_string($this->options['update']))
423 display_notification($this->options['update']);
427 $this->display_error();
436 if (!$this->options['insert'])
439 if ($mode == 'ADD') {
440 $this->data = $this->get_input();
441 if ($this->data_set->insert_check($this->data) && ($this->data_set->insert($this->data) !== false)) {
443 if (is_string($this->options['insert']))
444 display_notification($this->options['insert']);
445 $this->Mode = 'RESET';
449 $this->display_error();
451 $this->data = $this->data_set->get();
456 // Delete selected record
460 if (!$this->options['delete'])
463 if ($this->data_set->delete_check($this->selected_id) && $this->data_set->delete($this->selected_id))
465 if (is_string($this->options['delete']))
466 display_notification($this->options['delete']);
468 $this->display_error();
472 // Return to listing view
477 $this->selected_id = $this->_none;
478 if ($this->display_both)
480 $this->data = $this->data_set->get();
484 if (!$this->display_both)
485 $Ajax->activate($this->name.'_div');
486 $this->Mode = 'RESET';
489 // Clone record for new edition
493 if (!$this->options['clone'])
496 $this->_edit('Edit');
497 $this->selected_id = $this->_none;
500 Generate form controls
503 function _record_controls($list_view = false)
505 $clone = $this->options['clone'] && $this->selected_id != $this->_none;
507 div_start($this->name.'_controls');
511 $this->action_button('NEW');
513 if ($this->Mode == 'NEW' || $this->selected_id==$this->_none)
515 $this->action_button('ADD');
516 $this->action_button('RESET');
518 $this->action_button('UPDATE', $this->selected_id);
519 if ($clone && $this->display_both)
520 $this->action_button('CLONE', $this->selected_id);
521 $this->action_button('RESET');
529 //===========================================================================
534 // Submit buttons for form actions
536 function action_button($action, $selected_id=null)
538 list($value, $title, $icon, $aspect) = $this->tool_buttons[$action];
539 submit($this->name.$action.(isset($selected_id) ? "[$selected_id]" : ''), $value, true, $title, $aspect, $icon);
543 // Tool button for grid line actions.
545 function tool_button($name, $selected_id=null, $params='')
547 $b = $this->tool_buttons[$name];
549 return "<td align='center' $params>"
550 .button( "{$this->name}$name"
551 .($selected_id === null || $selected_id === $this->_none ? '': "[$selected_id]"),
552 $b[0], $b[1], $b[2], $b[3])."</td>";
555 // Main function - display current CRUD editor content
557 function show($Mode=null)
560 $Mode = $this->_check_mode(true);
562 div_start($this->name.'_div');
564 if (array_key_exists($Mode, $this->pre_handlers)) {
565 $fun = $this->pre_handlers[$Mode];
567 call_user_func($fun, $Mode);
572 if (isset($this->views[$this->Mode]))
573 $this->{$this->views[$this->Mode]}($Mode);
575 $this->{$this->views['']}($Mode); // default view
577 // this is needed only when we use temporary crud object together with ajax screen updates
578 hidden($this->name.'Mode'.'['.$this->selected_id.']', $this->Mode);
583 // Optional things like focus set performed on edition cancel.
590 // Show database content in pager/table
591 // parameter $Mode contains new mode on change, or '' otherwise
593 function list_view($Mode) {
594 display_notification(__FUNCTION__. ' is not defined...');
598 // Show record editor screen content
599 // parameter $Mode contains new mode on change, or '' otherwise
601 function editor_view($Mode) {
602 display_notification(__FUNCTION__. ' is not defined...');
606 class selector_crud_view extends simple_crud_view {
608 function __construct($name, $data_set = null, $options=array())
610 $this->display_both = true;
611 parent::__construct($name, $data_set, $options);
614 function _check_mode()
618 // list controls lookup
619 $prev_mode = $this->Mode;
622 $list = $this->name.'_id';
623 $this->prev_id = $this->selected_id;
624 $this->selected_id = get_post($list);
625 if (list_updated($list)) {
626 $Ajax->activate($this->name);
627 $Ajax->activate($this->name.'_controls');
628 $mod = $this->selected_id == $this->_none ? 'RESET' : 'Edit';
630 $mod = simple_crud_view::_check_mode();
633 if ($mod != $prev_mode) {
634 $Ajax->activate($this->name.'_controls');
636 if (get_post('_show_inactive_update'))
638 $Ajax->activate($this->name.'_id');
641 $_POST[$list] = $this->selected_id;
649 $_POST[$this->name.'_id'] = $this->selected_id = $this->_none;
650 $Ajax->activate($this->name.'_id');
655 // Template for line table editors
658 class table_crud_view extends simple_crud_view {
662 function __construct($name, &$data_set = null, $options=array())
664 $this->display_both = true;
665 parent::__construct($name, $data_set, $options);
669 * Returns selector string from data or current selected_id
672 function curr_id($data=null)
675 return $data ? implode('_', array_intersect_key($data, array_fill_keys($this->key, true))) : null; // php 5.2.0
677 return $this->selected_id;
681 * Show single line from record table
684 function show_record_row($line_no, $record=array())
687 $id = $this->curr_id($record); // extract key
690 $this->set_output($line, $view);
692 $editables = array();
693 if ($this->options['edit'] && $this->Mode=='Edit' && $this->selected_id==$id)
695 $editables = $this->data_set->edit_status($key);
697 $ctrls = $this->get_field_views($record, $editables);
698 alt_table_row_color($k);
699 foreach($ctrls as $key => $fld)
706 if ($this->options['edit']) {
708 $this->tool_button($this->selected_id==$id ? 'UPDATE' : 'Edit', $id);
711 if ($this->options['delete']$this->selected_id==$id) {
713 $this->tool_button( ? 'Cancel' : 'Delete', $id);
718 if ($this->options['insert'])
720 alt_table_row_color($k);
721 $this->new_line($line_no, $line);
726 function show_list_headers()
730 // Main function - display current CRUD table content
732 function show($Mode=null)
735 $Mode = $this->_check_mode(true);
736 div_start($this->name.'_div');
738 if (array_key_exists($Mode, $this->pre_handlers))
740 $fun = $this->pre_handlers[$Mode];
743 if (is_object($fun[0]))
744 $fun[0]->$fun[1]($Mode);
750 display_heading($this->title);
752 start_table(TABLESTYLE, "width=95%");
753 $this->show_list_headers();
755 foreach($this->data_set->get_all() as $line_no => $line) {
756 $this->show_record_row($line_no, $line);
758 $this->show_record_row();
761 hidden($this->name.'Mode'.'['.$this->selected_id.']', $this->Mode);