remove old readme
[atutor.git] / mods / _standard / patcher / classes / PatchCreator.class.php
1 <?php
2 /************************************************************************/
3 /* ATutor                                                               */
4 /************************************************************************/
5 /* Copyright (c) 2002-2010                                              */
6 /* Inclusive Design Institute                                           */
7 /* http://atutor.ca                                                     */
8 /*                                                                      */
9 /* This program is free software. You can redistribute it and/or        */
10 /* modify it under the terms of the GNU General Public License          */
11 /* as published by the Free Software Foundation.                        */
12 /************************************************************************/
13 // $Id$
14
15 /**
16 * Patch
17 * Class to create a zipped patch package
18 * zipped patch package contains patch.xml, the files to be added, overwritten
19 * @access       public
20 * @author       Cindy Qi Li
21 * @package      PatchCreator
22 */
23
24 define('AT_INCLUDE_PATH', '../../../../include/');
25 require_once (AT_INCLUDE_PATH.'vitals.inc.php');
26
27 require_once(AT_INCLUDE_PATH. "../mods/_standard/patcher/include/patch_xml_template.inc.php");
28
29 class PatchCreator {
30
31         // all private
32         var $patch_info_array = array();           // the patch info data
33         var $patch_xml_file;                                                                                     // location of patch.xml
34         var $current_patch_id;                     // current myown_patches.myown_patch_id
35         var $version_folder;                                                                                     // version folder. underneath patcher content folder, to hold all patch folders and corresponding upload files
36         var $patch_folder;                                                                                         // patch folder. underneath version folder, to hold all upload files
37
38         /**
39         * Constructor: Initialize object members
40         * @author  Cindy Qi Li
41         * @access  public
42         * @param   $patch_info_array    All information to create a patch. Example:
43         * Array
44         * (
45         *     [atutor_patch_id] => Patch001
46         *     [atutor_version_to_apply] => 1.6
47         *     [description] => this is a sample patch info array
48         *     [sql_statement] => 
49         *     [dependent_patches] => Array
50         *     (
51         *         [0] => P2
52         *         [1] => P3
53         *     )
54         *     [files] => Array
55         *         (
56         *             [0] => Array
57         *                 (
58         *                     [file_name] => script1.php
59         *                     [action] => add
60         *                     [directory] => admin/
61         *                     [upload_tmp_name] => C:\xampp\tmp\php252.tmp
62         *                 )
63         * 
64         *             [1] => Array
65         *                 (
66         *                     [file_name] => script2.php
67         *                     [action] => delete
68         *                     [directory] => tools/
69         *                 )
70         *         )
71         * 
72         * )
73         */
74         
75         function PatchCreator($patch_info_array, $patch_id)
76         {
77                 // add slashes if magic_quotes_gpc is off
78                 if (!get_magic_quotes_gpc())
79                 {
80                         $patch_info_array["description"] = addslashes($patch_info_array["description"]);
81                         $patch_info_array["sql_statement"] = addslashes($patch_info_array["sql_statement"]);
82                         
83                         for ($i = 0; $i < count($patch_info_array["files"]); $i++)
84                         {
85                                 $patch_info_array["files"][$i]["directory"] = addslashes($patch_info_array["files"][$i]["directory"]);
86                                 $patch_info_array["files"][$i]["upload_tmp_name"] = addslashes($patch_info_array["files"][$i]["upload_tmp_name"]);
87                                 $patch_info_array["files"][$i]["code_from"] = addslashes($patch_info_array["files"][$i]["code_from"]);
88                                 $patch_info_array["files"][$i]["code_to"] = addslashes($patch_info_array["files"][$i]["code_to"]);
89                         }
90                 }
91                 
92                 $this->patch_info_array = $patch_info_array; 
93                 $this->current_patch_id = $patch_id;
94                 
95                 $this->patch_xml_file = AT_CONTENT_DIR . "patcher/patch.xml";
96
97                 $this->version_folder = AT_CONTENT_DIR . "patcher/" . str_replace('.', '_', $this->patch_info_array["atutor_version_to_apply"]) . "/";
98                 $this->patch_folder = $this->version_folder . $this->patch_info_array["atutor_patch_id"] . "/";
99         }
100
101         /**
102         * Create Patch
103         * @access  public
104         * @return  true if created successfully
105         *          false if error happens
106         * @author  Cindy Qi Li
107         */
108         function create_patch()
109         {
110                 // save patch info into database & save uploaded files into content folder
111                 $this->saveInfo();
112                 
113                 // create patch.xml into $this->patch_xml_file
114                 $fp = fopen($this->patch_xml_file,'w');
115                 fwrite($fp,$this->createXML());
116                 fclose($fp);
117                 
118                 // create zip package and force download
119                 $this->createZIP();
120
121                 // clean up
122                 unlink($this->patch_xml_file);
123                 
124                 return true;
125         }
126
127         /**
128         * Save patch info into database & save uploaded files into content folder
129         * @access  public
130         * @return  xml string
131         * @author  Cindy Qi Li
132         */
133         function saveInfo() 
134         {
135                 global $db;
136                 
137                 if ($this->current_patch_id == 0)
138                 {
139                         $sql = "INSERT INTO ".TABLE_PREFIX."myown_patches 
140                                (atutor_patch_id, 
141                                 applied_version,
142                                 description,
143                                 sql_statement,
144                                 status,
145                                 last_modified)
146                                 VALUES ('".$this->patch_info_array["atutor_patch_id"]."', 
147                                         '".$this->patch_info_array["atutor_version_to_apply"]."', 
148                                         '".$this->patch_info_array["description"]."', 
149                                         '".$this->patch_info_array["sql_statement"]."', 
150                                         'Created',
151                                         now())";
152                 }
153                 else
154                 {
155                         $sql = "UPDATE ".TABLE_PREFIX."myown_patches 
156                                    SET atutor_patch_id = '". $this->patch_info_array["atutor_patch_id"] ."',
157                                        applied_version = '". $this->patch_info_array["atutor_version_to_apply"] ."',
158                                        description = '". $this->patch_info_array["description"] ."',
159                                        sql_statement = '". $this->patch_info_array["sql_statement"] ."',
160                                        status = 'Created',
161                                        last_modified = now()
162                                  WHERE myown_patch_id = ". $this->current_patch_id;
163                 }
164
165                 $result = mysql_query($sql, $db) or die(mysql_error());
166                 
167                 if ($this->current_patch_id == 0)
168                 {
169                         $this->current_patch_id = mysql_insert_id();
170                 }
171                 else // delete records for current_patch_id in tables myown_patches_dependent & myown_patches_files
172                 {
173                         $sql = "DELETE FROM ".TABLE_PREFIX."myown_patches_dependent WHERE myown_patch_id = " . $this->current_patch_id;
174                         $result = mysql_query($sql, $db) or die(mysql_error());
175                         
176                         $sql = "DELETE FROM ".TABLE_PREFIX."myown_patches_files WHERE myown_patch_id = " . $this->current_patch_id;
177                         $result = mysql_query($sql, $db) or die(mysql_error());
178                 }
179                 
180                 // insert records into table myown_patches_dependent
181                 if (is_array($this->patch_info_array["dependent_patches"]))
182                 {
183                         foreach ($this->patch_info_array["dependent_patches"] as $dependent_patch)
184                         {
185                                 $sql = "INSERT INTO ".TABLE_PREFIX."myown_patches_dependent 
186                                (myown_patch_id, 
187                                 dependent_patch_id)
188                                 VALUES ('".$this->current_patch_id."', 
189                                         '".$dependent_patch."')";
190
191                                 $result = mysql_query($sql, $db) or die(mysql_error());
192                         }
193                 }
194                 
195                 // insert records into table myown_patches_files
196                 if (is_array($this->patch_info_array["files"]))
197                 {
198                         foreach ($this->patch_info_array["files"] as $file_info)
199                         {
200                                 if ($file_info["upload_tmp_name"] <> "")
201                                         $upload_to = $this->saveFile($file_info);
202                                 else
203                                         $upload_to = "";
204                                         
205                                 $sql = "INSERT INTO ".TABLE_PREFIX."myown_patches_files
206                                (myown_patch_id, 
207                                 action,
208                                 name,
209                                 location,
210                                 code_from,
211                                 code_to,
212                                 uploaded_file)
213                                 VALUES ('".$this->current_patch_id."', 
214                                         '".$file_info["action"]."', 
215                                         '".$file_info["file_name"]."', 
216                                         '".$file_info["directory"]."', 
217                                         '".$file_info["code_from"]."', 
218                                         '".$file_info["code_to"]."',
219                                         '".addslashes($upload_to)."')";
220
221                                 $result = mysql_query($sql, $db) or die(mysql_error());
222                         }
223                 }
224         }
225
226         /**
227         * Save upload file into content folder
228         * @access  private
229         * @return  xml string
230         * @author  Cindy Qi Li
231         */
232         function saveFile($file_info) 
233         {
234                 // mkdir() function cannot create folder recursively, so have to acomplish the creation of patch folder by 2 steps.
235                 if (!file_exists($this->version_folder))        mkdir($this->version_folder);
236                 if (!file_exists($this->patch_folder))  mkdir($this->patch_folder);
237                 
238                 $upload_to = $this->patch_folder . $file_info['file_name'];
239                 
240                 // copy uploaded file into content folder
241                 copy($file_info["upload_tmp_name"], $upload_to);
242                 
243                 return realpath($upload_to);
244         }
245
246         /**
247         * Create patch.xml.
248         * @access  private
249         * @return  xml string
250         * @author  Cindy Qi Li
251         */
252         function createXML() 
253         {
254                 global $patch_xml, $dependent_patch_xml;
255                 global $patch_action_detail_xml, $patch_file_xml;
256                 
257                 // generate content of <dependent_patches> section
258                 if (is_array($this->patch_info_array["dependent_patches"]))
259                 {
260                         foreach ($this->patch_info_array["dependent_patches"] as $dependent_patch)
261                                 $dependent_patches .= str_replace('{DEPENDENT_PATCH}', $dependent_patch, $dependent_patch_xml);
262                 }
263                 
264                 // generate content of <files> section
265                 if (is_array($this->patch_info_array["files"]))
266                 {
267                         foreach ($this->patch_info_array["files"] as $file_info)
268                         {
269                                 $action_details = "";
270                                 
271                                 if ($file_info["action"] == "alter")
272                                 {
273                                         $action_details .= str_replace(array('{TYPE}', '{CODE_FROM}', '{CODE_TO}'), 
274                                                                           array('replace', 
275                                                                                                 htmlspecialchars(stripslashes($file_info["code_from"]), ENT_QUOTES), 
276                                                                                                 htmlspecialchars(stripslashes($file_info["code_to"]), ENT_QUOTES)),
277                                                                           $patch_action_detail_xml);
278                                 }
279                                 
280                                 $xml_files .= str_replace(array('{ACTION}', '{NAME}', '{LOCATION}', '{ACTION_DETAILS}'), 
281                                                                           array($file_info["action"], $file_info["file_name"], $file_info["directory"], $action_details),
282                                                                           $patch_file_xml);
283                         }
284                 }
285
286                 // generate patch.xml
287                 return str_replace(array('{ATUTOR_PATCH_ID}', 
288                                          '{APPLIED_VERSION}', 
289                                          '{DESCRIPTION}', 
290                                          '{SQL}', 
291                                          '{DEPENDENT_PATCHES}',
292                                          '{FILES}'), 
293                                                                  array($this->patch_info_array["atutor_patch_id"], 
294                                                                        $this->patch_info_array["atutor_version_to_apply"], 
295                                                                        htmlspecialchars(stripslashes($this->htmlNewLine($this->patch_info_array["description"])), ENT_QUOTES), 
296                                                                        htmlspecialchars(stripslashes($this->patch_info_array["sql_statement"]), ENT_QUOTES), 
297                                                                        $dependent_patches,
298                                                                        $xml_files),
299                                                                  $patch_xml);
300         }
301
302         /**
303         * Create xml for section <files> in patch.xml.
304         * @access  private
305         * @return  xml string
306         * @author  Cindy Qi Li
307         */
308         function createXMLFiles($file_info)
309         {
310                 
311                 $action_details = "";
312                 
313                 if ($file_info["action"] == "alter")
314                 {
315                         $action_details .= str_replace(array('{TYPE}', '{CODE_FROM}', '{CODE_TO}'), 
316                                                           array('replace', 
317                                                                                 htmlspecialchars(stripslashes($file_info["code_from"]), ENT_QUOTES), 
318                                                                                 htmlspecialchars(stripslashes($file_info["code_to"]), ENT_QUOTES)),
319                                                           $patch_action_detail_xml);
320                 }
321                 
322                 return str_replace(array('{ACTION}', '{NAME}', '{LOCATION}', '{ACTION_DETAILS}'), 
323                                                           array($file_info["action"], $file_info["file_name"], $file_info["directory"], $action_details),
324                                                           $patch_file_xml);
325         }
326         
327         /**
328         * Create zip file which contains patch.xml and the files to be added, overwritten, altered; and force to download
329         * @access  private
330         * @return  true   if successful
331         *          false  if errors
332         * @author  Cindy Qi Li
333         */
334         function createZIP() 
335         {
336                 require_once(AT_INCLUDE_PATH . '/classes/zipfile.class.php');
337
338                 $zipfile = new zipfile();
339         
340                 $zipfile->add_file(file_get_contents($this->patch_xml_file), 'patch.xml');
341
342                 if (is_array($this->patch_info_array["files"]))
343                 {
344                         foreach ($this->patch_info_array["files"] as $file_info)
345                         {
346                                 if ($file_info["upload_tmp_name"] <> '')
347                                 {
348                                         $file_name = preg_replace('/.php$/', '.new', $file_info['file_name']);
349                                         $zipfile->add_file(file_get_contents($file_info['upload_tmp_name']), $file_name);
350                                 }
351                         }
352                 }
353
354                 $zipfile->send_file($this->patch_info_array["atutor_patch_id"]);
355         }
356
357         /**
358         * replace new line string to html tag <br />
359         * @access  private
360         * @return  converted string
361         * @author  Cindy Qi Li
362         */
363         function htmlNewLine($str)
364         {
365                 $new_line_array = array("\n", "\r", "\n\r", "\r\n");
366
367                 $found_match = false;
368                 
369                 if (strlen(trim($str))==0) return "";
370                 
371                 foreach ($new_line_array as $new_line)
372                         if (preg_match('/'.preg_quote($new_line).'/', $str) > 0)
373                         {
374                                 $search_new_line = $new_line;
375                                 $found_match = true;
376                         }
377                  
378                 if ($found_match)
379                         return preg_replace('/'. preg_quote($search_new_line) .'/', "<br />", $str);
380                 else
381                         return $str;
382         }
383
384 }
385
386 ?>