UpdateDatabase.php
[Pman.Core] / UpdateDatabase.php
1 <?php
2
3 /**
4  *
5  * This applies database files from
6  * a) OLD - {MODULE}/DataObjects/XXXX.{dbtype}.sql
7  *
8  * b) NEW - {MODULE}/sql/XXX.sql (SHARED or translable)
9  *  and {MODULE}/{dbtype}/XXX.sql (SHARED or translable)
10  *
11  *
12  */
13
14 require_once 'Pman.php';
15 class Pman_Core_UpdateDatabase extends Pman
16 {
17     
18     static $cli_desc = "Update SQL - Beta (it will run updateData of all modules)";
19  
20     static $cli_opts = array(
21       
22         'prefix' => array(
23             'desc' => 'prefix for the password (eg. fred > xxx4fred - prefix is xxx4)',
24             'short' => 'p',
25             'default' => '',
26             'min' => 1,
27             'max' => 1,
28         ),
29         'data-only' => array(
30             'desc' => 'only run the updateData - do not run import the tables and procedures.',
31             'short' => 'p',
32             'default' => '',
33             'min' => 1,
34             'max' => 1,
35             
36         ),
37         'add-company' => array(
38             'desc' => 'add a company name of the company',
39             'short' => 'n',
40             'default' => '',
41             'min' => 1,
42             'max' => 1,
43         ),
44         'add-company-with-type' => array(
45             'desc' => 'the type of company (default OWNER)',
46             'short' => 't',
47             'default' => 'OWNER',
48             'min' => 1,
49             'max' => 1,
50         ),
51         'init' => array(
52             'desc' => 'Initialize the database (pg only supported)',
53             'short' => 'i',
54             'default' => '',
55             'min' => 1,
56             'max' => 1,
57         ),
58         'only-module-sql' => array(
59             'desc' => 'Only run sql import on this modules - eg. Core',
60             'default' => '',
61             'min' => 1,
62             'max' => 1,
63         ),
64         'skip-mysql-checks' => array(
65             'desc' => 'Skip mysql checks',
66             'default' => '',
67             'min' => 1,
68             'max' => 1,
69         ),
70         'procedures-only' => array(
71             'desc' => 'Only import procedures (not supported by most modules yet) - ignores sql directory',
72             'default' => '',
73             'min' => 1,
74             'max' => 1,
75         ),
76         
77         'json-person' => array(
78             'desc' => 'Person JSON file',
79             'default' => '',
80             'min' => 1,
81             'max' => 1,
82             
83         ),
84     );
85     
86     static function cli_opts()
87     {
88         
89         $ret = self::$cli_opts;
90         $ff = HTML_FlexyFramework::get();
91         $a = new Pman();
92         $mods = $a->modulesList();
93         foreach($mods as $m) {
94             
95             $fd = $ff->rootDir. "/Pman/$m/UpdateDatabase.php";
96             if (!file_exists($fd)) {
97                 continue;
98             }
99             
100             require_once $fd;
101             
102             $cls = new ReflectionClass('Pman_'. $m . '_UpdateDatabase');
103             
104             $ret = array_merge($ret, $cls->getStaticPropertyValue('cli_opts'));
105             
106             
107         }
108         
109         return $ret;
110     }
111     
112     var $opts = false;
113     var $disabled = array();
114     
115     
116     var $cli = false;
117     
118     var $local_base_url = false;
119     
120     function getAuth() {
121         
122         
123         $ff = HTML_FlexyFramework::get();
124         if (!empty($ff->cli)) {
125             $this->cli = true;
126             return true;
127         }
128         
129         parent::getAuth(); // load company!
130         $au = $this->getAuthUser();
131         if (!$au || $au->company()->comptype != 'OWNER') {
132             $this->jerr("Not authenticated", array('authFailure' => true));
133         }
134         $this->authUser = $au;
135         return true;
136     }
137     
138     function get($args, $opts=array())
139     {
140         PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($this, 'onPearError'));
141    
142         $this->checkSystem();
143         
144         if (class_exists('PDO_DataObjects_Introspection')) {
145             PDO_DataObject_Introspection::$cache = array();
146         }
147         
148         HTML_FlexyFramework::get()->generateDataobjectsCache(true);
149    
150         $ff = HTML_FlexyFramework::get();
151         
152         if(!isset($ff->Pman) || !isset($ff->Pman['local_base_url'])){
153             die("Please setup Pman[local_base_url]\n");
154         }
155         
156         $this->local_base_url = $ff->Pman['local_base_url'];
157         
158         if(!empty($ff->Core_Notify)){
159 //            require_once 'Pman/Core/NotifySmtpCheck.php';
160 //            $x = new Pman_Core_NotifySmtpCheck();
161 //            $x->check();
162         }
163         
164         $this->disabled = explode(',', $ff->disable);
165         
166         //$this->fixSequencesPgsql();exit;
167         $this->opts = $opts;
168         
169         // ask all the modules to verify the opts
170         
171         $this->checkOpts($opts);
172         
173         
174         // do this first, so the innodb change + utf8 fixes column max sizes
175         
176         // this will trigger errors about freetext indexes - we will have to remove them manually.?
177         // otherwise we need to do an sql query to find them, then remove them (not really worth it as it only affects really old code..)
178         
179         $this->runExtensions(); 
180
181         
182         if (empty($opts['data-only'])) {
183             $this->importSQL();
184         }
185         if (!empty($opts['only-module-sql'])) {
186             return;
187         }
188         
189         $this->runUpdateModulesData();
190         
191         if (!empty($opts['add-company']) && !in_array('Core', $this->disabled)) {
192             // make sure we have a good cache...?
193            
194             DB_DataObject::factory('core_company')->initCompanies($this, $opts);
195         }
196         
197         $this->runExtensions();
198         
199         $this->generateDataobjectsCache();
200         
201          
202     }
203     function output() {
204         return '';
205     }
206      /**
207      * imports SQL files from all DataObjects directories....
208      * 
209      * except any matching /migrate/
210      */
211     function importSQL()
212     {
213         
214         // loop through all the modules, and see if they have a importSQL method?
215         
216         
217         $ff = HTML_Flexyframework::get();
218         
219         $dburl = parse_url($ff->database); // used to be DB_DataObject['database'] - but not portable to PDO
220         
221         //$this->{'import' . $url['scheme']}($url);
222         
223         $dbtype = $dburl['scheme'];
224         
225         
226         $dirmethod = 'import' . $dburl['scheme'] . 'dir';
227         
228         
229        
230         
231         $ar = $this->modulesList();
232         
233         
234         foreach($ar as $m) {
235             
236             if(in_array($m, $this->disabled)){
237                 echo "module $m is disabled \n";
238                 continue;
239             }
240             
241             echo "Importing SQL from module $m\n";
242             if (!empty($this->opts['only-module-sql']) && $m != $this->opts['only-module-sql']) {
243                 continue;
244             }
245             
246             
247             // check to see if the class has
248             
249             
250             
251             $file = $this->rootDir. "/Pman/$m/UpdateDatabase.php";
252             if($m != 'Core' && file_exists($file)){
253                 
254                 require_once $file;
255                 $class = "Pman_{$m}_UpdateDatabase";
256                 $x = new $class;
257                 if(method_exists($x, 'importModuleSQL')){
258                     echo "Importing SQL from module $m using Module::importModuleSQL\n";
259                     $x->opts = $this->opts;
260                     $x->rootDir = $this->rootDir;
261                     $x->importModuleSQL($dburl);
262                     continue;
263                 }
264             };
265
266             echo "Importing SQL from module $m\n";
267             
268             
269             // if init has been called
270             // look in pgsql.ini
271             if (!empty($this->opts['init'])) {
272                 $this->{$dirmethod}($dburl, $this->rootDir. "/Pman/$m/{$dbtype}.init");
273                 
274             }
275             
276             
277             
278             $fd = $this->rootDir. "/Pman/$m/DataObjects";
279             
280             $this->{$dirmethod}($dburl, $fd);
281             
282             
283             // new -- sql directory..
284             // new style will not support migrate ... they have to go into mysql-migrate.... directories..
285             // new style will not support pg.sql etc.. naming - that's what the direcotries are for..
286             
287             $this->{$dirmethod}($dburl, $this->rootDir. "/Pman/$m/sql");
288             $this->{$dirmethod}($dburl, $this->rootDir. "/Pman/$m/{$dbtype}");
289             
290            
291             
292             if (!empty($this->opts['init']) && file_exists($this->rootDir. "/Pman/$m/{$dbtype}.initdata")) {
293                 if (class_exists('PDO_DataObjects_Introspection')) {
294                     PDO_DataObject_Introspection::$cache = array();
295                 }
296                 HTML_FlexyFramework::get()->generateDataobjectsCache(true);
297                 
298                 $this->{$dirmethod}($dburl, $this->rootDir. "/Pman/$m/{$dbtype}.initdata");
299                 $this->{'fixSequences'. $dbtype}();
300                 
301             }
302               
303             
304         }
305         
306         
307     }
308     
309     
310     
311      
312     /** -------------- code to handle importing a whole directory of files into the database  -------  **/
313     
314     
315     function importpgsqldir($url, $dir, $disable_triggers = false)
316     {
317         $ff = HTML_FlexyFramework::get();
318         
319         require_once 'System.php';
320         $cat = System::which('cat');
321         $psql = System::which('psql');
322         
323          
324         if (!empty($url['pass'])) { 
325             putenv("PGPASSWORD=". $url['pass']);
326         }
327            
328         $psql_cmd = $psql .
329             ' -h ' . $url['host'] .
330             ' -U' . escapeshellarg($url['user']) .
331              ' ' . basename($url['path']);
332         
333         
334         echo $psql_cmd . "\n" ;
335         echo "scan : $dir\n";
336         
337         if (is_file($dir)) {
338             $files = array($dir);
339
340         } else {
341         
342         
343             $files = glob($dir.'/*.sql');
344             uksort($files, 'strcasecmp');
345         }
346         //$lsort = create_function('$a,$b','return strlen($a) > strlen($b) ? 1 : -1;');
347         //usort($files, $lsort);
348         
349         
350         foreach($files as $bfn) {
351
352
353             if (preg_match('/migrate/i', basename($bfn))) { // skip migration scripts at present..
354                 continue;
355             }
356             if (preg_match('#\.[a-z]{2}\.sql#i', basename($bfn))
357                 && !preg_match('#\.pg\.sql#i', basename($bfn))
358             ) { // skip migration scripts at present..
359                 continue;
360             }
361             $fn = false;
362
363             if (!preg_match('/pgsql/', basename($dir) )) {
364                  if ( !preg_match('#\.pg\.sql$#', basename($bfn))) {
365                     $fn = $this->convertToPG($bfn);
366                 }
367             }
368
369             // files ending in .pg.sql are native postgres files.. ## depricated
370
371
372             $cmd = "$psql_cmd  < " . escapeshellarg($fn ? $fn : $bfn) . ' 2>&1' ;
373
374             echo "$bfn:   $cmd ". ($ff->cli ? "\n" : "<BR>\n");
375
376             passthru($cmd);
377
378             if ($fn) {
379                 unlink($fn);
380             }
381         }
382
383               
384              
385         
386     }
387     
388     
389     /**
390      * mysql - does not support conversions.
391      * 
392      *
393      */
394     function importmysqlidir($dburl, $dir) {
395         return $this->importmysqldir($dburl, $dir);
396     }
397     
398     function importmysqldir($dburl, $dir)
399     {
400         
401         $this->fixMysqlInnodb(); /// run once 
402         
403         echo "Import MYSQL :: $dir\n";
404         
405         
406         require_once 'System.php';
407         $cat = System::which('cat');
408         $mysql = System::which('mysql');
409         
410        
411            
412         $mysql_cmd = $mysql .
413             ' -h ' . $dburl['host'] .
414             ' -u' . escapeshellarg($dburl['user']) .
415             (!empty($dburl['pass']) ? ' -p' . escapeshellarg($dburl['pass'])  :  '') .
416             ' ' . basename($dburl['path']);
417         //echo $mysql_cmd . "\n" ;
418         
419         $files = glob($dir.'/*.sql');
420         uksort($files, 'strcasecmp');
421         
422        
423         foreach($files as $fn) {
424                 
425                  
426                 if (preg_match('/migrate/i', basename($fn))) { // skip migration scripts at present..
427                     continue;
428                 }
429                 // .my.sql but not .pg.sql
430                 if (preg_match('#\.[a-z]{2}\.sql#i', basename($fn))
431                     && !preg_match('#\.my\.sql#i', basename($fn))
432                 ) { // skip migration scripts at present..
433                     continue;
434                 }
435                 if (!strlen(trim($fn))) {
436                     continue;
437                 }
438                 
439                 $cmd = "$mysql_cmd -f < " . escapeshellarg($fn) ." 2>&1" ;
440                 
441                 echo basename($dir).'/'. basename($fn) .    '::' .  $cmd. ($this->cli ? "\n" : "<BR>\n");
442                 
443                 
444                 $fp = popen($cmd, "r"); 
445                 while(!feof($fp)) 
446                 { 
447                     // send the current file part to the browser 
448                     $line = trim(fgets($fp, 1024));
449                     if (empty($line)) {
450                         continue;
451                     }
452                     $matches = array();
453                     if (!preg_match('/^ERROR\s+([0-9]+)/', $line, $matches)) {
454                         echo " ---- {$line}\n"; flush();
455                         continue;
456                     }
457                     $continue =0;
458                     switch($matches[1]) {
459                         case 1017: // cause by renaming table -- old one does not exist..
460                         case 1050: // create tables triggers this..
461                         case 1060: //    Duplicate column name
462                         case 1061: // Duplicate key name - triggered by add index.. but could hide error. - unlikely though.
463                         case 1091: // drop index -- name does not exist.. might hide errors..
464                         
465                         case 1146: // drop a index on an unknown table.. - happens rarely...
466                         case 1054: // Unknown column -- triggered by CHANGE COLUMN - but may hide other errrors..
467                             $continue = 1;
468                             break;
469                         
470                     }
471                     if ($continue) {
472                         echo " ---- {$line}\n"; flush();
473                         continue;
474                     }
475                     // real errors...
476                     // 1051: // Unknown table -- normally drop = add iff exists..
477                     echo "File: $fn\n$line\n";
478                     exit;
479                     
480                     
481                 } 
482                 
483             
484                 
485         }
486        
487         
488         
489     }
490     
491     
492     /**
493      * simple regex based convert mysql to pgsql...
494      */
495     function convertToPG($src)
496     {
497         //echo "Convert $src\n";
498                
499         $fn = $this->tempName('sql');
500         
501         $ret = array( ); // pad it a bit.
502         $extra = array("", "" );
503         
504         $tbl = false;
505         foreach(file($src) as $l) {
506             $l = trim($l);
507             
508             if (!strlen($l) || $l[0] == '#') {
509                 continue;
510             }
511             $m = array();
512             if (preg_match('#create\s+table\s+([a-z0-9_]+)#i',  $l, $m)) {
513                 $tbl = $m[1];
514              }
515             if (preg_match('#create\s+table\s+\`([a-z0-9_]+)\`#i',  $l, $m)) {
516                 $tbl = 'shop_' . strtolower($m[1]);
517                 $l = preg_replace('#create\s+table\s+\`([a-z0-9_]+)\`#i', "CREATE TABLE {$tbl}", $l);
518             }
519             if (preg_match('#\`([a-z0-9_]+)\`#i',  $l, $m) && !preg_match('#alter\s+table\s+#i',  $l)) {
520                 $l = preg_replace('#\`([a-z0-9_]+)\`#i', "{$m[1]}_name", $l);
521             }
522             // autoinc
523             if ($tbl && preg_match('#auto_increment#i',  $l, $m)) {
524                 $l = preg_replace('#auto_increment#i', "default nextval('{$tbl}_seq')", $l);
525                 $extra[]  =   "create sequence {$tbl}_seq;";
526               
527             }
528             
529             if (preg_match('#alter\s+table\s+(\`[a-z0-9_]+\`)#i',  $l, $m)){
530                 $l = preg_replace('#alter\s+table\s+(\`[a-z0-9_]+\`)#i', "ALTER TABLE {$tbl}", $l);
531             }
532             
533             // enum value -- use the text instead..
534             
535             if ($tbl && preg_match('#([\w]+)\s+(enum\([\w|\W]+\))#i',  $l, $m)) {
536                 $l = preg_replace('#enum\([\w|\W]+\)#i', "TEXT", $l);
537             }
538             // ignore the alter enum
539             if ($tbl && preg_match('#alter\s+table\s+([\w|\W]+)\s+enum\([\w|\W]+\)#i',  $l, $m)) {
540                 continue;
541             }
542             
543             // UNIQUE KEY .. ignore
544             if ($tbl && preg_match('#UNIQUE KEY#i',  $l, $m)) {
545                 $last = array_pop($ret);
546                 $ret[] = trim($last, ",");
547                 continue;
548             }
549             
550             if ($tbl && preg_match('#RENAME\s+TO#i',  $l, $m)) {
551                 continue;
552             }
553             
554             if ($tbl && preg_match('#change\s+column#i',  $l, $m)) {
555                 continue;
556             }
557             
558             // INDEX lookup ..ignore
559             if ($tbl && preg_match('#INDEX lookup+([\w|\W]+)#i',  $l, $m)) {
560                $last = array_pop($ret);
561                $ret[] = trim($last, ",");
562                continue;
563                
564             }
565             
566             // CREATE INDEX ..ignore
567             if (preg_match('#alter\s+table\s+([a-z0-9_]+)\s+add\s+index\s+#i',  $l, $m)) {
568 //               $l = "CREATE INDEX  {$m[1]}_{$m[2]} ON {$m[1]} {$m[3]}";
569                 continue;
570              }
571              
572             // basic types..
573             $l = preg_replace('#int\([0-9]+\)#i', 'INT', $l);
574             
575             $l = preg_replace('# datetime#i', ' TIMESTAMP WITHOUT TIME ZONE', $l);
576             $l = preg_replace('# blob#i', ' TEXT', $l);
577             $l = preg_replace('# longtext#i', ' TEXT', $l);
578             $l = preg_replace('# tinyint#i', ' INT', $l);
579             
580             $ret[] = $l;
581             
582         }
583         
584         $ret = array_merge($extra,$ret);
585 //        echo implode("\n", $ret); exit;
586         
587         file_put_contents($fn, implode("\n", $ret));
588         
589         return $fn;
590     }
591     
592     
593     function checkOpts($opts)
594     {
595         
596         
597         foreach($opts as $o=>$v) {
598             if (!preg_match('/^json-/', $o) || empty($v)) {
599                 continue;
600             }
601             if (!file_exists($v)) {
602                 die("File does not exist : OPTION --{$o} = {$v} \n");
603             }
604         }
605         
606         $modules = array_reverse($this->modulesList());
607         
608         // move 'project' one to the end...
609         
610         foreach ($modules as $module){
611             $file = $this->rootDir. "/Pman/$module/UpdateDatabase.php";
612             if($module == 'Core' || !file_exists($file)){
613                 continue;
614             }
615             require_once $file;
616             $class = "Pman_{$module}_UpdateDatabase";
617             $x = new $class;
618             if(!method_exists($x, 'checkOpts')){
619                 continue;
620             };
621             $x->checkOpts($opts);
622         }
623                 
624     }
625     static function jsonImportFromArray($opts)
626     {
627         foreach($opts as $o=>$v) {
628             if (!preg_match('/^json-/', $o) || empty($v)) {
629                 continue;
630             }
631             $type = str_replace('_', '-', substr($o,5));
632             
633             $data= json_decode(file_get_contents($v),true);
634             $pg = HTML_FlexyFramework::get()->page;
635             DB_DataObject::factory($type)->importFromArray($pg ,$data,$opts);
636             
637         }
638         
639         
640         
641     }
642     
643     
644     
645     function runUpdateModulesData()
646     {
647         if (class_exists('PDO_DataObjects_Introspection')) {
648             PDO_DataObject_Introspection::$cache = array();
649         }
650         HTML_FlexyFramework::get()->generateDataobjectsCache(true);
651         
652         if(!in_array('Core', $this->disabled)){
653             echo "Running jsonImportFromArray\n";
654             Pman_Core_UpdateDatabase::jsonImportFromArray($this->opts);
655             
656
657             echo "Running updateData on modules\n";
658             // runs core...
659             echo "Core\n";
660             $this->updateData(); 
661         }
662         
663         $modules = array_reverse($this->modulesList());
664         
665         // move 'project' one to the end...
666         
667         foreach ($modules as $module){
668             if(in_array($module, $this->disabled)){
669                 continue;
670             }
671             $file = $this->rootDir. "/Pman/$module/UpdateDatabase.php";
672             if($module == 'Core' || !file_exists($file)){
673                 continue;
674             }
675             
676             require_once $file;
677             $class = "Pman_{$module}_UpdateDatabase";
678             $x = new $class;
679             if(!method_exists($x, 'updateData')){
680                 continue;
681             };
682             echo "$module\n";
683             $x->updateData();
684         }
685         
686     }
687     
688     
689     function updateDataEnums()
690     {
691         
692         $enum = DB_DataObject::Factory('core_enum');
693         //DB_DAtaObject::debugLevel(1);
694         $enum->initEnums(
695             array(
696                 array(
697                     'etype' => '',
698                     'name' => 'COMPTYPE',
699                     'display_name' =>  'Company Types',
700                     'is_system_enum' => 1,
701                     'cn' => array(
702                         array(
703                             'name' => 'OWNER',
704                             'display_name' => 'Owner',
705                             'seqid' => 999, // last...
706                             'is_system_enum' => 1,
707                         )
708                         
709                     )
710                 ),
711                 array(
712                     'etype' => '',
713                     'name' => 'HtmlEditor.font-family',
714                     'display_name' =>  'HTML Editor font families',
715                     'is_system_enum' => 1,
716                     'cn' => array(
717                         array(
718                             'name' => 'Helvetica,Arial,sans-serif',
719                             'display_name' => 'Helvetica',
720                             
721                         ),
722                         
723                         array(
724                             'name' => 'Courier New',
725                             'display_name' => 'Courier',
726                              
727                         ),
728                         array(
729                             'name' => 'Tahoma',
730                             'display_name' => 'Tahoma',
731                             
732                         ),
733                         array(
734                             'name' => 'Times New Roman,serif',
735                             'display_name' => 'Times',
736                            
737                         ),
738                         array(
739                             'name' => 'Verdana',
740                             'display_name' => 'Verdana',
741                             
742                         ),
743                         
744                             
745                         
746                     )
747                 )
748                
749             )
750         ); 
751         
752     }
753     function updateDataGroups()
754     {
755          
756         $groups = DB_DataObject::factory('core_group');
757         $groups->initGroups();
758         
759         $groups->initDatabase($this,array(
760             array(
761                 'name' => 'bcc-email', // group who are bcc'ed on all requests.
762                 'type' => 0, // system
763             ),
764             array(
765                 'name' => 'system-email-from',
766                 'type' => 0, // system
767             ),
768             array(
769                 'name' => 'core-person-signup-bcc',
770                 'type' => 0, // system
771             ),
772         
773
774         ));
775         
776     }
777     
778     function updateDataCompanies()
779     {
780          
781         // fix comptypes enums..
782         $c = DB_DataObject::Factory('core_company');
783         $c->selectAdd();
784         $c->selectAdd('distinct(comptype) as comptype');
785         $c->whereAdd("comptype != ''");
786         
787         $ctb = array();
788         foreach($c->fetchAll('comptype') as $cts) {
789             
790             
791             
792            $ctb[]= array( 'etype'=>'COMPTYPE', 'name' => $cts, 'display_name' => ucfirst(strtolower($cts)));
793         
794         }
795          $c = DB_DataObject::Factory('core_enum');
796          
797         $c->initEnums($ctb);
798         //DB_DataObject::debugLevel(1);
799         // fix comptypeid
800         $c = DB_DataObject::Factory('core_company');
801         $c->query("
802             UPDATE {$c->tableName()} 
803                 SET
804                     comptype_id = (SELECT id FROM core_enum where etype='comptype' and name={$c->tableName()}.comptype LIMIT 1)
805                 WHERE
806                     comptype_id = 0
807                     AND
808                     LENGTH(comptype) > 0
809                   
810                   
811                   ");
812          
813         
814         
815     }
816     
817     
818     function initEmails($templateDir, $emails)
819     {
820         $pg = HTML_FlexyFramework::get()->page;
821         foreach($emails as $name=>$data) {
822             $cm = DB_DataObject::factory('core_email');
823             $update = $cm->get('name', $name);
824             $old = clone($cm);
825             
826             if (empty($cm->bcc_group)) {
827                 if (empty($data['bcc_group'])) {
828                     $this->jerr("missing bcc_group for template $name");
829                 }
830                 $g = DB_DataObject::Factory('core_group')->lookup('name',$data['bcc_group']);
831                 
832                 if (empty($g->id)) {
833                     $this->jerr("bcc_group {$data['bcc_group']} does not exist when importing template $name");
834                 }
835                 
836                 if (!$g->members('email')) {
837                     $this->jerr("bcc_group {$data['bcc_group']} does not have any members");
838                 }
839                 
840                 $cm->bcc_group = $g->id;
841             }
842             
843             if (empty($cm->test_class)) {
844                 if (empty($data['test_class'])) {
845                     $this->jerr("missing test_class for template $name");
846                 }
847                 $cm->test_class = $data['test_class'];
848             }
849             
850             print_r('die here');exit;
851             
852             require_once $cm->test_class . '.php';
853             
854             $clsname = str_replace('/','_', $cm->test_class);
855             try {
856                 $method = new ReflectionMethod($clsname , 'test_'. $name) ;
857                 $got_it = $method->isStatic();
858             } catch(Exception $e) {
859                 $got_it = false;
860                 
861             }
862             if (!$got_it) {
863                 $this->jerr("template {$name} does not have a test method {$clsname}::test_{$name}");
864             }
865             if ($update) {
866                 $cm->update($old);
867                 echo "email: {$name} - checked\n";
868                 continue; /// we do not import the body content of templates that exist...
869             } else {
870                 
871                 //$cm->insert();
872             }
873             
874             
875     //        $basedir = $this->bootLoader->rootDir . $mail_template_dir;
876             
877             $opts = array(
878                 'update' => 1,
879                 'file' => $templateDir. $name .'.html'
880             );
881             
882             if (!empty($data['master'])) {
883                 $opts['master'] = $templateDir . $master .'.html';
884             }
885             require_once 'Pman/Core/Import/Core_email.php';
886             $x = new Pman_Core_Import_Core_email();
887             $x->updateOrCreateEmail('', $opts, $cm);
888             
889             echo "email: {$name} - CREATED\n";
890         }
891     }
892     
893     
894     function updateData()
895     {
896         // fill i18n data..
897         if (class_exists('PDO_DataObjects_Introspection')) {
898             PDO_DataObject_Introspection::$cache = array();
899         }
900         HTML_FlexyFramework::get()->generateDataobjectsCache(true);
901         $this->updateDataEnums();
902         $this->updateDataGroups();
903         $this->updateDataCompanies();
904         
905         $c = DB_DataObject::Factory('I18n');
906         $c->buildDB();
907          
908        
909         
910         
911     }
912     
913     function fixMysqlInnodb()
914     {
915         
916         static $done_check = false;
917         if ($done_check) {
918             return;
919         }
920         
921         
922         if (!empty($this->opts['skip-mysql-checks'])) {
923             return;
924         }
925         // innodb in single files is far more efficient that MYD or one big innodb file.
926         // first check if database is using this format.
927         $db = DB_DataObject::factory('core_enum');
928         $db->query("show variables like 'innodb_file_per_table'");
929         $db->fetch();
930         if ($db->Value == 'OFF') {
931             die("Error: set innodb_file_per_table = 1 in my.cnf (or run with --skip-mysql-checks\n\n");
932         }
933         
934         $db = DB_DataObject::factory('core_enum');
935         $db->query("select version() as version");
936         $db->fetch();
937         
938         if (version_compare($db->version, '5.7', '>=' )) {
939                 
940             $db = DB_DataObject::factory('core_enum');
941             $db->query("show variables like 'sql_mode'");
942             $db->fetch();
943             
944             $modes = explode(",", $db->Value);
945             
946             // these are 'new' problems with mysql.
947             if(
948                     in_array('NO_ZERO_IN_DATE', $modes) ||
949                     in_array('NO_ZERO_DATE', $modes) ||
950                     in_array('STRICT_TRANS_TABLES', $modes) || 
951                     !in_array('ALLOW_INVALID_DATES', $modes)
952             ){
953                 die("Error: set sql_mode include 'ALLOW_INVALID_DATES', remove 'NO_ZERO_IN_DATE' AND 'STRICT_TRANS_TABLES' AND 'NO_ZERO_DATE' in my.cnf\n\n");
954             }
955         }
956         
957         $done_check = true;;
958
959  
960         
961         
962         
963         
964         
965     }
966     
967     
968     /** ------------- schema fixing ... there is an issue with data imported having the wrong sequence names... --- */
969     
970     function fixSequencesMysql()
971     {
972         // not required...
973     }
974     
975     function fixSequencesPgsql()
976     {
977      
978      
979         //DB_DataObject::debugLevel(1);
980         $cs = DB_DataObject::factory('core_enum');
981         $cs->query("
982          SELECT
983                     'ALTER SEQUENCE '||
984                     CASE WHEN strpos(seq_name, '.') > 0 THEN
985                         min(seq_name)
986                     ELSE 
987                         quote_ident(min(schema_name)) ||'.'|| quote_ident(min(seq_name))
988                     END 
989                     
990                     ||' OWNED BY '|| quote_ident(min(schema_name)) || '.' ||
991                     quote_ident(min(table_name)) ||'.'|| quote_ident(min(column_name)) ||';' as cmd
992              FROM (
993                       
994                        SELECT 
995                      n.nspname AS schema_name,
996                      c.relname AS table_name,
997                      a.attname AS column_name, 
998                      regexp_replace(regexp_replace(d.adsrc, E'nextval\\\\(+[''\\\"]*', ''),E'[''\\\"]*::.*\$','') AS seq_name 
999                  FROM pg_class c 
1000                  JOIN pg_attribute a ON (c.oid=a.attrelid) 
1001                  JOIN pg_attrdef d ON (a.attrelid=d.adrelid AND a.attnum=d.adnum) 
1002                  JOIN pg_namespace n ON (c.relnamespace=n.oid)
1003                  WHERE has_schema_privilege(n.oid,'USAGE')
1004                    AND n.nspname NOT LIKE 'pg!_%' escape '!'
1005                    AND has_table_privilege(c.oid,'SELECT')
1006                    AND (NOT a.attisdropped)
1007                    AND d.adsrc ~ '^nextval'
1008               
1009              ) seq
1010              WHERE
1011                  CASE WHEN strpos(seq_name, '.') > 0 THEN
1012                      substring(seq_name, 1,strpos(seq_name,'.')-1)
1013                 ELSE
1014                     schema_name
1015                 END = schema_name
1016              
1017              GROUP BY seq_name HAVING count(*)=1
1018              ");
1019         $cmds = array();
1020         while ($cs->fetch()) {
1021             $cmds[] = $cs->cmd;
1022         }
1023         foreach($cmds as $cmd) {
1024             $cs = DB_DataObject::factory('core_enum');
1025             echo "$cmd\n";
1026             $cs->query($cmd);
1027         }
1028         $cs = DB_DataObject::factory('core_enum');
1029          $cs->query("
1030                SELECT  'SELECT SETVAL(' ||
1031                          quote_literal(quote_ident(nspname) || '.' || quote_ident(S.relname)) ||
1032                         ', MAX(' || quote_ident(C.attname)|| ')::integer )  FROM ' || nspname || '.' || quote_ident(T.relname)|| ';' as cmd 
1033                 FROM pg_class AS S,
1034                     pg_depend AS D,
1035                     pg_class AS T,
1036                     pg_attribute AS C,
1037                     pg_namespace AS NS
1038                 WHERE S.relkind = 'S'
1039                     AND S.oid = D.objid
1040                     AND D.refobjid = T.oid
1041                     AND D.refobjid = C.attrelid
1042                     AND D.refobjsubid = C.attnum
1043                     AND NS.oid = T.relnamespace
1044                 ORDER BY S.relname   
1045         ");
1046          $cmds = array();
1047         while ($cs->fetch()) {
1048             $cmds[] = $cs->cmd;
1049         }
1050         foreach($cmds as $cmd) {
1051             $cs = DB_DataObject::factory('core_enum');
1052             echo "$cmd\n";
1053             $cs->query($cmd);
1054         }
1055        
1056     }
1057     
1058     var $extensions = array(
1059         'EngineCharset',
1060         'Links',
1061     );
1062     
1063     function runExtensions()
1064     {
1065         
1066         $ff = HTML_Flexyframework::get();
1067         
1068         $dburl = parse_url($ff->database);
1069         
1070         $dbtype = $dburl['scheme'];
1071        
1072         foreach($this->extensions as $ext) {
1073        
1074             $scls = ucfirst($dbtype). $ext;
1075             $cls = __CLASS__ . '_'. $scls;
1076             $fn = implode('/',explode('_', $cls)).'.php';
1077             if (!file_exists(__DIR__.'/UpdateDatabase/'. $scls .'.php')) {
1078                 return;
1079             }
1080             require_once $fn;
1081             $c = new $cls();
1082             
1083         }
1084         
1085     }
1086     
1087     
1088     function checkSystem($req = false, $pref = false)
1089     {
1090         // most of these are from File_Convert...
1091         
1092         // these are required - and have simple dependancies.
1093         require_once 'System.php';
1094         $req = $req !== false ? $req : array( 
1095             'convert',
1096             'grep',
1097             'pdfinfo',
1098             'pdftoppm',
1099             'rsvg-convert',  //librsvg2-bin
1100             'strings',
1101             'oathtool'
1102         );
1103          
1104          
1105          
1106         // these are prefered - but may have complicated depenacies
1107         $pref = $pref !== false ? $pref :  array(
1108             'abiword',
1109             'faad',
1110             'ffmpeg',
1111             'html2text', // not availabe in debian squeeze
1112             'pdftocairo',  //poppler-utils - not available in debian squeeze.
1113
1114             'lame',
1115             'ssconvert',
1116             'unoconv',
1117             'wkhtmltopdf',
1118             'xvfb-run',
1119         );
1120         $res = array();
1121         $fail = false;
1122         foreach($req as $r) {
1123             if (!System::which($r)) {
1124                 $res[] = $r;
1125             }
1126             $fail = true;
1127         }
1128         if ($res) {
1129             die("Missing these programs - need installing\n" . implode("\n",$res). "\n");
1130         }
1131         foreach($pref as $r) {
1132             if (!System::which($r)) {
1133                 $res[] = $r;
1134             }
1135             $fail = true;
1136         }
1137         if ($res) {
1138             echo "WARNING: Missing these programs - they may need installing\n". implode("\n",$res);
1139             sleep(5);
1140         }
1141         
1142         
1143     }
1144     
1145     function generateDataobjectsCache()
1146     {
1147         $url = "http://localhost{$this->local_base_url}/Core/RefreshDatabaseCache";
1148             
1149         $this->curl($url);
1150         
1151     }
1152     
1153     function curl($url, $request = array(), $method = 'GET') 
1154     {
1155         if($method == 'GET'){
1156             $request = http_build_query($request);
1157             $url = $url . "?" . $request;  
1158         }
1159         
1160         $ch = curl_init($url);
1161         
1162         if ($method == 'POST') {
1163             
1164             curl_setopt($ch, CURLOPT_POST, 1);
1165             curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
1166             
1167         } else {
1168             
1169             curl_setopt($ch, CURLOPT_HTTPHEADER,
1170                     array("Content-Type: application/x-www-form-urlencoded", "Content-Length: " . strlen($request)));
1171             
1172         }
1173         
1174         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1175         
1176         curl_setopt($ch, CURLOPT_HEADER, false);
1177         curl_setopt($ch, CURLOPT_VERBOSE, 0);
1178         curl_setopt($ch, CURLOPT_TIMEOUT, 30);
1179         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
1180
1181         $response = curl_exec($ch);
1182         
1183         curl_close($ch);
1184         
1185         return $response;
1186     }
1187     
1188     
1189     
1190 }