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