Feature 5388: Print Invoices (documents) list gets too long. Fixed by default 180...
[fa-stable.git] / includes / main.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 include_once($path_to_root . "/includes/db/connect_db.inc");
13
14 include_once($path_to_root . "/admin/db/transactions_db.inc");
15 include_once($path_to_root . "/includes/types.inc");
16 include_once($path_to_root . "/includes/references.inc");
17 include_once($path_to_root . "/includes/db/comments_db.inc");
18 include_once($path_to_root . "/includes/db/sql_functions.inc");
19 include_once($path_to_root . "/includes/db/audit_trail_db.inc");
20
21 include_once($path_to_root . "/admin/db/users_db.inc");
22 include_once($path_to_root . "/includes/ui/ui_view.inc");
23 include_once($path_to_root . "/includes/ui/ui_controls.inc");
24
25 $page_nested = -1;
26 // static js files path
27 $js_path = $path_to_root.'/js/';
28 // standard external js scripts included in all files
29 $js_static = array('JsHttpRequest.js', 'behaviour.js', 'utils.js', 'inserts.js');
30 // additional js source included in header
31 $js_lib = $js_userlib = array();
32
33 function page($title, $no_menu=false, $is_index=false, $onload="", $js="", $script_only=false, $css='')
34 {
35
36         global $path_to_root, $page_security, $page_nested;
37
38         if (++$page_nested) return;
39
40         $hide_menu = $no_menu;
41
42         include_once($path_to_root . "/includes/page/header.inc");
43
44         page_header($title, $no_menu, $is_index, $onload, $js, $css);
45         check_page_security($page_security);
46 //      error_box();
47         if($script_only) {
48                 echo '<noscript>';
49                 echo display_heading(_('This page is usable only with javascript enabled browsers.'));
50                 echo '</noscript>';
51                 div_start('_page_body', null, true);
52         } else {
53                 div_start('_page_body'); // whole page content for ajax reloading
54         }
55 }
56
57 function end_page($no_menu=false, $is_index=false, $final_screen=false, $type_no=0, $trans_no=0)
58 {
59         global $path_to_root, $page_nested;
60
61         if ($page_nested-- > 0) return;
62
63         if (!$is_index && function_exists('hyperlink_back'))
64                 hyperlink_back(true, $no_menu, $type_no, $trans_no, $final_screen);
65         div_end();      // end of _page_body section
66
67         include_once($path_to_root . "/includes/page/footer.inc");
68         page_footer($no_menu, $is_index);
69 }
70
71 function css_files_ensure_init() {
72         global $css_files, $path_to_root;
73
74         if (!isset($css_files))
75         {
76                 $theme = user_theme();
77                 $css_files = array();
78                 $css_files[] = $path_to_root . "/themes/$theme/default.css";
79         }
80 }
81
82 function add_css_file($filename)
83 {
84         global $css_files;
85         css_files_ensure_init();
86         $css_files[] = $filename;
87 }
88
89 function cache_js_file($fpath, $text) 
90 {
91         global $SysPrefs;
92
93         if (!$SysPrefs->go_debug) $text = js_compress($text);
94
95     $file = force_open($fpath);
96         if (!$file) return false;
97         if (!fwrite($file, $text)) return false;
98         return fclose($file);
99
100 }
101
102 /*
103         Open file for writing with creration of subfolders if needed.
104 */
105 function force_open($fname)
106 {
107         $file = pathinfo($fname);
108
109         $path = $fname[0] == '/' ? '/' : '';
110         $tree = explode('/', $file['dirname']);
111         foreach($tree as $level) {
112                 $path .= $level;
113                 if (!file_exists($path)) {
114                         if (!mkdir($path)) {
115                                 return null;
116                         }
117                 }
118                 $path .= '/';
119         }
120         return fopen($fname, 'w');
121 }
122
123 function add_js_file($filename) 
124 {
125           global $js_static;
126
127           $search = array_search($filename, $js_static);
128           if ($search === false || $search === null) // php>4.2.0 returns null
129                 $js_static[] = $filename;       
130 }
131
132 function add_js_ufile($filename) 
133 {
134           global $js_userlib;
135
136           $search = array_search($filename, $js_userlib);
137           if ($search === false || $search === null) // php>4.2.0 returns null
138                 $js_userlib[] = $filename;
139 }
140
141 function add_js_source($text) 
142 {
143           global $js_lib;
144
145           $search = array_search($text, $js_lib);
146           if ($search === false || $search === null) // php>4.2.0 returns null
147                 $js_lib[] = $text;
148 }
149
150 /**
151  * Compresses the Javascript code for more efficient delivery.
152  * copyright (c) 2005 by Jared White & J. Max Wilson
153  * http://www.xajaxproject.org
154  * Added removing comments from output.
155  * Warning: Fails on RegExp with quotes - use new RegExp() in this case.
156  */
157 function js_compress($sJS)
158 {
159         //remove windows cariage returns
160         $sJS = str_replace("\r","",$sJS);
161         
162         //array to store replaced literal strings
163         $literal_strings = array();
164         
165         //explode the string into lines
166         $lines = explode("\n",$sJS);
167         //loop through all the lines, building a new string at the same time as removing literal strings
168         $clean = "";
169         $inComment = false;
170         $literal = "";
171         $inQuote = false;
172         $escaped = false;
173         $quoteChar = "";
174         
175         for($i=0;$i<count($lines);$i++)
176         {
177                 $line = $lines[$i];
178                 $inNormalComment = false;
179         
180                 //loop through line's characters and take out any literal strings, replace them with ___i___ where i is the index of this string
181                 $len = strlen($line);
182                 if (version_compare(PHP_VERSION, '7.0.0') >= 0) // uninitialized string offser error fix. @Braath Waate
183                         $line .= chr(32);
184                 
185                 for($j=0;$j<$len;$j++)
186                 {
187                         $c = $line[$j];         // this is _really_ faster than subst
188                         $d = $c.$line[$j+1];
189         
190                         //look for start of quote
191                         if(!$inQuote && !$inComment)
192                         {
193                                 //is this character a quote or a comment
194                                 if(($c=="\"" || $c=="'") && !$inComment && !$inNormalComment)
195                                 {
196                                         $inQuote = true;
197                                         $inComment = false;
198                                         $escaped = false;
199                                         $quoteChar = $c;
200                                         $literal = $c;
201                                 }
202                                 else if($d=="/*" && !$inNormalComment)
203                                 {
204                                         $inQuote = false;
205                                         $inComment = true;
206                                         $escaped = false;
207                                         $quoteChar = $d;
208                                         $literal = $d;  
209                                         $j++;   
210                                 }
211                                 else if($d=="//") //ignore string markers that are found inside comments
212                                 {
213                                         $inNormalComment = true;
214                                         $clean .= $c;
215                                 }
216                                 else
217                                 {
218                                         $clean .= $c;
219                                 }
220                         }
221                         else //allready in a string so find end quote
222                         {
223                                 if($c == $quoteChar && !$escaped && !$inComment)
224                                 {
225                                         $inQuote = false;
226                                         $literal .= $c;
227         
228                                         //subsitute in a marker for the string
229                                         $clean .= "___" . count($literal_strings) . "___";
230         
231                                         //push the string onto our array
232                                         array_push($literal_strings,$literal);
233         
234                                 }
235                                 else if($inComment && $d=="*/")
236                                 {
237                                         $inComment = false;
238                                         $literal .= $d;
239         
240                                         //subsitute in a marker for the string
241                                         $clean .= "___" . count($literal_strings) . "___";
242         
243                                         //push the string onto our array
244                                         array_push($literal_strings,$literal);
245         
246                                         $j++;
247                                 }
248                                 else if($c == "\\" && !$escaped)
249                                         $escaped = true;
250                                 else
251                                         $escaped = false;
252         
253                                 $literal .= $c;
254                         }
255                 }
256                 if($inComment) $literal .= "\n";
257                 $clean .= "\n";
258         }
259         //explode the clean string into lines again
260         $lines = explode("\n",$clean);
261         
262         //now process each line at a time
263         for($i=0;$i<count($lines);$i++)
264         {
265                 $line = $lines[$i];
266         
267                 //remove comments
268                 $line = preg_replace("/\/\/(.*)/","",$line);
269         
270                 //strip leading and trailing whitespace
271                 $line = trim($line);
272         
273                 //remove all whitespace with a single space
274                 $line = preg_replace("/\s+/"," ",$line);
275         
276                 //remove any whitespace that occurs after/before an operator
277                 $line = preg_replace("/\s*([!\}\{;,&=\|\-\+\*\/\)\(:])\s*/","\\1",$line);
278         
279                 $lines[$i] = $line;
280         }
281         
282         //implode the lines
283         $sJS = implode("\n",$lines);
284         
285         //make sure there is a max of 1 \n after each line
286         $sJS = preg_replace("/[\n]+/","\n",$sJS);
287         
288         //strip out line breaks that immediately follow a semi-colon
289         $sJS = preg_replace("/;\n/",";",$sJS);
290         
291         //curly brackets aren't on their own
292         $sJS = preg_replace("/[\n]*\{[\n]*/","{",$sJS);
293         
294         //finally loop through and replace all the literal strings:
295         for($i=0;$i<count($literal_strings);$i++) {
296             if (strpos($literal_strings[$i],"/*")!==false) 
297                 $literal_strings[$i]= '';
298                 $sJS = str_replace("___".$i."___",$literal_strings[$i],$sJS);
299         }
300         return $sJS;
301 }
302
303 /*
304         Check if file can be updated, restoring subdirectories 
305         if needed. Returns 1 when no confilcts, -1 when file exists and is writable
306 */
307 function check_write($path)
308 {
309         if ($path == ''//|| $path == '.' || $path == '..'
310         ) return 0;
311
312         return is_writable($path) ? (is_dir($path) ? 1 : -1) 
313                 : (is_file($path) ? 0 : ($path == '.' || $path == '..' ? 0 : check_write(dirname($path))));
314 }
315
316 /*
317         Copies set of files. When $strict is set
318         also removes files from the $to which 
319         does not exists in $from directory but arelisted in $flist.
320 */
321 function copy_files($flist, $from, $to, $strict=false)
322 {
323         foreach ($flist as $file) {
324                 if (file_exists($from.'/'.$file)) {
325                         if (!copy_file($file, $from, $to))
326                                 return false;
327                 } else if ($strict) {
328                                 unlink($to.'/'.$file);
329                 }
330         }
331         return true;
332 }
333
334 /*
335         Copies file from base to target directory, restoring subdirectories 
336         if needed.
337 */
338 function copy_file($file, $from, $to)
339 {
340
341         if (!is_dir(dirname($file=='.' ? $to : ($to.'/'.$file)))) {
342                 if (!copy_file(dirname($file), null, $to))
343                         return false;
344         }
345         if (!$from) {
346         //              error_log( 'dodanie katalogu '.$to.'/'.$file);
347                 return @mkdir($file=='.' ? $to : ($to.'/'.$file));
348         }
349         else {
350         //              error_log( 'skopiowanie '.$to.'/'.$file);
351                 return @copy($from.'/'.$file, $to.'/'.$file);
352         }
353 }
354 /*
355         Search for file, looking first for company specific version, then for 
356         version provided by any extension module, finally in main FA directory.
357         Also adds include path for any related files, and sets $local_path_to_root 
358         to enable local translation domains.
359         
360         Returns found file path or null.
361 */
362 function find_custom_file($rep)
363 {
364         global $installed_extensions, $path_to_root, $local_path_to_root;
365
366         // customized per company version
367         $path = company_path();
368         $file = $path.$rep;
369         if (file_exists($file)) {
370                 // add local include path
371                 $local_path_to_root = $path;
372                 set_include_path(dirname($file).PATH_SEPARATOR.get_include_path());
373                 return $file;
374         }
375         // file added by active extension modules
376         if (count($installed_extensions) > 0)
377         {
378                 $extensions = $installed_extensions;
379                 foreach ($extensions as $ext)
380                         if (($ext['active'] && $ext['type'] == 'extension')) {
381                                 $path = $path_to_root.'/'.$ext['path'];
382                                 $file = $path.$rep;
383                                 if (file_exists($file)) {
384                                         set_include_path($path.PATH_SEPARATOR.get_include_path());
385                                         $local_path_to_root = $path;
386                                         return $file;
387                                 }
388                         }
389         }
390         // standard location
391         $file = $path_to_root.$rep;
392         if (file_exists($file))
393                 return $file;
394
395         return null;
396 }
397 /*
398         
399         Protect against directory traversal.
400         Changes all not POSIX compatible chars to underscore.
401 */
402 function clean_file_name($filename) {
403     $filename = str_replace(chr(0), '', $filename);
404     return preg_replace('/[^a-zA-Z0-9.\-_]/', '_', $filename);
405 }
406
407 /*
408         Simple random password generator.
409 */
410 function generate_password()
411 {
412         if (PHP_VERSION >= '5.3')
413                 $bytes = openssl_random_pseudo_bytes(8, $cstrong);
414         else
415                 $bytes = sprintf("08%x", mt_rand(0,0xffffffff));
416
417         return  base64_encode($bytes);
418 }
419
420 if (!function_exists('array_fill_keys')) // since 5.2
421 {
422         function array_fill_keys($keys, $value)
423         {
424                 return (object)array_combine($keys, array_fill(count($keys), $value));
425         }
426 }
427
428 /*
429         This function aims to generate cryptographically strong random identifier.
430         Result identifier has length 4[strength/8/3] 
431 */
432 function random_id($strength = 128)
433 {
434         $n = ceil($strength/8);
435
436         if (function_exists('openssl_random_pseudo_bytes'))
437                 $bin = openssl_random_pseudo_bytes($n, $cstrong);       // openssl on php 5.3 and up
438         else if (file_exists('/dev/urandom'))
439                 $bin = file_get_contents('/dev/urandom', false, null, -1, $n);  // linux std random device
440         else {
441                 $bin = '';
442                 for($i=0; $i < $n; $i++)
443                         $bin .= chr(mt_rand(0, 255));   // Mersene Twister generator
444         }
445         $id = strtr(base64_encode($bin), '+/=', '-_x'); // see RFC 4648 Section 5
446
447         return $id;
448 }