sync
[Pman.Admin] / Export.php
1 <?php
2
3
4 /**
5  * This should allow you to dump a series of data, so it could be restored later...
6  * The format will be an SQL file...
7  *
8  * usage:
9  *    php index.php  Admin/Dump --table=Project --where="id=123"
10  *              --dump-dir=/directory_to_put_sql+shell files
11  *
12  *    outputs list of generated files.
13  *    
14  *     RESTORE FILES:
15  *      {DATE}.sql - the recreate sql including all dependancies, run with mysql DB -f  < ....
16  *      {DATE}.restore.sh - shell script to recreate files that where removed (excluding thumgs)
17  *      
18  *     BACKUP
19  *      {DATE}.copy.sh - shell script that backs up all the related files.
20  *      
21  *     DESTROY
22  *      {DATE}.delete.sql - the delete data sql.
23  *      {DATE}.delete.sh - shell script to delete the files related to these records
24  
25  *    
26  *  Basically it has to output all the records and their dependants. (parent and children)
27  *
28  *  Then when deleting it deletes record + children..
29  *
30  *
31  *  Each ouput is simple insert statement..
32  *
33  *
34  *  TODO - handle Images table (or similar) where we use tablename=XXXX, tid=.... etc..
35  *
36  *
37  *  INTERFACES :
38  *
39  *      DataObjects->archivePaths() - returns array ( sourcedirectory, remainder of path to dependant file )
40  *      DataObjects->listThumbs() - returns array ( list of full path to thumbnail urls. )
41  *
42  *
43  *  DISCOVERY
44  *    
45  *
46  * 
47  */
48
49 require_once 'Pman.php';
50
51 class Pman_Admin_Export extends Pman_Admin_Dump {
52     
53      static $cli_desc = "Dump database and related files so it could be restored later..";
54     
55     
56     function getAuth()
57     {
58         
59         if (!HTML_FlexyFramework::get()->cli) {
60             die("Access only permitted from cli");
61         }
62         
63     }
64     var $args = array();
65     var $deps = array(); // list of dependants
66     var $out = array(); // list of created sql/shell scripts.
67     
68     var $uid = array();
69     
70     function get($path, $opts = Array() )
71     {
72         ini_set('memory_limit', '256M'); // we need alot of memory
73         set_time_limit(0);
74         
75         $argv = $_SERVER['argv'];
76         array_shift($argv);
77         array_shift($argv);
78         
79         $opts = explode(',', 'table==,where==,dump-dir==,debug=');
80         require_once 'Console/Getopt.php';
81         $go = Console_Getopt::getopt2($argv, '', $opts );
82         if (is_object($go)) {
83             die($go->toString());
84         }
85          
86         foreach($go[0] as $ar) {
87             $args[substr($ar[0],2)] = $ar[1];
88         }
89         $errs = array();
90         foreach($opts as $req) {
91             if (substr($req,-2, 2) != '==') { // skip optional arguments
92                 continue;
93             }
94             if (empty($args[substr($req,0, -2)])) {
95                 $errs[] = "--".substr($req,0, -2) . ' is required';
96             }
97         }
98         if (!empty($errs)) {
99             die(print_R($errs,true));
100         }
101         if (!empty($args['debug'])) {
102             DB_DataObject::debugLevel($args['debug']);
103         }
104         $this->args = $args;
105         $this->out = array();
106         
107         
108         
109         
110         
111         
112         
113         
114         
115         $this->discoverChildren($this->args['table'], $this->args['where'], true);
116         //print_R($this->deletes);
117         //print_r($this->dumps);
118         //exit;
119         
120         $this->discover($this->args['table'], $this->args['where'], true);
121          
122         
123         if (!file_exists($args['dump-dir'])) {
124             mkdir($args['dump-dir'], 0777, true);
125         }
126           
127           
128           // create uid's
129         
130         // dump items..
131         
132         
133           
134         echo "GENERATED FILES:\n";
135         // summary
136         echo "    ". implode("\n    ", $this->out). "\n";
137         
138         
139         exit;
140         
141       
142          
143     }
144      
145     var $deletes = array(); // TABLE => [key] => TRUE|FALSE
146     var $dumps = array(); // TABLE => [key] => TRUE|FALSE - if it's been scanned..
147     var $dscan = array(); // TABLE:COL => [value => TRUE|FALSE] - if its been scanned..
148     var $childfiles = array(); // array of [ 'sourcedirectory' , 'subdirectory(s) and filename' ]
149     var $childthumbs = array(); // array of [ 'filename', 'filename' ,......]
150     var $filesize = 0; // size of files to be saved. (not total deletd..)
151     var $filetotal = 0; // number of distinct files to be saved (not total deleted)
152     /**
153      * scan table for
154      * a) what depends on it (eg. child elements) - which will be deleted.
155      * b) what it depends on it (eg. parent elements) - which will be dumped..
156      */
157       
158     
159     
160     function generateDelete() {  
161         $target = $this->args['dump-dir'] .'/'. date('Y-m-d').'.delete.sql';
162         $this->out[] = $target;
163         $fh = fopen($target, 'w');
164         
165         
166         
167         foreach($this->deletes as $tbl=>$ar) {
168             
169             $do = DB_DataObject::factory($tbl);
170             $tbl = $do->tableName();
171             $keys = $do->keys();
172             $key = $keys[0];
173             $do->whereAddIn($keys[0] , array_keys($ar), 'int');
174             $do->find();
175             $archivePaths = method_exists($do,'archivePaths');
176             $listThumbs = method_exists($do,'listThumbs');
177             while ($do->fetch()) {
178                 
179                 if ($archivePaths) {
180                     $ct = $do->archivePaths();
181                     if ($ct) {
182                         $this->childfiles[] = $ct;
183                     }
184                 }
185                 if ($listThumbs) {
186                     $ct = $do->listThumbs();
187                     if($ct) {
188                         $this->childthumbs[] = $ct;
189                     }
190                 }
191                 $id = $do->$key;
192                 
193                 fwrite($fh, "DELETE FROM `$tbl` WHERE `$key` = $id;\n"); // we assume id's and nice column names...
194             }
195             $do->free();
196         }
197         fclose($fh);
198     }
199     function generateShell() {
200         
201         if (empty($this->childfiles) && empty($this->childthumbs)) {
202             return;
203         }
204         $target = $this->args['dump-dir'] .'/'. date('Y-m-d').'.copy.sh';
205         $this->out[] = $target;
206         $fh = fopen($target, 'w');
207         
208         $target = $this->args['dump-dir'] .'/'. date('Y-m-d').'.delete.sh';
209         $this->out[] = $target;
210         $fh2 = fopen($target, 'w');
211         
212         $target = $this->args['dump-dir'] .'/'. date('Y-m-d').'.restore.sh';
213         $this->out[] = $target;
214         $fh3 = fopen($target, 'w');
215         
216       
217         $done = array();
218         $donedir  = array();
219         foreach($this->childfiles as  $v) {
220             
221             if (isset($done[$v[1]])) {
222                 continue;
223             }
224             
225             $done[$v[1]] = true;
226             
227             $this->filesize += filesize($v[0].'/'.$v[1]);
228             $this->filetotal++;
229             $fdir = dirname($this->args['dump-dir'] .'/'.$v[1]);
230             if (!isset($donedir[$fdir])) { 
231                 fwrite($fh,"mkdir -p " . escapeshellarg(dirname($this->args['dump-dir'] .'/'.$v[1])) ."\n" );
232             }
233             fwrite($fh,"cp " . escapeshellarg($v[0].'/'.$v[1]) . ' ' . escapeshellarg($this->args['dump-dir'] .'/'.$v[1]) ."\n" );
234             if (!isset($donedir[$fdir])) { 
235                 fwrite($fh3,"mkdir -p " . escapeshellarg(dirname($v[0].'/'.$v[1])) ."\n" );
236             }
237             $donedir[$fdir] = true;
238             
239             fwrite($fh3,"cp " .  escapeshellarg($this->args['dump-dir'] .'/'.$v[1]) . ' ' . escapeshellarg($v[0].'/'.$v[1]) . "\n" );
240             fwrite($fh2,"rm " . escapeshellarg($v[0].'/'.$v[1]) ."\n" );
241         }
242         fclose($fh);
243         fclose($fh3); // restore does not need to bother with thumbnails.
244          
245         
246         
247         foreach($this->childthumbs as  $v) {
248             foreach($v as $vv) { 
249                 fwrite($fh2,"rm " . escapeshellarg($vv). "\n");
250             }
251         }
252         fclose($fh2);
253     }
254      
255     function generateInsert()
256     {
257         $target = $this->args['dump-dir'] .'/'. date('Y-m-d').'.sql';
258         $this->out[] = $target;
259         $fh = fopen($target,'w');
260          
261          
262         
263         foreach($this->dumps as $tbl => $ar) {
264             if (empty($ar)) {
265                 continue;
266             }
267             $do = DB_DataObject::factory($tbl);
268              
269             $keys = $do->keys();
270          
271             $do->whereAddIn($keys[0] , array_keys($ar), 'int');
272             $do->find();
273             while ($do->fetch()) {
274                 fwrite($fh,$this->toInsert($do));
275             }
276              $do->free();
277             
278         }
279         fclose($fh);
280         
281         
282     }
283
284     
285       
286     /**
287      * toInsert - does not handle NULLS... 
288      */
289     function toInsert($do)
290     {
291         $kcol = array_shift($do->keys());
292          
293         // for auto_inc column we need to use a 'set argument'...
294         $items = $do->table();
295         //print_R($items);
296         
297         // for
298         $leftq     = '';
299         $rightq    = '';
300         
301         $table = $do->tableName();
302         
303          
304         foreach(  $items  as $k=>$v)
305         {
306             if ($leftq) {
307                 $leftq  .= ', ';
308                 $rightq .= ', ';
309             }
310             
311             $leftq .= '`' . $k . '`';
312             
313              
314             
315             
316             if ($v & DB_DATAOBJECT_STR) {
317                 $rightq .= $do->_quote((string) (
318                         ($v & DB_DATAOBJECT_BOOL) ? 
319                             // this is thanks to the braindead idea of postgres to 
320                             // use t/f for boolean.
321                             (($do->$k === 'f') ? 0 : (int)(bool) $do->$k) :  
322                             $do->$k
323                     )) . " ";
324                 continue;
325             }
326             if (is_numeric($do->$k)) {
327                 $rightq .=" {$do->$k} ";
328                 continue;
329             }
330             $rightq .= ' ' . intval($do->$k) . ' ';
331         }
332         
333         return "INSERT INTO `{$table}` ($leftq) VALUES ($rightq);\n";
334         
335     }
336     
337     
338 }