Fixed hidden fields support for ajax driven divs.
[fa-stable.git] / includes / ui / ui_controls.inc
1 <?php
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 ***********************************************************************/
12 /*
13         Retrieve value of POST variable(s).
14         For $name passed as array $dflt is not used, 
15         default values can be passed as values with non-numeric keys instead.
16         If some field have user formatted numeric value, pass float default value to
17         convert automatically to POSIX.
18 */
19 function get_post($name, $dflt='')
20 {
21         if (is_array($name)) {
22                 $ret = array();
23                 foreach($name as $key => $dflt)
24                         if (!is_numeric($key)) {
25                                 $ret[$key] = is_float($dflt) ? input_num($key, $dflt) : get_post($key, $dflt);
26                         } else {
27                                 $ret[$dflt] = get_post($dflt, null);
28                         }
29                 return $ret;
30         } else
31                 return is_float($dflt) ? input_num($name, $dflt) : 
32                                 ((!isset($_POST[$name]) || $_POST[$name] === '') ? $dflt : $_POST[$name]);
33 }
34 //---------------------------------------------------------------------------------
35
36 function start_form($multi=false, $dummy=false, $action="", $name="")
37 {
38         // $dummy - leaved for compatibility with 2.0 API
39
40         if ($name != "")
41                 $name = "name='$name'";
42         if ($action == "")
43                 $action = $_SERVER['PHP_SELF'];
44
45         if ($multi)
46                 echo "<form enctype='multipart/form-data' method='post' action='$action' $name>\n";
47         else
48                 echo "<form method='post' action='$action' $name>\n";
49
50 }
51
52 /*
53         Flush hidden fields buffer.
54 */
55 function output_hidden()
56 {
57         global $hidden_fields;
58
59         if (is_array($hidden_fields))
60                 echo implode('', $hidden_fields);
61         $hidden_fields = array();
62 }
63 //---------------------------------------------------------------------------------
64
65 function end_form($breaks=0)
66 {
67         global $Ajax, $hidden_fields;
68
69         $_SESSION['csrf_token'] = hash('sha256', uniqid(mt_rand(), true));
70         if ($breaks)
71                 br($breaks);
72         hidden('_focus');
73         hidden('_modified', get_post('_modified', 0));
74         hidden('_token', $_SESSION['csrf_token']);
75         
76         output_hidden();
77         echo "</form>\n";
78         $Ajax->activate('_token');
79 }
80
81 function check_csrf_token()
82 {
83         if ($_SESSION['csrf_token'] != @$_POST['_token'])
84         {
85                 display_error(_("Request from outside of this page is forbidden."));
86                 error_log(_("CSRF attack detected from: ").@$_SERVER['HTTP_HOST'].' ('.@$_SERVER['HTTP_REFERER'].')');
87                 return false;
88         }
89         return true;
90 }
91
92 function start_table($class=false, $extra="", $padding='2', $spacing='0')
93 {
94         echo "<center><table";
95         if ($class == TABLESTYLE_NOBORDER)
96                 echo " class='tablestyle_noborder'";
97         elseif ($class == TABLESTYLE2)
98                 echo " class='tablestyle2'";
99         elseif ($class == TABLESTYLE)
100                 echo " class='tablestyle'";
101         if ($extra != "")
102                 echo " $extra";
103         echo " cellpadding='$padding' cellspacing='$spacing'>\n";
104 }
105
106 function end_table($breaks=0)
107 {
108         echo "</table></center>\n";
109         output_hidden();
110         if ($breaks)
111                 br($breaks);
112 }
113
114 function start_outer_table($class=false, $extra="", $padding='2', $spacing='0', $br=false)
115 {
116         if ($br)
117                 br();
118         start_table($class, $extra, $padding, $spacing);
119         echo "<tr valign=top><td>\n"; // outer table
120 }
121
122 function table_section($number=1, $width=false)
123 {
124         if ($number > 1)
125         {
126                 echo "</table>\n";
127                 output_hidden();
128                 $width = ($width ? "width='$width'" : "");
129                 //echo "</td><td class='tableseparator' $width>\n"; // outer table
130                 echo "</td><td style='border-left:1px solid #cccccc;' $width>\n"; // outer table
131         }
132         echo "<table class='tablestyle_inner'>\n";
133 }       
134
135 function end_outer_table($breaks=0, $close_table=true)
136 {
137         if ($close_table)
138         {
139                 echo "</table>\n";
140                 output_hidden();
141         }
142         echo "</td></tr>\n";
143         end_table($breaks);
144 }
145 //
146 //  outer table spacer
147 //
148 function vertical_space($params='')
149 {
150         echo "</td></tr><tr><td valign=center $params>";
151 }
152
153 function meta_forward($forward_to, $params="")
154 {
155     global $Ajax;
156         echo "<meta http-equiv='Refresh' content='0; url=$forward_to?$params'>\n";
157         echo "<center><br>" . _("You should automatically be forwarded.");
158         echo " " . _("If this does not happen") . " " . "<a href='$forward_to?$params'>" . _("click here") . "</a> " . _("to continue") . ".<br><br></center>\n";
159         if ($params !='') $params = '?'.$params;
160         $Ajax->redirect($forward_to.$params);
161         exit;
162 }
163
164 //-----------------------------------------------------------------------------------
165 // Find and replace hotkey marker.
166 // if $clean == true marker is removed and clean label is returned 
167 // (for use in wiki help system), otherwise result is array of label 
168 // with underlined hotkey letter and access property string.
169 //
170 function access_string($label, $clean=false)
171 {
172         $access = '';
173         $slices = array();
174
175         if (preg_match('/(.*)&([a-zA-Z0-9])(.*)/', $label, $slices))    
176         {
177                 $label = $clean ? $slices[1].$slices[2].$slices[3] :
178                         $slices[1].'<u>'.$slices[2].'</u>'.$slices[3];
179                 $access = " accesskey='".strtoupper($slices[2])."'";
180         }
181         
182         $label = str_replace( '&&', '&', $label);
183
184         return $clean ? $label : array($label, $access);
185 }
186
187 function hyperlink_back($center=true, $no_menu=true, $type_no=0, $trans_no=0, $final=false)
188 {
189         global $path_to_root;
190
191         if ($center)
192                 echo "<center>";
193         $id = 0;        
194         if ($no_menu && $trans_no != 0)
195         {
196                 include_once($path_to_root."/admin/db/attachments_db.inc");
197                 $id = has_attachment($type_no, $trans_no);
198                 $attach = get_attachment_string($type_no, $trans_no);
199         echo $attach;   
200     }
201         $width = ($id != 0 ? "30%" : "20%");    
202         start_table(false, "width='$width'");
203         start_row();
204         if ($no_menu)
205         {
206                 echo "<td align=center><a href='javascript:window.print();'>"._("Print")."</a></td>\n";
207         }
208         echo "<td align=center><a href='javascript:goBack(".($final ? '-2' : '').");'>".($no_menu ? _("Close") : _("Back"))."</a></td>\n";
209         end_row();
210         end_table();
211         if ($center)
212                 echo "</center>";
213         echo "<br>";
214 }
215
216 function hyperlink_no_params($target, $label, $center=true)
217 {
218         $id = default_focus();
219         $pars = access_string($label);
220         if ($target == '')
221                 $target = $_SERVER['PHP_SELF'];
222         if ($center)
223                 echo "<br><center>";
224         echo "<a href='$target' id='$id' $pars[1]>$pars[0]</a>\n";
225         if ($center)
226                 echo "</center>";
227 }
228
229 function hyperlink_no_params_td($target, $label)
230 {
231         echo "<td>";
232         hyperlink_no_params($target, $label);
233         echo "</td>\n";
234 }
235
236 function viewer_link($label, $url='', $class='', $id='',  $icon=null)
237 {
238         global $path_to_root;
239         
240         if ($class != '')
241                 $class = " class='$class'";
242
243         if ($id != '')
244                 $class = " id='$id'";
245
246         if ($url != "")
247         {
248                 $pars = access_string($label);
249                 if (user_graphic_links() && $icon)
250                         $pars[0] = set_icon($icon, $pars[0]);
251                 $preview_str = "<a target='_blank' $class $id href='$path_to_root/$url' onclick=\"javascript:openWindow(this.href,this.target); return false;\"$pars[1]>$pars[0]</a>";
252         }
253         else
254                 $preview_str = $label;
255  return $preview_str;
256 }
257
258 function menu_link($url, $label, $id=null)
259 {
260
261         $id = default_focus($id);
262         $pars = access_string($label);
263         return "<a href='$url' class='menu_option' id='$id' $pars[1]>$pars[0]</a>";
264 }
265
266 function submenu_option($title, $url, $id=null)
267 {
268         global $path_to_root;
269         display_note(menu_link($path_to_root . $url, $title, $id), 0, 1);
270 }
271
272 function submenu_view($title, $type, $number, $id=null)
273 {
274         display_note(get_trans_view_str($type, $number, $title, false, 'viewlink', $id), 0, 1);
275 }
276
277 function submenu_print($title, $type, $number, $id=null, $email=0, $extra=0)
278 {
279         display_note(print_document_link($number, $title, true, $type, false, 'printlink', $id, $email, $extra), 0, 1);
280 }
281 //-----------------------------------------------------------------------------------
282
283 function hyperlink_params($target, $label, $params, $center=true)
284 {
285         $id = default_focus();
286         
287         $pars = access_string($label);
288         if ($target == '')
289                 $target = $_SERVER['PHP_SELF'];
290         if ($center)
291                 echo "<br><center>";
292         echo "<a id='$id' href='$target?$params'$pars[1]>$pars[0]</a>\n";
293         if ($center)
294                 echo "</center>";
295 }
296
297 function hyperlink_params_td($target, $label, $params)
298 {
299         echo "<td>";
300         hyperlink_params($target, $label, $params, false);
301         echo "</td>\n";
302 }
303
304 //-----------------------------------------------------------------------------------
305
306 function hyperlink_params_separate($target, $label, $params, $center=false)
307 {
308         $id = default_focus();
309
310         $pars = access_string($label);
311         if ($center)
312                 echo "<br><center>";
313         echo "<a target='_blank' id='$id' href='$target?$params' $pars[1]>$pars[0]</a>\n";
314         if ($center)
315                 echo "</center>";
316 }
317
318 function hyperlink_params_separate_td($target, $label, $params)
319 {
320         echo "<td>";
321         hyperlink_params_separate($target, $label, $params);
322         echo "</td>\n";
323 }
324
325 //--------------------------------------------------------------------------------------------------
326
327 function alt_table_row_color(&$k, $extra_class=null)
328 {
329         $classes = $extra_class ? array($extra_class) : array();
330         if ($k == 1)
331         {
332                 array_push($classes, 'oddrow');
333                 $k = 0;
334         }
335         else
336         {
337                 array_push($classes, 'evenrow');
338                 $k++;
339         }
340         echo "<tr class='".implode(' ', $classes)."'>\n";
341 }
342
343 function table_section_title($msg, $colspan=2)
344 {
345         echo "<tr><td colspan=$colspan class='tableheader'>$msg</td></tr>\n";
346 }
347
348 function table_header($labels, $params='')
349 {
350         start_row();
351         foreach ($labels as $label)
352                 labelheader_cell($label, $params);
353         end_row();
354 }
355 //-----------------------------------------------------------------------------------
356
357 function start_row($param="")
358 {
359         if ($param != "")
360                 echo "<tr $param>\n";
361         else
362                 echo "<tr>\n";
363 }
364
365 function end_row()
366 {
367         echo "</tr>\n";
368 }
369
370 function br($num=1)
371 {
372         for ($i = 0; $i < $num; $i++)
373                 echo "<br>";
374 }
375
376 $ajax_divs = array();
377
378 function div_start($id='', $trigger=null, $non_ajax=false)
379 {
380     global $ajax_divs;
381
382         if ($non_ajax) { // div for non-ajax elements
383                 array_push($ajax_divs, array($id, null));
384                 echo "<div style='display:none' class='js_only' ".($id !='' ? "id='$id'" : '').">";
385         } else { // ajax ready div
386                 array_push($ajax_divs, array($id, $trigger===null ? $id : $trigger));
387                 echo "<div ". ($id !='' ? "id='$id'" : '').">";
388                 ob_start();
389         }
390 }
391
392 function div_end()
393 {
394     global $ajax_divs, $Ajax;
395
396         output_hidden();
397     if (count($ajax_divs))
398     {
399                 $div = array_pop($ajax_divs);
400                 if ($div[1] !== null)
401                         $Ajax->addUpdate($div[1], $div[0], ob_get_flush());
402     }
403         echo "</div>";
404 }
405
406 //-----------------------------------------------------------------------------
407 //      Tabbed area:
408 //      $name - prefix for widget internal elements:
409 //              Nth tab submit name:  {$name}_N
410 //              div id: _{$name}_div
411 //              sel (hidden) name: _{$name}_sel
412 // $tabs - array of tabs; string: tab title or array(tab_title, enabled_status)
413
414 function tabbed_content_start($name, $tabs, $dft='') {
415     global $Ajax;
416
417     $selname = '_'.$name.'_sel';
418         $div = '_'.$name.'_div';
419
420         $sel = find_submit($name.'_', false);
421         if($sel==null)
422                 $sel = get_post($selname, (string)($dft==='' ? key($tabs) : $dft));
423
424         if ($sel!==@$_POST[$selname])
425                 $Ajax->activate($name);
426
427         $_POST[$selname] = $sel;
428
429         div_start($name);
430         $str = "<ul class='ajaxtabs' rel='$div'>\n";
431         foreach($tabs as $tab_no => $tab) {
432                 
433                 $acc = access_string(is_array($tab) ? $tab[0] : $tab);
434                 $disabled = (is_array($tab) && !$tab[1])  ? 'disabled ' : '';
435                 $str .= ( "<li>"
436                         ."<button type='submit' name='{$name}_".$tab_no
437                         ."' class='".((string)$tab_no===$sel ? 'current':'ajaxbutton')."' $acc[1] $disabled>"
438                         ."<span>$acc[0]</span>"
439                         ."</button>\n"
440                         ."</li>\n" );
441         }
442
443         $str .= "</ul>\n";
444         $str .= "<div class='spaceBox'></div>\n";
445         $str .= "<input type='hidden' name='$selname' value='$sel'>\n";
446         $str .= "<div class='contentBox' id='$div'>\n";
447         echo $str;
448 }
449
450 function tabbed_content_end() {
451         output_hidden();
452         echo "</div>"; // content box (don't change to div_end() unless div_start() is used above)
453         div_end(); // tabs widget
454 }
455
456 function tab_changed($name)
457 {
458         $to = find_submit("{$name}_", false);
459         if (!$to) return null;
460
461         return array('from' => $from = get_post("_{$name}_sel"),
462                 'to' => $to);
463 }
464
465 /* Table editor interfaces. Key is editor type
466         0 => url of editor page
467         1 => hotkey code
468         2 => context help
469 */
470 $popup_editors = array(
471         'customer' => array('/sales/manage/customers.php?debtor_no=', 
472                 113,    _("Customers"), 900, 500),
473         'branch' => array('/sales/manage/customer_branches.php?SelectedBranch=', 
474                 114, _("Branches"), 900, 700),
475         'supplier' => array('/purchasing/manage/suppliers.php?supplier_id=', 
476                 113, _("Suppliers"), 900, 700),
477         'item' => array('/inventory/manage/items.php?stock_id=', 
478                 115, _("Items"), 800, 600)
479 );
480 /*
481         Bind editors for various selectors.
482         $type - type of editor
483         $input - name of related input field
484         $caller - optional function key code (available values F1-F12: 112-123,
485                 true: default)
486 */
487 function set_editor($type, $input, $caller=true)
488 {
489         global $path_to_root, $Editors, $popup_editors, $Pagehelp;
490
491         $key = $caller===true ? $popup_editors[$type][1] : $caller;
492
493         $Editors[$key] = array( $path_to_root . $popup_editors[$type][0], $input, 
494                 $popup_editors[$type][3], $popup_editors[$type][4]);
495         
496         $help = 'F' . ($key - 111) . ' - ';
497         $help .= $popup_editors[$type][2];
498         $Pagehelp[] = $help;
499 }
500 //------------------------------------------------------------------------------
501 // Procedures below are now obsolete. Preserved for eventual future use.
502
503 /*
504         External page call with saving current context.
505         $call - url of external page
506         $ctx - optional. name of SESSION context object or array of names of POST 
507                 variables saved on call
508 */
509 function context_call($call, $ctx='')
510 {
511         if (is_array($ctx)) 
512         {
513                 foreach($ctx as $postname)
514                 {
515                         $context[$postname] = get_post($postname);
516                 }
517         } else 
518                 $context = isset($_SESSION[$ctx]) ? $_SESSION[$ctx] : null;
519
520         array_unshift($_SESSION['Context'], array('name' => $ctx, 
521                 'ctx' => $context,
522                 'caller' => $_SERVER['PHP_SELF'],
523                 'ret' => array()));
524         meta_forward($call);
525 }
526 /*
527         Restores context after external page call and
528         returns array of data passed by external page.
529 */
530 function context_restore()
531 {
532         if ( count($_SESSION['Context'])) {
533                 if ($_SERVER['PHP_SELF'] == $_SESSION['Context'][0]['caller']) {
534                         $ctx = array_shift($_SESSION['Context']);
535                         if ($ctx) {
536                                 if (is_array($ctx['ctx'])) {
537                                         foreach($ctx['ctx'] as $name => $val) 
538                                         {
539                                                 $_POST[$name] = $val;
540                                         }
541                                 } else
542                                         if ($ctx['name']!='')
543                                                 $_SESSION[$ctx['name']] = $ctx['ctx'];
544                                 return $ctx['ret'];
545                         }
546                 }
547         }
548         return false;
549 }
550
551 /*
552         Return to caller page if the page was called from external context.
553 */
554 function context_return($ret)
555 {
556         if ( count($_SESSION['Context'])) {
557                 $ctx = &$_SESSION['Context'][0];
558                 $ctx['ret'] = $ret;
559                 meta_forward( $ctx['caller'] );
560         }
561 }
562 /*
563         Clearing context stack after page cancel.
564 */
565 function context_reset()
566 {
567         $_SESSION['Context'] = array();
568 }
569 /*
570         Context stack initialization
571 */
572 if (!isset($_SESSION['Context'])) {
573                 context_reset();
574 }
575 /*
576         Redirector for selector F4 calls.
577         $sel_editors is array of selname=>editor_page
578 */
579 function editor_redirect($sel_editors, $save_fun='') {
580         foreach ($sel_editors as $selname=>$editor)
581                 if (isset($_POST['_'.$selname.'_editor'])) {
582                         if (function_exists($save_fun))
583                                 $save_fun();
584                         unset($_POST['_'.$selname.'_editor']);
585                         context_call($editor, array_keys($_POST));
586                 }
587 }
588 /*
589         Return procedure for selector F4 calls
590 */
591 function editor_return($vars, $restore_fun='') {
592         if (function_exists($restore_fun))
593                 $restore_fun();
594
595         if ($ret = context_restore()) {
596                 foreach ($vars as $postname=>$retname)
597                         if (isset($ret[$retname])) {
598                                 $_POST[$postname] = $ret[$retname];
599                                 set_focus($postname);
600                         }
601         }
602 }
603
604 function confirm_dialog($submit, $msg) {
605         if (find_post($submit)) {
606                 display_warning($msg);
607                 br();
608                 submit_center_first('DialogConfirm', _("Proceed"), '', true);
609                 submit_center_last('DialogCancel', _("Cancel"), '', 'cancel');
610                 return 0;
611         } else
612                 return get_post('DialogConfirm', 0);
613 }
614
615 /*
616         Block menu/shortcut links during transaction procesing.
617 */
618 function page_processing($msg = false)
619 {
620         global $Ajax;
621
622         if ($msg === true)
623                 $msg = _('Entered data has not been saved yet.\nDo you want to abandon changes?');
624
625         $js = "_validate._processing=" . (
626                 $msg ? '\''.strtr($msg, array("\n"=>'\\n')) . '\';' : 'null;');
627         if (in_ajax()) {
628                 $Ajax->addScript(true, $js);
629         } else
630                 add_js_source($js);
631 }
632
633 function page_modified($status = true)
634 {
635         global $Ajax;
636
637         $js = "_validate._modified=" . ($status ? 1:0).';';
638         if (in_ajax()) {
639                 $Ajax->addScript(true, $js);
640         } else
641                 add_js_source($js);
642 }
643
644 ?>