Prevent empty log lines and a couple of include cleanups.
[fa-stable.git] / admin / includes / fa_patch.class.inc
1 <?php
2 /**********************************************************************
3     Copyright (C) FrontAccounting, LLC.
4         Released under the terms of the GNU General Public License, GPL, 
5         as published by the Free Software Foundation, either version 3 
6         of the License, or (at your option) any later version.
7     This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
10     See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ***********************************************************************/
12 include_once($path_to_root."/includes/packages.inc");
13 //
14 //      Utility class contains basic database upgrade routines.
15 //
16 class fa_patch {
17         var $previous;                          // previous database version 
18         var $version;                           // version after upgrade
19         var $description;                       // short patch description
20
21         var $sql;                                       // basic sql file
22
23         var $cur_company;
24         var $backup;                            // pre-upgrade backup filename
25
26         var $errors = array();
27         var     $max_upgrade_time = 300;
28
29         function fa_patch()
30         {
31                 global $path_to_root;
32
33                 include $path_to_root."/config_db.php";
34
35                 $this->companies = $db_connections;
36
37                 return $this->companies;
38         }
39
40         /*
41                 Collect/log messages generated during upgrade process.
42         */
43         function log_error($msg, $type='Error')
44         {
45                 if ($type == 'Error')
46                         $this->errors[] = $msg;
47                 error_log(sprintf('[%s] %s', $type, $msg));
48                 return false;
49         }
50
51         /*
52                 Check and disable incompatible extensions.
53         */
54         function update_extensions()
55         {
56                 global $version;
57
58                 $mods = get_company_extensions();
59                 $exts = get_company_extensions($this->cur_company);
60
61                 $fixed = false;
62                 foreach($mods as $key => $ins) {
63                         foreach($exts as $ext)
64                                 if ($ext['name'] == $ins['name'] && (!check_src_ext_version($ins['version']))) {
65                                         $mods[$key]['active'] = false;
66                                         $this->log_error(sprintf(_("Uncompatible extension '%s' disabled for company %d."), $ins['name'], $this->cur_company), 'Notice');
67                                         $fixed = true;
68                                         continue 2;
69                                 }
70                 }
71                 if ($fixed)
72                         write_extensions($mods, $this->cur_company);
73         }
74
75         /*
76                 Pre-install maintenance: login to company, open upgrade log, make a backup
77         */
78         function pre_install($company)
79         {
80                 global $SysPrefs;
81
82                 $this->cur_company = $company;
83                 $this->errors = array();
84                 $this->backup = null;
85
86                 $this->save_log = ini_set('error_log', dirname(__FILE__).'/../../tmp/upgrade.'.$this->cur_company.'.log');
87                 $this->log_error(sprintf(_("Upgrade started for company %s."), $this->cur_company), 'Info');
88
89                 if (!set_global_connection($this->cur_company))
90                         return $this->log_error(_("Cannot connect to company database."));
91
92                 $cur_ver = get_company_pref('version_id', true);
93                 if ($cur_ver != $this->previous)
94                         return $this->log_error(sprintf(_("Cannot upgrade company %s: database version is incompatible ('%s' instead of '%s')."),
95                                 $this->cur_company, $cur_ver, $this->previous));
96
97                 $this->update_extensions();     // disable uncompatible extensions
98
99                 if (!$this->prepare())  // fetch params, perform additional checks (if any)
100                   return false;
101
102                 if (!$this->sql)
103                         return true;    // skip security backup if database content is not changed
104
105                 $this->backup = db_backup($this->companies[$this->cur_company], 'no', 'Security backup before upgrade',
106                         $SysPrefs->backup_dir($this->cur_company));
107
108                 if (!$this->backup)
109                   return $this->log_error(_("Security backup failed."));
110
111                 $this->log_error(sprintf(_("Security backup in file %s done."), $this->backup), 'Info');
112                 return true;
113         }
114
115         /*
116                 Basic install procedure using sql file.
117         */
118         function sql_install($company, $force=false) 
119         {
120                 global $path_to_root;
121
122                 if ($this->sql != '')   // perform basic upgrade operations defined in sql file
123                 {
124                         $result = true;
125
126                         if ($result === true)
127                                 $result = db_import($path_to_root. '/sql/'.$this->sql, $this->companies[$company],
128                                         $force, true, false, true);
129
130                         if ($result !== true)
131                         {
132                                 if (is_array($result))
133                                 {
134                                         foreach($result as $err)
135                                                 $this->log_error($err[1] . ':'. $err[0]);
136                                 } else
137                                 {
138                                         $this->log_error($result);
139                                         unset($this->backup); // prevent restore (database was not touched due to other errors)
140                                 }
141                                 return false;
142                         }
143                 }
144                 return true;
145         }
146
147         /*
148                 Post install procedures: update database version, or restore databse from backup file in case of errors
149         */
150         function post_install($result=true)
151         {
152                 global $db_version;
153
154                 if ($result !== true)
155                 {
156                         if ($this->backup)
157                         {
158                                 if (!set_global_connection($this->cur_company)) // reset connection to clear encoding
159                                         return $this->log_error(_("Cannot connect to company database for database restore."));
160
161                                 set_time_limit($this->max_upgrade_time);
162                                 $result = db_import($this->backup, $this->companies[$this->cur_company], true, false);
163                                 if ($result)
164                                         $this->log_error(_("Upgrade failed. Original database content restored successfully."), 'Info');
165                                 else
166                                         $thi->log_error(sprintf(_("Database restore operation failed. Original database content is in %s file."), $this->backup));
167                                 $this->post_fail($this->cur_company);
168                         }
169                 } else {
170                         update_company_prefs(array('version_id' => $this->version));
171                 }
172
173                 $this->log_error(sprintf(_("Upgrade for company %s finished."), $this->cur_company), 'Info');
174
175                 set_global_connection();
176                 ini_set('error_log', $this->save_log);
177
178                 if (db_fixed())
179                         db_set_encoding();
180
181                 return $result;
182         }
183
184         /*
185                 Main routine for single company upgrade.
186         */
187         function upgrade_company($comp, $force=false)
188         {
189                 $result = $this->pre_install($comp) && $this->sql_install($comp, $force) && $this->install($comp, $force);
190
191                 $this->post_install($result);
192
193                 return count($this->errors) == 0;
194         }
195
196         /*
197                 Additional version specific php/sql upgrade procedures.
198                 This procedure is performed after basic sql upgrade script is run.
199         */
200         function install($company, $force=false) 
201         {
202                 return true;
203         }
204         /*
205                 Optional cleanup procedure.
206                 This procedure is run in case of upgrade failure, before the backup is restored.
207         */
208         function post_fail($company)
209         {
210         }
211
212     /*
213                 Present upgrade parameters to administrator
214                 This function presents upgrade choices, after selection company to be upgraded.
215     */
216         function show_params($comp)
217     {
218         }
219
220     /*
221             Fetch & check upgrade parameters, check additional upgrade pre-conditions.
222                 This function is run after successfull switching to target database connection.
223     */
224         function prepare()
225     {
226                 return true;
227         }
228
229 }
230
231 /*
232         Return databases status info.
233 */
234 function get_site_status($connections)
235 {
236                 global $SysPrefs;
237
238                 $info = array();
239
240                 foreach($connections as $i => $conn)
241                 {
242                         $info[$i]['status'] = set_global_connection($i) !== false;
243
244                         $info[$i]['name'] = $conn['name'];
245                         $info[$i]['table_set'] = $conn['host'].'/'.$conn['dbname'].':'.$conn['tbpref'].'*';
246                         if ($info[$i]['status'])
247                         {
248                                 $info[$i]['version'] = get_company_pref('version_id');
249                         }
250                 }
251                 set_global_connection();
252                 $SysPrefs->refresh();
253
254                 return $info;
255 }
256
257 /*
258         Creates table of installer objects sorted by applicable db scheme version.
259 */
260 function get_installers()
261 {
262         global $path_to_root;
263
264         $patchdir = $path_to_root."/sql/";
265         $upgrades = array();    
266         $datadir = @opendir($patchdir);
267
268         if ($datadir)
269         {
270                 while(false !== ($fname = readdir($datadir)))
271                 { // check all php files but index.php
272                         if (!is_dir($patchdir . $fname) && ($fname != 'index.php')
273                                 && stristr($fname, '.php') != false && $fname[0] != '.')
274                         {
275                                 unset($install);
276                                 include_once($patchdir . $fname);
277                                 if (isset($install)) // add installer if found
278                                         $upgrades[$install->previous] =  $install;
279                         }
280                 }
281                 ksort($upgrades); // sort by file name
282         }
283         return $upgrades;
284 }