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