]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - scripts/fixup_utf8.php
Merge branch '1.1.x'
[quix0rs-gnu-social.git] / scripts / fixup_utf8.php
1 #!/usr/bin/env php
2 <?php
3 /*
4  * StatusNet - the distributed open-source microblogging tool
5  * Copyright (C) 2009, StatusNet, Inc.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 // Abort if called from a web server
22
23 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
24
25 $helptext = <<<ENDOFHELP
26 fixup_utf8.php <maxdate> <maxid> <minid>
27
28 Fixup records in a database that stored the data incorrectly (pre-0.7.4 for StatusNet).
29
30 ENDOFHELP;
31
32 require_once INSTALLDIR.'/scripts/commandline.inc';
33 require_once 'DB.php';
34
35 class UTF8FixerUpper
36 {
37     var $dbl = null;
38     var $dbu = null;
39     var $args = array();
40
41     function __construct($args)
42     {
43         $this->args = $args;
44
45         if (!empty($args['max_date'])) {
46             $this->max_date = strftime('%Y-%m-%d %H:%M:%S', strtotime($args['max_date']));
47         } else {
48             $this->max_date = strftime('%Y-%m-%d %H:%M:%S', time());
49         }
50
51         $this->dbl = $this->doConnect('latin1');
52
53         if (empty($this->dbl)) {
54             return;
55         }
56
57         $this->dbu = $this->doConnect('utf8');
58
59         if (empty($this->dbu)) {
60             return;
61         }
62     }
63
64     function doConnect($charset)
65     {
66         $db = DB::connect(common_config('db', 'database'),
67                           array('persistent' => false));
68
69         if (PEAR::isError($db)) {
70             echo "ERROR: " . $db->getMessage() . "\n";
71             return NULL;
72         }
73
74         $conn = $db->connection;
75
76         $succ = mysqli_set_charset($conn, $charset);
77
78         if (!$succ) {
79             echo "ERROR: couldn't set charset\n";
80             $db->disconnect();
81             return NULL;
82         }
83
84         $result = $db->autoCommit(true);
85
86         if (PEAR::isError($result)) {
87             echo "ERROR: " . $result->getMessage() . "\n";
88             $db->disconnect();
89             return NULL;
90         }
91
92         return $db;
93     }
94
95     function fixup()
96     {
97         $this->fixupNotices($this->args['max_notice'],
98                             $this->args['min_notice']);
99         $this->fixupProfiles();
100         $this->fixupGroups();
101         $this->fixupMessages();
102     }
103
104     function fixupNotices($max_id, $min_id) {
105
106         // Do a separate DB connection
107
108         $sth = $this->dbu->prepare("UPDATE notice SET content = UNHEX(?), rendered = UNHEX(?) WHERE id = ?");
109
110         if (PEAR::isError($sth)) {
111             echo "ERROR: " . $sth->getMessage() . "\n";
112             return;
113         }
114
115         $sql = 'SELECT id, content, rendered FROM notice ' .
116           'WHERE LENGTH(content) != CHAR_LENGTH(content) '.
117           'AND modified < "'.$this->max_date.'" ';
118
119         if (!empty($max_id)) {
120             $sql .= ' AND id <= ' . $max_id;
121         }
122
123         if (!empty($min_id)) {
124             $sql .= ' AND id >= ' . $min_id;
125         }
126
127         $sql .= ' ORDER BY id DESC';
128
129         $rn = $this->dbl->query($sql);
130
131         if (PEAR::isError($rn)) {
132             echo "ERROR: " . $rn->getMessage() . "\n";
133             return;
134         }
135
136         echo "Number of rows: " . $rn->numRows() . "\n";
137
138         $notice = array();
139
140         while (DB_OK == $rn->fetchInto($notice)) {
141
142             $id = ($notice[0])+0;
143             $content = bin2hex($notice[1]);
144             $rendered = bin2hex($notice[2]);
145
146             echo "$id...";
147
148             $result = $this->dbu->execute($sth, array($content, $rendered, $id));
149
150             if (PEAR::isError($result)) {
151                 echo "ERROR: " . $result->getMessage() . "\n";
152                 continue;
153             }
154
155             $cnt = $this->dbu->affectedRows();
156
157             if ($cnt != 1) {
158                 echo "ERROR: 0 rows affected\n";
159                 continue;
160             }
161
162             $notice = Notice::staticGet('id', $id);
163             $notice->decache();
164             $notice->free();
165
166             echo "OK\n";
167         }
168     }
169
170     function fixupProfiles()
171     {
172         // Do a separate DB connection
173
174         $sth = $this->dbu->prepare("UPDATE profile SET ".
175                                    "fullname = UNHEX(?),".
176                                    "location = UNHEX(?), ".
177                                    "bio = UNHEX(?) ".
178                                    "WHERE id = ?");
179
180         if (PEAR::isError($sth)) {
181             echo "ERROR: " . $sth->getMessage() . "\n";
182             return;
183         }
184
185         $sql = 'SELECT id, fullname, location, bio FROM profile ' .
186           'WHERE (LENGTH(fullname) != CHAR_LENGTH(fullname) '.
187           'OR LENGTH(location) != CHAR_LENGTH(location) '.
188           'OR LENGTH(bio) != CHAR_LENGTH(bio)) '.
189           'AND modified < "'.$this->max_date.'" '.
190           ' ORDER BY modified DESC';
191
192         $rn = $this->dbl->query($sql);
193
194         if (PEAR::isError($rn)) {
195             echo "ERROR: " . $rn->getMessage() . "\n";
196             return;
197         }
198
199         echo "Number of rows: " . $rn->numRows() . "\n";
200
201         $profile = array();
202
203         while (DB_OK == $rn->fetchInto($profile)) {
204
205             $id = ($profile[0])+0;
206             $fullname = bin2hex($profile[1]);
207             $location = bin2hex($profile[2]);
208             $bio = bin2hex($profile[3]);
209
210             echo "$id...";
211
212             $result = $this->dbu->execute($sth, array($fullname, $location, $bio, $id));
213
214             if (PEAR::isError($result)) {
215                 echo "ERROR: " . $result->getMessage() . "\n";
216                 continue;
217             }
218
219             $cnt = $this->dbu->affectedRows();
220
221             if ($cnt != 1) {
222                 echo "ERROR: 0 rows affected\n";
223                 continue;
224             }
225
226             $profile = Profile::staticGet('id', $id);
227             $profile->decache();
228             $profile->free();
229
230             echo "OK\n";
231         }
232     }
233
234     function fixupGroups()
235     {
236         // Do a separate DB connection
237
238         $sth = $this->dbu->prepare("UPDATE user_group SET ".
239                                    "fullname = UNHEX(?),".
240                                    "location = UNHEX(?), ".
241                                    "description = UNHEX(?) ".
242                                    "WHERE id = ?");
243
244         if (PEAR::isError($sth)) {
245             echo "ERROR: " . $sth->getMessage() . "\n";
246             return;
247         }
248
249         $sql = 'SELECT id, fullname, location, description FROM user_group ' .
250           'WHERE LENGTH(fullname) != CHAR_LENGTH(fullname) '.
251           'OR LENGTH(location) != CHAR_LENGTH(location) '.
252           'OR LENGTH(description) != CHAR_LENGTH(description) '.
253           'AND modified < "'.$this->max_date.'" '.
254           'ORDER BY modified DESC';
255
256         $rn = $this->dbl->query($sql);
257
258         if (PEAR::isError($rn)) {
259             echo "ERROR: " . $rn->getMessage() . "\n";
260             return;
261         }
262
263         echo "Number of rows: " . $rn->numRows() . "\n";
264
265         $user_group = array();
266
267         while (DB_OK == $rn->fetchInto($user_group)) {
268
269             $id = ($user_group[0])+0;
270             $fullname = bin2hex($user_group[1]);
271             $location = bin2hex($user_group[2]);
272             $description = bin2hex($user_group[3]);
273
274             echo "$id...";
275
276             $result = $this->dbu->execute($sth, array($fullname, $location, $description, $id));
277
278             if (PEAR::isError($result)) {
279                 echo "ERROR: " . $result->getMessage() . "\n";
280                 continue;
281             }
282
283             $cnt = $this->dbu->affectedRows();
284
285             if ($cnt != 1) {
286                 echo "ERROR: 0 rows affected\n";
287                 continue;
288             }
289
290             $user_group = User_group::staticGet('id', $id);
291             $user_group->decache();
292             $user_group->free();
293
294             echo "OK\n";
295         }
296     }
297
298     function fixupMessages() {
299
300         // Do a separate DB connection
301
302         $sth = $this->dbu->prepare("UPDATE message SET content = UNHEX(?), rendered = UNHEX(?) WHERE id = ?");
303
304         if (PEAR::isError($sth)) {
305             echo "ERROR: " . $sth->getMessage() . "\n";
306             return;
307         }
308
309         $sql = 'SELECT id, content, rendered FROM message ' .
310           'WHERE LENGTH(content) != CHAR_LENGTH(content) '.
311           'AND modified < "'.$this->max_date.'" '.
312           'ORDER BY id DESC';
313
314         $rn = $this->dbl->query($sql);
315
316         if (PEAR::isError($rn)) {
317             echo "ERROR: " . $rn->getMessage() . "\n";
318             return;
319         }
320
321         echo "Number of rows: " . $rn->numRows() . "\n";
322
323         $message = array();
324
325         while (DB_OK == $rn->fetchInto($message)) {
326
327             $id = ($message[0])+0;
328             $content = bin2hex($message[1]);
329             $rendered = bin2hex($message[2]);
330
331             echo "$id...";
332
333             $result = $this->dbu->execute($sth, array($content, $rendered, $id));
334
335             if (PEAR::isError($result)) {
336                 echo "ERROR: " . $result->getMessage() . "\n";
337                 continue;
338             }
339
340             $cnt = $this->dbu->affectedRows();
341
342             if ($cnt != 1) {
343                 echo "ERROR: 0 rows affected\n";
344                 continue;
345             }
346
347             $message = Message::staticGet('id', $id);
348             $message->decache();
349             $message->free();
350
351             echo "OK\n";
352         }
353     }
354 }
355
356 $max_date = (count($args) > 0) ? $args[0] : null;
357 $max_id = (count($args) > 1) ? $args[1] : null;
358 $min_id = (count($args) > 2) ? $args[2] : null;
359
360 $fixer = new UTF8FixerUpper(array('max_date' => $max_date,
361                                   'max_notice' => $max_id,
362                                   'min_notice' => $min_id));
363
364 $fixer->fixup();
365