PageRenderTime 53ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/admin/db_update.php

https://bitbucket.org/gencer/punbb
PHP | 2244 lines | 1647 code | 391 blank | 206 comment | 259 complexity | 56aedd7577a3b65a62facc2a19b15aa4 MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Database updating script.
  4. *
  5. * Updates the database to the latest version.
  6. *
  7. * @copyright (C) 2008-2012 PunBB, partially based on code (C) 2008-2009 FluxBB.org
  8. * @license http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  9. * @package PunBB
  10. */
  11. define('UPDATE_TO', '1.4.2');
  12. define('UPDATE_TO_DB_REVISION', 5);
  13. // The number of items to process per pageview (lower this if the update script times out during UTF-8 conversion)
  14. define('PER_PAGE', 300);
  15. define('MIN_MYSQL_VERSION', '4.1.2');
  16. // Make sure we are running at least PHP 5.0.0
  17. if (!function_exists('version_compare') || version_compare(PHP_VERSION, '5.0.0', '<'))
  18. exit('You are running PHP version '.PHP_VERSION.'. '.UPDATE_TO.' requires at least PHP 5.0.0 to run properly. You must upgrade your PHP installation before you can continue.');
  19. define('FORUM_ROOT', '../');
  20. // Attempt to load the configuration file config.php
  21. if (file_exists(FORUM_ROOT.'config.php'))
  22. include FORUM_ROOT.'config.php';
  23. if (defined('PUN'))
  24. define('FORUM', 1);
  25. // If FORUM isn't defined, config.php is missing or corrupt or we are outside the root directory
  26. if (!defined('FORUM'))
  27. exit('Cannot find config.php, are you sure it exists?');
  28. // Enable debug mode
  29. if (!defined('FORUM_DEBUG'))
  30. define('FORUM_DEBUG', 1);
  31. // Define avatars type
  32. define('FORUM_AVATAR_NONE', 0);
  33. define('FORUM_AVATAR_GIF', 1);
  34. define('FORUM_AVATAR_JPG', 2);
  35. define('FORUM_AVATAR_PNG', 3);
  36. // Turn on full PHP error reporting
  37. error_reporting(E_ALL);
  38. // Turn off magic_quotes_runtime
  39. if (get_magic_quotes_runtime())
  40. @ini_set('magic_quotes_runtime', false);
  41. // Turn off PHP time limit
  42. @set_time_limit(0);
  43. // If a cookie name is not specified in config.php, we use the default (forum_cookie)
  44. if (empty($cookie_name))
  45. $cookie_name = 'forum_cookie';
  46. // If the cache directory is not specified, we use the default setting
  47. if (!defined('FORUM_CACHE_DIR'))
  48. define('FORUM_CACHE_DIR', FORUM_ROOT.'cache/');
  49. // Load the functions script
  50. require FORUM_ROOT.'include/functions.php';
  51. // Load UTF-8 functions
  52. require FORUM_ROOT.'include/utf8/utf8.php';
  53. require FORUM_ROOT.'include/utf8/ucwords.php';
  54. require FORUM_ROOT.'include/utf8/trim.php';
  55. // Strip out "bad" UTF-8 characters
  56. forum_remove_bad_characters();
  57. // If the request_uri is invalid try fix it
  58. if (!defined('FORUM_IGNORE_REQUEST_URI'))
  59. forum_fix_request_uri();
  60. // Instruct DB abstraction layer that we don't want it to "SET NAMES". If we need to, we'll do it ourselves below.
  61. define('FORUM_NO_SET_NAMES', 1);
  62. // Load DB abstraction layer and try to connect
  63. require FORUM_ROOT.'include/dblayer/common_db.php';
  64. // Start a transaction
  65. $forum_db->start_transaction();
  66. // Check current version
  67. $query = array(
  68. 'SELECT' => 'conf_value',
  69. 'FROM' => 'config',
  70. 'WHERE' => 'conf_name = \'o_cur_version\''
  71. );
  72. $result = $forum_db->query_build($query);
  73. $cur_version = $forum_db->result($result);
  74. if (version_compare($cur_version, '1.2', '<'))
  75. error('Version mismatch. The database \''.$db_name.'\' doesn\'t seem to be running a PunBB database schema supported by this update script.', __FILE__, __LINE__);
  76. // If we've already done charset conversion in a previous update, we have to do SET NAMES
  77. $forum_db->set_names(version_compare($cur_version, '1.3', '>=') ? 'utf8' : 'latin1');
  78. // If MySQL, make sure it's at least 4.1.2
  79. if (in_array($db_type, array('mysql', 'mysqli', 'mysql_innodb', 'mysqli_innodb')))
  80. {
  81. $mysql_info = $forum_db->get_version();
  82. if (version_compare($mysql_info['version'], MIN_MYSQL_VERSION, '<'))
  83. error('You are running MySQL version '.$mysql_version.'. PunBB '.UPDATE_TO.' requires at least MySQL '.MIN_MYSQL_VERSION.' to run properly. You must upgrade your MySQL installation before you can continue.');
  84. }
  85. // Get the forum config
  86. $query = array(
  87. 'SELECT' => '*',
  88. 'FROM' => 'config'
  89. );
  90. $result = $forum_db->query_build($query);
  91. while ($cur_config_item = $forum_db->fetch_row($result))
  92. $forum_config[$cur_config_item[0]] = $cur_config_item[1];
  93. // Check the database revision and the current version
  94. if (isset($forum_config['o_database_revision']) && $forum_config['o_database_revision'] >= UPDATE_TO_DB_REVISION && version_compare($forum_config['o_cur_version'], UPDATE_TO, '>='))
  95. error('Your database is already as up-to-date as this script can make it.');
  96. // If $base_url isn't set, use o_base_url from config
  97. if (!isset($base_url))
  98. $base_url = $forum_config['o_base_url'];
  99. // There's no $forum_user, but we need the style element
  100. // We default to Oxygen if the default style is invalid (a 1.2 to 1.3 upgrade most likely)
  101. if (file_exists(FORUM_ROOT.'style/'.$forum_config['o_default_style'].'/'.$forum_config['o_default_style'].'.php'))
  102. $forum_user['style'] = $forum_config['o_default_style'];
  103. else
  104. {
  105. $forum_user['style'] = 'Oxygen';
  106. $query = array(
  107. 'UPDATE' => 'config',
  108. 'SET' => 'conf_value = \'Oxygen\'',
  109. 'WHERE' => 'conf_name = \'o_default_style\''
  110. );
  111. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  112. }
  113. // Make sure the default language exists
  114. // We default to English if the default language is invalid (a 1.2 to 1.3 upgrade most likely)
  115. if (!file_exists(FORUM_ROOT.'lang/'.$forum_config['o_default_lang'].'/common.php'))
  116. {
  117. $query = array(
  118. 'UPDATE' => 'config',
  119. 'SET' => 'conf_value = \'English\'',
  120. 'WHERE' => 'conf_name = \'o_default_lang\''
  121. );
  122. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  123. }
  124. //
  125. // Determines whether $str is UTF-8 encoded or not
  126. //
  127. function seems_utf8($str)
  128. {
  129. $str_len = strlen($str);
  130. for ($i = 0; $i < $str_len; ++$i)
  131. {
  132. if (ord($str[$i]) < 0x80) continue; # 0bbbbbbb
  133. else if ((ord($str[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
  134. else if ((ord($str[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
  135. else if ((ord($str[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
  136. else if ((ord($str[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
  137. else if ((ord($str[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
  138. else return false; # Does not match any model
  139. for ($j = 0; $j < $n; ++$j) # n bytes matching 10bbbbbb follow ?
  140. {
  141. if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80))
  142. return false;
  143. }
  144. }
  145. return true;
  146. }
  147. //
  148. // Translates the number from an HTML numeric entity into an UTF-8 character
  149. //
  150. function dcr2utf8($src)
  151. {
  152. $dest = '';
  153. if ($src < 0)
  154. return false;
  155. else if ($src <= 0x007f)
  156. $dest .= chr($src);
  157. else if ($src <= 0x07ff)
  158. {
  159. $dest .= chr(0xc0 | ($src >> 6));
  160. $dest .= chr(0x80 | ($src & 0x003f));
  161. }
  162. else if ($src == 0xFEFF)
  163. {
  164. // nop -- zap the BOM
  165. }
  166. else if ($src >= 0xD800 && $src <= 0xDFFF)
  167. {
  168. // found a surrogate
  169. return false;
  170. }
  171. else if ($src <= 0xffff)
  172. {
  173. $dest .= chr(0xe0 | ($src >> 12));
  174. $dest .= chr(0x80 | (($src >> 6) & 0x003f));
  175. $dest .= chr(0x80 | ($src & 0x003f));
  176. }
  177. else if ($src <= 0x10ffff)
  178. {
  179. $dest .= chr(0xf0 | ($src >> 18));
  180. $dest .= chr(0x80 | (($src >> 12) & 0x3f));
  181. $dest .= chr(0x80 | (($src >> 6) & 0x3f));
  182. $dest .= chr(0x80 | ($src & 0x3f));
  183. }
  184. else
  185. {
  186. // out of range
  187. return false;
  188. }
  189. return $dest;
  190. }
  191. //
  192. // Attemts to convert $str from $old_charset to UTF-8. Also converts HTML entities (including numeric entities) to UTF-8 characters.
  193. //
  194. function convert_to_utf8(&$str, $old_charset)
  195. {
  196. if ($str == '')
  197. return false;
  198. $save = $str;
  199. // Replace literal entities (for non-UTF-8 compliant html_entity_encode)
  200. if (version_compare(PHP_VERSION, '5.0.0', '<') && $old_charset == 'ISO-8859-1' || $old_charset == 'ISO-8859-15')
  201. $str = html_entity_decode($str, ENT_QUOTES, $old_charset);
  202. if (!seems_utf8($str))
  203. {
  204. if ($old_charset == 'ISO-8859-1')
  205. $str = utf8_encode($str);
  206. else if (function_exists('iconv'))
  207. $str = iconv($old_charset, 'UTF-8', $str);
  208. else if (function_exists('mb_convert_encoding'))
  209. $str = mb_convert_encoding($str, 'UTF-8', $old_charset);
  210. }
  211. // Replace literal entities (for UTF-8 compliant html_entity_encode)
  212. if (version_compare(PHP_VERSION, '5.0.0', '>='))
  213. $str = html_entity_decode($str, ENT_QUOTES, 'UTF-8');
  214. // Replace numeric entities
  215. $str = preg_replace_callback('/&#([0-9]+);/', 'utf8_callback_1', $str);
  216. $str = preg_replace_callback('/&#x([a-f0-9]+);/i', 'utf8_callback_2', $str);
  217. return ($save != $str);
  218. }
  219. function utf8_callback_1($matches)
  220. {
  221. return dcr2utf8($matches[1]);
  222. }
  223. function utf8_callback_2($matches)
  224. {
  225. return dcr2utf8(hexdec($matches[1]));
  226. }
  227. //
  228. // Tries to determine whether post data in the database is UTF-8 encoded or not
  229. //
  230. function db_seems_utf8()
  231. {
  232. global $db_type, $forum_db;
  233. $seems_utf8 = true;
  234. $query = array(
  235. 'SELECT' => 'MIN(id), MAX(id), COUNT(id)',
  236. 'FROM' => 'posts'
  237. );
  238. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  239. list($min_id, $max_id, $count_id) = $forum_db->fetch_row($result);
  240. if ($count_id == 0)
  241. return false;
  242. // Get a random soup of data and check if it appears to be UTF-8
  243. for ($i = 0; $i < 100; ++$i)
  244. {
  245. $id = ($i == 0) ? $min_id : (($i == 1) ? $max_id : rand($min_id, $max_id));
  246. $query = array(
  247. 'SELECT' => 'p.message, p.poster, t.subject, f.forum_name',
  248. 'FROM' => 'posts AS p',
  249. 'JOINS' => array(
  250. array(
  251. 'INNER JOIN' => 'topics AS t',
  252. 'ON' => 't.id = p.topic_id'
  253. ),
  254. array(
  255. 'INNER JOIN' => 'forums AS f',
  256. 'ON' => 'f.id = t.forum_id'
  257. )
  258. ),
  259. 'WHERE' => 'p.id >= '.$id,
  260. 'LIMIT' => '1'
  261. );
  262. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  263. $random_row = $forum_db->fetch_row($result);
  264. if (!seems_utf8($random_row[0].$random_row[1].$random_row[2].$random_row[3]))
  265. {
  266. $seems_utf8 = false;
  267. break;
  268. }
  269. }
  270. return $seems_utf8;
  271. }
  272. //
  273. // Safely converts text type columns into utf8 (MySQL only)
  274. // Function based on update_convert_table_utf8() from the Drupal project (http://drupal.org/)
  275. //
  276. function convert_table_utf8($table)
  277. {
  278. global $forum_db;
  279. $types = array(
  280. 'char' => 'binary',
  281. 'varchar' => 'varbinary',
  282. 'tinytext' => 'tinyblob',
  283. 'mediumtext' => 'mediumblob',
  284. 'text' => 'blob',
  285. 'longtext' => 'longblob'
  286. );
  287. // Set table default charset to utf8
  288. $forum_db->query('ALTER TABLE `'.$table.'` CHARACTER SET utf8') or error(__FILE__, __LINE__);
  289. // Find out which columns need converting and build SQL statements
  290. $result = $forum_db->query('SHOW FULL COLUMNS FROM `'.$table.'`') or error(__FILE__, __LINE__);
  291. while ($cur_column = $forum_db->fetch_assoc($result))
  292. {
  293. list($type) = explode('(', $cur_column['Type']);
  294. if (isset($types[$type]) && strpos($cur_column['Collation'], 'utf8') === false)
  295. {
  296. $allow_null = ($cur_column['Null'] == 'YES');
  297. $forum_db->alter_field($table, $cur_column['Field'], preg_replace('/'.$type.'/i', $types[$type], $cur_column['Type']), $allow_null, $cur_column['Default']);
  298. $forum_db->alter_field($table, $cur_column['Field'], $cur_column['Type'].' CHARACTER SET utf8', $allow_null, $cur_column['Default']);
  299. }
  300. }
  301. }
  302. // Move avatars to DB
  303. function convert_avatars()
  304. {
  305. global $forum_config, $forum_db;
  306. $avatar_dir = FORUM_ROOT.'img/avatars/';
  307. if (!is_dir($avatar_dir))
  308. {
  309. return false;
  310. }
  311. if ($handle = opendir($avatar_dir))
  312. {
  313. while (false !== ($avatar = readdir($handle)))
  314. {
  315. $avatar_file = $avatar_dir.$avatar;
  316. if (!is_file($avatar_file))
  317. {
  318. continue;
  319. }
  320. //echo $avatar_file;
  321. $avatar = basename($avatar_file);
  322. if (preg_match('/^(\d+)\.(png|gif|jpg)/', $avatar, $matches))
  323. {
  324. $user_id = intval($matches[1], 10);
  325. $avatar_ext = $matches[2];
  326. $avatar_type = FORUM_AVATAR_NONE;
  327. if ($avatar_ext == 'png')
  328. {
  329. $avatar_type = FORUM_AVATAR_PNG;
  330. }
  331. else if ($avatar_ext == 'gif')
  332. {
  333. $avatar_type = FORUM_AVATAR_GIF;
  334. }
  335. else if ($avatar_ext == 'jpg')
  336. {
  337. $avatar_type = FORUM_AVATAR_JPG;
  338. }
  339. // Check user and avatar type
  340. if ($user_id < 2 || $avatar_type == FORUM_AVATAR_NONE)
  341. {
  342. continue;
  343. }
  344. // Now check the width/height
  345. list($width, $height, $type,) = @/**/getimagesize($avatar_file);
  346. if (empty($width) || empty($height) || $width > $forum_config['o_avatars_width'] || $height > $forum_config['o_avatars_height'])
  347. {
  348. @/**/unlink($avatar_file);
  349. }
  350. else
  351. {
  352. // Save to DB
  353. $query = array(
  354. 'UPDATE' => 'users',
  355. 'SET' => 'avatar=\''.$avatar_type.'\', avatar_height=\''.$height.'\', avatar_width=\''.$width.'\'',
  356. 'WHERE' => 'id='.$user_id
  357. );
  358. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  359. }
  360. }
  361. }
  362. closedir($handle);
  363. }
  364. }
  365. header('Content-type: text/html; charset=utf-8');
  366. // Empty all output buffers and stop buffering
  367. while (@ob_end_clean());
  368. $stage = isset($_GET['stage']) ? $_GET['stage'] : '';
  369. $old_charset = isset($_GET['req_old_charset']) ? str_replace('ISO8859', 'ISO-8859', strtoupper($_GET['req_old_charset'])) : 'ISO-8859-1';
  370. $start_at = isset($_GET['start_at']) ? intval($_GET['start_at']) : 0;
  371. $query_str = '';
  372. switch ($stage)
  373. {
  374. // Show form
  375. case '':
  376. $db_seems_utf8 = db_seems_utf8();
  377. ?>
  378. <!DOCTYPE html>
  379. <!--[if lt IE 7 ]> <html class="oldie ie6" lang="en" dir="ltr"> <![endif]-->
  380. <!--[if IE 7 ]> <html class="oldie ie7" lang="en" dir="ltr"> <![endif]-->
  381. <!--[if IE 8 ]> <html class="oldie ie8" lang="en" dir="ltr"> <![endif]-->
  382. <!--[if gt IE 8]><!--> <html lang="en" dir="ltr"> <!--<![endif]-->
  383. <head>
  384. <meta charset="utf-8" />
  385. <title>PunBB Database Update</title>
  386. <link rel="stylesheet" type="text/css" href="<?php echo $base_url ?>/style/Oxygen/Oxygen.min.css" />
  387. <script type="text/javascript" src="<?php echo $base_url ?>/include/js/min/punbb.common.min.js"></script>
  388. </head>
  389. <body>
  390. <div id="brd-update" class="brd-page">
  391. <div id="brd-wrap" class="brd">
  392. <div id="brd-head" class="gen-content">
  393. <p id="brd-title"><strong>PunBB Database Update</strong></p>
  394. <p id="brd-desc">Update database tables of current installation</p>
  395. </div>
  396. <div id="brd-main" class="main basic">
  397. <div class="main-head">
  398. <h1 class="hn"><span>PunBB Database Update: Perform update of database tables</span></h1>
  399. </div>
  400. <div class="main-content frm">
  401. <div class="ct-box info-box">
  402. <ul class="spaced">
  403. <li class="warn"><span><strong>WARNING!</strong> This script will update your PunBB forum database. The update procedure might take anything from a few seconds to a few minutes (or in extreme cases, hours) depending on the speed of the server, the size of the forum database and the number of changes required.</span></li>
  404. <li><span>Do not forget to make a backup of the database before continuing.</span></li>
  405. <li><span>Did you read the update instructions in the documentation? If not, start there.</span></li>
  406. <?php
  407. if (strpos($cur_version, '1.2') === 0 && (!$db_seems_utf8 || isset($_GET['force'])))
  408. {
  409. if (!function_exists('iconv') && !function_exists('mb_convert_encoding'))
  410. {
  411. ?>
  412. <li class="important"><strong>IMPORTANT!</strong> PunBB has detected that this PHP environment does not have support for the encoding mechanisms required to do UTF-8 conversion from character sets other than ISO-8859-1. What this means is that if the current character set is not ISO-8859-1, PunBB won't be able to convert your forum database to UTF-8 and you will have to do it manually. Instructions for doing manual charset conversion can be found in the update instructions.</span></li>
  413. <?php
  414. }
  415. }
  416. $current_url = get_current_url();
  417. if (strpos($cur_version, '1.2') === 0 && $db_seems_utf8 && !isset($_GET['force']))
  418. {
  419. ?>
  420. <li class="important"><span><strong>IMPORTANT!</strong> Based on a random selection of 100 posts, topic subjects, usernames and forum names from the database, it appears as if text in the database is currently UTF-8 encoded. This is a good thing. Based on this, the update process will not attempt to do charset conversion. If you have reason to believe that the charset conversion is required nonetheless, you can <a href="<?php echo $current_url.((substr_count($current_url, '?') == 1) ? '&amp;' : '?').'force=1' ?>">force the conversion to run</a>.</span></li>
  421. <?php
  422. }
  423. ?>
  424. </ul>
  425. </div>
  426. <form class="frm-form" method="get" accept-charset="utf-8" action="<?php echo $current_url ?>">
  427. <div class="hidden">
  428. <input type="hidden" name="stage" value="start" />
  429. </div>
  430. <?php
  431. if (strpos($cur_version, '1.2') === 0 && (!$db_seems_utf8 || isset($_GET['force'])))
  432. {
  433. ?>
  434. <div class="ct-box info-box">
  435. <p class="important"><strong>Enable conversion:</strong> When enabled this update script will, after it has made the required structural changes to the database, convert all text in the database from the current character set to UTF-8. This conversion is required if you're upgrading from PunBB 1.2 and you are not currently using an UTF-8 language pack.</p>
  436. <p class="important"><strong>Current character set:</strong> If the primary language in your forum is English, you can leave this at the default value. However, if your forum is non-English, you should enter the character set of the primary language pack used in the forum.</p>
  437. </div>
  438. <div id="req-msg" class="req-warn ct-box error-box">
  439. <p class="important"><strong>Important!</strong> All fields labelled <em>(Required)</em> must be completed before submitting this form.</p>
  440. </div>
  441. <fieldset class="frm-group group1">
  442. <legend class="group-legend"><span>Charset conversion</span></legend>
  443. <div class="sf-set set1">
  444. <div class="sf-box checkbox">
  445. <span class="fld-input"><input type="checkbox" id="fld1" name="convert_charset" value="1" checked="checked" /></span>
  446. <label for="fld1"><span>Enable conversion:</span> Perform database charset conversion.</label>
  447. </div>
  448. </div>
  449. <div class="sf-set set2">
  450. <div class="sf-box text required">
  451. <label for="fld2"><span>Current character set: <em>(Required)</em></span> <small>Accept default for English forums otherwise the character set of the primary langauge pack.</small></label><br />
  452. <span class="fld-input"><input type="text" id="fld2" name="req_old_charset" size="12" maxlength="20" value="ISO-8859-1" /></span>
  453. </div>
  454. </div>
  455. </fieldset>
  456. <?php
  457. }
  458. ?>
  459. <div class="frm-buttons">
  460. <span class="submit primary"><input type="submit" name="start" value="Start update" /></span>
  461. </div>
  462. </form>
  463. </div>
  464. </div>
  465. </div>
  466. </div>
  467. </body>
  468. </html>
  469. <?php
  470. break;
  471. // Start by updating the database structure
  472. case 'start':
  473. // Put back dropped search tables
  474. if (!$forum_db->table_exists('search_cache') && in_array($db_type, array('mysql', 'mysqli', 'mysql_innodb', 'mysqli_innodb')))
  475. {
  476. $schema = array(
  477. 'FIELDS' => array(
  478. 'id' => array(
  479. 'datatype' => 'INT(10) UNSIGNED',
  480. 'allow_null' => false,
  481. 'default' => '0'
  482. ),
  483. 'ident' => array(
  484. 'datatype' => 'VARCHAR(200)',
  485. 'allow_null' => false,
  486. 'default' => '\'\''
  487. ),
  488. 'search_data' => array(
  489. 'datatype' => 'TEXT',
  490. 'allow_null' => true
  491. )
  492. ),
  493. 'PRIMARY KEY' => array('id'),
  494. 'INDEXES' => array(
  495. 'ident_idx' => array('ident(8)')
  496. )
  497. );
  498. $forum_db->create_table('search_cache', $schema);
  499. $schema = array(
  500. 'FIELDS' => array(
  501. 'post_id' => array(
  502. 'datatype' => 'INT(10) UNSIGNED',
  503. 'allow_null' => false,
  504. 'default' => '0'
  505. ),
  506. 'word_id' => array(
  507. 'datatype' => 'INT(10) UNSIGNED',
  508. 'allow_null' => false,
  509. 'default' => '0'
  510. ),
  511. 'subject_match' => array(
  512. 'datatype' => 'TINYINT(1)',
  513. 'allow_null' => false,
  514. 'default' => '0'
  515. )
  516. ),
  517. 'INDEXES' => array(
  518. 'word_id_idx' => array('word_id'),
  519. 'post_id_idx' => array('post_id')
  520. )
  521. );
  522. $forum_db->create_table('search_matches', $schema);
  523. $schema = array(
  524. 'FIELDS' => array(
  525. 'id' => array(
  526. 'datatype' => 'SERIAL',
  527. 'allow_null' => false
  528. ),
  529. 'word' => array(
  530. 'datatype' => 'VARCHAR(20)',
  531. 'allow_null' => false,
  532. 'default' => '\'\'',
  533. 'collation' => 'bin'
  534. )
  535. ),
  536. 'PRIMARY KEY' => array('word'),
  537. 'INDEXES' => array(
  538. 'id_idx' => array('id')
  539. )
  540. );
  541. $forum_db->create_table('search_words', $schema);
  542. }
  543. // Add the extensions table if it doesn't already exist
  544. if (!$forum_db->table_exists('extensions'))
  545. {
  546. $schema = array(
  547. 'FIELDS' => array(
  548. 'id' => array(
  549. 'datatype' => 'VARCHAR(150)',
  550. 'allow_null' => false,
  551. 'default' => '\'\''
  552. ),
  553. 'title' => array(
  554. 'datatype' => 'VARCHAR(255)',
  555. 'allow_null' => false,
  556. 'default' => '\'\''
  557. ),
  558. 'version' => array(
  559. 'datatype' => 'VARCHAR(25)',
  560. 'allow_null' => false,
  561. 'default' => '\'\''
  562. ),
  563. 'description' => array(
  564. 'datatype' => 'TEXT',
  565. 'allow_null' => true
  566. ),
  567. 'author' => array(
  568. 'datatype' => 'VARCHAR(50)',
  569. 'allow_null' => false,
  570. 'default' => '\'\''
  571. ),
  572. 'uninstall' => array(
  573. 'datatype' => 'TEXT',
  574. 'allow_null' => true
  575. ),
  576. 'uninstall_note' => array(
  577. 'datatype' => 'TEXT',
  578. 'allow_null' => true
  579. ),
  580. 'disabled' => array(
  581. 'datatype' => 'TINYINT(1)',
  582. 'allow_null' => false,
  583. 'default' => '0'
  584. ),
  585. 'dependencies' => array(
  586. 'datatype' => 'VARCHAR(255)',
  587. 'allow_null' => false,
  588. 'default' => '\'\''
  589. )
  590. ),
  591. 'PRIMARY KEY' => array('id')
  592. );
  593. $forum_db->create_table('extensions', $schema);
  594. }
  595. // Make sure the collation on "word" in the search_words table is utf8_bin
  596. if (in_array($db_type, array('mysql', 'mysqli', 'mysql_innodb', 'mysqli_innodb')))
  597. {
  598. $result = $forum_db->query('SHOW FULL COLUMNS FROM '.$forum_db->prefix.'search_words') or error(__FILE__, __LINE__);
  599. while ($cur_column = $forum_db->fetch_assoc($result))
  600. {
  601. if ($cur_column['Field'] === 'word')
  602. {
  603. if ($cur_column['Collation'] !== 'utf8_bin')
  604. $forum_db->alter_field('search_words', 'word', 'VARCHAR(20) CHARACTER SET utf8 COLLATE utf8_bin', false, '');
  605. break;
  606. }
  607. }
  608. }
  609. // Add uninstall_note field to extensions
  610. $forum_db->add_field('extensions', 'uninstall_note', 'TEXT', true, null, 'uninstall');
  611. // Drop uninstall_notes (plural) field
  612. $forum_db->drop_field('extensions', 'uninstall_notes');
  613. // Add disabled field to extensions
  614. $forum_db->add_field('extensions', 'disabled', 'TINYINT(1)', false, 0, 'uninstall_note');
  615. // Add dependencies field to extensions
  616. $forum_db->add_field('extensions', 'dependencies', 'VARCHAR(255)', false, '', 'disabled');
  617. // Add the extension_hooks table
  618. if (!$forum_db->table_exists('extension_hooks'))
  619. {
  620. $schema = array(
  621. 'FIELDS' => array(
  622. 'id' => array(
  623. 'datatype' => 'VARCHAR(150)',
  624. 'allow_null' => false,
  625. 'default' => '\'\''
  626. ),
  627. 'extension_id' => array(
  628. 'datatype' => 'VARCHAR(50)',
  629. 'allow_null' => false,
  630. 'default' => '\'\''
  631. ),
  632. 'code' => array(
  633. 'datatype' => 'TEXT',
  634. 'allow_null' => true
  635. ),
  636. 'installed' => array(
  637. 'datatype' => 'INT(10) UNSIGNED',
  638. 'allow_null' => false,
  639. 'default' => '0'
  640. ),
  641. 'priority' => array(
  642. 'datatype' => 'TINYINT(1) UNSIGNED',
  643. 'allow_null' => false,
  644. 'default' => '5'
  645. )
  646. ),
  647. 'PRIMARY KEY' => array('id', 'extension_id')
  648. );
  649. $forum_db->create_table('extension_hooks', $schema);
  650. }
  651. // Add priority field to extension_hooks
  652. $forum_db->add_field('extension_hooks', 'priority', 'TINYINT(1)', false, 5, 'installed');
  653. // Extend id field in extension_hooks to 150
  654. $forum_db->alter_field('extension_hooks', 'id', 'VARCHAR(150)', false, '');
  655. // Add the subscriptions forum table if it doesn't already exist
  656. if (!$forum_db->table_exists('forum_subscriptions'))
  657. {
  658. $schema = array(
  659. 'FIELDS' => array(
  660. 'user_id' => array(
  661. 'datatype' => 'INT(10) UNSIGNED',
  662. 'allow_null' => false,
  663. 'default' => '0'
  664. ),
  665. 'forum_id' => array(
  666. 'datatype' => 'INT(10) UNSIGNED',
  667. 'allow_null' => false,
  668. 'default' => '0'
  669. )
  670. ),
  671. 'PRIMARY KEY' => array('user_id', 'forum_id')
  672. );
  673. $forum_db->create_table('forum_subscriptions', $schema);
  674. }
  675. // Make all e-mail fields VARCHAR(80)
  676. $forum_db->alter_field('bans', 'email', 'VARCHAR(80)', true);
  677. $forum_db->alter_field('posts', 'poster_email', 'VARCHAR(80)', true);
  678. $forum_db->alter_field('users', 'email', 'VARCHAR(80)', false, '');
  679. $forum_db->alter_field('users', 'jabber', 'VARCHAR(80)', true);
  680. $forum_db->alter_field('users', 'msn', 'VARCHAR(80)', true);
  681. $forum_db->alter_field('users', 'activate_string', 'VARCHAR(80)', true);
  682. // Add avatars field
  683. $forum_db->add_field('users', 'avatar', 'TINYINT(3) UNSIGNED', false, 0);
  684. $forum_db->add_field('users', 'avatar_width', 'TINYINT(3) UNSIGNED', false, 0, 'avatar');
  685. $forum_db->add_field('users', 'avatar_height', 'TINYINT(3) UNSIGNED', false, 0, 'avatar_width');
  686. // Add new profile fileds
  687. $forum_db->add_field('users', 'facebook', 'VARCHAR(100)', true, null, 'url');
  688. $forum_db->add_field('users', 'twitter', 'VARCHAR(100)', true, null, 'facebook');
  689. $forum_db->add_field('users', 'linkedin', 'VARCHAR(100)', true, null, 'twitter');
  690. $forum_db->add_field('users', 'skype', 'VARCHAR(100)', true, null, 'linkedin');
  691. // Add avatars to DB
  692. convert_avatars();
  693. // Remove NOT NULL from TEXT fields for consistency. See http://dev.punbb.org/changeset/596
  694. $forum_db->alter_field('posts', 'message', 'TEXT', true);
  695. $forum_db->alter_field('reports', 'message', 'TEXT', true);
  696. // Drop fulltext indexes (should only apply to SVN installs)
  697. if (in_array($db_type, array('mysql', 'mysqli', 'mysql_innodb', 'mysqli_innodb')))
  698. {
  699. $forum_db->drop_index('topics', 'subject_idx');
  700. $forum_db->drop_index('posts', 'message_idx');
  701. }
  702. // Make all IP fields VARCHAR(39) to support IPv6
  703. $forum_db->alter_field('posts', 'poster_ip', 'VARCHAR(39)', true);
  704. $forum_db->alter_field('users', 'registration_ip', 'VARCHAR(39)', false, '0.0.0.0');
  705. // Add the DST option to the users table
  706. $forum_db->add_field('users', 'dst', 'TINYINT(1)', false, 0, 'timezone');
  707. // Add the salt field to the users table
  708. $forum_db->add_field('users', 'salt', 'VARCHAR(12)', true, null, 'password');
  709. // Add the access_keys field to the users table
  710. $forum_db->add_field('users', 'access_keys', 'TINYINT(1)', false, 0, 'show_sig');
  711. // Add the CSRF token field to the online table
  712. $forum_db->add_field('online', 'csrf_token', 'VARCHAR(40)', false, '', null);
  713. // Add the prev_url field to the online table
  714. $forum_db->add_field('online', 'prev_url', 'VARCHAR(255)', true, null, null);
  715. // Add the last_post field to the online table
  716. $forum_db->add_field('online', 'last_post', 'INT(10) UNSIGNED', true, null, null);
  717. // Add the last_search field to the online table
  718. $forum_db->add_field('online', 'last_search', 'INT(10) UNSIGNED', true, null, null);
  719. // Drop use_avatar column from users table
  720. $forum_db->drop_field('users', 'use_avatar');
  721. // Drop save_pass column from users table
  722. $forum_db->drop_field('users', 'save_pass');
  723. // Drop g_edit_subjects_interval column from groups table
  724. $forum_db->drop_field('groups', 'g_edit_subjects_interval');
  725. $new_config = array();
  726. // Add quote depth option
  727. if (!array_key_exists('o_quote_depth', $forum_config))
  728. $new_config[] = '\'o_quote_depth\', \'3\'';
  729. // Add database revision number
  730. if (!array_key_exists('o_database_revision', $forum_config))
  731. $new_config[] = '\'o_database_revision\', \'0\'';
  732. // Add default email setting option
  733. if (!array_key_exists('o_default_email_setting', $forum_config))
  734. $new_config[] = '\'o_default_email_setting\', \'1\'';
  735. // Make sure we have o_additional_navlinks (was added in 1.2.1)
  736. if (!array_key_exists('o_additional_navlinks', $forum_config))
  737. $new_config[] = '\'o_additional_navlinks\', \'\'';
  738. // Insert new config options o_sef
  739. if (!array_key_exists('o_sef', $forum_config))
  740. $new_config[] = '\'o_sef\', \'Default\'';
  741. // Insert new config option o_topic_views
  742. if (!array_key_exists('o_topic_views', $forum_config))
  743. $new_config[] = '\'o_topic_views\', \'1\'';
  744. // Insert new config option o_signatures
  745. if (!array_key_exists('o_signatures', $forum_config))
  746. $new_config[] = '\'o_signatures\', \'1\'';
  747. // Insert new config option o_smtp_ssl
  748. if (!array_key_exists('o_smtp_ssl', $forum_config))
  749. $new_config[] = '\'o_smtp_ssl\', \'0\'';
  750. // Insert new config option o_check_for_updates
  751. if (!array_key_exists('o_check_for_updates', $forum_config))
  752. {
  753. $check_for_updates = (function_exists('curl_init') || function_exists('fsockopen') || in_array(strtolower(@ini_get('allow_url_fopen')), array('on', 'true', '1'))) ? 1 : 0;
  754. $new_config[] = '\'o_check_for_updates\', \''.$check_for_updates.'\'';
  755. }
  756. // Insert new config option o_check_for_version
  757. if (!array_key_exists('o_check_for_versions', $forum_config))
  758. {
  759. $o_check_for_versions = array_key_exists('o_check_for_updates', $forum_config) ? $forum_config['o_check_for_updates'] : $check_for_updates;
  760. $new_config[] = '\'o_check_for_versions\', \''.$o_check_for_versions.'\'';
  761. }
  762. // Insert new config option o_announcement_heading
  763. if (!array_key_exists('o_announcement_heading', $forum_config))
  764. $new_config[] = '\'o_announcement_heading\', \'\'';
  765. // Insert new config option o_default_dst
  766. if (!array_key_exists('o_default_dst', $forum_config))
  767. $new_config[] = '\'o_default_dst\', \'0\'';
  768. // Insert new config option o_show_moderators
  769. if (!array_key_exists('o_show_moderators', $forum_config))
  770. $new_config[] = '\'o_show_moderators\', \'0\'';
  771. // Insert new config option o_show_moderators
  772. if (!array_key_exists('o_mask_passwords', $forum_config))
  773. $new_config[] = '\'o_mask_passwords\', \'1\'';
  774. if (!empty($new_config))
  775. {
  776. $query = array(
  777. 'INSERT' => 'conf_name, conf_value',
  778. 'INTO' => 'config',
  779. 'VALUES' => $new_config
  780. );
  781. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  782. }
  783. unset($new_config);
  784. // Server timezone is now simply the default timezone
  785. if (!array_key_exists('o_default_timezone', $forum_config))
  786. {
  787. $query = array(
  788. 'UPDATE' => 'config',
  789. 'SET' => 'conf_name = \'o_default_timezone\'',
  790. 'WHERE' => 'conf_name = \'o_server_timezone\''
  791. );
  792. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  793. }
  794. // Increase visit timeout to 30 minutes (only if it hasn't been changed from the default)
  795. if ($forum_config['o_timeout_visit'] == '600')
  796. {
  797. $query = array(
  798. 'UPDATE' => 'config',
  799. 'SET' => 'conf_value = \'1800\'',
  800. 'WHERE' => 'conf_name = \'o_timeout_visit\''
  801. );
  802. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  803. }
  804. // Update redirect timeout
  805. if (version_compare($cur_version, '1.4', '<') && $forum_config['o_redirect_delay'] == '1')
  806. {
  807. $query = array(
  808. 'UPDATE' => 'config',
  809. 'SET' => 'conf_value = \'0\'',
  810. 'WHERE' => 'conf_name = \'o_redirect_delay\''
  811. );
  812. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  813. }
  814. // Remove obsolete g_post_polls permission from groups table
  815. $forum_db->drop_field('groups', 'g_post_polls');
  816. // Make room for multiple moderator groups
  817. if (!$forum_db->field_exists('groups', 'g_moderator'))
  818. {
  819. // Add g_moderator column to groups table
  820. $forum_db->add_field('groups', 'g_moderator', 'TINYINT(1)', false, 0, 'g_user_title');
  821. // Give the moderator group moderator privileges
  822. $query = array(
  823. 'UPDATE' => 'groups',
  824. 'SET' => 'g_moderator = 1',
  825. 'WHERE' => 'g_id = 2'
  826. );
  827. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  828. // Shuffle the group IDs around a bit
  829. $query = array(
  830. 'SELECT' => 'MAX(g_id) + 1',
  831. 'FROM' => 'groups'
  832. );
  833. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  834. $temp_id = $forum_db->result($result);
  835. $query = array(
  836. 'UPDATE' => 'groups',
  837. 'SET' => 'g_id='.$temp_id,
  838. 'WHERE' => 'g_id=2'
  839. );
  840. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  841. $query = array(
  842. 'UPDATE' => 'groups',
  843. 'SET' => 'g_id=2',
  844. 'WHERE' => 'g_id=3'
  845. );
  846. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  847. $query = array(
  848. 'UPDATE' => 'groups',
  849. 'SET' => 'g_id=3',
  850. 'WHERE' => 'g_id=4'
  851. );
  852. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  853. $query = array(
  854. 'UPDATE' => 'groups',
  855. 'SET' => 'g_id=4',
  856. 'WHERE' => 'g_id='.$temp_id
  857. );
  858. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  859. $query = array(
  860. 'UPDATE' => 'users',
  861. 'SET' => 'group_id='.$temp_id,
  862. 'WHERE' => 'group_id=2'
  863. );
  864. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  865. $query = array(
  866. 'UPDATE' => 'users',
  867. 'SET' => 'group_id=2',
  868. 'WHERE' => 'group_id=3'
  869. );
  870. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  871. $query = array(
  872. 'UPDATE' => 'users',
  873. 'SET' => 'group_id=3',
  874. 'WHERE' => 'group_id=4'
  875. );
  876. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  877. $query = array(
  878. 'UPDATE' => 'users',
  879. 'SET' => 'group_id=4',
  880. 'WHERE' => 'group_id='.$temp_id
  881. );
  882. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  883. $query = array(
  884. 'UPDATE' => 'forum_perms',
  885. 'SET' => 'group_id='.$temp_id,
  886. 'WHERE' => 'group_id=2'
  887. );
  888. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  889. $query = array(
  890. 'UPDATE' => 'forum_perms',
  891. 'SET' => 'group_id=2',
  892. 'WHERE' => 'group_id=3'
  893. );
  894. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  895. $query = array(
  896. 'UPDATE' => 'forum_perms',
  897. 'SET' => 'group_id=3',
  898. 'WHERE' => 'group_id=4'
  899. );
  900. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  901. $query = array(
  902. 'UPDATE' => 'forum_perms',
  903. 'SET' => 'group_id=4',
  904. 'WHERE' => 'group_id='.$temp_id
  905. );
  906. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  907. // Update the default usergroup if it uses the old ID for the members group
  908. $query = array(
  909. 'UPDATE' => 'config',
  910. 'SET' => 'conf_value = \'3\'',
  911. 'WHERE' => 'conf_name = \'o_default_user_group\' and conf_value = \'4\''
  912. );
  913. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  914. }
  915. // Replace obsolete p_mod_edit_users config setting with new per-group permission
  916. if (array_key_exists('p_mod_edit_users', $forum_config))
  917. {
  918. $query = array(
  919. 'DELETE' => 'config',
  920. 'WHERE' => 'conf_name = \'p_mod_edit_users\''
  921. );
  922. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  923. $forum_db->add_field('groups', 'g_mod_edit_users', 'TINYINT(1)', false, 0, 'g_moderator');
  924. $query = array(
  925. 'UPDATE' => 'groups',
  926. 'SET' => 'g_mod_edit_users = '.$forum_config['p_mod_edit_users'],
  927. 'WHERE' => 'g_moderator = 1'
  928. );
  929. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  930. }
  931. // Replace obsolete p_mod_rename_users config setting with new per-group permission
  932. if (array_key_exists('p_mod_rename_users', $forum_config))
  933. {
  934. $query = array(
  935. 'DELETE' => 'config',
  936. 'WHERE' => 'conf_name = \'p_mod_rename_users\''
  937. );
  938. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  939. $forum_db->add_field('groups', 'g_mod_rename_users', 'TINYINT(1)', false, 0, 'g_mod_edit_users');
  940. $query = array(
  941. 'UPDATE' => 'groups',
  942. 'SET' => 'g_mod_rename_users = '.$forum_config['p_mod_rename_users'],
  943. 'WHERE' => 'g_moderator = 1'
  944. );
  945. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  946. }
  947. // Replace obsolete p_mod_change_passwords config setting with new per-group permission
  948. if (array_key_exists('p_mod_change_passwords', $forum_config))
  949. {
  950. $query = array(
  951. 'DELETE' => 'config',
  952. 'WHERE' => 'conf_name = \'p_mod_change_passwords\''
  953. );
  954. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  955. $forum_db->add_field('groups', 'g_mod_change_passwords', 'TINYINT(1)', false, 0, 'g_mod_rename_users');
  956. $query = array(
  957. 'UPDATE' => 'groups',
  958. 'SET' => 'g_mod_change_passwords = '.$forum_config['p_mod_change_passwords'],
  959. 'WHERE' => 'g_moderator = 1'
  960. );
  961. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  962. }
  963. // Replace obsolete p_mod_ban_users config setting with new per-group permission
  964. if (array_key_exists('p_mod_ban_users', $forum_config))
  965. {
  966. $query = array(
  967. 'DELETE' => 'config',
  968. 'WHERE' => 'conf_name = \'p_mod_ban_users\''
  969. );
  970. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  971. $forum_db->add_field('groups', 'g_mod_ban_users', 'TINYINT(1)', false, 0, 'g_mod_change_passwords');
  972. $query = array(
  973. 'UPDATE' => 'groups',
  974. 'SET' => 'g_mod_ban_users = '.$forum_config['p_mod_ban_users'],
  975. 'WHERE' => 'g_moderator = 1'
  976. );
  977. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  978. }
  979. // We need to add a unique index to avoid users having multiple rows in the online table
  980. if (!$forum_db->index_exists('online', 'user_id_ident_idx'))
  981. {
  982. $query = array(
  983. 'DELETE' => 'online'
  984. );
  985. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  986. switch ($db_type)
  987. {
  988. case 'mysql':
  989. case 'mysql_innodb':
  990. case 'mysqli':
  991. case 'mysqli_innodb':
  992. $forum_db->add_index('online', 'user_id_ident_idx', array('user_id', 'ident(25)'), true);
  993. break;
  994. default:
  995. $forum_db->add_index('online', 'user_id_ident_idx', array('user_id', 'ident'), true);
  996. break;
  997. }
  998. }
  999. // Remove the redundant user_id_idx on the online table
  1000. $forum_db->drop_index('online', 'user_id_idx');
  1001. // Add an index to ident on the online table
  1002. switch ($db_type)
  1003. {
  1004. case 'mysql':
  1005. case 'mysql_innodb':
  1006. case 'mysqli':
  1007. case 'mysqli_innodb':
  1008. $forum_db->add_index('online', 'ident_idx', array('ident(25)'));
  1009. break;
  1010. default:
  1011. $forum_db->add_index('online', 'ident_idx', array('ident'));
  1012. break;
  1013. }
  1014. // Add an index to logged on the online table
  1015. $forum_db->add_index('online', 'logged_idx', array('logged'));
  1016. // Add an index on last_post in the topics table
  1017. $forum_db->add_index('topics', 'last_post_idx', array('last_post'));
  1018. // Remove any remnants of the now defunct post approval system
  1019. $forum_db->drop_field('forums', 'approval');
  1020. $forum_db->drop_field('groups', 'g_posts_approved');
  1021. $forum_db->drop_field('posts', 'approved');
  1022. // Add g_view_users field to groups table
  1023. $forum_db->add_field('groups', 'g_view_users', 'TINYINT(1)', false, 1, 'g_read_board');
  1024. // Add the time/date format settings to the user table
  1025. $forum_db->add_field('users', 'time_format', 'INT(10)', false, 0, 'dst');
  1026. $forum_db->add_field('users', 'date_format', 'INT(10)', false, 0, 'dst');
  1027. // Add the last_search column to the users table
  1028. $forum_db->add_field('users', 'last_search', 'INT(10)', true, null, 'last_post');
  1029. // Add the last_email_sent column to the users table and the g_send_email and
  1030. // g_email_flood columns to the groups table
  1031. $forum_db->add_field('users', 'last_email_sent', 'INT(10)', true, null, 'last_search');
  1032. $forum_db->add_field('groups', 'g_send_email', 'TINYINT(1)', false, 1, 'g_search_users');
  1033. $forum_db->add_field('groups', 'g_email_flood', 'INT(10)', false, 60, 'g_search_flood');
  1034. // Set non-default g_send_email and g_flood_email values properly
  1035. $query = array(
  1036. 'UPDATE' => 'groups',
  1037. 'SET' => 'g_send_email = 0',
  1038. 'WHERE' => 'g_id = 2'
  1039. );
  1040. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1041. $query = array(
  1042. 'UPDATE' => 'groups',
  1043. 'SET' => 'g_email_flood = 0',
  1044. 'WHERE' => 'g_id IN (1,2,4)'
  1045. );
  1046. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1047. // Add the auto notify/subscription option to the users table
  1048. $forum_db->add_field('users', 'auto_notify', 'TINYINT(1)', false, 0, 'notify_with_post');
  1049. // Add the first_post_id column to the topics table
  1050. if (!$forum_db->field_exists('topics', 'first_post_id'))
  1051. {
  1052. $forum_db->add_field('topics', 'first_post_id', 'INT(10) UNSIGNED', false, 0, 'posted');
  1053. $forum_db->add_index('topics', 'first_post_id_idx', array('first_post_id'));
  1054. // Now that we've added the column and indexed it, we need to give it correct data
  1055. $query = array(
  1056. 'SELECT' => 'MIN(id) AS first_post, topic_id',
  1057. 'FROM' => 'posts',
  1058. 'GROUP BY' => 'topic_id'
  1059. );
  1060. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1061. while ($cur_post = $forum_db->fetch_assoc($result))
  1062. {
  1063. $query = array(
  1064. 'UPDATE' => 'topics',
  1065. 'SET' => 'first_post_id = '.$cur_post['first_post'],
  1066. 'WHERE' => 'id = '.$cur_post['topic_id']
  1067. );
  1068. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1069. }
  1070. }
  1071. // Add the index for the post time
  1072. if (!$forum_db->index_exists('posts', 'posted_idx'))
  1073. $forum_db->add_index('posts', 'posted_idx', array('posted'));
  1074. // Move any users with the old unverified status to their new group
  1075. $query = array(
  1076. 'UPDATE' => 'users',
  1077. 'SET' => 'group_id=0',
  1078. 'WHERE' => 'group_id=32000'
  1079. );
  1080. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1081. // Add the ban_creator column to the bans table
  1082. $forum_db->add_field('bans', 'ban_creator', 'INT(10) UNSIGNED', false, 0);
  1083. // Remove any hotfix extensions this update supersedes
  1084. $query = array(
  1085. 'SELECT' => 'id',
  1086. 'FROM' => 'extensions',
  1087. 'WHERE' => 'id LIKE \'hotfix_%\' AND version != \''.UPDATE_TO.'\''
  1088. );
  1089. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1090. while ($cur_ext = $forum_db->fetch_assoc($result))
  1091. {
  1092. $query = array(
  1093. 'DELETE' => 'extension_hooks',
  1094. 'WHERE' => 'extension_id = \''.$cur_ext['id'].'\''
  1095. );
  1096. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1097. $query = array(
  1098. 'DELETE' => 'extensions',
  1099. 'WHERE' => 'id = \''.$cur_ext['id'].'\''
  1100. );
  1101. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1102. }
  1103. // Fix linkedIn possible XSS founded in 1.4.0
  1104. if (version_compare($cur_version, '1.3', '>') && version_compare($cur_version, '1.4.1', '<'))
  1105. {
  1106. if ($forum_db->field_exists('users', 'linkedin'))
  1107. {
  1108. $query = array(
  1109. 'SELECT' => 'id, linkedin',
  1110. 'FROM' => 'users',
  1111. 'WHERE' => 'linkedin IS NOT NULL'
  1112. );
  1113. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1114. while ($cur_user = $forum_db->fetch_assoc($result))
  1115. {
  1116. if ($cur_user['linkedin'] != '' &&
  1117. strpos(strtolower($cur_user['linkedin']), 'http://') !== 0 &&
  1118. strpos(strtolower($cur_user['linkedin']), 'https://') !== 0)
  1119. {
  1120. $query = array(
  1121. 'UPDATE' => 'users',
  1122. 'SET' => 'linkedin=\''.$forum_db->escape('http://'.$cur_user['linkedin']).'\'',
  1123. 'WHERE' => 'id = \''.$cur_user['id'].'\''
  1124. );
  1125. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1126. }
  1127. }
  1128. }
  1129. }
  1130. // Should we do charset conversion or not?
  1131. if (version_compare($cur_version, '1.3', '>='))
  1132. $query_str = '?stage=finish';
  1133. elseif (strpos($cur_version, '1.2') === 0 && isset($_GET['convert_charset']))
  1134. $query_str = '?stage=conv_misc&req_old_charset='.$old_charset.'&req_per_page='.PER_PAGE;
  1135. else
  1136. $query_str = '?stage=conv_tables';
  1137. break;
  1138. // Convert config, categories, forums, groups, ranks and censor words
  1139. case 'conv_misc':
  1140. if (strpos($cur_version, '1.2') !== 0)
  1141. {
  1142. $query_str = '?stage=conv_tables';
  1143. break;
  1144. }
  1145. // We need to set names to utf8 before we execute update query
  1146. $forum_db->set_names('utf8');
  1147. // Convert config
  1148. echo 'Converting configuration…'."<br />\n";
  1149. foreach ($forum_config as $conf_name => $conf_value)
  1150. {
  1151. if (convert_to_utf8($conf_value, $old_charset))
  1152. {
  1153. $query = array(
  1154. 'UPDATE' => 'config',
  1155. 'SET' => 'conf_value = \''.$forum_db->escape($conf_value).'\'',
  1156. 'WHERE' => 'conf_name = \''.$conf_name.'\''
  1157. );
  1158. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1159. }
  1160. }
  1161. // Convert categories
  1162. echo 'Converting categories…'."<br />\n";
  1163. $query = array(
  1164. 'SELECT' => 'id, cat_name',
  1165. 'FROM' => 'categories',
  1166. 'ORDER BY' => 'id'
  1167. );
  1168. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1169. while ($cur_item = $forum_db->fetch_assoc($result))
  1170. {
  1171. if (convert_to_utf8($cur_item['cat_name'], $old_charset))
  1172. {
  1173. $query = array(
  1174. 'UPDATE' => 'categories',
  1175. 'SET' => 'cat_name = \''.$forum_db->escape($cur_item['cat_name']).'\'',
  1176. 'WHERE' => 'id = '.$cur_item['id']
  1177. );
  1178. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1179. }
  1180. }
  1181. // Convert forums
  1182. echo 'Converting forums…'."<br />\n";
  1183. $query = array(
  1184. 'SELECT' => 'id, forum_name, forum_desc, moderators',
  1185. 'FROM' => 'forums',
  1186. 'ORDER BY' => 'id'
  1187. );
  1188. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1189. while ($cur_item = $forum_db->fetch_assoc($result))
  1190. {
  1191. $moderators = ($cur_item['moderators'] != '') ? unserialize($cur_item['moderators']) : array();
  1192. $moderators_utf8 = array();
  1193. foreach ($moderators as $mod_username => $mod_user_id)
  1194. {
  1195. convert_to_utf8($mod_username, $old_charset);
  1196. $moderators_utf8[$mod_username] = $mod_user_id;
  1197. }
  1198. if (convert_to_utf8($cur_item['forum_name'], $old_charset) | convert_to_utf8($cur_item['forum_desc'], $old_charset) || $moderators !== $moderators_utf8)
  1199. {
  1200. $cur_item['forum_desc'] = $cur_item['forum_desc'] != '' ? '\''.$forum_db->escape($cur_item['forum_desc']).'\'' : 'NULL';
  1201. $cur_item['moderators'] = !empty($moderators_utf8) ? '\''.$forum_db->escape(serialize($moderators_utf8)).'\'' : 'NULL';
  1202. $query = array(
  1203. 'UPDATE' => 'forums',
  1204. 'SET' => 'forum_name = \''.$forum_db->escape($cur_item['forum_name']).'\', forum_desc = '.$cur_item['forum_desc'].', moderators = '.$cur_item['moderators'],
  1205. 'WHERE' => 'id = '.$cur_item['id']
  1206. );
  1207. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1208. }
  1209. }
  1210. // Convert groups
  1211. echo 'Converting groups…'."<br />\n";
  1212. $query = array(
  1213. 'SELECT' => 'g_id, g_title, g_user_title',
  1214. 'FROM' => 'groups',
  1215. 'ORDER BY' => 'g_id'
  1216. );
  1217. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1218. while ($cur_item = $forum_db->fetch_assoc($result))
  1219. {
  1220. if (convert_to_utf8($cur_item['g_title'], $old_charset) | convert_to_utf8($cur_item['g_user_title'], $old_charset))
  1221. {
  1222. $cur_item['g_user_title'] = $cur_item['g_user_title'] != '' ? '\''.$forum_db->escape($cur_item['g_user_title']).'\'' : 'NULL';
  1223. $query = array(
  1224. 'UPDATE' => 'groups',
  1225. 'SET' => 'g_title = \''.$forum_db->escape($cur_item['g_title']).'\', g_user_title = '.$cur_item['g_user_title'].'',
  1226. 'WHERE' => 'g_id = '.$cur_item['g_id']
  1227. );
  1228. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1229. }
  1230. }
  1231. // Convert ranks
  1232. echo 'Converting ranks…'."<br />\n";
  1233. $query = array(
  1234. 'SELECT' => 'id, rank',
  1235. 'FROM' => 'ranks',
  1236. 'ORDER BY' => 'id'
  1237. );
  1238. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1239. while ($cur_item = $forum_db->fetch_assoc($result))
  1240. {
  1241. if (convert_to_utf8($cur_item['rank'], $old_charset))
  1242. {
  1243. $query = array(
  1244. 'UPDATE' => 'ranks',
  1245. 'SET' => 'rank = \''.$forum_db->escape($cur_item['rank']).'\'',
  1246. 'WHERE' => 'id = '.$cur_item['id']
  1247. );
  1248. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1249. }
  1250. }
  1251. // Convert censor words
  1252. echo 'Converting censor words…'."<br />\n";
  1253. $query = array(
  1254. 'SELECT' => 'id, search_for, replace_with',
  1255. 'FROM' => 'censoring',
  1256. 'ORDER BY' => 'id'
  1257. );
  1258. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1259. while ($cur_item = $forum_db->fetch_assoc($result))
  1260. {
  1261. if (convert_to_utf8($cur_item['search_for'], $old_charset) | convert_to_utf8($cur_item['replace_with'], $old_charset))
  1262. {
  1263. $query = array(
  1264. 'UPDATE' => 'censoring',
  1265. 'SET' => 'search_for = \''.$forum_db->escape($cur_item['search_for']).'\', replace_with = \''.$forum_db->escape($cur_item['replace_with']).'\'',
  1266. 'WHERE' => 'id = '.$cur_item['id']
  1267. );
  1268. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1269. }
  1270. }
  1271. $query_str = '?stage=conv_reports&req_old_charset='.$old_charset.'&req_per_page='.PER_PAGE;
  1272. break;
  1273. // Convert reports
  1274. case 'conv_reports':
  1275. if (strpos($cur_version, '1.2') !== 0)
  1276. {
  1277. $query_str = '?stage=conv_tables';
  1278. break;
  1279. }
  1280. // We need to set names to utf8 before we execute update query
  1281. $forum_db->set_names('utf8');
  1282. // Determine where to start
  1283. if ($start_at == 0)
  1284. {
  1285. // Get the first report ID from the db
  1286. $query = array(
  1287. 'SELECT' => 'id',
  1288. 'FROM' => 'reports',
  1289. 'ORDER BY' => 'id',
  1290. 'LIMIT' => '1'
  1291. );
  1292. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1293. $start_at = $forum_db->result($result);
  1294. if (is_null($start_at) || $start_at === false)
  1295. $start_at = 0;
  1296. }
  1297. $end_at = $start_at + PER_PAGE;
  1298. // Fetch reports to process this cycle
  1299. $query = array(
  1300. 'SELECT' => 'id, message',
  1301. 'FROM' => 'reports',
  1302. 'WHERE' => 'id >= '.$start_at.' AND id < '.$end_at,
  1303. 'ORDER BY' => 'id'
  1304. );
  1305. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1306. while ($cur_item = $forum_db->fetch_assoc($result))
  1307. {
  1308. echo 'Converting report '.$cur_item['id'].'…<br />'."\n";
  1309. if (convert_to_utf8($cur_item['message'], $old_charset))
  1310. {
  1311. $query = array(
  1312. 'UPDATE' => 'reports',
  1313. 'SET' => 'message = \''.$forum_db->escape($cur_item['message']).'\'',
  1314. 'WHERE' => 'id = '.$cur_item['id']
  1315. );
  1316. $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1317. }
  1318. }
  1319. // Check if there is more work to do
  1320. $query = array(
  1321. 'SELECT' => 'id',
  1322. 'FROM' => 'reports',
  1323. 'WHERE' => 'id >= '.$end_at,
  1324. 'ORDER BY' => 'id ASC',
  1325. 'LIMIT' => '1'
  1326. );
  1327. $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  1328. $start_id = $f

Large files files are truncated, but you can click here to view the full file