changed git call from https to git readonly
[atutor.git] / mods / patcher / classes / Patch.class.php
1 <?php
2 /************************************************************************/
3 /* ATutor                                                                                                                               */
4 /************************************************************************/
5 /* Copyright (c) 2002-2008 by Greg Gay, Joel Kronenberg & Heidi Hazelton*/
6 /* Adaptive Technology Resource Centre / University of Toronto                  */
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: Patch.class.php 7208 2008-02-08 16:07:24Z cindy $
14
15 /**
16 * Patch
17 * Class for patch installation
18 * @access       public
19 * @author       Cindy Qi Li
20 * @package      Patch
21 */
22
23 define('AT_INCLUDE_PATH', '../../../include/');
24
25 require_once(AT_INCLUDE_PATH. "../mods/patcher/include/common.inc.php");
26
27 class Patch {
28
29         // all private
30         var $patch_array = array();           // the patch data
31         var $patch_summary_array = array();   // patch summary information 
32         var $patch_id;                        // current patches.patches_id
33         var $patch_file_id;                   // current patches_files.patches_files_id
34         
35         var $need_access_to_folders = array();// folders that need to have write permission
36         var $need_access_to_files = array();  // files that need to have write permission
37         var $backup_files = array();          // backup files
38         var $patch_files = array();           // patch files
39
40         var $errors = array();                // error messages
41         var $baseURL;                         // patch folder at update.atutor.ca
42         var $backup_suffix;                   // suffix appended for backup files
43         var $patch_suffix;                    // suffix appended for patch files copied from update.atutor.ca
44         var $skipFilesModified = false;       // if set to true, report error for files that have been modified by user
45         var $module_content_dir;              // content folder used to create patch.sql
46
47         // constant, URL of user's ATutor release version in SVN 
48         var $svn_tag_folder = 'http://atutorsvn.atrc.utoronto.ca/repos/atutor/tags/';
49         var $sql_file = 'patch.sql';
50         var $relative_to_atutor_root = '../../';   // relative path from mods/patcher to root
51
52         /**
53         * Constructor: Initialize object members
54         * @access  public
55         * @param   $patch_array The name of the file to find charset definition
56         *          $patch_summary_array
57         *          $skipFilesModified
58         * @author  Cindy Qi Li
59         */
60         function Patch($patch_array, $patch_summary_array, $skipFilesModified, $patch_folder) 
61         {
62                 // add relative path to move to ATutor root folder
63                 for ($i = 0; $i < count($patch_array[files]); $i++)
64                 {
65                         $patch_array[files][$i]['location'] = $this->relative_to_atutor_root . $patch_array[files][$i]['location'];
66                 }
67                 
68                 $this->patch_array = $patch_array; 
69                 $this->patch_summary_array = $patch_summary_array;
70                 
71                 $this->baseURL = $patch_folder;
72                 $this ->backup_suffix = $patch_array['atutor_patch_id'] . ".old";
73                 $this ->patch_suffix = $patch_array['atutor_patch_id'];
74                 $this->skipFilesModified = $skipFilesModified;
75                 
76                 $this->module_content_dir = AT_CONTENT_DIR . "patcher";
77
78                 session_start();
79                 
80                 if (!is_array($_SESSION['remove_permission'])) $_SESSION['remove_permission']=array();
81                 
82         }
83
84         /**
85         * Main process to apply patch.
86         * @access  public
87         * @return  true  if patch is successfully applied
88         *          false if failed
89         * @author  Cindy Qi Li
90         */
91         function applyPatch() 
92         {
93                 // Checks on 
94                 // 1. if svn server is up
95                 // 2. if the local file is customized by user
96                 // 3. if script has write priviledge on local file/folder
97                 // 4. if dependent patches have been installed
98                 if (!$this->pingDomain($this->svn_tag_folder)) return false;
99                 
100                 if (!$this->checkDependentPatches()) return false;
101
102                 if (!$this->skipFilesModified && $this->hasFilesModified()) return false;
103                 
104                 if (!$this->checkPriviledge()) return false;
105                 // End of check
106
107                 if (strlen(trim($this->patch_array['sql'])) > 0) $this->runSQL();
108
109                 // Start applying patch
110                 $this->createPatchesRecord($this->patch_summary_array);
111
112                 // if no file action defined, return true
113                 if (!is_array($this->patch_array[files])) return true;
114                 
115                 foreach ($this->patch_array[files] as $row_num => $patch_file)
116                 {
117                         $this->createPatchesFilesRecord($this->patch_array['files'][$row_num]);
118
119                         if ($patch_file['action'] == 'alter')
120                         {
121                                 $this->alterFile($row_num);
122                         }
123                         else if ($patch_file['action'] == 'add')
124                         {
125                                 $this->addFile($row_num);
126                         }
127                         else if ($patch_file['action'] == 'delete')
128                         {
129                                 $this->deleteFile($row_num);
130                         }
131                         else if ($patch_file['action'] == 'overwrite')
132                         {
133                                 $this->overwriteFile($row_num);
134                         }
135                 }
136                 
137                 // if only has backup files info, patch is considered successfully installed
138                 // if has permission to remove, considered partly installed
139                 $updateInfo = array();
140
141                 if (count($this->backup_files) > 0)
142                 {
143                         foreach($this->backup_files as $backup_file)
144                                 $backup_files .= $backup_file. '|';
145                 
146                         $updateInfo = array("backup_files"=>preg_quote($backup_files));
147                 }
148         
149                 if (count($this->patch_files) > 0)
150                 {
151                         foreach($this->patch_files as $patch_file)
152                                 $patch_files .= $patch_file. '|';
153                 
154                         $updateInfo = array_merge($updateInfo, array("patch_files"=>preg_quote($patch_files)));
155                 }
156         
157                 if (is_array($_SESSION['remove_permission']) && count($_SESSION['remove_permission']))
158                 {
159                         foreach($_SESSION['remove_permission'] as $remove_permission_file)
160                                 $remove_permission_files .= $remove_permission_file. '|';
161
162                         $updateInfo = array_merge($updateInfo, array("remove_permission_files"=>preg_quote($remove_permission_files), "status"=>"Partly Installed"));
163                 }
164                 else
165                 {
166                         $updateInfo = array_merge($updateInfo, array("status"=>"Installed"));
167                 } 
168
169                 updatePatchesRecord($this->patch_id, $updateInfo);
170                 
171                 unset($_SESSION['remove_permission']);
172
173                 return true;
174         }
175
176         /**
177         * return patch array
178         * @access  public
179         * @return  patch array
180         * @author  Cindy Qi Li
181         */
182         function getPatchArray() 
183         {
184                 return $this->patch_array;
185         }
186         
187         /**
188         * return patch id processed by this object
189         * @access  public
190         * @return  patch id
191         * @author  Cindy Qi Li
192         */
193         function getPatchID() 
194         {
195                 return $this->patch_id;
196         }
197         
198         /**
199         * Check if script has write permission to the files and folders that need to be written
200         * if no permission, warn user to give permission
201         * @access  private
202         * @return  true  if there are files or folders that script has no permission
203         *          false if permissions are in place
204         * @author  Cindy Qi Li
205         */
206         function checkPriviledge()
207         {
208                 global $id, $who;
209                 
210                 // no file action is defined, return true;
211                 if (!is_array($this->patch_array[files])) return true;
212                 
213                 foreach ($this->patch_array[files] as $row_num => $patch_file)
214                 {
215                         $real_location = realpath($patch_file['location']);
216                         if (!is_writable($patch_file['location']) && !in_array($real_location, $this->need_access_to_folders))
217                         {
218                                 $this->need_access_to_folders[] = $real_location;
219
220                                 if (!in_array($real_location, $_SESSION['remove_permission']))
221                                         $_SESSION['remove_permission'][] = $real_location;
222                         }
223
224                         if ($patch_file['action'] == 'alter' || $patch_file['action'] == 'delete' || $patch_file['action'] == 'overwrite')
225                         {
226                                 $file = $patch_file['location'] . "/" . $patch_file['name'];
227
228                                 $real_file = realpath($file);
229                                 if (file_exists($file) && !is_writable($file) && !in_array($real_file, $this->need_access_to_files))
230                                 {
231                                         $this->need_access_to_files[] = $real_file;
232
233                                         if (!in_array($real_file, $_SESSION['remove_permission']) && $patch_file['action'] <> 'delete')
234                                                 $_SESSION['remove_permission'][] = $real_file;
235                                 }
236                         }
237                 }
238                 
239                 if (count($this->need_access_to_folders) > 0 || count($this->need_access_to_files) > 0)
240                 {
241                         $this->errors[] = _AT('grant_write_permission');
242                         
243                         foreach($this->need_access_to_folders as $folder)
244                         {
245                                 $this->errors[0] .= '<strong>'. $folder . "</strong><br>";
246                         }
247
248                         foreach($this->need_access_to_files as $file)
249                         {
250                                 $this->errors[0] .= '<strong>'. $file . "</strong><br>";
251                         }
252
253                         $notes = '<form action="'. $_SERVER['PHP_SELF'].'?id='.$id.'&who='. $who .'" method="post" name="skip_files_modified">
254                   <div class="row buttons">
255                                 <input type="submit" name="yes" value="'._AT('continue').'" accesskey="y" />
256                                 <input type="submit" name="no" value="'. _AT('cancel'). '" />
257                                 <input type="hidden" name="install" value="' . $_POST['install'] . '" />
258                                 <input type="hidden" name="install_upload" value="' . $_POST['install_upload'] . '" />
259                         </div>
260                         </form>';
261                         
262                         print_errors($this->errors, $notes);
263                 
264                         unset($this->errors);
265                         return false;
266                 }
267                 
268                 return true;
269         }
270         
271         /**
272         * Check if all the dependent patches have been installed.
273         * @access  private
274         * @return  true  if all the dependent patches have been installed
275         *          false if any dependent patch has not been installed.
276         * @author  Cindy Qi Li
277         */
278         function checkDependentPatches()
279         {
280                 global $msg;
281                 
282                 $dependent_patches_installed = true;
283                 
284                 // if no dependent patch defined, return true
285                 if (!is_array($this->patch_summary_array["dependent_patches"])) return true;
286                 
287                 foreach($this->patch_summary_array["dependent_patches"] as $num => $dependent_patch)
288                 {
289                         if (!is_patch_installed($dependent_patch))
290                         {
291                                 $dependent_patches_installed = false;
292                                 $dependent_patches .= $dependent_patch. ", ";
293                         }
294                 }
295                 
296                 if (!$dependent_patches_installed)
297                 {
298                         $errors = array('PATCH_DEPENDENCY', substr($dependent_patches, 0, -2));
299                         $msg->addError($errors);
300                         return false;
301                 }
302                 
303                 return true;
304         }
305         
306         /**
307         * Loop thru all the patch files that will be overwitten or altered, 
308         * to find out if they are modified by user. If it's modified, warn user.
309         * @access  private
310         * @return  true  if there are files being modified
311         *          false if no file is modified
312         * @author  Cindy Qi Li
313         */
314         function hasFilesModified()
315         {
316                 $overwrite_modified_files = false;
317                 $alter_modified_files = false;
318                 $has_not_exist_files = false;
319                 
320                 // no file action is defined, return nothing is modified (false)
321                 if (!is_array($this->patch_array[files])) return false;
322                 
323                 foreach ($this->patch_array[files] as $row_num => $patch_file)
324                 {
325                         if ($patch_file["action"]=='alter' || $patch_file["action"]=='overwrite')
326                         {
327                                 if (!file_exists($patch_file['location'] . $patch_file['name']))
328                                 {
329                                         $not_exist_files .= $patch_file['location'] . $patch_file['name'] . '<br>';
330                                         $has_not_exist_files = true;
331                                 }
332                                 else if ($this->isFileModified($patch_file['location'], $patch_file['name']))
333                                 {
334                                         if ($patch_file['action']=='overwrite')
335                                         {
336                                                 $overwrite_files .= realpath($patch_file['location'] . $patch_file['name']) . '<br>';
337                                                 $overwrite_modified_files = true;
338                                         }
339                                         if ($patch_file['action']=='alter')
340                                         {
341                                                 $alter_files .= realpath($patch_file['location'] . $patch_file['name']) . '<br>';
342                                                 $alter_modified_files = true;
343                                         }
344                                 }
345                         }
346                 }
347
348                 if ($has_not_exist_files) $this->errors[] = _AT('patch_local_file_not_exist'). $not_exist_files;
349                 if ($overwrite_modified_files)    $this->errors[] = _AT('patcher_overwrite_modified_files') . $overwrite_files;
350                 if ($alter_modified_files)    $this->errors[] = _AT('patcher_alter_modified_files') . $alter_files;
351                 if (count($this->errors) > 0)
352                 {
353                         if ($has_not_exist_files)
354                                 $notes = '';
355                         else
356                                 $notes = '
357                           <form action="'. $_SERVER['PHP_SELF'].'?id='.$_POST['id'].'&who='. $_POST['who'] .'" method="post" name="skip_files_modified">
358                           <div class="row buttons">
359                                         <input type="submit" name="yes" value="'._AT('yes').'" accesskey="y" />
360                                         <input type="submit" name="no" value="'. _AT('no'). '" />
361                                         <input type="hidden" name="install" value="' . $_POST['install'] . '" />
362                                         <input type="hidden" name="install_upload" value="' . $_POST['install_upload'] . '" />
363                                 </div>
364                                 </form>';
365
366                         print_errors($this->errors, $notes);
367                 
368                         unset($this->errors);
369                         return true;
370                 }
371                 
372                 return false;
373         }
374
375         /**
376         * Compare user's local file with SVN backup for user's ATutor version,
377         * if different, check table at_patches_files to see if user's local file
378         * was altered by previous patch installation. If it is, return false 
379         * (not modified), otherwise, return true (modified).
380         * @access  private
381         * @param   $folder  folder of the file to be compared
382         *          $file    name of the file to be compared
383         * @return  true     if the file is modified
384         *          false    if the file is not modified
385         * @author  Cindy Qi Li
386         */
387         function isFileModified($folder, $file)
388         {
389                 global $db;
390
391                 $svn_file = $this->svn_tag_folder . 'atutor_' . str_replace('.', '_', VERSION) .
392                             str_replace('../..', '', $folder) .$file;
393                 $local_file = $folder.$file;
394
395                 // check if the local file has been modified by user. if it is, don't overwrite
396                 if ($this->compareFiles($svn_file, $local_file) <> 0)
397                 {
398                         // check if the file was changed by previous installed patches
399                         $sql = "SELECT count(*) num_of_updates FROM " . TABLE_PREFIX. "patches patches, " . TABLE_PREFIX."patches_files patches_files " .
400                                "WHERE patches.applied_version = '" . VERSION . "' ".
401                                "  AND patches.status = 'Installed' " .
402                                "  AND patches.patches_id = patches_files.patches_id " .
403                                "  AND patches_files.name = '" . $file . "'";
404                         
405                         $result = mysql_query($sql, $db) or die(mysql_error());
406                         $row = mysql_fetch_assoc($result);
407                         
408                         if ($row["num_of_updates"] == 0) return true;
409                 }
410                 return false;
411         }
412
413         /**
414         * Run SQL defined in patch.xml
415         * @access  private
416         * @author  Cindy Qi Li
417         */
418         function runSQL()
419         {
420                 // run sql
421                 // As sqlutility.class.php reads sql from a file, write sql to module content folder
422                 $patch_sql_file = $this->module_content_dir . '/' . $this->sql_file;
423
424                 $fp = fopen($patch_sql_file, 'w');
425                 fwrite($fp, trim($this->patch_array['sql']));
426                 fclose($fp);
427
428                 require(AT_INCLUDE_PATH . 'classes/sqlutility.class.php');
429                 $sqlUtility =& new SqlUtility();
430         
431                 $sqlUtility->queryFromFile($patch_sql_file, TABLE_PREFIX);
432                 
433                 @unlink($patch_sql_file);
434                 
435                 return true;
436         }
437                 
438         /**
439         * Copy file from update.atutor.ca to user's computer
440         * @access  private
441         * @param   $row_num     row number of patch record to be processed
442         * @author  Cindy Qi Li
443         */
444         function addFile($row_num)
445         {
446                 $this->copyFile($this->baseURL . preg_replace('/.php$/', '.new', $this->patch_array['files'][$row_num]['name']), $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name']);
447                 
448                 return true;
449         }
450         
451         /**
452         * Delete file, backup before deletion
453         * @access  private
454         * @param   $row_num     row number of patch record to be processed
455         * @author  Cindy Qi Li
456         */
457         function deleteFile($row_num)
458         {
459                 $local_file = $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name'];
460                 $backup_file = $local_file . "." . $this->backup_suffix;
461                 
462                 if (file_exists($local_file))
463                 {
464                         // move file to backup
465                         $this->copyFile($local_file, $backup_file);
466                         $this->backup_files[] = realpath($backup_file);
467                         @unlink($local_file);
468                 }
469                 
470                 return true;
471                 
472         }
473         
474         /**
475         * Alter file based on <action_detail>
476         * If user's local file is modified and user agrees to proceed with applying patch,
477         * alter user's local file.
478         * @access  private
479         * @param   $row_num     row number of patch record to be processed
480         * @author  Cindy Qi Li
481         */
482         function alterFile($row_num)
483         {
484                 $local_file = $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name'];
485                 
486                 // backup user's file
487                 $backup_file = $local_file . "." . $this->backup_suffix;
488                 $this->copyFile($local_file, $backup_file);
489                 $this->backup_files[] = realpath($backup_file);
490                 
491                 $local_file_content = file_get_contents($local_file);
492
493                 // Modify user's file
494                 foreach ($this->patch_array['files'][$row_num]['action_detail'] as $garbage => $alter_file_action)
495                 {
496                         if ($alter_file_action['type'] == 'delete')
497                         {
498                                 $local_file_content = $this->strReplace($alter_file_action['code_from'], '', $local_file_content);
499                         }
500
501                         if ($alter_file_action['type'] == 'replace')
502                                 $local_file_content = $this->strReplace($alter_file_action['code_from'], $alter_file_action['code_to'], $local_file_content);
503
504                         $this->createPatchesFilesActionsRecord($alter_file_action);
505                 }
506
507                 $fp = fopen($local_file, 'w');
508                 fwrite($fp, $local_file_content);
509                 fclose($fp);
510
511                 return true;
512         }
513         
514         /**
515         * Fetch file from update.atutor.ca and overwrite user's local file if the local file is not modified
516         * If user's local file is modified and user agrees to proceed with applying patch,
517         * copy the new file to user's local for them to merge manually.
518         * @access  private
519         * @param   $row_num     row number of patch record to be processed
520         * @author  Cindy Qi Li
521         */
522         function overwriteFile($row_num)
523         {
524                 $local_file = $this->patch_array['files'][$row_num]['location'].$this->patch_array['files'][$row_num]['name'];
525                 $patch_file = $this->baseURL . preg_replace('/.php$/', '.new', $this->patch_array['files'][$row_num]['name']);
526                 
527                 // if local file is modified and user agrees to proceed with applying patch,
528                 // copy the new file to user's local for them to merge manually
529                 if ($this->skipFilesModified && $this->isFileModified($this->patch_array['files'][$row_num]['location'], $this->patch_array['files'][$row_num]['name']))
530                 {
531                         $local_patch_file = $local_file . "." . $this->patch_suffix;
532
533                         $this->copyFile($patch_file, $local_patch_file);
534                         
535                         $this->patch_files[] = realpath($local_patch_file);
536                 }
537                 else
538                 {
539                         $backup_file = $local_file . "." . $this->backup_suffix;
540                         
541                         // backup user's file
542                         $this->copyFile($local_file, $backup_file);
543                         $this->backup_files[] = realpath($backup_file);
544                         
545                         // overwrite user's file
546                         $this->copyFile($patch_file, $local_file);
547                 }
548                 
549                 return true;
550         }
551         
552         /**
553         * Copy file $src to $dest. $src can be a local file or a remote file
554         * @access  private
555         * @param   $src location of the source file
556         *          $dest        location of the destination file
557         * @author  Cindy Qi Li
558         */
559         function copyFile($src, $dest)
560         {
561                 $content = file_get_contents($src);
562                 $fp = fopen($dest, 'w');
563                 fwrite($fp, $content);
564                 fclose($fp);
565                 
566                 return true;
567         }
568         
569         /**
570         * Compare files $src against $dest
571         * @access  private
572         * @param   $src location of the source file
573         *          $dest        location of the destination file
574         * @return  Returns < 0 if $src is less than $dest ; > 0 if $src is greater than $dest, and 0 if they are equal.
575         * @author  Cindy Qi Li
576         */
577         function compareFiles($src, $dest)
578         {
579                 // use preg_replace to delete 
580                 // 1. the line starting with // $Id:
581                 // 2. the line starting with $lm = '$LastChangedDate, ending with ;
582                 // These lines are created by SVN. It could be different in different copies of the same file.
583                 $pattern = '/\/\/ \$Id.*\$|\$lm = \'\$LastChangedDate.*;/';
584                 
585                 $src_content = preg_replace($pattern, '', file_get_contents($src));
586                 $dest_content = preg_replace($pattern, '', file_get_contents($dest));
587                 
588                 return strcasecmp($src_content, $dest_content);
589         }
590         
591         /**
592         * Replace single/multiple lines of string. 
593         * This function handles different new line character at windows/unix platform
594         * @access  private
595         * @param   $search      String to replace from
596         *          $replace     String to replace to
597         *          $subject Subject to be handled  
598         * @return  return replaced string, if nothing is replaced, return original subject
599         * @author  Cindy Qi Li
600         */
601         function strReplace($search, $replace, $subject)
602         {
603                 $new_line_array = array("\n", "\r", "\n\r", "\r\n");
604                 
605                 foreach ($new_line_array as $new_line)
606                 {
607                         if (preg_match('/'.preg_quote($new_line).'/', $search) > 0)   $search_new_line = $new_line;
608                         if (preg_match('/'.preg_quote($new_line).'/', $replace) > 0)   $replace_new_line = $new_line;
609                         if (preg_match('/'.preg_quote($new_line).'/', $subject) > 0)   $subject_new_line = $new_line;
610                 }
611
612                 // replace new line chars in $search & $replace to new line in $subject
613                 if ($search_new_line <> "" && $search_new_line <> $subject_new_line) 
614                         $search = preg_replace('/'.preg_quote($search_new_line).'/', $subject_new_line, $search);
615                 
616                 if ($replace_new_line <> "" && $replace_new_line <> $subject_new_line) 
617                         $replace = preg_replace('/'.preg_quote($replace_new_line).'/', $subject_new_line, $replace);
618                 
619                 return preg_replace('/'. preg_quote($search, '/') .'/', $replace, $subject);
620         }
621         
622         /**
623         * Check if the server is down
624         * @access  private
625         * @param   $domain      Server Domain
626         * @return  return false if server is down, otherwise, return true
627         * @author  Cindy Qi Li
628         */
629         function pingDomain($domain)
630         {
631     $file = @fopen ($domain, 'r');
632
633     if (!$file) 
634     {
635         return false;
636     }
637     return true;
638         }
639
640         /**
641         * Insert record into table patches
642         * @access  private
643         * @param   $patch_summary_array Patch summary information
644         * @author  Cindy Qi Li
645         */
646         function createPatchesRecord($patch_summary_array)
647         {
648                 global $db;
649                 
650                 $sql = "INSERT INTO " . TABLE_PREFIX. "patches " .
651                                          "(atutor_patch_id, 
652                                            applied_version,
653                                            patch_folder,
654                                            description,
655                                            available_to,
656                                            sql_statement,
657                                            status)
658                                           VALUES
659                                           ('".$patch_summary_array["atutor_patch_id"]."',
660                                            '".$patch_summary_array["applied_version"]."',
661                                            '".preg_quote($patch_summary_array["patch_folder"])."',
662                                            '".preg_quote($patch_summary_array["description"])."',
663                                            '".$patch_summary_array["available_to"]."',
664                                            '',
665                                            '".$patch_summary_array["status"]."'
666                                            )";
667
668                 $result = mysql_query($sql, $db) or die(mysql_error());
669                 
670                 $this->patch_id = mysql_insert_id();
671                 
672                 return true;
673         }
674
675         /**
676         * Insert record into table patches_files
677         * @access  private
678         * @param   $patch_files_array   Patch information
679         * @author  Cindy Qi Li
680         */
681         function createPatchesFilesRecord($patch_files_array)
682         {
683                 global $db;
684                 
685                 $sql = "INSERT INTO " . TABLE_PREFIX. "patches_files " .
686                                          "(patches_id, 
687                                            action,
688                                            name,
689                                            location)
690                                           VALUES
691                                           (".$this->patch_id.",
692                                            '".$patch_files_array['action']."',
693                                            '".preg_quote($patch_files_array['name'])."',
694                                            '".preg_quote($patch_files_array['location'])."'
695                                            )";
696
697                 $result = mysql_query($sql, $db) or die(mysql_error());
698                 
699                 $this->patch_file_id = mysql_insert_id();
700
701                 return true;
702         }
703
704         /**
705         * Insert record into table patches_files_actions
706         * @access  private
707         * @param   $patch_files_actions_array   alter file actions and contents
708         * @author  Cindy Qi Li
709         */
710         function createPatchesFilesActionsRecord($patch_files_actions_array)
711         {
712                 global $db;
713                 
714                 $sql = "INSERT INTO " . TABLE_PREFIX. "patches_files_actions " .
715                                          "(patches_files_id, 
716                                            action,
717                                            code_from,
718                                            code_to)
719                                           VALUES
720                                           (".$this->patch_file_id.",
721                                            '".$patch_files_actions_array['type']."',
722                                            '".preg_replace('/\'/', '\\\'', $patch_files_actions_array['code_from'])."',
723                                            '".preg_replace('/\'/', '\\\'', $patch_files_actions_array['code_to'])."'
724                                            )";
725
726                 $result = mysql_query($sql, $db) or die(mysql_error());
727                 
728                 return true;
729         }
730 }
731
732 ?>