Pman.php
[Pman.Base] / Pman.php
1 <?php 
2 /**
3  * Pman Base class
4  * 
5  * Provides:
6  *  - base application setup (variables etc to javascript)
7  * 
8  *  - authentication and permission info about user / application
9  *  - json output methods.
10  *  - file upload error checking - checkFileUploadError
11  *  - logging to event table
12  *  - sendTemplate code (normally use the Person version for sending to specific people..)
13  * 
14  *  - doc managment code?? - remarks and tracking??? - MOVEME
15  *  - authentication link checking?? MOVEME?
16  *  - authentication reset password ?? MOVEME?
17  *  ?? arrayClean.. what's it doing here?!? ;)
18  *
19  *
20  *  OPTIONS
21  *  Pman['local_autoauth']   // who to login as when using localhost
22  *  Pman['isDev']  // can the site show develpment info.?
23  *  Pman['uiConfig']  // extra variable to export to front end..
24  *  Pman['auth_comptype'] // -- if set to 'OWNER' then only users with company=OWNER can log in
25  *  Pman['authTable'] // the authentication table (default 'person')
26  *
27  * 
28  * Usefull implemetors
29  * DB_DataObject*:*toEventString (for logging - this is generically prefixed to all database operations.)
30  *   - any data object where this method exists, the result will get prefixed to the log remarks
31  */
32
33  
34     
35  
36  
37 require_once 'Pman/Core/AssetTrait.php';
38
39 class Pman extends HTML_FlexyFramework_Page 
40 {
41     use Pman_Core_AssetTrait;
42     //outputJavascriptDir()
43     //outputCssDir();
44     
45     var $appName= "";
46     var $appLogo= "";
47     var $appShortName= "";
48     var $appVersion = "1.8";
49     var $version = 'dev';
50     var $onloadTrack = 0;
51     var $linkFail = "";
52     var $showNewPass = 0;
53     var $logoPrefix = '';
54     var $appModules = '';
55     var $appDisabled = array(); // array of disabled modules..
56                     // (based on config option disable)
57     
58     var $authUser; // always contains the authenticated user..
59     
60     var $disable_jstemplate = false; /// disable inclusion of jstemplate code..
61     var $company = false;
62     
63     var $css_path = ''; // can inject a specific path into the base HTML page.
64     
65     /**
66      * ------------- Standard getAuth/get/post methods of framework.
67      * 
68      * 
69      */
70     
71     function getAuth() // everyone allowed in!!!!!
72     {
73         $this->loadOwnerCompany();
74         
75         return true;
76     }
77     
78     function init($base = false) 
79     {
80         
81         if (isset($this->_hasInit)) {
82             return;
83         }
84         $this->_hasInit = true;
85          // move away from doing this ... you can access bootLoader.XXXXXX in the master template..
86         $boot = HTML_FlexyFramework::get();
87         // echo'<PRE>';print_R($boot);exit;
88         $this->appName= $boot->appName;
89         $this->appNameShort= $boot->appNameShort;
90         
91         
92         $this->appModules= $boot->enable;
93         
94 //        echo $this->arrayToJsInclude($files);        
95         $this->isDev = empty($boot->Pman['isDev']) ? false : $boot->Pman['isDev'];
96         
97         $this->css_path = empty($boot->Pman['css_path']) ? '' : $boot->Pman['css_path'];
98         
99         $this->appDisable = $boot->disable;
100         $this->appDisabled = explode(',', $boot->disable);
101         $this->version = $boot->version; 
102         $this->uiConfig = empty($boot->Pman['uiConfig']) ? false : $boot->Pman['uiConfig']; 
103         
104         if (!empty($ff->Pman['local_autoauth']) && 
105             ($_SERVER['SERVER_ADDR'] == '127.0.0.1') &&
106             ($_SERVER['REMOTE_ADDR'] == '127.0.0.1') 
107         ) {
108             $this->isDev = true;
109         }
110         
111         if ($_SERVER['SERVER_ADDR'] == '127.0.0.1') &&
112             ($_SERVER['REMOTE_ADDR'] == '127.0.0.1')  &&
113             !empty($_REQUEST['isDev'])
114         ) {
115             $this->isDev = true;
116         }
117         
118         // if a file Pman_{module}_Pman exists.. and it has an init function... - call that..
119         
120         //var_dump($this->appModules);
121         
122         
123         
124     }
125     /*
126      * module init is only loaded on main page call, and includes checks for configuration settings.
127      */
128     function initModules()
129     {
130         foreach(explode(',',$this->appModules) as $m) {
131             $cls = 'Pman_'. $m . '_Pman';
132             //echo $cls;
133             //echo $this->rootDir . '/'.str_replace('_','/', $cls). '.php';
134             
135             if (!file_exists($this->rootDir . '/'.str_replace('_','/', $cls). '.php')) {
136                 continue;
137             }
138             require_once str_replace('_','/', $cls). '.php';
139             $c = new $cls();
140             if (method_exists($c,'init')) {
141                 $c->init($this);
142             }
143         }
144     }
145     
146     
147     
148     function get($base, $opts=array()) 
149     {
150         $this->init();
151         if (empty($base)) {
152             $this->initModules();
153         }
154         
155             //$this->allowSignup= empty($opts['allowSignup']) ? 0 : 1;
156         $bits = explode('/', $base);
157       
158         
159         // should really be moved to Login...
160         
161         if ($bits[0] == 'PasswordReset') {
162             $this->linkFail = $this->resetPassword(@$bits[1],@$bits[2],@$bits[3]);
163             header('Content-type: text/html; charset=utf-8');
164             return;
165         } 
166          
167         $au = $this->getAuthUser();
168         if ($au) {
169             $ff= HTML_FlexyFramework::get();
170            
171             if (!empty($ff->Pman['auth_comptype']) && $au->id > 0 &&
172                 ( !$au->company_id || ($ff->Pman['auth_comptype'] != $au->company()->comptype))) {
173          
174                 $au->logout();
175                 
176                 $this->jerr("Login not permited to outside companies - please reload");
177             }
178             $this->addEvent("RELOAD");
179         }
180         
181         
182         if (strlen($base)) {
183             $this->jerror("BADURL","invalid url: $base");
184         }
185         // deliver template
186         if (isset($_GET['onloadTrack'])) {
187             $this->onloadTrack = (int)$_GET['onloadTrack'];
188         }
189         // getting this to work with xhtml is a nightmare
190         // = nbsp / <img> issues screw everyting up.
191          //var_dump($this->isDev);
192         // force regeneration on load for development enviroments..
193         
194         HTML_FlexyFramework::get()->generateDataobjectsCache($this->isDev && !empty($_REQUEST['isDev']));
195         
196         //header('Content-type: application/xhtml+xml; charset=utf-8');
197         
198         
199         
200         if ($this->company && $this->company->logo_id) {
201             $im = DB_DataObject::Factory('Images');
202             $im->get($this->company->logo_id);
203             $this->appLogo = $this->baseURL . '/Images/Thumb/x100/'. $this->company->logo_id .'/' . $im->filename;
204         }
205         
206         header('Content-type: text/html; charset=utf-8');
207          
208     }
209     function post($base) {
210         return $this->get($base);
211     }
212     
213     
214     // --------------- AUTHENTICATION or  system information
215     /**
216      * loadOwnerCompany:
217      * finds the compay with comptype=='OWNER'
218      * ?? what about comptype_id-name ?????
219      *
220      * @return {Pman_Core_DataObjects_Companies} the owner company
221      */
222     function loadOwnerCompany()
223     {
224         // only applies if authtable is person..
225         $ff = HTML_FlexyFramework::get();
226         if (!empty($ff->Pman['authTable']) && !in_array($ff->Pman['authTable'] , [ 'core_person', 'Person' ])) {
227             return false;
228         }
229         
230         $this->company = DB_DataObject::Factory('core_company');
231         if (!is_a($this->company, 'DB_DataObject')) { // non-core pman projects
232             return false; 
233         }
234         $this->company->get('comptype', 'OWNER');
235         return $this->company;
236     }
237     
238     
239     
240     /**
241      * getAuthUser: - get the authenticated user..
242      *
243      * @return {DB_DataObject} of type Pman[authTable] if authenticated.
244      */
245     
246     function getAuthUser()
247     {
248         if (!empty($this->authUser)) {
249             return $this->authUser;
250         }
251         $ff = HTML_FlexyFramework::get();
252         $tbl = empty($ff->Pman['authTable']) ? 'core_person' : $ff->Pman['authTable'];
253         
254         $u = DB_DataObject::factory( $tbl );
255         
256         if (is_a($u,'PEAR_Error') || !$u->isAuth()) {
257             return false;
258         }
259         $this->authUser =$u->getAuthUser();
260         return $this->authUser ;
261     }
262     /**
263      * hasPerm:
264      * wrapper arround authuser->hasPerm
265      * @see Pman_Core_DataObject_Core_person::hasPerm
266      *
267      * @param {String} $name  The permission name (eg. Projects.List)
268      * @param {String} $lvl   eg. (C)reate (E)dit (D)elete ... etc.
269      * 
270      */
271     function hasPerm($name, $lvl)  // do we have a permission
272     {
273         static $pcache = array();
274         $au = $this->getAuthUser();
275         return $au && $au->hasPerm($name,$lvl);
276         
277     }
278    
279     /**
280      * modulesList:  List the modules in the application
281      *
282      * @return {Array} list of modules
283      */
284     function modulesList()
285     {
286         $boot = HTML_FlexyFramework::get();
287         // echo'<PRE>';print_R($boot);exit;
288          
289          
290         $mods = explode(',', $boot->enable);
291         if (in_array('Core',$mods)) { // core has to be the first  modules loaded as it contains Pman.js
292             array_unshift($mods,   'Core');
293         }
294         
295         if (in_array($boot->appNameShort,$mods)) { // Project has to be the last  modules loaded as it contains Pman.js
296             unset($mods[array_search($boot->appNameShort, $mods)]);
297             $mods[] = $boot->appNameShort;
298         }
299         
300         $mods = array_unique($mods);
301          
302         $disabled =  explode(',', $boot->disable ? $boot->disable : '');
303         $ret = array();
304         foreach($mods as $mod) {
305             // add the css file..
306             if (in_array($mod, $disabled)) {
307                 continue;
308             }
309             $ret[] = $mod;
310         }
311         return $ret;
312     }
313     
314      
315     
316     
317     function hasModule($name) 
318     {
319         $this->init();
320         if (!strpos( $name,'.') ) {
321             // use enable / disable..
322             return in_array($name, $this->modules()); 
323         }
324         
325         $x = DB_DataObject::factory('core_group_right');
326         $ar = $x->defaultPermData();
327         if (empty($ar[$name]) || empty($ar[$name][0])) {
328             return false;
329         }
330         return true;
331     }
332     
333      
334     
335     
336
337     
338     
339     
340         
341     /**
342      * ---------------- Global Tools ---------------   
343      */
344     function checkFileUploadError()  // check for file upload errors.
345     {    
346         if (
347             empty($_FILES['File']) 
348             || empty($_FILES['File']['name']) 
349             || empty($_FILES['File']['tmp_name']) 
350             || empty($_FILES['File']['type']) 
351             || !empty($_FILES['File']['error']) 
352             || empty($_FILES['File']['size']) 
353         ) {
354             $this->jerr("File upload error: <PRE>" . print_r($_FILES,true) . print_r($_POST,true) . "</PRE>");
355         }
356     }
357     
358     
359     /**
360      * generate a tempory file with an extension (dont forget to delete it)
361      */
362     
363     function tempName($ext)
364     {
365         $x = tempnam(ini_get('session.save_path'), HTML_FlexyFramework::get()->appNameShort.'TMP');
366         unlink($x);
367         return $x .'.'. $ext;
368     }
369    
370     
371     /**
372      * ------------- Authentication password reset ------ ??? MOVEME?
373      * 
374      * 
375      */
376     
377     
378     function resetPassword($id,$t, $key)
379     {
380         
381         $au = $this->getAuthUser();
382         if ($au) {
383             return "Already Logged in - no need to use Password Reset";
384         }
385         
386         $u = DB_DataObject::factory('core_person');
387         //$u->company_id = $this->company->id;
388         $u->active = 1;
389         if (!$u->get($id) || !strlen($u->passwd)) {
390             return "invalid id";
391         }
392         
393         // validate key.. 
394         if ($key != $u->genPassKey($t)) {
395             return "invalid key";
396         }
397         $uu = clone($u);
398         $u->no_reset_sent = 0;
399         $u->update($uu);
400         
401         if ($t < strtotime("NOW - 1 DAY")) {
402             return "expired";
403         }
404         $this->showNewPass = implode("/", array($id,$t,$key));
405         return false;
406     }
407     
408     /**
409      * jerrAuth: standard auth failure - with data that let's the UI know..
410      */
411     function jerrAuth()
412     {
413         $au = $this->authUser();
414         if ($au) {
415             // is it an authfailure?
416             $this->jerr("Permission denied to view this resource", array('authFailure' => true));
417         }
418         $this->jerr("Not authenticated", array('authFailure' => true));
419     }
420      
421      
422      
423     /**
424      * ---------------- Standard JSON outputers. - used everywhere
425      */
426       /**
427      * ---------------- Standard JSON outputers. - used everywhere
428      * JSON error - simple error with logging.
429      * @see Pman::jerror
430      */
431     
432     function jerr($str, $errors=array(), $content_type = false) // standard error reporting..
433     {
434         return $this->jerror('ERROR', $str,$errors,$content_type);
435     }
436     /**
437      * Recomended JSON error indicator
438      *
439      * 
440      * @param string $type  - normally 'ERROR' - you can use this to track error types.
441      * @param string $message - error message displayed to user.
442      * @param array $errors - optioanl data to pass to front end.
443      * @param string $content_type - use text/plain to return plan text - ?? not sure why...
444      *
445      */
446     
447     function jerror($type, $str, $errors=array(), $content_type = false) // standard error reporting..
448     {
449         if ($type !== false) {
450             $this->addEvent($type, false, $str);
451         }
452          
453         $cli = HTML_FlexyFramework::get()->cli;
454         if ($cli) {
455             echo "ERROR: " .$str . "\n";
456             exit(1); // cli --- exit code to stop shell execution if necessary.
457         }
458         
459         
460         if ($content_type == 'text/plain') {
461             header('Content-Disposition: attachment; filename="error.txt"');
462             header('Content-type: '. $content_type);
463             echo "ERROR: " .$str . "\n";
464             exit;
465         } 
466         
467         
468         
469         require_once 'Services/JSON.php';
470         $json = new Services_JSON();
471         
472         // log all errors!!!
473         
474         $retHTML = isset($_SERVER['CONTENT_TYPE']) && 
475                 preg_match('#multipart/form-data#i', $_SERVER['CONTENT_TYPE']);
476         
477         if ($retHTML){
478             if (isset($_REQUEST['returnHTML']) && $_REQUEST['returnHTML'] == 'NO') {
479                 $retHTML = false;
480             }
481         } else {
482             $retHTML = isset($_REQUEST['returnHTML']) && $_REQUEST['returnHTML'] !='NO';
483         }
484         
485         
486         if ($retHTML) {
487             header('Content-type: text/html');
488             echo "<HTML><HEAD></HEAD><BODY>";
489             echo  $json->encodeUnsafe(array(
490                     'success'=> false, 
491                     'errorMsg' => $str,
492                     'message' => $str, // compate with exeption / loadexception.
493
494                     'errors' => $errors ? $errors : true, // used by forms to flag errors.
495                     'authFailure' => !empty($errors['authFailure']),
496                 ));
497             echo "</BODY></HTML>";
498             exit;
499         }
500         
501         if (isset($_REQUEST['_debug'])) {
502             echo '<PRE>'.htmlspecialchars(print_r(array(
503                 'success'=> false, 
504                 'data'=> array(), 
505                 'errorMsg' => $str,
506                 'message' => $str, // compate with exeption / loadexception.
507                 'errors' => $errors ? $errors : true, // used by forms to flag errors.
508                 'authFailure' => !empty($errors['authFailure']),
509             ),true));
510             exit;
511                 
512         }
513         
514         echo $json->encode(array(
515             'success'=> false, 
516             'data'=> array(), 
517             'errorMsg' => $str,
518             'message' => $str, // compate with exeption / loadexception.
519             'errors' => $errors ? $errors : true, // used by forms to flag errors.
520             'authFailure' => !empty($errors['authFailure']),
521         ));
522         
523         
524         exit;
525         
526     }
527     function jok($str)
528     {
529         $cli = HTML_FlexyFramework::get()->cli;
530         if ($cli) {
531             echo "OK: " .$str . "\n";
532             exit;
533         }
534         require_once 'Services/JSON.php';
535         $json = new Services_JSON();
536         
537         $retHTML = isset($_SERVER['CONTENT_TYPE']) && 
538                 preg_match('#multipart/form-data#i', $_SERVER['CONTENT_TYPE']);
539         
540         if ($retHTML){
541             if (isset($_REQUEST['returnHTML']) && $_REQUEST['returnHTML'] == 'NO') {
542                 $retHTML = false;
543             }
544         } else {
545             $retHTML = isset($_REQUEST['returnHTML']) && $_REQUEST['returnHTML'] !='NO';
546         }
547         
548         if ($retHTML) {
549             header('Content-type: text/html');
550             echo "<HTML><HEAD></HEAD><BODY>";
551             // encode html characters so they can be read..
552             echo  str_replace(array('<','>'), array('\u003c','\u003e'),
553                         $json->encodeUnsafe(array('success'=> true, 'data' => $str)));
554             echo "</BODY></HTML>";
555             exit;
556         }
557         
558         
559         echo  $json->encode(array('success'=> true, 'data' => $str));
560         
561         exit;
562         
563     }
564     /**
565      * output data for grids or tree
566      * @ar {Array} ar Array of data
567      * @total {Number|false} total number of records (or false to return count(ar)
568      * @extra {Array} extra key value list of data to pass as extra data.
569      * 
570      */
571     function jdata($ar,$total=false, $extra=array(), $cachekey = false)
572     {
573         // should do mobile checking???
574         if ($total == false) {
575             $total = count($ar);
576         }
577         $extra=  $extra ? $extra : array();
578         require_once 'Services/JSON.php';
579         $json = new Services_JSON();
580         
581         $retHTML = isset($_SERVER['CONTENT_TYPE']) && 
582                 preg_match('#multipart/form-data#i', $_SERVER['CONTENT_TYPE']);
583         
584         if ($retHTML){
585             if (isset($_REQUEST['returnHTML']) && $_REQUEST['returnHTML'] == 'NO') {
586                 $retHTML = false;
587             }
588         } else {
589             $retHTML = isset($_REQUEST['returnHTML']) && $_REQUEST['returnHTML'] !='NO';
590         }
591         
592         if ($retHTML) {
593             
594             header('Content-type: text/html');
595             echo "<HTML><HEAD></HEAD><BODY>";
596             // encode html characters so they can be read..
597             echo  str_replace(array('<','>'), array('\u003c','\u003e'),
598                         $json->encodeUnsafe(array('success' =>  true, 'total'=> $total, 'data' => $ar) + $extra));
599             echo "</BODY></HTML>";
600             exit;
601         }
602         
603         
604         // see if trimming will help...
605         if (!empty($_REQUEST['_pman_short'])) {
606             $nar = array();
607             
608             foreach($ar as $as) {
609                 $add = array();
610                 foreach($as as $k=>$v) {
611                     if (is_string($v) && !strlen(trim($v))) {
612                         continue;
613                     }
614                     $add[$k] = $v;
615                 }
616                 $nar[] = $add;
617             }
618             $ar = $nar;
619               
620         }
621         
622       
623         $ret =  $json->encode(array('success' =>  true, 'total'=> $total, 'data' => $ar) + $extra);  
624         
625         if (!empty($cachekey)) {
626             
627             $fn = ini_get('session.save_path') . '/json-cache'.date('/Y/m/d').'.'. $cachekey . '.cache.json';
628             if (!file_exists(dirname($fn))) {
629                 mkdir(dirname($fn), 0777,true);
630             }
631             file_put_contents($fn, $ret);
632         }
633         echo $ret;
634         exit;
635     }
636     
637     
638     
639     /** a daily cache **/
640     function jdataCache($cachekey)
641     {
642         $fn = ini_get('session.save_path') . '/json-cache'.date('/Y/m/d').'.'. $cachekey . '.cache.json';
643         if (file_exists($fn)) {
644             header('Content-type: application/json');
645             echo file_get_contents($fn);
646             exit;
647         }
648         return false;
649         
650     }
651     
652    
653     
654     /**
655      * ---------------- OUTPUT
656      */
657     function hasBg($fn) // used on front page to check if logos exist..
658     {
659         return file_exists($this->rootDir.'/Pman/'.$this->appNameShort.'/templates/images/'.  $fn);
660     }
661      /**
662      * outputJavascriptIncludes:
663      *
664      * output <script....> for all the modules in the applcaiton
665      *
666      */
667     function outputJavascriptIncludes()  
668     {
669         // BC support - currently 1 project still relies on this.. (MO portal) 
670         $o = HTML_FlexyFramework::get()->Pman_Core;
671         if (isset($o['packseed'])) {
672             return $this->outputJavascriptIncludesBC();
673         }
674         
675         
676         $mods = $this->modulesList();
677         
678         $is_bootstrap = in_array('BAdmin', $mods);
679         
680         foreach($mods as $mod) {
681             // add the css file..
682             
683             if ($is_bootstrap) {
684                 if (!file_exists($this->rootDir."/Pman/$mod/is_bootstrap")) {
685                     echo '<!-- missing '. $this->rootDir."/Pman/$mod/is_bootstrap  - skipping -->";
686                     continue;
687                 }
688                 
689             }
690         
691             $this->outputJavascriptDir("Pman/$mod/widgets", "*.js");
692             $this->outputJavascriptDir("Pman/$mod", "*.js");
693             
694         }
695         
696         if (empty($this->disable_jstemplate)) {
697         // and finally the JsTemplate...
698             echo '<script type="text/javascript" src="'. $this->baseURL. '/Core/JsTemplate"></script>'."\n";
699         }
700          
701     }
702     
703      /**
704      * outputCSSIncludes:
705      *
706      * output <link rel=stylesheet......> for all the modules in the applcaiton
707      *
708      *
709      * This could css minify as well.
710      */
711     function outputCSSIncludes() // includes on CSS links.
712     {
713         
714         $mods = $this->modulesList();
715         
716         
717         foreach($mods as $mod) {
718             // add the css file..
719             $this->outputCSSDir("Pman/$mod","*.css");
720             
721             
722         }
723          
724     }
725     
726     
727     
728     
729     
730     
731     
732     
733     
734     
735     
736     
737     
738     
739     
740     
741     
742     // --- OLD CODE - in for BC on MO project.... - needs removing...
743     
744     // used on old versions.....
745     function outputJavascriptIncludesBC()  
746     {
747         
748         $mods = $this->modulesList();
749         
750         foreach($mods as $mod) {
751             // add the css file..
752         
753              
754             $files = $this->moduleJavascriptList($mod.'/widgets');
755             foreach($files as $f) {
756                 echo '<script type="text/javascript" src="'. $f. '"></script>'."\n";
757             }
758             
759             $files = $this->moduleJavascriptList($mod);
760             foreach($files as $f) {
761                 echo '<script type="text/javascript" src="'. $f. '"></script>'."\n";
762             }
763             
764         }
765         if (empty($this->disable_jstemplate)) {
766         // and finally the JsTemplate...
767             echo '<script type="text/javascript" src="'. $this->baseURL. '/Core/JsTemplate"></script>'."\n";
768         }
769          
770     }
771     /**
772      * Gather infor for javascript files..
773      *
774      * @param {String} $mod the module to get info about.
775      * @return {StdClass}  details about module.
776      */
777     function moduleJavascriptFilesInfo($mod)
778     {
779         
780         static $cache = array();
781         
782         if (isset($cache[$mod])) {
783             return $cache[$mod];
784         }
785         
786         
787         $ff = HTML_FlexyFramework::get();
788         
789         $base = dirname($_SERVER['SCRIPT_FILENAME']);
790         $dir =   $this->rootDir.'/Pman/'. $mod;
791         $path = $this->rootURL ."/Pman/$mod/";
792         
793         $ar = glob($dir . '/*.js');
794         
795         $files = array();
796         $arfiles = array();
797         $maxtime = 0;
798         $mtime = 0;
799         foreach($ar as $fn) {
800             $f = basename($fn);
801             // got the 'module file..'
802             $mtime = filemtime($dir . '/'. $f);
803             $maxtime = max($mtime, $maxtime);
804             $arfiles[$fn] = $mtime;
805             $files[] = $path . $f . '?ts='.$mtime;
806         }
807         
808         ksort($arfiles); // just sort by name so it's consistant for serialize..
809         
810         $compile  = empty($ff->Pman['public_cache_dir']) ? 0 : 1;
811         $basedir = $compile ? $ff->Pman['public_cache_dir'] : false;
812         $baseurl = $compile ? $ff->Pman['public_cache_url'] : false;
813         
814        
815         
816         
817         $lsort = create_function('$a,$b','return strlen($a) > strlen($b) ? 1 : -1;');
818         usort($files, $lsort);
819         
820         $smod = str_replace('/','.',$mod);
821         
822         $output = date('Y-m-d-H-i-s-', $maxtime). $smod .'-'.md5(serialize($arfiles)) .'.js';
823         
824         
825         // why are translations done like this - we just build them on the fly frmo the database..
826         $tmtime = file_exists($this->rootDir.'/_translations_/'. $smod.'.js')
827             ? filemtime($this->rootDir.'/_translations_/'. $smod.'.js') : 0;
828         
829         $cache[$mod]  = (object) array(
830             'smod' =>               $smod, // module name without '/'
831             'files' =>              $files, // list of all files.
832             'filesmtime' =>         $arfiles,  // map of mtime=>file
833             'maxtime' =>            $maxtime, // max mtime
834             'compile' =>            $this->isDev ? false : $compile,
835             'translation_file' =>   $base .'/_translations_/' . $smod .  '.js',
836             'translation_mtime' =>  $tmtime,
837             'output' =>             $output,
838             'translation_data' =>   preg_replace('/\.js$/', '.__translation__.js', $output),
839             'translation_base' =>   $dir .'/', //prefix of filename (without moudle name))
840             'basedir' =>            $basedir,   
841             'baseurl' =>            $baseurl,
842             'module_dir' =>         $dir,  
843         );
844         return $cache[$mod];
845     }
846      
847     
848     /**
849      *  moduleJavascriptList: list the javascript files in a module
850      *
851      *  The original version of this.. still needs more thought...
852      *
853      *  Compiled is in Pman/_compiled_/{$mod}/{LATEST...}.js
854      *  Translations are in Pman/_translations_/{$mod}.js
855      *  
856      *  if that stuff does not exist just list files in  Pman/{$mod}/*.js
857      *
858      *  Compiled could be done on the fly..
859      * 
860      *
861      *
862      *  @param {String} $mod  the module to look at - eg. Pman/{$mod}/*.js
863      *  @return {Array} list of include paths (either compiled or raw)
864      *
865      */
866
867     
868     
869     function moduleJavascriptList($mod)
870     {
871         
872         
873         $dir =   $this->rootDir.'/Pman/'. $mod;
874         
875         
876         if (!file_exists($dir)) {
877             echo '<!-- missing directory '. htmlspecialchars($dir) .' -->';
878             return array();
879         }
880         
881         $info = $this->moduleJavascriptFilesInfo($mod);
882        
883         
884           
885         if (empty($info->files)) {
886             return array();
887         }
888         // finally sort the files, so they are in the right order..
889         
890         // only compile this stuff if public_cache is set..
891         
892          
893         // suggestions...
894         //  public_cache_dir =   /var/www/myproject_cache
895         //  public_cache_url =   /myproject_cache    (with Alias apache /myproject_cache/ /var/www/myproject_cache/)
896         
897         // bit of debugging
898         if (!$info->compile) {
899             echo "<!-- Javascript compile turned off (isDev on, or public_cache_dir not set) -->\n";
900             return $info->files;
901         }
902         
903         // where are we going to write all of this..
904         // This has to be done via a 
905         if (!file_exists($info->basedir.'/'.$info->output) || !filesize($info->basedir.'/'.$info->output)) {
906             require_once 'Pman/Core/JsCompile.php';
907             $x = new Pman_Core_JsCompile();
908             
909             $x->pack($info->filesmtime,$info->basedir.'/'.$info->output, $info->translation_base);
910         } else {
911             echo "<!-- file exists not exist: {$info->basedir}/{$info->output} -->\n";
912         }
913         
914         if (file_exists($info->basedir.'/'.$info->output) &&
915                 filesize($info->basedir.'/'.$info->output)) {
916             
917             $ret =array(
918                 $info->baseurl.'/'. $info->output,
919               
920             );
921             // output all the ava
922             // fixme  - this needs the max datetime for the translation file..
923             $ret[] = $this->baseURL."/Admin/InterfaceTranslations/".$mod.".js"; //?ts=".$info->translation_mtime;
924             
925             //if ($info->translation_mtime) {
926             //    $ret[] = $this->rootURL."/_translations_/". $info->smod.".js?ts=".$info->translation_mtime;
927             //}
928             return $ret;
929         }
930         
931         
932         
933         // give up and output original files...
934         
935          
936         return $info->files;
937
938         
939     }
940     
941     /**
942      * Error handling...
943      *  PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($this, 'onPearError'));
944      */
945     function initErrorHandling()
946     {
947         if (!class_exists('HTML_FlexyFramework2')) {
948             // what about older code that still users PEAR?
949             PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($this, 'onPearError'));
950         }
951         set_exception_handler(array($this,'onException'));
952         
953     }
954     
955     
956     static $permitError = false; // static why?
957     
958     var $showErrorToUser = true;
959     
960     function onPearError($err)
961     {
962         return $this->onException($err);
963         
964     }
965     
966     
967     function onException($ex)
968     {
969          static $reported = false;
970         if ($reported) {
971             return;
972         }
973         
974         if (Pman::$permitError) {
975             return;
976         }
977         
978         
979         $reported = true;
980         $out = is_a($ex,'Exception') ? $ex->getMessage() : $ex->toString();
981         
982         
983         //print_R($bt); exit;
984         $ret = array();
985         $n = 0;
986         $bt = is_a($ex,'Exception') ? $ex->getTrace() : $ex->backtrace;
987         foreach( $bt as $b) {
988             $ret[] = @$b['file'] . '(' . @$b['line'] . ')@' .   @$b['class'] . '::' . @$b['function'];
989             if ($n > 20) {
990                 break;
991             }
992             $n++;
993         }
994         //convert the huge backtrace into something that is readable..
995         $out .= "\n" . implode("\n",  $ret);
996         
997         $this->addEvent("EXCEPTION", false, $out);
998         
999         if ($this->showErrorToUser) {
1000             print_R($out);exit;
1001         }
1002         // not sure why this is here... - perhaps doing a jerr() was actually caught by the UI, and hidden from the user..?
1003         $this->jerror(false,"An error Occured, please contact the website owner");
1004         
1005         //$this->jerr($out);
1006         
1007         
1008     }
1009     
1010     
1011     /**
1012      * ---------------- Logging ---------------   
1013      */
1014     
1015     /**
1016      * addEventOnce:
1017      * Log an action (only if it has not been logged already.
1018      * 
1019      * @param {String} action  - group/name of event
1020      * @param {DataObject|false} obj - dataobject action occured on.
1021      * @param {String} any remarks
1022      * @return {false|DB_DataObject} Event object.,
1023      */
1024     
1025     function addEventOnce($act, $obj = false, $remarks = '') 
1026     {
1027         if (!empty(HTML_FlexyFramework::get()->Pman['disable_events'])) {
1028             return;
1029         }
1030         $e = DB_DataObject::factory('Events');
1031         $e->init($act,$obj,$remarks); 
1032         if ($e->find(true)) {
1033             return false;
1034         }
1035         return $this->addEvent($act, $obj, $remarks);
1036     }
1037     /**
1038      * addEvent:
1039      * Log an action.
1040      * 
1041      * @param {String} action  - group/name of event
1042      * @param {DataObject|false} obj - dataobject action occured on.
1043      * @param {String} any remarks
1044      * @return {DB_DataObject} Event object.,
1045      */
1046     
1047     function addEvent($act, $obj = false, $remarks = '') 
1048     {
1049         
1050         if (!empty(HTML_FlexyFramework::get()->Pman['disable_events'])) {
1051             return;
1052         }
1053         $au = $this->getAuthUser();
1054        
1055         $e = DB_DataObject::factory('Events');
1056         $e->init($act,$obj,$remarks); 
1057          
1058         $e->event_when = $e->sqlValue('NOW()');
1059         
1060         $eid = $e->insert();
1061         
1062         // fixme - this should be in onInsert..
1063         $wa = DB_DataObject::factory('core_watch');
1064         if (method_exists($wa,'notifyEvent')) {
1065             $wa->notifyEvent($e); // trigger any actions..
1066         }
1067         
1068         
1069         $e->onInsert(isset($_REQUEST) ? $_REQUEST : array() , $this);
1070         
1071        
1072         return $e;
1073         
1074     }
1075     
1076     function addEventNotifyOnly($act, $obj = false, $remarks = '')
1077     {
1078          $au = $this->getAuthUser();
1079        
1080         $e = DB_DataObject::factory('Events');
1081         $e->init($act,$obj,$remarks); 
1082          
1083         $e->event_when = $e->sqlValue('NOW()');
1084         $wa = DB_DataObject::factory('core_watch');
1085         if (method_exists($wa,'notifyEvent')) {
1086             $wa->notifyEvent($e); // trigger any actions..
1087         }
1088     }
1089     
1090     
1091     // ------------------ DEPERCIATED ----------------------------
1092      
1093     // DEPRECITAED - use moduleslist
1094     function modules()  { return $this->modulesList();  }
1095     
1096     // DEPRECIATED.. - use getAuthUser...
1097     function staticGetAuthUser()  { $x = new Pman(); return $x->getAuthUser();  }
1098      
1099     
1100     // DEPRICATED  USE Pman_Core_Mailer
1101     
1102     function emailTemplate($templateFile, $args)
1103     {
1104     
1105         require_once 'Pman/Core/Mailer.php';
1106         $r = new Pman_Core_Mailer(array(
1107             'template'=>$templateFile,
1108             'contents' => $args,
1109             'page' => $this
1110         ));
1111         return $r->toData();
1112          
1113     }
1114     // DEPRICATED - USE Pman_Core_Mailer 
1115     // WHAT Part about DEPRICATED Does no one understand??
1116     function sendTemplate($templateFile, $args)
1117     {
1118         require_once 'Pman/Core/Mailer.php';
1119         $r = new Pman_Core_Mailer(array(
1120             'template'=>$templateFile,
1121             'contents' => array(),
1122             'page' => $this
1123         ));
1124         return $r->send();
1125         
1126     
1127     }
1128 }