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     }
681     
682     
683     function updateDataEnums()
684     {
685         
686         $enum = DB_DataObject::Factory('core_enum');
687         //DB_DAtaObject::debugLevel(1);
688         $enum->initEnums(
689             array(
690                 array(
691                     'etype' => '',
692                     'name' => 'COMPTYPE',
693                     'display_name' =>  'Company Types',
694                     'is_system_enum' => 1,
695                     'cn' => array(
696                         array(
697                             'name' => 'OWNER',
698                             'display_name' => 'Owner',
699                             'seqid' => 999, // last...
700                             'is_system_enum' => 1,
701                         )
702                         
703                     )
704                 ),
705                 array(
706                     'etype' => '',
707                     'name' => 'HtmlEditor.font-family',
708                     'display_name' =>  'HTML Editor font families',
709                     'is_system_enum' => 1,
710                     'cn' => array(
711                         array(
712                             'name' => 'Helvetica,Arial,sans-serif',
713                             'display_name' => 'Helvetica',
714                             
715                         ),
716                         
717                         array(
718                             'name' => 'Courier New',
719                             'display_name' => 'Courier',
720                              
721                         ),
722                         array(
723                             'name' => 'Tahoma',
724                             'display_name' => 'Tahoma',
725                             
726                         ),
727                         array(
728                             'name' => 'Times New Roman,serif',
729                             'display_name' => 'Times',
730                            
731                         ),
732                         array(
733                             'name' => 'Verdana',
734                             'display_name' => 'Verdana',
735                             
736                         ),
737                         
738                             
739                         
740                     )
741                 ),
742             )
743         ); 
744         
745     }
746     function updateDataGroups()
747     {
748          
749         $groups = DB_DataObject::factory('core_group');
750         $groups->initGroups();
751         
752         $groups->initDatabase($this,array(
753             array(
754                 'name' => 'bcc-email', // group who are bcc'ed on all requests.
755                 'type' => 0, // system
756             ),
757             array(
758                 'name' => 'system-email-from',
759                 'type' => 0, // system
760             ),
761             array(
762                 'name' => 'core-person-signup-bcc',
763                 'type' => 0, // system
764             ),
765         ));
766         
767     }
768     
769     function updateDataCompanies()
770     {
771          
772         // fix comptypes enums..
773         $c = DB_DataObject::Factory('core_company');
774         $c->selectAdd();
775         $c->selectAdd('distinct(comptype) as comptype');
776         $c->whereAdd("comptype != ''");
777         
778         $ctb = array();
779         foreach($c->fetchAll('comptype') as $cts) {
780             
781             
782             
783            $ctb[]= array( 'etype'=>'COMPTYPE', 'name' => $cts, 'display_name' => ucfirst(strtolower($cts)));
784         
785         }
786          $c = DB_DataObject::Factory('core_enum');
787          
788         $c->initEnums($ctb);
789         //DB_DataObject::debugLevel(1);
790         // fix comptypeid
791         $c = DB_DataObject::Factory('core_company');
792         $c->query("
793             UPDATE {$c->tableName()} 
794                 SET
795                     comptype_id = (SELECT id FROM core_enum where etype='comptype' and name={$c->tableName()}.comptype LIMIT 1)
796                 WHERE
797                     comptype_id = 0
798                     AND
799                     LENGTH(comptype) > 0
800                   
801                   
802                   ");
803          
804         
805         
806     }
807     
808     
809     function initEmails($templateDir, $emails)
810     {
811       
812         $pg = HTML_FlexyFramework::get()->page;
813         foreach($emails as $name=>$data) {
814             $cm = DB_DataObject::factory('core_email');
815             $update = $cm->get('name', $name);
816             $old = clone($cm);
817             
818             if (empty($cm->bcc_group)) {
819                 if (empty($data['bcc_group'])) {
820                     $this->jerr("missing bcc_group for template $name");
821                 }
822                 $g = DB_DataObject::Factory('core_group')->lookup('name',$data['bcc_group']);
823                 
824                 if (empty($g->id)) {
825                     $this->jerr("bcc_group {$data['bcc_group']} does not exist when importing template $name");
826                 }
827                 
828                 
829                 if (!$g->members('email')) {
830                     $this->jerr("bcc_group {$data['bcc_group']} does not have any members");
831                 }
832                 
833                 $cm->bcc_group = $g->id;
834             }
835             if (empty($cm->test_class)) {
836                 if (empty($data['test_class'])) {
837                     $this->jerr("missing test_class for template $name");
838                 }
839                 $cm->test_class = $data['test_class'];
840             }
841             require_once $cm->test_class . '.php';
842             
843             $clsname = str_replace('/','_', $cm->test_class);
844             try {
845                 $method = new ReflectionMethod($clsname , 'test_'. $name) ;
846                 $got_it = $method->isStatic();
847             } catch(Exception $e) {
848                 $got_it = false;
849                 
850             }
851             if (!$got_it) {
852                 $this->jerr("template {$name} does not have a test method {$clsname}::test_{$name}");
853             }
854             if ($update) {
855                 $cm->update($old);
856                 echo "email: {$name} - checked\n";
857                 continue; /// we do not import the body content of templates that exist...
858             } else {
859                 
860                 //$cm->insert();
861             }
862             
863             
864     //        $basedir = $this->bootLoader->rootDir . $mail_template_dir;
865             
866             $opts = array(
867                 'update' => 1,
868                 'file' => $templateDir. $name .'.html'
869             );
870             
871             if (!empty($data['master'])) {
872                 $opts['master'] = $templateDir . $master .'.html';
873             }
874             require_once 'Pman/Core/Import/Core_email.php';
875             $x = new Pman_Core_Import_Core_email();
876             $x->updateOrCreateEmail('', $opts, $cm);
877             
878             echo "email: {$name} - CREATED\n";
879         }
880     }
881     
882     
883     function updateData()
884     {
885         // fill i18n data..
886         if (class_exists('PDO_DataObjects_Introspection')) {
887             PDO_DataObject_Introspection::$cache = array();
888         }
889         HTML_FlexyFramework::get()->generateDataobjectsCache(true);
890         $this->updateDataEnums();
891         $this->updateDataGroups();
892         $this->updateDataCompanies();
893         
894         $c = DB_DataObject::Factory('I18n');
895         $c->buildDB();
896          
897        
898         
899         
900     }
901     
902     function fixMysqlInnodb()
903     {
904         
905         static $done_check = false;
906         if ($done_check) {
907             return;
908         }
909         // innodb in single files is far more efficient that MYD or one big innodb file.
910         // first check if database is using this format.
911         $db = DB_DataObject::factory('core_enum');
912         $db->query("show variables like 'innodb_file_per_table'");
913         $db->fetch();
914         if ($db->Value == 'OFF') {
915             die("Error: set innodb_file_per_table = 1 in my.cnf\n\n");
916         }
917         
918         $db = DB_DataObject::factory('core_enum');
919         $db->query("select version() as version");
920         $db->fetch();
921         
922         if (version_compare($db->version, '5.7', '>=' )) {
923                 
924             $db = DB_DataObject::factory('core_enum');
925             $db->query("show variables like 'sql_mode'");
926             $db->fetch();
927             
928             $modes = explode(",", $db->Value);
929             
930             // these are 'new' problems with mysql.
931             if(
932                     in_array('NO_ZERO_IN_DATE', $modes) ||
933                     in_array('NO_ZERO_DATE', $modes) ||
934                     in_array('STRICT_TRANS_TABLES', $modes) || 
935                     !in_array('ALLOW_INVALID_DATES', $modes)
936             ){
937                 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");
938             }
939         }
940         
941         $done_check = true;;
942
943  
944         
945         
946         
947         
948         
949     }
950     
951     
952     /** ------------- schema fixing ... there is an issue with data imported having the wrong sequence names... --- */
953     
954     function fixSequencesMysql()
955     {
956         // not required...
957     }
958     
959     function fixSequencesPgsql()
960     {
961      
962      
963         //DB_DataObject::debugLevel(1);
964         $cs = DB_DataObject::factory('core_enum');
965         $cs->query("
966          SELECT
967                     'ALTER SEQUENCE '||
968                     CASE WHEN strpos(seq_name, '.') > 0 THEN
969                         min(seq_name)
970                     ELSE 
971                         quote_ident(min(schema_name)) ||'.'|| quote_ident(min(seq_name))
972                     END 
973                     
974                     ||' OWNED BY '|| quote_ident(min(schema_name)) || '.' ||
975                     quote_ident(min(table_name)) ||'.'|| quote_ident(min(column_name)) ||';' as cmd
976              FROM (
977                       
978                        SELECT 
979                      n.nspname AS schema_name,
980                      c.relname AS table_name,
981                      a.attname AS column_name, 
982                      regexp_replace(regexp_replace(d.adsrc, E'nextval\\\\(+[''\\\"]*', ''),E'[''\\\"]*::.*\$','') AS seq_name 
983                  FROM pg_class c 
984                  JOIN pg_attribute a ON (c.oid=a.attrelid) 
985                  JOIN pg_attrdef d ON (a.attrelid=d.adrelid AND a.attnum=d.adnum) 
986                  JOIN pg_namespace n ON (c.relnamespace=n.oid)
987                  WHERE has_schema_privilege(n.oid,'USAGE')
988                    AND n.nspname NOT LIKE 'pg!_%' escape '!'
989                    AND has_table_privilege(c.oid,'SELECT')
990                    AND (NOT a.attisdropped)
991                    AND d.adsrc ~ '^nextval'
992               
993              ) seq
994              WHERE
995                  CASE WHEN strpos(seq_name, '.') > 0 THEN
996                      substring(seq_name, 1,strpos(seq_name,'.')-1)
997                 ELSE
998                     schema_name
999                 END = schema_name
1000              
1001              GROUP BY seq_name HAVING count(*)=1
1002              ");
1003         $cmds = array();
1004         while ($cs->fetch()) {
1005             $cmds[] = $cs->cmd;
1006         }
1007         foreach($cmds as $cmd) {
1008             $cs = DB_DataObject::factory('core_enum');
1009             echo "$cmd\n";
1010             $cs->query($cmd);
1011         }
1012         $cs = DB_DataObject::factory('core_enum');
1013          $cs->query("
1014                SELECT  'SELECT SETVAL(' ||
1015                          quote_literal(quote_ident(nspname) || '.' || quote_ident(S.relname)) ||
1016                         ', MAX(' || quote_ident(C.attname)|| ')::integer )  FROM ' || nspname || '.' || quote_ident(T.relname)|| ';' as cmd 
1017                 FROM pg_class AS S,
1018                     pg_depend AS D,
1019                     pg_class AS T,
1020                     pg_attribute AS C,
1021                     pg_namespace AS NS
1022                 WHERE S.relkind = 'S'
1023                     AND S.oid = D.objid
1024                     AND D.refobjid = T.oid
1025                     AND D.refobjid = C.attrelid
1026                     AND D.refobjsubid = C.attnum
1027                     AND NS.oid = T.relnamespace
1028                 ORDER BY S.relname   
1029         ");
1030          $cmds = array();
1031         while ($cs->fetch()) {
1032             $cmds[] = $cs->cmd;
1033         }
1034         foreach($cmds as $cmd) {
1035             $cs = DB_DataObject::factory('core_enum');
1036             echo "$cmd\n";
1037             $cs->query($cmd);
1038         }
1039        
1040     }
1041     
1042     var $extensions = array(
1043         'EngineCharset',
1044         'Links',
1045     );
1046     
1047     function runExtensions()
1048     {
1049         
1050         $ff = HTML_Flexyframework::get();
1051         
1052         $dburl = parse_url($ff->database);
1053         
1054         $dbtype = $dburl['scheme'];
1055        
1056         foreach($this->extensions as $ext) {
1057        
1058             $scls = ucfirst($dbtype). $ext;
1059             $cls = __CLASS__ . '_'. $scls;
1060             $fn = implode('/',explode('_', $cls)).'.php';
1061             if (!file_exists(__DIR__.'/UpdateDatabase/'. $scls .'.php')) {
1062                 return;
1063             }
1064             require_once $fn;
1065             $c = new $cls();
1066             
1067         }
1068         
1069     }
1070     
1071     
1072     function checkSystem()
1073     {
1074         // most of these are from File_Convert...
1075         
1076         // these are required - and have simple dependancies.
1077         require_once 'System.php';
1078         $req = array( 
1079             'convert',
1080             'grep',
1081             'pdfinfo',
1082             'pdftoppm',
1083             'rsvg-convert',  //librsvg2-bin
1084             'strings',
1085         );
1086          
1087          
1088          
1089         // these are prefered - but may have complicated depenacies
1090         $pref= array(
1091             'abiword',
1092             'faad',
1093             'ffmpeg',
1094             'html2text', // not availabe in debian squeeze
1095             'pdftocairo',  //poppler-utils - not available in debian squeeze.
1096
1097             'lame',
1098             'ssconvert',
1099             'unoconv',
1100             'wkhtmltopdf',
1101             'xvfb-run',
1102         );
1103         $res = array();
1104         $fail = false;
1105         foreach($req as $r) {
1106             if (!System::which($r)) {
1107                 $res[] = $r;
1108             }
1109             $fail = true;
1110         }
1111         if ($res) {
1112             die("Missing these programs - need installing\n" . implode("\n",$res). "\n");
1113         }
1114         foreach($pref as $r) {
1115             if (!System::which($r)) {
1116                 $res[] = $r;
1117             }
1118             $fail = true;
1119         }
1120         if ($res) {
1121             echo "WARNING: Missing these programs - they may need installing\n". implode("\n",$res);
1122             sleep(5);
1123         }
1124         
1125         
1126     }
1127     
1128     function generateDataobjectsCache()
1129     {
1130         $url = "http://localhost{$this->local_base_url}/Roo/Core/RefreshDatabaseCache";
1131             
1132         $response = $this->curl($url);
1133         
1134         echo "here\n";
1135         print_r($response);exit;
1136     }
1137     
1138     function curl($url, $request = array(), $method = 'GET') 
1139     {
1140         if($method == 'GET'){
1141             $request = http_build_query($request);
1142             $url = $url . "?" . $request;  
1143         }
1144         
1145         $ch = curl_init($url);
1146         
1147         if ($method == 'POST') {
1148             
1149             curl_setopt($ch, CURLOPT_POST, 1);
1150             curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
1151             
1152         } else {
1153             
1154             curl_setopt($ch, CURLOPT_HTTPHEADER,
1155                     array("Content-Type: application/x-www-form-urlencoded", "Content-Length: " . strlen($request)));
1156             
1157         }
1158         
1159         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1160         
1161         curl_setopt($ch, CURLOPT_HEADER, false);
1162         curl_setopt($ch, CURLOPT_VERBOSE, 1);
1163         curl_setopt($ch, CURLOPT_TIMEOUT, 30);
1164         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
1165
1166         $response = curl_exec($ch);
1167         
1168         curl_close($ch);
1169         
1170         return $response;
1171     }
1172     
1173     
1174     
1175 }