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