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