PageRenderTime 78ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 1ms

/db_update.php

https://bitbucket.org/samovarchik/fluxbb.pe
PHP | 2506 lines | 1703 code | 504 blank | 299 comment | 307 complexity | 77c17742443f40dac5b7fa190fab8fe1 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /**
  3. * @copyright Copyright (C) 2011-2012 Yoory Nagumanov
  4. * @copyright Copyright (C) 2008-2012 FluxBB
  5. * @copyright based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
  6. * @license http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
  7. */
  8. // The FluxBB version this script updates to
  9. define('UPDATE_TO', '1.4.5');
  10. define('UPDATE_TO_PE', '0.1-alpha4');
  11. define('UPDATE_TO_DB_REVISION', 11);
  12. define('UPDATE_TO_PE_DB_REVISION', 18);
  13. define('UPDATE_TO_SI_REVISION', 2);
  14. define('UPDATE_TO_PARSER_REVISION', 2);
  15. define('MIN_PHP_VERSION', '5.3.0');
  16. define('MIN_MYSQL_VERSION', '4.1.2');
  17. define('MIN_PGSQL_VERSION', '7.0.0');
  18. define('PUN_SEARCH_MIN_WORD', 3);
  19. define('PUN_SEARCH_MAX_WORD', 20);
  20. // Enable DEBUG mode by removing // from the following line
  21. //define('PUN_DEBUG', 1);
  22. // Show the Exception stack trace on error page.
  23. // Works only in DEBUG mode (needs PUN_DEBUG to be defined too)
  24. // DO NOT enable this in a production environment! (Leakage of sensitive information may occur!)
  25. //define('SHOW_EXCEPTION_STACK_TRACE', 1);
  26. // This displays all executed queries in the page footer.
  27. // DO NOT enable this in a production environment! (Leakage of sensitive information may occur!)
  28. //define('PUN_SHOW_QUERIES', 1);
  29. // The MySQL connection character set that was used for FluxBB 1.2 - in 99% of cases this should be detected automatically,
  30. // but can be overridden using the below constant if required.
  31. //define('FORUM_DEFAULT_CHARSET', 'latin1');
  32. // The number of items to process per page view (lower this if the update script times out during UTF-8 conversion)
  33. define('PER_PAGE', 300);
  34. // Don't set to UTF-8 until after we've found out what the default character set is
  35. define('FORUM_NO_SET_NAMES', 1);
  36. // Make sure we are running at least MIN_PHP_VERSION
  37. if (!function_exists('version_compare') || version_compare(PHP_VERSION, MIN_PHP_VERSION, '<'))
  38. exit('You are running PHP version '.PHP_VERSION.'. FluxBB '.UPDATE_TO.' requires at least PHP '.MIN_PHP_VERSION.' to run properly. You must upgrade your PHP installation before you can continue.');
  39. define('PUN_ROOT', dirname(__FILE__).'/');
  40. require PUN_ROOT . 'protected/pebase.php';
  41. /* На данный момент, PUN_ROOT и Pe::get('root') содержат абсолютно идентичные значения. Заменять PUN_ROOT на Pe::get('root') в тексте скрипта нет смысла по соображениям эффективности. Аналогичная ситуация у FORUM_CACHE_DIR и Pe::get('forum_cache_dir'). Однако некоторые подмены вида Pe::set('var', $var) ниже в скрипте необходимы для правильной работы функций, находящихся в подключаемых скриптах. */
  42. if (file_exists(PUN_ROOT . 'protected/config/main.php'))
  43. Pe::config(include(PUN_ROOT . 'protected/config/main.php'));
  44. else
  45. throw new PeException('Application is not configured');
  46. /* TODO: попрежнему далеко от совершенства. Даже если база данных обновится корректно, нужно как-то сообщить пользователям, что еще нужно конфиг переместить и изменить его формат :(
  47. C PE на PE должно обновляться нормально. И с FluxBB на FluxBB.PE должно нормально обвновлять структуру базы данных (но не config.php!). Если, конечно, где-то не запрятан глюк.
  48. */
  49. // Attempt to load the configuration file config.php (FluxBB)
  50. if (file_exists(PUN_ROOT.'config.php'))
  51. {
  52. include PUN_ROOT.'config.php';
  53. // If we have the 1.3-legacy constant defined, define the proper 1.4 constant so we don't get an incorrect "need to install" message
  54. if (defined('FORUM'))
  55. define('PUN', FORUM);
  56. // If PUN isn't defined, config.php is missing or corrupt
  57. if (!defined('PUN'))
  58. {
  59. header('Location: install.php');
  60. exit;
  61. }
  62. Pe::config(array(
  63. 'db_type' => $db_type,
  64. 'db_host' => $db_host,
  65. 'db_name' => $db_name,
  66. 'db_username' => $db_username,
  67. 'db_password' => $db_password,
  68. 'db_prefix' => $db_prefix,
  69. 'p_connect' => $p_connect,
  70. 'cookie_name' => $cookie_name,
  71. 'cookie_domain' => $cookie_domain,
  72. 'cookie_path' => $cookie_path,
  73. 'cookie_secure' => $cookie_secure,
  74. 'cookie_seed' => $cookie_seed,
  75. ));
  76. }
  77. // Attempt to load the configuration file config.php (PunBB PE)
  78. else if (file_exists(PUN_ROOT.'include/config.php'))
  79. {
  80. include PUN_ROOT.'include/config.php';
  81. // If we have the 1.3-legacy constant defined, define the proper 1.4 constant so we don't get an incorrect "need to install" message
  82. if (defined('FORUM'))
  83. define('PUN', FORUM);
  84. // If PUN isn't defined, config.php is missing or corrupt
  85. if (!defined('PUN'))
  86. {
  87. header('Location: install.php');
  88. exit;
  89. }
  90. Pe::config(array(
  91. 'db_type' => $db_type,
  92. 'db_host' => $db_host,
  93. 'db_name' => $db_name,
  94. 'db_username' => $db_username,
  95. 'db_password' => $db_password,
  96. 'db_prefix' => $db_prefix,
  97. 'p_connect' => $p_connect,
  98. 'cookie_name' => $cookie_name,
  99. 'cookie_domain' => $cookie_domain,
  100. 'cookie_path' => $cookie_path,
  101. 'cookie_secure' => $cookie_secure,
  102. 'cookie_seed' => $cookie_seed,
  103. ));
  104. }
  105. // Check to see whether FluxBB PE is already installed
  106. else if (file_exists(PUN_ROOT.'protected/config/config.php'))
  107. {
  108. $cfg = require(PUN_ROOT.'protected/config/config.php');
  109. Pe::config($cfg);
  110. extract($cfg);
  111. unset($cfg);
  112. }
  113. else
  114. {
  115. header('Location: install.php');
  116. exit;
  117. }
  118. /* TODO: подумать, есть ли смысл завести отдельный конфиг для db_update.php (как для install.php)?
  119. * Сейчас склоняюсь, что нет, но тем не менее отдельный файл с некоторыми дефайнами отсюда можно сделать.
  120. *
  121. * С другой стороны хотелось бы исключить лишние зависимости, если они есть.
  122. * Насколько я вижу из кода, db_update.php старается как можно меньше использовать
  123. * возможности движка, которым нужна база данных.
  124. */
  125. // Enable debug mode
  126. if (!defined('PUN_DEBUG'))
  127. define('PUN_DEBUG', 1);
  128. // Load the functions script
  129. require PUN_ROOT.'include/functions.php';
  130. // Load UTF-8 functions
  131. require PUN_ROOT.'include/utf8/utf8.php';
  132. // Strip out "bad" UTF-8 characters
  133. forum_remove_bad_characters();
  134. // Reverse the effect of register_globals
  135. forum_unregister_globals();
  136. // Turn on full PHP error reporting
  137. error_reporting(E_ALL);
  138. // Force POSIX locale (to prevent functions such as strtolower() from messing up UTF-8 strings)
  139. setlocale(LC_CTYPE, 'C');
  140. // Turn off magic_quotes_runtime
  141. if (get_magic_quotes_runtime())
  142. set_magic_quotes_runtime(0);
  143. // Strip slashes from GET/POST/COOKIE (if magic_quotes_gpc is enabled)
  144. if (get_magic_quotes_gpc())
  145. {
  146. function stripslashes_array($array)
  147. {
  148. return is_array($array) ? array_map('stripslashes_array', $array) : stripslashes($array);
  149. }
  150. $_GET = stripslashes_array($_GET);
  151. $_POST = stripslashes_array($_POST);
  152. $_COOKIE = stripslashes_array($_COOKIE);
  153. $_REQUEST = stripslashes_array($_REQUEST);
  154. }
  155. // If a cookie name is not specified in config.php, we use the default (forum_cookie)
  156. if (empty($cookie_name))
  157. $cookie_name = 'pun_cookie';
  158. // If the cache directory is not specified, we use the default setting
  159. if (!defined('FORUM_CACHE_DIR'))
  160. define('FORUM_CACHE_DIR', PUN_ROOT.'cache/');
  161. // Turn off PHP time limit
  162. @set_time_limit(0);
  163. // Define a few commonly used constants
  164. define('PUN_UNVERIFIED', 0);
  165. define('PUN_ADMIN', 1);
  166. define('PUN_MOD', 2);
  167. define('PUN_GUEST', 3);
  168. define('PUN_MEMBER', 4);
  169. // Load DB abstraction layer and try to connect
  170. /* @var $db \pe\dblayer\DBLayer */
  171. $db = Pe::get('db');
  172. // Check what the default character set is - since 1.2 didn't specify any we will use whatever the default was (usually latin1)
  173. $old_connection_charset = defined('FORUM_DEFAULT_CHARSET') ? FORUM_DEFAULT_CHARSET : $db->get_names();
  174. // Set the connection to UTF-8 now
  175. $db->set_names('utf8');
  176. // Get the forum config
  177. $result = $db->query('SELECT * FROM '.$db->prefix.'config');
  178. while ($cur_config_item = $db->fetch_row($result))
  179. $pun_config[$cur_config_item[0]] = $cur_config_item[1];
  180. // Временное решение:
  181. Pe::set('pun_config', $pun_config); // Чтобы не обрабатывался вызов к CacheLoader (и может быть CacheGenerator)
  182. // Load language file
  183. $default_lang = $pun_config['o_default_lang'];
  184. if (!file_exists(PUN_ROOT.'lang/'.$default_lang.'/update.php'))
  185. $default_lang = 'English';
  186. $lang_update = require PUN_ROOT.'lang/'.$default_lang.'/update.php';
  187. // Set the $pun_user to satisfy the dependences
  188. Pe::set('pun_user', array('lang' => $default_lang));
  189. // Check current version
  190. $cur_version = $pun_config['o_cur_version'];
  191. if (version_compare($cur_version, '1.2', '<'))
  192. throw new PeException(sprintf($lang_update['Version mismatch error'], $db_name));
  193. // Do some DB type specific checks
  194. $mysql = false;
  195. switch ($db_type)
  196. {
  197. case 'mysql':
  198. case 'mysqli':
  199. case 'mysql_innodb':
  200. case 'mysqli_innodb':
  201. $mysql_info = $db->get_version();
  202. if (version_compare($mysql_info['version'], MIN_MYSQL_VERSION, '<'))
  203. throw new PeDbException(sprintf($lang_update['You are running error'], 'MySQL', $mysql_info['version'], UPDATE_TO, MIN_MYSQL_VERSION));
  204. $mysql = true;
  205. break;
  206. case 'pgsql':
  207. $pgsql_info = $db->get_version();
  208. if (version_compare($pgsql_info['version'], MIN_PGSQL_VERSION, '<'))
  209. throw new PeDbException(sprintf($lang_update['You are running error'], 'PostgreSQL', $pgsql_info['version'], UPDATE_TO, MIN_PGSQL_VERSION));
  210. break;
  211. }
  212. // Check the database, search index and parser revision and the current version
  213. if (isset($pun_config['o_database_revision']) && $pun_config['o_database_revision'] >= UPDATE_TO_DB_REVISION &&
  214. isset($pun_config['o_pe_database_revision']) && $pun_config['o_pe_database_revision'] >= UPDATE_TO_PE_DB_REVISION&&
  215. isset($pun_config['o_searchindex_revision']) && $pun_config['o_searchindex_revision'] >= UPDATE_TO_SI_REVISION &&
  216. isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION &&
  217. version_compare($pun_config['o_cur_version'], UPDATE_TO, '>=') &&
  218. version_compare($pun_config['o_cur_pe_version'], UPDATE_TO_PE, '>='))
  219. throw new PeException($lang_update['No update error']);
  220. $default_style = $pun_config['o_default_style'];
  221. if (!file_exists(PUN_ROOT.'style/'.$default_style.'.css'))
  222. $default_style = 'Air';
  223. // Start a session, used to queue up errors if duplicate users occur when converting from FluxBB v1.2.
  224. session_start();
  225. //
  226. // Determines whether $str is UTF-8 encoded or not
  227. //
  228. function seems_utf8($str)
  229. {
  230. $str_len = strlen($str);
  231. for ($i = 0; $i < $str_len; ++$i)
  232. {
  233. if (ord($str[$i]) < 0x80) continue; # 0bbbbbbb
  234. else if ((ord($str[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
  235. else if ((ord($str[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
  236. else if ((ord($str[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
  237. else if ((ord($str[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
  238. else if ((ord($str[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
  239. else return false; # Does not match any model
  240. for ($j = 0; $j < $n; ++$j) # n bytes matching 10bbbbbb follow ?
  241. {
  242. if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80))
  243. return false;
  244. }
  245. }
  246. return true;
  247. }
  248. //
  249. // Translates the number from a HTML numeric entity into an UTF-8 character
  250. //
  251. function dcr2utf8($src)
  252. {
  253. $dest = '';
  254. if ($src < 0)
  255. return false;
  256. else if ($src <= 0x007f)
  257. $dest .= chr($src);
  258. else if ($src <= 0x07ff)
  259. {
  260. $dest .= chr(0xc0 | ($src >> 6));
  261. $dest .= chr(0x80 | ($src & 0x003f));
  262. }
  263. else if ($src == 0xFEFF)
  264. {
  265. // nop -- zap the BOM
  266. }
  267. else if ($src >= 0xD800 && $src <= 0xDFFF)
  268. {
  269. // found a surrogate
  270. return false;
  271. }
  272. else if ($src <= 0xffff)
  273. {
  274. $dest .= chr(0xe0 | ($src >> 12));
  275. $dest .= chr(0x80 | (($src >> 6) & 0x003f));
  276. $dest .= chr(0x80 | ($src & 0x003f));
  277. }
  278. else if ($src <= 0x10ffff)
  279. {
  280. $dest .= chr(0xf0 | ($src >> 18));
  281. $dest .= chr(0x80 | (($src >> 12) & 0x3f));
  282. $dest .= chr(0x80 | (($src >> 6) & 0x3f));
  283. $dest .= chr(0x80 | ($src & 0x3f));
  284. }
  285. else
  286. {
  287. // out of range
  288. return false;
  289. }
  290. return $dest;
  291. }
  292. //
  293. // Attempts to convert $str from $old_charset to UTF-8. Also converts HTML entities (including numeric entities) to UTF-8 characters
  294. //
  295. function convert_to_utf8(&$str, $old_charset)
  296. {
  297. if ($str === null || $str == '')
  298. return false;
  299. $save = $str;
  300. // Replace literal entities (for non-UTF-8 compliant html_entity_encode)
  301. if (version_compare(PHP_VERSION, '5.0.0', '<') && $old_charset == 'ISO-8859-1' || $old_charset == 'ISO-8859-15')
  302. $str = html_entity_decode($str, ENT_QUOTES, $old_charset);
  303. if ($old_charset != 'UTF-8' && !seems_utf8($str))
  304. {
  305. if (function_exists('iconv'))
  306. $str = iconv($old_charset == 'ISO-8859-1' ? 'WINDOWS-1252' : 'ISO-8859-1', 'UTF-8', $str);
  307. else if (function_exists('mb_convert_encoding'))
  308. $str = mb_convert_encoding($str, 'UTF-8', $old_charset == 'ISO-8859-1' ? 'WINDOWS-1252' : 'ISO-8859-1');
  309. else if ($old_charset == 'ISO-8859-1')
  310. $str = utf8_encode($str);
  311. }
  312. // Replace literal entities (for UTF-8 compliant html_entity_encode)
  313. if (version_compare(PHP_VERSION, '5.0.0', '>='))
  314. $str = html_entity_decode($str, ENT_QUOTES, 'UTF-8');
  315. // Replace numeric entities
  316. $str = preg_replace_callback('/&#([0-9]+);/', 'utf8_callback_1', $str);
  317. $str = preg_replace_callback('/&#x([a-f0-9]+);/i', 'utf8_callback_2', $str);
  318. // Remove "bad" characters
  319. $str = remove_bad_characters($str);
  320. return ($save != $str);
  321. }
  322. function utf8_callback_1($matches)
  323. {
  324. return dcr2utf8($matches[1]);
  325. }
  326. function utf8_callback_2($matches)
  327. {
  328. return dcr2utf8(hexdec($matches[1]));
  329. }
  330. //
  331. // Alter a table to be utf8. MySQL only
  332. // Function based on update_convert_table_utf8() from the Drupal project (http://drupal.org/)
  333. //
  334. function alter_table_utf8($table)
  335. {
  336. global $mysql, $db;
  337. static $types;
  338. if (!$mysql)
  339. return;
  340. if (!isset($types))
  341. {
  342. $types = array(
  343. 'char' => 'binary',
  344. 'varchar' => 'varbinary',
  345. 'tinytext' => 'tinyblob',
  346. 'mediumtext' => 'mediumblob',
  347. 'text' => 'blob',
  348. 'longtext' => 'longblob'
  349. );
  350. }
  351. // Set table default charset to utf8
  352. $db->query('ALTER TABLE '.$table.' CHARACTER SET utf8');
  353. // Find out which columns need converting and build SQL statements
  354. $result = $db->query('SHOW FULL COLUMNS FROM '.$table);
  355. while ($cur_column = $db->fetch_assoc($result))
  356. {
  357. if ($cur_column['Collation'] === null)
  358. continue;
  359. list($type) = explode('(', $cur_column['Type']);
  360. if (isset($types[$type]) && strpos($cur_column['Collation'], 'utf8') === false)
  361. {
  362. $allow_null = ($cur_column['Null'] == 'YES');
  363. $collate = (substr($cur_column['Collation'], -3) == 'bin') ? 'utf8_bin' : 'utf8_general_ci';
  364. $db->alter_field($table, $cur_column['Field'], preg_replace('/'.$type.'/i', $types[$type], $cur_column['Type']), $allow_null, $cur_column['Default'], null, true);
  365. $db->alter_field($table, $cur_column['Field'], $cur_column['Type'].' CHARACTER SET utf8 COLLATE '.$collate, $allow_null, $cur_column['Default'], null, true);
  366. }
  367. }
  368. }
  369. //
  370. // Safely converts text type columns into utf8
  371. // If finished returns true, otherwise returns $end_at
  372. //
  373. function convert_table_utf8($table, $callback, $old_charset, $key = null, $start_at = null, $error_callback = null)
  374. {
  375. global $mysql, $db, $old_connection_charset;
  376. $finished = true;
  377. $end_at = 0;
  378. if ($mysql)
  379. {
  380. // Only set up the tables if we are doing this in 1 go, or its the first go
  381. if ($start_at === null || $start_at == 0)
  382. {
  383. // Drop any temp table that exists, in-case it's left over from a failed update
  384. $db->drop_table($table.'_utf8', true);
  385. // Copy the table
  386. $db->query('CREATE TABLE '.$table.'_utf8 LIKE '.$table);
  387. // Set table default charset to utf8
  388. alter_table_utf8($table.'_utf8');
  389. }
  390. // Change to the old character set so MySQL doesn't attempt to perform conversion on the data from the old table
  391. $db->set_names($old_connection_charset);
  392. // Move & Convert everything
  393. $result = $db->query('SELECT * FROM '.$table.($start_at === null ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.($start_at === null ? '' : ' LIMIT '.PER_PAGE), false);
  394. // Change back to utf8 mode so we can insert it into the new table
  395. $db->set_names('utf8');
  396. while ($cur_item = $db->fetch_assoc($result))
  397. {
  398. $cur_item = call_user_func($callback, $cur_item, $old_charset);
  399. $temp = array();
  400. foreach ($cur_item as $idx => $value)
  401. $temp[$idx] = $value === null ? 'NULL' : '\''.$db->escape($value).'\'';
  402. try
  403. {
  404. $db->query('INSERT INTO '.$table.'_utf8('.implode(',', array_keys($temp)).') VALUES ('.implode(',', array_values($temp)).')');
  405. }
  406. catch (PeDbException $ex)
  407. {
  408. if ($error_callback === null)
  409. throw $ex;
  410. else
  411. call_user_func($error_callback, $cur_item);
  412. }
  413. $end_at = $cur_item[$key];
  414. }
  415. // If we aren't doing this all in 1 go and $end_at has a value (i.e. we have processed at least 1 row), figure out if we have more to do or not
  416. if ($start_at !== null && $end_at > 0)
  417. {
  418. $result = $db->query('SELECT 1 FROM '.$table.' WHERE '.$key.'>'.$end_at.' ORDER BY '.$key.' ASC LIMIT 1');
  419. $finished = $db->num_rows($result) == 0;
  420. }
  421. // Only swap the tables if we are doing this in 1 go, or its the last go
  422. if ($finished)
  423. {
  424. // Delete old table
  425. $db->drop_table($table, true);
  426. // Rename table
  427. $db->query('ALTER TABLE '.$table.'_utf8 RENAME '.$table);
  428. return true;
  429. }
  430. return $end_at;
  431. }
  432. else
  433. {
  434. // Convert everything
  435. $result = $db->query('SELECT * FROM '.$table.($start_at === null ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.($start_at === null ? '' : ' LIMIT '.PER_PAGE));
  436. while ($cur_item = $db->fetch_assoc($result))
  437. {
  438. $cur_item = call_user_func($callback, $cur_item, $old_charset);
  439. $temp = array();
  440. foreach ($cur_item as $idx => $value)
  441. $temp[] = $idx.'='.($value === null ? 'NULL' : '\''.$db->escape($value).'\'');
  442. if (!empty($temp))
  443. $db->query('UPDATE '.$table.' SET '.implode(', ', $temp).' WHERE '.$key.'=\''.$db->escape($cur_item[$key]).'\'');
  444. $end_at = $cur_item[$key];
  445. }
  446. if ($start_at !== null && $end_at > 0)
  447. {
  448. $result = $db->query('SELECT 1 FROM '.$table.' WHERE '.$key.'>'.$end_at.' ORDER BY '.$key.' ASC LIMIT 1');
  449. if ($db->num_rows($result) == 0)
  450. return true;
  451. return $end_at;
  452. }
  453. return true;
  454. }
  455. }
  456. header('Content-type: text/html; charset=utf-8');
  457. // Empty all output buffers and stop buffering
  458. while (@ob_end_clean());
  459. $stage = isset($_REQUEST['stage']) ? $_REQUEST['stage'] : '';
  460. $old_charset = isset($_REQUEST['req_old_charset']) ? str_replace('ISO8859', 'ISO-8859', strtoupper($_REQUEST['req_old_charset'])) : 'ISO-8859-1';
  461. $start_at = isset($_REQUEST['start_at']) ? intval($_REQUEST['start_at']) : 0;
  462. $query_str = '';
  463. // Show form
  464. if (empty($stage))
  465. {
  466. ?>
  467. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  468. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
  469. <head>
  470. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  471. <title><?php echo $lang_update['Update'] ?></title>
  472. <link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
  473. </head>
  474. <body onload="document.getElementById('install').req_db_type.focus();document.getElementById('install').start.disabled=false;">
  475. <div id="pundb_update" class="pun">
  476. <div class="top-box"><div><!-- Top Corners --></div></div>
  477. <div class="punwrap">
  478. <div id="brdheader" class="block">
  479. <div class="box">
  480. <div id="brdtitle" class="inbox">
  481. <h1><span><?php echo $lang_update['Update'] ?></span></h1>
  482. <div id="brddesc"><p><?php echo $lang_update['Update message'] ?></p><p><strong><?php echo $lang_update['Note']; ?></strong> <?php echo $lang_update['Members message']; ?></p></div>
  483. </div>
  484. </div>
  485. </div>
  486. <div id="brdmain">
  487. <div class="blockform">
  488. <h2><span><?php echo $lang_update['Update'] ?></span></h2>
  489. <div class="box">
  490. <form method="post" action="db_update.php">
  491. <input type="hidden" name="stage" value="start" />
  492. <div class="inform">
  493. <fieldset>
  494. <legend><?php echo $lang_update['Administrator only'] ?></legend>
  495. <div class="infldset">
  496. <p><?php echo $lang_update['Database password info'] ?></p>
  497. <p><strong><?php echo $lang_update['Note']; ?></strong> <?php echo $lang_update['Database password note'] ?></p>
  498. <label class="required"><strong><?php echo $lang_update['Database password'] ?> <span><?php echo $lang_update['Required'] ?></span></strong><br /><input type="password" id="req_db_pass" name="req_db_pass" /><br /></label>
  499. </div>
  500. </fieldset>
  501. </div>
  502. <div class="inform">
  503. <div class="forminfo">
  504. <p><?php echo $lang_update['Intro 1'] ?></p>
  505. <p><?php echo $lang_update['Intro 2'] ?></p>
  506. <?php
  507. if (strpos($cur_version, '1.2') === 0)
  508. {
  509. if (!function_exists('iconv') && !function_exists('mb_convert_encoding'))
  510. {
  511. ?>
  512. <p><?php echo $lang_update['No charset conversion'] ?></p>
  513. <?php
  514. }
  515. ?>
  516. </div>
  517. </div>
  518. <div class="inform">
  519. <div class="forminfo">
  520. <p><?php echo $lang_update['Enable conversion'] ?></p>
  521. <p><?php echo $lang_update['Current character set'] ?></p>
  522. </div>
  523. <fieldset>
  524. <legend><?php echo $lang_update['Charset conversion'] ?></legend>
  525. <div class="infldset">
  526. <div class="rbox">
  527. <label><input type="checkbox" name="convert_charset" value="1" checked="checked" /><?php echo $lang_update['Enable conversion label'] ?><br /></label>
  528. </div>
  529. <label>
  530. <strong><?php echo $lang_update['Current character set label'] ?></strong><br /><?php echo $lang_update['Current character set info'] ?><br />
  531. <input type="text" name="req_old_charset" size="12" maxlength="20" value="<?php echo $old_charset ?>" /><br />
  532. </label>
  533. </div>
  534. </fieldset>
  535. <?php
  536. }
  537. else
  538. echo "\t\t\t\t".'</div>'."\n";
  539. ?>
  540. </div>
  541. <p class="buttons"><input type="submit" name="start" value="<?php echo $lang_update['Start update'] ?>" /></p>
  542. </form>
  543. </div>
  544. </div>
  545. </div>
  546. </div>
  547. <div class="end-box"><div><!-- Bottom Corners --></div></div>
  548. </div>
  549. </body>
  550. </html>
  551. <?php
  552. $db->end_transaction();
  553. $db->close();
  554. exit;
  555. }
  556. // Read the lock file
  557. $lock = file_exists(FORUM_CACHE_DIR.'db_update.lock') ? trim(file_get_contents(FORUM_CACHE_DIR.'db_update.lock')) : false;
  558. $lock_error = false;
  559. // Generate or fetch the UID - this confirms we have a valid admin
  560. if (isset($_POST['req_db_pass']))
  561. {
  562. $req_db_pass = strtolower(trim($_POST['req_db_pass']));
  563. switch ($db_type)
  564. {
  565. // For SQLite we compare against the database file name, since the password is left blank
  566. case 'sqlite':
  567. if ($req_db_pass != strtolower($db_name))
  568. throw new PeAccessException(sprintf($lang_update['Invalid file error'], 'config.php'));
  569. break;
  570. // For everything else, check the password matches
  571. default:
  572. if ($req_db_pass != strtolower($db_password))
  573. throw new PeAccessException(sprintf($lang_update['Invalid password error'], 'config.php'));
  574. break;
  575. }
  576. // Generate a unique id to identify this session, only if this is a valid session
  577. $uid = pun_hash($req_db_pass.'|'.uniqid(rand(), true));
  578. if ($lock) // We already have a lock file
  579. $lock_error = true;
  580. else // Create the lock file
  581. {
  582. $fh = @fopen(FORUM_CACHE_DIR.'db_update.lock', 'wb');
  583. if (!$fh)
  584. throw new PeAccessException(sprintf($lang_update['Unable to lock error'], 'cache'));
  585. fwrite($fh, $uid);
  586. fclose($fh);
  587. }
  588. }
  589. else if (isset($_GET['uid']))
  590. {
  591. $uid = trim($_GET['uid']);
  592. if (!$lock || $lock != $uid) // The lock doesn't exist or doesn't match the given UID
  593. $lock_error = true;
  594. }
  595. else
  596. throw new PeAccessException($lang_update['No password error']);
  597. // If there is an error with the lock file
  598. if ($lock_error)
  599. throw new PeAccessException(sprintf($lang_update['Script runs error'], FORUM_CACHE_DIR.'db_update.lock'));
  600. switch ($stage)
  601. {
  602. // Start by updating the database structure
  603. case 'start':
  604. $query_str = '?stage=preparse_posts';
  605. // If we don't need to update the database, skip this stage
  606. if (isset($pun_config['o_database_revision']) && $pun_config['o_database_revision'] >= UPDATE_TO_DB_REVISION && isset($pun_config['o_pe_database_revision']) && $pun_config['o_pe_database_revision'] >= UPDATE_TO_PE_DB_REVISION)
  607. break;
  608. // Make all email fields VARCHAR(80)
  609. $db->alter_field('bans', 'email', 'VARCHAR(80)', true);
  610. $db->alter_field('posts', 'poster_email', 'VARCHAR(80)', true);
  611. $db->alter_field('users', 'email', 'VARCHAR(80)', false, '');
  612. $db->alter_field('users', 'jabber', 'VARCHAR(80)', true);
  613. $db->alter_field('users', 'msn', 'VARCHAR(80)', true);
  614. $db->alter_field('users', 'activate_string', 'VARCHAR(80)', true);
  615. // Make all IP fields VARCHAR(39) to support IPv6
  616. $db->alter_field('posts', 'poster_ip', 'VARCHAR(39)', true);
  617. $db->alter_field('users', 'registration_ip', 'VARCHAR(39)', false, '0.0.0.0');
  618. // Make the message field MEDIUMTEXT to allow proper conversion of 65535 character posts to UTF-8
  619. $db->alter_field('posts', 'message', 'MEDIUMTEXT', true);
  620. // Add the kind field to the forums table
  621. $db->add_field('forums', 'kind', 'TINYINT(1)', false, Pe::get('pun_kind_forum'), 'cat_id');
  622. // Limited support for PunBB Power Edition
  623. if ($db->field_exists('categories', 'kind'))
  624. {
  625. $result = $db->query('SELECT id, kind FROM '.$db->prefix.'categories');
  626. while ($cur_cat = $db->fetch_assoc($result))
  627. $db->query('UPDATE '.$db->prefix.'forums SET kind='.$cur_cat['kind'].' WHERE cat_id='.$cur_cat['id']);
  628. $db->drop_field('categories', 'kind');
  629. }
  630. // Add the DST option to the users table
  631. $db->add_field('users', 'dst', 'TINYINT(1)', false, 0, 'timezone');
  632. // Add the last_post field to the online table
  633. $db->add_field('online', 'last_post', 'INT(10) UNSIGNED', true, null, null);
  634. // Add the last_search field to the online table
  635. $db->add_field('online', 'last_search', 'INT(10) UNSIGNED', true, null, null);
  636. // Add the last_search column to the users table
  637. $db->add_field('users', 'last_search', 'INT(10) UNSIGNED', true, null, 'last_post');
  638. // Add the file_bonus column to the users table
  639. $db->add_field('users', 'file_bonus', 'INT(10)', false, 0);
  640. // Add the num_files column to the users table
  641. $db->add_field('users', 'num_files', 'INT(10)', false, 0);
  642. // Add the total_files column to the users table
  643. $db->add_field('users', 'total_files', 'INT(10)', false, 0);
  644. // Add the last_mark_read column to the users table
  645. $db->add_field('users', 'last_mark_read', 'INT(10)', false, 0);
  646. // Add the g_file_download column to the groups table
  647. $db->add_field('groups', 'g_file_download', 'TINYINT(1)', false, 1);
  648. // Add the g_file_upload column to the groups table
  649. $db->add_field('groups', 'g_file_upload', 'TINYINT(1)', false, 0);
  650. // Add the g_file_private column to the groups table
  651. $db->add_field('groups', 'g_file_private', 'TINYINT(1)', false, 0);
  652. // Add the g_file_limit column to the groups table
  653. $db->add_field('groups', 'g_file_limit', 'INT(10)', false, 0);
  654. // Add the g_file_limit_mode column to the groups table
  655. $db->add_field('groups', 'g_file_limit_mode', 'TINYINT(1)', false, 1);
  656. // Add the g_file_bonus_mode column to the groups table
  657. $db->add_field('groups', 'g_file_bonus_mode', 'TINYINT(1)', false, 0);
  658. // Update unchangeable File Upload settings for Administrator group
  659. $db->query('UPDATE '.$db->prefix.'groups SET g_file_download=1, g_file_upload=1, g_file_private=1, g_file_limit=999, g_file_limit_mode=0 WHERE g_id=1');
  660. // Add the g_see_hidden_text column to the groups table
  661. $db->add_field('groups', 'g_see_hidden_text', 'TINYINT(1)', false, 0);
  662. // Make admin able to see hidden text
  663. $db->query('UPDATE '.$db->prefix.'groups SET g_see_hidden_text=1 WHERE g_id=1');
  664. // Add the cat_desc column to the categories table
  665. $db->add_field('categories', 'cat_desc', 'TEXT', false, '');
  666. // Add the cat_enabled column to the categories table
  667. $db->add_field('categories', 'cat_enabled', 'TINYINT(1)', false, 1);
  668. // Drop use_avatar column from users table
  669. $db->drop_field('users', 'use_avatar');
  670. // Drop save_pass column from users table
  671. $db->drop_field('users', 'save_pass');
  672. // Drop g_edit_subjects_interval column from groups table
  673. $db->drop_field('groups', 'g_edit_subjects_interval');
  674. // Add PE version
  675. if (!array_key_exists('o_cur_pe_version', $pun_config))
  676. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_cur_pe_version\', \'0\')');
  677. // Add database revision number
  678. if (!array_key_exists('o_database_revision', $pun_config))
  679. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_database_revision\', \'0\')');
  680. // Add PE database revision number
  681. if (!array_key_exists('o_pe_database_revision', $pun_config))
  682. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_pe_database_revision\', \'0\')');
  683. // Add search index revision number
  684. if (!array_key_exists('o_searchindex_revision', $pun_config))
  685. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_searchindex_revision\', \'0\')');
  686. // Add parser revision number
  687. if (!array_key_exists('o_parser_revision', $pun_config))
  688. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_parser_revision\', \'0\')');
  689. // Add default email setting option
  690. if (!array_key_exists('o_default_email_setting', $pun_config))
  691. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_default_email_setting\', \'1\')');
  692. // Make sure we have o_additional_navlinks (was added in 1.2.1)
  693. if (!array_key_exists('o_additional_navlinks', $pun_config))
  694. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_additional_navlinks\', \'\')');
  695. // Insert new config option o_topic_views
  696. if (!array_key_exists('o_topic_views', $pun_config))
  697. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_topic_views\', \'1\')');
  698. // Insert new config option o_signatures
  699. if (!array_key_exists('o_signatures', $pun_config))
  700. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_signatures\', \'1\')');
  701. // Insert new config option o_smtp_ssl
  702. if (!array_key_exists('o_smtp_ssl', $pun_config))
  703. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_smtp_ssl\', \'0\')');
  704. // Insert new config option o_default_dst
  705. if (!array_key_exists('o_default_dst', $pun_config))
  706. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_default_dst\', \'0\')');
  707. // Insert new config option o_quote_depth
  708. if (!array_key_exists('o_quote_depth', $pun_config))
  709. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_quote_depth\', \'3\')');
  710. // Insert new config option o_feed_type
  711. if (!array_key_exists('o_feed_type', $pun_config))
  712. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_feed_type\', \'2\')');
  713. // Insert new config option o_feed_ttl
  714. if (!array_key_exists('o_feed_ttl', $pun_config))
  715. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_feed_ttl\', \'0\')');
  716. // Insert new config option file_upload_path
  717. if (!array_key_exists('file_upload_path', $pun_config))
  718. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'file_upload_path\', \'upload/\')');
  719. // Insert new config option file_current_subpath
  720. if (!array_key_exists('file_current_subpath', $pun_config))
  721. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'file_current_subpath\', \'\')');
  722. // Insert new config option file_max_subpath_files
  723. if (!array_key_exists('file_max_subpath_files', $pun_config))
  724. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'file_max_subpath_files\', \'10000\')');
  725. // Insert new config option file_friendly_url
  726. if (!array_key_exists('file_friendly_url', $pun_config))
  727. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'file_friendly_url\', \'0\')');
  728. // Insert new config option file_thumb_path
  729. if (!array_key_exists('file_thumb_path', $pun_config))
  730. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'file_thumb_path\', \'img/thumb/\')');
  731. // Insert new config option file_preview_path
  732. if (!array_key_exists('file_preview_path', $pun_config))
  733. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'file_preview_path\', \'img/preview/\')');
  734. // Insert new config option file_allowed_ext
  735. if (!array_key_exists('file_allowed_ext', $pun_config))
  736. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'file_allowed_ext\', \'gif,png,jpg,jpeg,zip,rar\')');
  737. // Insert new config option file_image_ext
  738. if (!array_key_exists('file_image_ext', $pun_config))
  739. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'file_image_ext\', \'gif,png,jpg,jpeg\')');
  740. // Insert new config option file_max_size
  741. if (!array_key_exists('file_max_size', $pun_config))
  742. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'file_max_size\', \'2097152\')');
  743. // Insert new config option file_max_dimension
  744. if (!array_key_exists('file_max_dimension', $pun_config))
  745. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'file_max_dimension\', \'3000\')');
  746. // Insert new config option file_preview_dimension
  747. if (!array_key_exists('file_preview_dimension', $pun_config))
  748. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'file_preview_dimension\', \'500\')');
  749. // Insert new config option file_thumb_dimension
  750. if (!array_key_exists('file_thumb_dimension', $pun_config))
  751. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'file_thumb_dimension\', \'100\')');
  752. // Insert new config option pe_enabled_kinds
  753. if (!array_key_exists('pe_enabled_kinds', $pun_config))
  754. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'pe_enabled_kinds\', \''.$db->escape(serialize(array(Pe::get('pun_kind_forum')))).'\')');
  755. // Insert new config option pe_navigation_type
  756. if (!array_key_exists('pe_navigation_type', $pun_config))
  757. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'pe_navigation_type\', \'1\')');
  758. // Insert new config option pe_default_kind
  759. if (!array_key_exists('pe_default_kind', $pun_config))
  760. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'pe_default_kind\', \''.Pe::get('pun_kind_forum').'\')');
  761. // Insert new config option o_topics_track_timeout
  762. if (!array_key_exists('o_topics_track_timeout', $pun_config))
  763. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_topics_track_timeout\', \'15\')');
  764. // Insert new config option o_new_topics_timeout
  765. if (!array_key_exists('o_new_topics_timeout', $pun_config))
  766. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_new_topics_timeout\', \'5\')');
  767. // Insert new config option pe_default_category
  768. if (!array_key_exists('pe_default_category', $pun_config))
  769. {
  770. // Fetch the first real category ID form the DB or use 0 if none.
  771. $result = $db->query('SELECT id FROM '.$db->prefix.'categories ORDER BY id ASC LIMIT 1');
  772. $def_cat = $db->num_rows($result) ? $db->result($result) : 0;
  773. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'pe_default_category\', \''.$def_cat.'\')');
  774. }
  775. // Insert new config option pe_default_index
  776. if (!array_key_exists('pe_default_index', $pun_config))
  777. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'pe_default_index\', \'0\')');
  778. // Insert config option o_base_url which was removed in 1.3
  779. if (!array_key_exists('o_base_url', $pun_config))
  780. {
  781. // If it isn't in $pun_config['o_base_url'] it should be in $base_url, but just in-case it isn't we can make a guess at it
  782. if (!isset($base_url))
  783. {
  784. // Make an educated guess regarding base_url
  785. $base_url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://'; // protocol
  786. $base_url .= preg_replace('/:(80|443)$/', '', $_SERVER['HTTP_HOST']); // host[:port]
  787. $base_url .= str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'])); // path
  788. }
  789. if (substr($base_url, -1) == '/')
  790. $base_url = substr($base_url, 0, -1);
  791. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_base_url\', \''.$db->escape($base_url).'\')');
  792. }
  793. if (strpos($cur_version, '1.2') === 0)
  794. {
  795. // Groups are almost the same as 1.2:
  796. // unverified: 32000 -> 0
  797. $db->query('UPDATE '.$db->prefix.'users SET group_id = 0 WHERE group_id = 32000');
  798. }
  799. else if (strpos($cur_version, '1.3') === 0)
  800. {
  801. // Groups have changed quite a lot from 1.3:
  802. // unverified: 0 -> 0
  803. // admin: 1 -> 1
  804. // mod: ? -> 2
  805. // guest: 2 -> 3
  806. // member: ? -> 4
  807. $result = $db->query('SELECT MAX(g_id) + 1 FROM '.$db->prefix.'groups');
  808. $temp_id = $db->result($result);
  809. $result = $db->query('SELECT g_id FROM '.$db->prefix.'groups WHERE g_moderator = 1 AND g_id > 1 LIMIT 1');
  810. if ($db->num_rows($result))
  811. $mod_gid = $db->result($result);
  812. else
  813. {
  814. $db->query('INSERT INTO '.$db->prefix.'groups (g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood) VALUES('."'Moderators', 'Moderator', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0)");
  815. $mod_gid = $db->insert_id();
  816. }
  817. $member_gid = $pun_config['o_default_user_group'];
  818. // move the mod group to a temp place
  819. $db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$temp_id.' WHERE g_id = '.$mod_gid);
  820. $db->query('UPDATE '.$db->prefix.'users SET group_id = '.$temp_id.' WHERE group_id = '.$mod_gid);
  821. $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$temp_id.' WHERE group_id = '.$mod_gid);
  822. if ($member_gid == $mod_gid) $member_gid = $temp_id;
  823. // move whoever is in 3 to a spare slot
  824. $db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$mod_gid.' WHERE g_id = 3');
  825. $db->query('UPDATE '.$db->prefix.'users SET group_id = '.$mod_gid.' WHERE group_id = 3');
  826. $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$mod_gid.' WHERE group_id = 3');
  827. if ($member_gid == 3) $member_gid = $mod_gid;
  828. // move guest to 3
  829. $db->query('UPDATE '.$db->prefix.'groups SET g_id = 3 WHERE g_id = 2');
  830. $db->query('UPDATE '.$db->prefix.'users SET group_id = 3 WHERE group_id = 2');
  831. $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = 3 WHERE group_id = 2');
  832. if ($member_gid == 2) $member_gid = 3;
  833. // move mod group in temp place to 2
  834. $db->query('UPDATE '.$db->prefix.'groups SET g_id = 2 WHERE g_id = '.$temp_id);
  835. $db->query('UPDATE '.$db->prefix.'users SET group_id = 2 WHERE group_id = '.$temp_id);
  836. $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = 2 WHERE group_id = '.$temp_id);
  837. if ($member_gid == $temp_id) $member_gid = 2;
  838. // Only move stuff around if it isn't already in the right place
  839. if ($member_gid != $mod_gid || $member_gid != 4)
  840. {
  841. // move members to temp place
  842. $db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$temp_id.' WHERE g_id = '.$member_gid);
  843. $db->query('UPDATE '.$db->prefix.'users SET group_id = '.$temp_id.' WHERE group_id = '.$member_gid);
  844. $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$temp_id.' WHERE group_id = '.$member_gid);
  845. // move whoever is in 4 to members place
  846. $db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$member_gid.' WHERE g_id = 4');
  847. $db->query('UPDATE '.$db->prefix.'users SET group_id = '.$member_gid.' WHERE group_id = 4');
  848. $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$member_gid.' WHERE group_id = 4');
  849. // move members in temp place to 4
  850. $db->query('UPDATE '.$db->prefix.'groups SET g_id = 4 WHERE g_id = '.$temp_id);
  851. $db->query('UPDATE '.$db->prefix.'users SET group_id = 4 WHERE group_id = '.$temp_id);
  852. $db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = 4 WHERE group_id = '.$temp_id);
  853. }
  854. $db->query('UPDATE '.$db->prefix.'config SET conf_value=\''.$member_gid.'\' WHERE conf_name=\'o_default_user_group\'');
  855. }
  856. // Server time zone is now simply the default time zone
  857. if (!array_key_exists('o_default_timezone', $pun_config))
  858. $db->query('UPDATE '.$db->prefix.'config SET conf_name = \'o_default_timezone\' WHERE conf_name = \'o_server_timezone\'');
  859. // Increase visit timeout to 30 minutes (only if it hasn't been changed from the default)
  860. if (!array_key_exists('o_database_revision', $pun_config) && $pun_config['o_timeout_visit'] == '600')
  861. $db->query('UPDATE '.$db->prefix.'config SET conf_value = \'1800\' WHERE conf_name = \'o_timeout_visit\'');
  862. // Remove obsolete g_post_polls permission from groups table
  863. $db->drop_field('groups', 'g_post_polls');
  864. // Make room for multiple moderator groups
  865. if (!$db->field_exists('groups', 'g_moderator'))
  866. {
  867. // Add g_moderator column to groups table
  868. $db->add_field('groups', 'g_moderator', 'TINYINT(1)', false, 0, 'g_user_title');
  869. // Give the moderator group moderator privileges
  870. $db->query('UPDATE '.$db->prefix.'groups SET g_moderator = 1 WHERE g_id = 2');
  871. }
  872. // Replace obsolete p_mod_edit_users config setting with new per-group permission
  873. if (array_key_exists('p_mod_edit_users', $pun_config))
  874. {
  875. $db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_edit_users\'');
  876. $db->add_field('groups', 'g_mod_edit_users', 'TINYINT(1)', false, 0, 'g_moderator');
  877. $db->query('UPDATE '.$db->prefix.'groups SET g_mod_edit_users = '.$pun_config['p_mod_edit_users'].' WHERE g_moderator = 1');
  878. }
  879. // Replace obsolete p_mod_rename_users config setting with new per-group permission
  880. if (array_key_exists('p_mod_rename_users', $pun_config))
  881. {
  882. $db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_rename_users\'');
  883. $db->add_field('groups', 'g_mod_rename_users', 'TINYINT(1)', false, 0, 'g_mod_edit_users');
  884. $db->query('UPDATE '.$db->prefix.'groups SET g_mod_rename_users = '.$pun_config['p_mod_rename_users'].' WHERE g_moderator = 1');
  885. }
  886. // Replace obsolete p_mod_change_passwords config setting with new per-group permission
  887. if (array_key_exists('p_mod_change_passwords', $pun_config))
  888. {
  889. $db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_change_passwords\'');
  890. $db->add_field('groups', 'g_mod_change_passwords', 'TINYINT(1)', false, 0, 'g_mod_rename_users');
  891. $db->query('UPDATE '.$db->prefix.'groups SET g_mod_change_passwords = '.$pun_config['p_mod_change_passwords'].' WHERE g_moderator = 1');
  892. }
  893. // Replace obsolete p_mod_ban_users config setting with new per-group permission
  894. if (array_key_exists('p_mod_ban_users', $pun_config))
  895. {
  896. $db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_ban_users\'');
  897. $db->add_field('groups', 'g_mod_ban_users', 'TINYINT(1)', false, 0, 'g_mod_change_passwords');
  898. $db->query('UPDATE '.$db->prefix.'groups SET g_mod_ban_users = '.$pun_config['p_mod_ban_users'].' WHERE g_moderator = 1');
  899. }
  900. // We need to add a unique index to avoid users having multiple rows in the online table
  901. if (!$db->index_exists('online', 'user_id_ident_idx'))
  902. {
  903. $db->truncate_table('online');
  904. if ($mysql)
  905. $db->add_index('online', 'user_id_ident_idx', array('user_id', 'ident(25)'), true);
  906. else
  907. $db->add_index('online', 'user_id_ident_idx', array('user_id', 'ident'), true);
  908. }
  909. // Remove the redundant user_id_idx on the online table
  910. $db->drop_index('online', 'user_id_idx');
  911. // Add an index to ident on the online table
  912. if ($mysql)
  913. $db->add_index('online', 'ident_idx', array('ident(25)'));
  914. else
  915. $db->add_index('online', 'ident_idx', array('ident'));
  916. // Add an index to logged in the online table
  917. $db->add_index('online', 'logged_idx', array('logged'));
  918. // Add an index to last_post in the topics table
  919. $db->add_index('topics', 'last_post_idx', array('last_post'));
  920. // Add an index to username on the bans table
  921. if ($mysql)
  922. $db->add_index('bans', 'username_idx', array('username(25)'));
  923. else
  924. $db->add_index('bans', 'username_idx', array('username'));
  925. // Change the username_idx on users to a unique index of max size 25
  926. $db->drop_index('users', 'username_idx');
  927. $field = $mysql ? 'username(25)' : 'username';
  928. // Attempt to add a unique index. If the user doesn't use a transactional database this can fail due to multiple matching usernames in the
  929. // users table. This is bad, but just giving up if it happens is even worse! If it fails just add a regular non-unique index.
  930. if (!$db->add_index('users', 'username_idx', array($field), true))
  931. $db->add_index('users', 'username_idx', array($field));
  932. // Add g_view_users field to groups table
  933. $db->add_field('groups', 'g_view_users', 'TINYINT(1)', false, 1, 'g_read_board');
  934. // Add the last_email_sent column to the users table and the g_send_email and
  935. // g_email_flood columns to the groups table
  936. $db->add_field('users', 'last_email_sent', 'INT(10) UNSIGNED', true, null, 'last_search');
  937. $db->add_field('groups', 'g_send_email', 'TINYINT(1)', false, 1, 'g_search_users');
  938. $db->add_field('groups', 'g_email_flood', 'SMALLINT(6)', false, 60, 'g_search_flood');
  939. // Set non-default g_send_email and g_flood_email values properly
  940. $db->query('UPDATE '.$db->prefix.'groups SET g_send_email = 0 WHERE g_id = 3');
  941. $db->query('UPDATE '.$db->prefix.'groups SET g_email_flood = 0 WHERE g_id IN (1,2,3)');
  942. // Add the auto notify/subscription option to the users table
  943. $db->add_field('users', 'auto_notify', 'TINYINT(1)', false, 1, 'notify_with_post');
  944. // Change the auto_notify field default value to '1' (in FluxBB 1.4 it is '0' by default)
  945. $db->alter_field('users', 'auto_notify', 'TINYINT(1)', false, 1);
  946. // Add the first_post_id column to the topics table
  947. if (!$db->field_exists('topics', 'first_post_id'))
  948. {
  949. $db->add_field('topics', 'first_post_id', 'INT(10) UNSIGNED', false, 0, 'posted');
  950. $db->add_index('topics', 'first_post_id_idx', array('first_post_id'));
  951. // Now that we've added the column and indexed it, we need to give it correct data
  952. $result = $db->query('SELECT MIN(id) AS first_post, topic_id FROM '.$db->prefix.'posts GROUP BY topic_id');
  953. while ($cur_post = $db->fetch_assoc($result))
  954. $db->query('UPDATE '.$db->prefix.'topics SET first_post_id = '.$cur_post['first_post'].' WHERE id = '.$cur_post['topic_id']);
  955. }
  956. // Move any users with the old unverified status to their new group
  957. $db->query('UPDATE '.$db->prefix.'users SET group_id=0 WHERE group_id=32000');
  958. // Add the ban_creator column to the bans table
  959. $db->add_field('bans', 'ban_creator', 'INT(10) UNSIGNED', false, 0);
  960. // Add the time/date format settings to the user table
  961. $db->add_field('users', 'time_format', 'TINYINT(1)', false, 0, 'dst');
  962. $db->add_field('users', 'date_format', 'TINYINT(1)', false, 0, 'dst');
  963. // Change the search_data field to mediumtext
  964. $db->alter_field('search_cache', 'search_data', 'MEDIUMTEXT', true);
  965. // Incase we had the fulltext search extension installed (1.3-legacy), remove it
  966. $db->drop_index('topics', 'subject_idx');
  967. $db->drop_index('posts', 'message_idx');
  968. // Incase we had the fulltext search mod installed (1.2), remove it
  969. $db->drop_index('topics', 'subject_fulltext_search');
  970. $db->drop_index('posts', 'message_fulltext_search');
  971. // If the search_cache table has been dropped by the fulltext search extension, recreate it
  972. if (!$db->table_exists('search_cache'))
  973. {
  974. $schema = array(
  975. 'FIELDS' => array(
  976. 'id' => array(
  977. 'datatype' => 'INT(10) UNSIGNED',
  978. 'allow_null' => false,
  979. 'default' => '0'
  980. ),
  981. 'ident' => array(
  982. 'datatype' => 'VARCHAR(200)',
  983. 'allow_null' => false,
  984. 'default' => '\'\''
  985. ),
  986. 'search_data' => array(
  987. 'datatype' => 'MEDIUMTEXT',
  988. 'allow_null' => true
  989. )
  990. ),
  991. 'PRIMARY KEY' => array('id'),
  992. 'INDEXES' => array(
  993. 'ident_idx' => array('ident')
  994. )
  995. );
  996. if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
  997. $schema['INDEXES']['ident_idx'] = array('ident(8)');
  998. $db->create_table('search_cache', $schema);
  999. }
  1000. // If the search_matches table has been dropped by the fulltext search extension, recreate it
  1001. if (!$db->table_exists('search_matches'))
  1002. {
  1003. $schema = array(
  1004. 'FIELDS' => array(
  1005. 'post_id' => array(
  1006. 'datatype' => 'INT(10) UNSIGNED',
  1007. 'allow_null' => false,
  1008. 'default' => '0'
  1009. ),
  1010. 'word_id' => array(
  1011. 'datatype' => 'INT(10) UNSIGNED',
  1012. 'allow_null' => false,
  1013. 'default' => '0'
  1014. ),
  1015. 'subject_match' => array(
  1016. 'datatype' => 'TINYINT(1)',
  1017. 'allow_null' => false,
  1018. 'default' => '0'
  1019. )
  1020. ),
  1021. 'INDEXES' => array(
  1022. 'word_id_idx' => array('word_id'),
  1023. 'post_id_idx' => array('post_id')
  1024. )
  1025. );
  1026. $db->create_table('search_matches', $schema);
  1027. }
  1028. // If the search_words table has been dropped by the fulltext search extension, recreate it
  1029. if (!$db->table_exists('search_words'))
  1030. {
  1031. $schema = array(
  1032. 'FIELDS' => array(
  1033. 'id' => array(
  1034. 'datatype' => 'SERIAL',
  1035. 'allow_null' => false
  1036. ),
  1037. 'word' => array(
  1038. 'datatype' => 'VARCHAR(20)',
  1039. 'allow_null' => false,
  1040. 'default' => '\'\'',
  1041. 'collation' => 'bin'
  1042. )
  1043. ),
  1044. 'PRIMARY KEY' => array('word'),
  1045. 'INDEXES' => array(
  1046. 'id_idx' => array('id')
  1047. )
  1048. );
  1049. if ($db_type == 'sqlite')
  1050. {
  1051. $schema['PRIMARY KEY'] = array('id');
  1052. $schema['UNIQUE KEYS'] = array('word_idx' => array('word'));
  1053. }
  1054. $db->create_table('search_words', $schema);
  1055. }
  1056. // Rename the subscription table
  1057. $db->rename_table('subscriptions', 'topic_subscriptions');
  1058. // Add the send_email column to the topic_subscriptions table
  1059. $db->add_field('topic_subscriptions', 'send_email', 'TINYINT(1)', false, '1');
  1060. // Add the is_favorite column to the topic_subscriptions table
  1061. $db->add_field('topic_subscriptions', 'is_favorite', 'TINYINT(1)', false, '0');
  1062. // if we don't have the forum_subscriptions table, create it
  1063. if (!$db->table_exists('forum_subscriptions'))
  1064. {
  1065. $schema = array(
  1066. 'FIELDS' => array(
  1067. 'user_id' => array(
  1068. 'datatype' => 'INT(10) UNSIGNED',
  1069. 'allow_null' => false,
  1070. 'default' => '0'
  1071. ),
  1072. 'forum_id' => array(
  1073. 'datatype' => 'INT(10) UNSIGNED',
  1074. 'allow_null' => false,
  1075. 'default' => '0'
  1076. ),
  1077. 'send_email' => array(
  1078. 'datatype' => 'TINYINT(1)',
  1079. 'allow_null' => false,
  1080. 'default' => '1'
  1081. ),
  1082. 'follow_replies' => array(
  1083. 'datatype' => 'TINYINT(1)',
  1084. 'allow_null' => false,
  1085. 'default' => '0'
  1086. )
  1087. ),
  1088. 'PRIMARY KEY' => array('user_id', 'forum_id')
  1089. );
  1090. $db->create_table('forum_subscriptions', $schema);
  1091. }
  1092. // Add the send_email column to the forum_subscriptions table
  1093. $db->add_field('forum_subscriptions', 'send_email', 'TINYINT(1)', false, '1');
  1094. // Add the follow_replies column to the forum_subscriptions table
  1095. $db->add_field('forum_subscriptions', 'follow_replies', 'TINYINT(1)', false, '0');
  1096. // if we don't have the category_subscriptions table, create it
  1097. if (!$db->table_exists('category_subscriptions'))
  1098. {
  1099. $schema = array(
  1100. 'FIELDS' => array(
  1101. 'user_id' => array(
  1102. 'datatype' => 'INT(10) UNSIGNED',
  1103. 'allow_null' => false,
  1104. 'default' => '0'
  1105. ),
  1106. 'cat_id' => array(
  1107. 'datatype' => 'INT(10) UNSIGNED',
  1108. 'allow_null' => false,
  1109. 'default' => '0'
  1110. ),
  1111. 'send_email' => array(
  1112. 'datatype' => 'TINYINT(1)',
  1113. 'allow_null' => false,
  1114. 'default' => '1'
  1115. ),
  1116. 'follow_replies' => array(
  1117. 'datatype' => 'TINYINT(1)',
  1118. 'allow_null' => false,
  1119. 'default' => '0'
  1120. )
  1121. ),
  1122. 'PRIMARY KEY' => array('user_id', 'cat_id')
  1123. );
  1124. $db->create_table('category_subscriptions', $schema);
  1125. }
  1126. // Insert new config option o_category_subscriptions
  1127. if (!array_key_exists('o_category_subscriptions', $pun_config))
  1128. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_category_subscriptions\', \'0\')');
  1129. // Insert new config option o_forum_subscriptions
  1130. if (!array_key_exists('o_forum_subscriptions', $pun_config))
  1131. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_forum_subscriptions\', \'1\')');
  1132. // Rename config option o_subscriptions to o_topic_subscriptions
  1133. if (!array_key_exists('o_topic_subscriptions', $pun_config))
  1134. $db->query('UPDATE '.$db->prefix.'config SET conf_name=\'o_topic_subscriptions\' WHERE conf_name=\'o_subscriptions\'');
  1135. // Insert new config option o_use_email_pool
  1136. if (!array_key_exists('o_use_email_pool', $pun_config))
  1137. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_use_email_pool\', \'0\')');
  1138. // Insert new config option o_email_batch_size
  1139. if (!array_key_exists('o_email_batch_size', $pun_config))
  1140. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_email_batch_size\', \'20\')');
  1141. // Create tables for email pool
  1142. if (!$db->table_exists('email_pool_messages'))
  1143. {
  1144. $schema = array(
  1145. 'FIELDS' => array(
  1146. 'id' => array(
  1147. 'datatype' => 'SERIAL',
  1148. 'allow_null' => false
  1149. ),
  1150. 'mail_subject' => array(
  1151. 'datatype' => 'VARCHAR(255)',
  1152. 'allow_null' => false,
  1153. 'default' => '\'\''
  1154. ),
  1155. 'mail_message' => array(
  1156. 'datatype' => 'MEDIUMTEXT',
  1157. 'allow_null' => true
  1158. )
  1159. ),
  1160. 'PRIMARY KEY' => array('id')
  1161. );
  1162. $db->create_table('email_pool_messages', $schema);
  1163. }
  1164. if (!$db->table_exists('email_pool_recipients'))
  1165. {
  1166. $schema = array(
  1167. 'FIELDS' => array(
  1168. 'user_id' => array(
  1169. 'datatype' => 'INT(10) UNSIGNED',
  1170. 'allow_null' => false,
  1171. 'default' => '0'
  1172. ),
  1173. 'pool_msg_id' => array(
  1174. 'datatype' => 'INT(10) UNSIGNED',
  1175. 'allow_null' => false,
  1176. 'default' => '0'
  1177. ),
  1178. 'email' => array(
  1179. 'datatype' => 'VARCHAR(80)',
  1180. 'allow_null' => false,
  1181. 'default' => '\'\''
  1182. )
  1183. ),
  1184. 'PRIMARY KEY' => array('user_id', 'pool_msg_id')
  1185. );
  1186. $db->create_table('email_pool_recipients', $schema);
  1187. }
  1188. // if we don't have the uploads table, create it
  1189. if (!$db->table_exists('uploads'))
  1190. {
  1191. $schema = array(
  1192. 'FIELDS' => array(
  1193. 'id' => array(
  1194. 'datatype' => 'SERIAL',
  1195. 'allow_null' => false
  1196. ),
  1197. 'user_id' => array(
  1198. 'datatype' => 'INT(10) UNSIGNED',
  1199. 'allow_null' => false,
  1200. 'default' => '0'
  1201. ),
  1202. 'uploaded' => array(
  1203. 'datatype' => 'INT(10) UNSIGNED',
  1204. 'allow_null' => false,
  1205. 'default' => '0'
  1206. ),
  1207. 'filename' => array(
  1208. 'datatype' => 'VARCHAR(255)',
  1209. 'allow_null' => false,
  1210. 'default' => '\'error.file\''
  1211. ),
  1212. 'mime' => array(
  1213. 'datatype' => 'VARCHAR(64)',
  1214. 'allow_null' => false,
  1215. 'default' => '\'\''
  1216. ),
  1217. 'location' => array(
  1218. 'datatype' => 'TEXT',
  1219. 'allow_null' => false
  1220. ),
  1221. 'size' => array(
  1222. 'datatype' => 'INT(10) UNSIGNED',
  1223. 'allow_null' => false,
  1224. 'default' => '0'
  1225. ),
  1226. 'image_dim' => array(
  1227. 'datatype' => 'VARCHAR(64)',
  1228. 'allow_null' => false,
  1229. 'default' => '\'\''
  1230. ),
  1231. 'downloads' => array(
  1232. 'datatype' => 'INT(10) UNSIGNED',
  1233. 'allow_null' => false,
  1234. 'default' => '0'
  1235. ),
  1236. 'topic_id' => array(
  1237. 'datatype' => 'INT(10) UNSIGNED',
  1238. 'allow_null' => false,
  1239. 'default' => '0'
  1240. ),
  1241. 'post_id' => array(
  1242. 'datatype' => 'INT(10) UNSIGNED',
  1243. 'allow_null' => false,
  1244. 'default' => '0'
  1245. ),
  1246. 'album_id' => array(
  1247. 'datatype' => 'INT(10) UNSIGNED',
  1248. 'allow_null' => false,
  1249. 'default' => '0'
  1250. )
  1251. ),
  1252. 'PRIMARY KEY' => array('id'),
  1253. 'INDEXES' => array(
  1254. 'user_id_idx' => array('user_id'),
  1255. 'album_id_idx' => array('album_id'),
  1256. 'topic_id_idx' => array('topic_id'),
  1257. 'filename_idx' => array('filename')
  1258. )
  1259. );
  1260. if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
  1261. $schema['INDEXES']['filename_idx'] = array('filename(25)');
  1262. $db->create_table('uploads', $schema);
  1263. }
  1264. /* TODO:
  1265. * Этот участо кода только для alpha -> alpha обновлений.
  1266. * В дальнейшем его надо будет удалить, исходя из соображений, что с alpha-версий обновляться будут только samovarchik.info и локальные тестовые сайты.
  1267. */
  1268. else
  1269. {
  1270. $db->add_field('uploads', 'album_id', 'INT(10) UNSIGNED', false, 0, 'post_id');
  1271. $db->add_index('uploads', 'album_id_idx', array('album_id'));
  1272. $db->add_index('uploads', 'topic_id_idx', array('topic_id'));
  1273. $db->drop_field('uploads', 'is_public');
  1274. }
  1275. // if we don't have the albums table, create it
  1276. if (!$db->table_exists('albums'))
  1277. {
  1278. $schema = array(
  1279. 'FIELDS' => array(
  1280. 'id' => array(
  1281. 'datatype' => 'SERIAL',
  1282. 'allow_null' => false
  1283. ),
  1284. 'user_id' => array(
  1285. 'datatype' => 'INT(10) UNSIGNED',
  1286. 'allow_null' => false,
  1287. 'default' => '0'
  1288. ),
  1289. 'album_name' => array(
  1290. 'datatype' => 'VARCHAR(255)',
  1291. 'allow_null' => false,
  1292. 'default' => '\'Default Album\''
  1293. ),
  1294. 'files_count' => array(
  1295. 'datatype' => 'INT(10) UNSIGNED',
  1296. 'allow_null' => false,
  1297. 'default' => '0'
  1298. ),
  1299. 'comments_count' => array(
  1300. 'datatype' => 'INT(10) UNSIGNED',
  1301. 'allow_null' => false,
  1302. 'default' => '0'
  1303. ),
  1304. 'downloads_count' => array(
  1305. 'datatype' => 'INT(10) UNSIGNED',
  1306. 'allow_null' => false,
  1307. 'default' => '0'
  1308. ),
  1309. 'hidden' => array(
  1310. 'datatype' => 'TINYINT(1)',
  1311. 'allow_null' => false,
  1312. 'default' => '0'
  1313. ),
  1314. 'private' => array(
  1315. 'datatype' => 'TINYINT(1)',
  1316. 'allow_null' => false,
  1317. 'default' => '0'
  1318. )
  1319. ),
  1320. 'PRIMARY KEY' => array('id'),
  1321. 'INDEXES' => array(
  1322. 'user_id_idx' => array('user_id'),
  1323. )
  1324. );
  1325. $db->create_table('albums', $schema);
  1326. }
  1327. // Check if the comments forum exists
  1328. if (!array_key_exists('file_comment_forum', $pun_config))
  1329. {
  1330. // Create new forum for file comments
  1331. $db->query('INSERT INTO '.$db_prefix.'forums (kind, cat_id, forum_name) VALUES ('.Pe::get('pun_kind_file').', 0, \''.$db->escape($lang_update['Comments forum']).'\')');
  1332. $db->query('INSERT INTO '.$db_prefix.'config (conf_name, conf_value) VALUES (\'file_comment_forum\', \''.$db->insert_id().'\')');
  1333. }
  1334. // if we don't have the topics_track table, create it
  1335. if (!$db->table_exists('topics_track'))
  1336. {
  1337. $schema = array(
  1338. 'FIELDS' => array(
  1339. 'user_id' => array(
  1340. 'datatype' => 'INT(10) UNSIGNED',
  1341. 'allow_null' => false,
  1342. 'default' => '0'
  1343. ),
  1344. 'topic_id' => array(
  1345. 'datatype' => 'INT(10) UNSIGNED',
  1346. 'allow_null' => false,
  1347. 'default' => '0'
  1348. ),
  1349. 'board_id' => array(
  1350. 'datatype' => 'INT(10) UNSIGNED',
  1351. 'allow_null' => false,
  1352. 'default' => '0'
  1353. ),
  1354. 'mark_time' => array(
  1355. 'datatype' => 'INT(10) UNSIGNED',
  1356. 'allow_null' => false,
  1357. 'default' => '0'
  1358. )
  1359. ),
  1360. 'PRIMARY KEY' => array('user_id', 'topic_id'),
  1361. 'INDEXES' => array(
  1362. 'board_id_idx' => array('board_id'),
  1363. )
  1364. );
  1365. $db->create_table('topics_track', $schema);
  1366. }
  1367. // if we don't have the boards_track table, create it
  1368. if (!$db->table_exists('boards_track'))
  1369. {
  1370. $schema = array(
  1371. 'FIELDS' => array(
  1372. 'user_id' => array(
  1373. 'datatype' => 'INT(10) UNSIGNED',
  1374. 'allow_null' => false,
  1375. 'default' => '0'
  1376. ),
  1377. 'board_id' => array(
  1378. 'datatype' => 'INT(10) UNSIGNED',
  1379. 'allow_null' => false,
  1380. 'default' => '0'
  1381. ),
  1382. 'mark_time' => array(
  1383. 'datatype' => 'INT(10) UNSIGNED',
  1384. 'allow_null' => false,
  1385. 'default' => '0'
  1386. )
  1387. ),
  1388. 'PRIMARY KEY' => array('user_id', 'board_id')
  1389. );
  1390. $db->create_table('boards_track', $schema);
  1391. }
  1392. // if we don't have the drafts table, create it
  1393. if (!$db->table_exists('drafts'))
  1394. {
  1395. $schema = array(
  1396. 'FIELDS' => array(
  1397. 'id' => array(
  1398. 'datatype' => 'SERIAL',
  1399. 'allow_null' => false
  1400. ),
  1401. 'user_id' => array(
  1402. 'datatype' => 'INT(10) UNSIGNED',
  1403. 'allow_null' => false,
  1404. 'default' => '0'
  1405. ),
  1406. 'topic_id' => array(
  1407. 'datatype' => 'INT(10) UNSIGNED',
  1408. 'allow_null' => true
  1409. ),
  1410. 'board_id' => array(
  1411. 'datatype' => 'INT(10) UNSIGNED',
  1412. 'allow_null' => true
  1413. ),
  1414. 'saved' => array(
  1415. 'datatype' => 'INT(10) UNSIGNED',
  1416. 'allow_null' => false,
  1417. 'default' => '0'
  1418. ),
  1419. 'subject' => array(
  1420. 'datatype' => 'VARCHAR(255)',
  1421. 'allow_null' => false,
  1422. 'default' => '\'\''
  1423. ),
  1424. 'message' => array(
  1425. 'datatype' => 'MEDIUMTEXT',
  1426. 'allow_null' => true
  1427. ),
  1428. 'hide_smilies' => array(
  1429. 'datatype' => 'TINYINT(1)',
  1430. 'allow_null' => false,
  1431. 'default' => '0'
  1432. ),
  1433. 'sticky' => array(
  1434. 'datatype' => 'TINYINT(1)',
  1435. 'allow_null' => false,
  1436. 'default' => '0'
  1437. ),
  1438. 'subscribe' => array(
  1439. 'datatype' => 'TINYINT(1)',
  1440. 'allow_null' => false,
  1441. 'default' => '0'
  1442. ),
  1443. 'num_old_versions' => array(
  1444. 'datatype' => 'INT(10) UNSIGNED',
  1445. 'allow_null' => false,
  1446. 'default' => '0'
  1447. )
  1448. ),
  1449. 'PRIMARY KEY' => array('id'),
  1450. 'INDEXES' => array(
  1451. 'user_id_idx' => array('user_id')
  1452. )
  1453. );
  1454. $db->create_table('drafts', $schema);
  1455. }
  1456. // Insert new config option o_drafts
  1457. if (!array_key_exists('o_drafts', $pun_config))
  1458. $db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_drafts\', \'0\')');
  1459. // Change the default style if the old doesn't exist anymore
  1460. if ($pun_config['o_default_style'] != $default_style)
  1461. $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.$db->escape($default_style).'\' WHERE conf_name = \'o_default_style\'');
  1462. // Should we do charset conversion or not?
  1463. if (strpos($cur_version, '1.2') === 0 && isset($_POST['convert_charset']))
  1464. $query_str = '?stage=conv_bans&req_old_charset='.$old_charset;
  1465. break;
  1466. // Convert bans
  1467. case 'conv_bans':
  1468. $query_str = '?stage=conv_categories&req_old_charset='.$old_charset;
  1469. function _conv_bans($cur_item, $old_charset)
  1470. {
  1471. global $lang_update;
  1472. echo sprintf($lang_update['Converting item'], $lang_update['ban'], $cur_item['id']).'<br />'."\n";
  1473. convert_to_utf8($cur_item['username'], $old_charset);
  1474. convert_to_utf8($cur_item['message'], $old_charset);
  1475. return $cur_item;
  1476. }
  1477. $end_at = convert_table_utf8($db->prefix.'bans', '_conv_bans', $old_charset, 'id', $start_at);
  1478. if ($end_at !== true)
  1479. $query_str = '?stage=conv_bans&req_old_charset='.$old_charset.'&start_at='.$end_at;
  1480. break;
  1481. // Convert categories
  1482. case 'conv_categories':
  1483. $query_str = '?stage=conv_censors&req_old_charset='.$old_charset;
  1484. echo sprintf($lang_update['Converting'], $lang_update['categories']).'<br />'."\n";
  1485. function _conv_categories($cur_item, $old_charset)
  1486. {
  1487. convert_to_utf8($cur_item['cat_name'], $old_charset);
  1488. return $cur_item;
  1489. }
  1490. convert_table_utf8($db->prefix.'categories', '_conv_categories', $old_charset, 'id');
  1491. break;
  1492. // Convert censor words
  1493. case 'conv_censors':
  1494. $query_str = '?stage=conv_config&req_old_charset='.$old_charset;
  1495. echo sprintf($lang_update['Converting'], $lang_update['censor words']).'<br />'."\n";
  1496. function _conv_censoring($cur_item, $old_charset)
  1497. {
  1498. convert_to_utf8($cur_item['search_for'], $old_charset);
  1499. convert_to_utf8($cur_item['replace_with'], $old_charset);
  1500. return $cur_item;
  1501. }
  1502. convert_table_utf8($db->prefix.'censoring', '_conv_censoring', $old_charset, 'id');
  1503. break;
  1504. // Convert config
  1505. case 'conv_config':
  1506. $query_str = '?stage=conv_forums&req_old_charset='.$old_charset;
  1507. echo sprintf($lang_update['Converting'], $lang_update['configuration']).'<br />'."\n";
  1508. function _conv_config($cur_item, $old_charset)
  1509. {
  1510. convert_to_utf8($cur_item['conf_value'], $old_charset);
  1511. return $cur_item;
  1512. }
  1513. convert_table_utf8($db->prefix.'config', '_conv_config', $old_charset, 'conf_name');
  1514. break;
  1515. // Convert forums
  1516. case 'conv_forums':
  1517. $query_str = '?stage=conv_perms&req_old_charset='.$old_charset;
  1518. echo sprintf($lang_update['Converting'], $lang_update['forums']).'<br />'."\n";
  1519. function _conv_forums($cur_item, $old_charset)
  1520. {
  1521. $moderators = ($cur_item['moderators'] != '') ? unserialize($cur_item['moderators']) : array();
  1522. $moderators_utf8 = array();
  1523. foreach ($moderators as $mod_username => $mod_user_id)
  1524. {
  1525. convert_to_utf8($mod_username, $old_charset);
  1526. $moderators_utf8[$mod_username] = $mod_user_id;
  1527. }
  1528. convert_to_utf8($cur_item['forum_name'], $old_charset);
  1529. convert_to_utf8($cur_item['forum_desc'], $old_charset);
  1530. if (!empty($moderators_utf8))
  1531. $cur_item['moderators'] = serialize($moderators_utf8);
  1532. return $cur_item;
  1533. }
  1534. convert_table_utf8($db->prefix.'forums', '_conv_forums', $old_charset, 'id');
  1535. break;
  1536. // Convert forum permissions
  1537. case 'conv_perms':
  1538. $query_str = '?stage=conv_groups&req_old_charset='.$old_charset;
  1539. alter_table_utf8($db->prefix.'forum_perms');
  1540. break;
  1541. // Convert groups
  1542. case 'conv_groups':
  1543. $query_str = '?stage=conv_online&req_old_charset='.$old_charset;
  1544. echo sprintf($lang_update['Converting'], $lang_update['groups']).'<br />'."\n";
  1545. function _conv_groups($cur_item, $old_charset)
  1546. {
  1547. convert_to_utf8($cur_item['g_title'], $old_charset);
  1548. convert_to_utf8($cur_item['g_user_title'], $old_charset);
  1549. return $cur_item;
  1550. }
  1551. convert_table_utf8($db->prefix.'groups', '_conv_groups', $old_charset, 'g_id');
  1552. break;
  1553. // Convert online
  1554. case 'conv_online':
  1555. $query_str = '?stage=conv_posts&req_old_charset='.$old_charset;
  1556. // Truncate the table
  1557. $db->truncate_table('online');
  1558. alter_table_utf8($db->prefix.'online');
  1559. break;
  1560. // Convert posts
  1561. case 'conv_posts':
  1562. $query_str = '?stage=conv_ranks&req_old_charset='.$old_charset;
  1563. function _conv_posts($cur_item, $old_charset)
  1564. {
  1565. global $lang_update;
  1566. echo sprintf($lang_update['Converting item'], $lang_update['post'], $cur_item['id']).'<br />'."\n";
  1567. convert_to_utf8($cur_item['poster'], $old_charset);
  1568. convert_to_utf8($cur_item['message'], $old_charset);
  1569. convert_to_utf8($cur_item['edited_by'], $old_charset);
  1570. return $cur_item;
  1571. }
  1572. $end_at = convert_table_utf8($db->prefix.'posts', '_conv_posts', $old_charset, 'id', $start_at);
  1573. if ($end_at !== true)
  1574. $query_str = '?stage=conv_posts&req_old_charset='.$old_charset.'&start_at='.$end_at;
  1575. break;
  1576. // Convert ranks
  1577. case 'conv_ranks':
  1578. $query_str = '?stage=conv_reports&req_old_charset='.$old_charset;
  1579. echo sprintf($lang_update['Converting'], $lang_update['ranks']).'<br />'."\n";
  1580. function _conv_ranks($cur_item, $old_charset)
  1581. {
  1582. convert_to_utf8($cur_item['rank'], $old_charset);
  1583. return $cur_item;
  1584. }
  1585. convert_table_utf8($db->prefix.'ranks', '_conv_ranks', $old_charset, 'id');
  1586. break;
  1587. // Convert reports
  1588. case 'conv_reports':
  1589. $query_str = '?stage=conv_search_cache&req_old_charset='.$old_charset;
  1590. function _conv_reports($cur_item, $old_charset)
  1591. {
  1592. global $lang_update;
  1593. echo sprintf($lang_update['Converting item'], $lang_update['report'], $cur_item['id']).'<br />'."\n";
  1594. convert_to_utf8($cur_item['message'], $old_charset);
  1595. return $cur_item;
  1596. }
  1597. $end_at = convert_table_utf8($db->prefix.'reports', '_conv_reports', $old_charset, 'id', $start_at);
  1598. if ($end_at !== true)
  1599. $query_str = '?stage=conv_reports&req_old_charset='.$old_charset.'&start_at='.$end_at;
  1600. break;
  1601. // Convert search cache
  1602. case 'conv_search_cache':
  1603. $query_str = '?stage=conv_search_matches&req_old_charset='.$old_charset;
  1604. // Truncate the table
  1605. $db->truncate_table('search_cache');
  1606. alter_table_utf8($db->prefix.'search_cache');
  1607. break;
  1608. // Convert search matches
  1609. case 'conv_search_matches':
  1610. $query_str = '?stage=conv_search_words&req_old_charset='.$old_charset;
  1611. // Truncate the table
  1612. $db->truncate_table('search_matches');
  1613. alter_table_utf8($db->prefix.'search_matches');
  1614. break;
  1615. // Convert search words
  1616. case 'conv_search_words':
  1617. $query_str = '?stage=conv_subscriptions&req_old_charset='.$old_charset;
  1618. // Truncate the table
  1619. $db->truncate_table('search_words');
  1620. // Reset the sequence for the search words (not needed for SQLite)
  1621. switch ($db_type)
  1622. {
  1623. case 'mysql':
  1624. case 'mysqli':
  1625. case 'mysql_innodb':
  1626. case 'mysqli_innodb':
  1627. $db->query('ALTER TABLE '.$db->prefix.'search_words auto_increment=1');
  1628. break;
  1629. case 'pgsql';
  1630. $db->query('SELECT setval(\''.$db->prefix.'search_words_id_seq\', 1, false)');
  1631. break;
  1632. }
  1633. alter_table_utf8($db->prefix.'search_words');
  1634. break;
  1635. // Convert subscriptions
  1636. case 'conv_subscriptions':
  1637. $query_str = '?stage=conv_topics&req_old_charset='.$old_charset;
  1638. // By this stage we should have already renamed the subscription table
  1639. alter_table_utf8($db->prefix.'topic_subscriptions');
  1640. alter_table_utf8($db->prefix.'forum_subscriptions'); // This should actually already be utf8, but for consistency...
  1641. break;
  1642. // Convert topics
  1643. case 'conv_topics':
  1644. $query_str = '?stage=conv_users&req_old_charset='.$old_charset;
  1645. function _conv_topics($cur_item, $old_charset)
  1646. {
  1647. global $lang_update;
  1648. echo sprintf($lang_update['Converting item'], $lang_update['topic'], $cur_item['id']).'<br />'."\n";
  1649. convert_to_utf8($cur_item['poster'], $old_charset);
  1650. convert_to_utf8($cur_item['subject'], $old_charset);
  1651. convert_to_utf8($cur_item['last_poster'], $old_charset);
  1652. return $cur_item;
  1653. }
  1654. $end_at = convert_table_utf8($db->prefix.'topics', '_conv_topics', $old_charset, 'id', $start_at);
  1655. if ($end_at !== true)
  1656. $query_str = '?stage=conv_topics&req_old_charset='.$old_charset.'&start_at='.$end_at;
  1657. break;
  1658. // Convert users
  1659. case 'conv_users':
  1660. $query_str = '?stage=preparse_posts';
  1661. if ($start_at == 0)
  1662. $_SESSION['dupe_users'] = array();
  1663. function _conv_users($cur_item, $old_charset)
  1664. {
  1665. global $lang_update;
  1666. echo sprintf($lang_update['Converting item'], $lang_update['user'], $cur_item['id']).'<br />'."\n";
  1667. convert_to_utf8($cur_item['username'], $old_charset);
  1668. convert_to_utf8($cur_item['title'], $old_charset);
  1669. convert_to_utf8($cur_item['realname'], $old_charset);
  1670. convert_to_utf8($cur_item['location'], $old_charset);
  1671. convert_to_utf8($cur_item['signature'], $old_charset);
  1672. convert_to_utf8($cur_item['admin_note'], $old_charset);
  1673. return $cur_item;
  1674. }
  1675. function _error_users($cur_user)
  1676. {
  1677. $_SESSION['dupe_users'][$cur_user['id']] = $cur_user;
  1678. }
  1679. $end_at = convert_table_utf8($db->prefix.'users', '_conv_users', $old_charset, 'id', $start_at, '_error_users');
  1680. if ($end_at !== true)
  1681. $query_str = '?stage=conv_users&req_old_charset='.$old_charset.'&start_at='.$end_at;
  1682. else if (!empty($_SESSION['dupe_users']))
  1683. $query_str = '?stage=conv_users_dupe';
  1684. break;
  1685. // Handle any duplicate users which occured due to conversion
  1686. case 'conv_users_dupe':
  1687. $query_str = '?stage=preparse_posts';
  1688. if (!$mysql || empty($_SESSION['dupe_users']))
  1689. break;
  1690. if (isset($_POST['form_sent']))
  1691. {
  1692. $errors = array();
  1693. foreach ($_SESSION['dupe_users'] as $id => $cur_user)
  1694. {
  1695. $errors[$id] = array();
  1696. $username = pun_trim($_POST['dupe_users'][$id]);
  1697. if (pun_strlen($username) < 2)
  1698. $errors[$id][] = $lang_update['Username too short error'];
  1699. else if (pun_strlen($username) > 25) // This usually doesn't happen since the form element only accepts 25 characters
  1700. $errors[$id][] = $lang_update['Username too long error'];
  1701. else if (!strcasecmp($username, 'Guest'))
  1702. $errors[$id][] = $lang_update['Username Guest reserved error'];
  1703. else if (preg_match('/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/', $username) || preg_match('/((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))/', $username))
  1704. $errors[$id][] = $lang_update['Username IP format error'];
  1705. else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
  1706. $errors[$id][] = $lang_update['Username bad characters error'];
  1707. else if (preg_match('/(?:\[\/?(?:b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|\*)\]|\[(?:img|url|quote|list)=)/i', $username))
  1708. $errors[$id][] = $lang_update['Username BBCode error'];
  1709. $result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(ucp_preg_replace('/[^\p{L}\p{N}]/u', '', $username)).'\')) AND id>1');
  1710. if ($db->num_rows($result))
  1711. {
  1712. $busy = $db->result($result);
  1713. $errors[$id][] = sprintf($lang_update['Username duplicate error'], pun_htmlspecialchars($busy));
  1714. }
  1715. if (empty($errors[$id]))
  1716. {
  1717. $old_username = $cur_user['username'];
  1718. $_SESSION['dupe_users'][$id]['username'] = $cur_user['username'] = $username;
  1719. $temp = array();
  1720. foreach ($cur_user as $idx => $value)
  1721. $temp[$idx] = $value === null ? 'NULL' : '\''.$db->escape($value).'\'';
  1722. // Insert the renamed user
  1723. $db->query('INSERT INTO '.$db->prefix.'users('.implode(',', array_keys($temp)).') VALUES ('.implode(',', array_values($temp)).')');
  1724. // Renaming a user also affects a bunch of other stuff, lets fix that too...
  1725. $db->query('UPDATE '.$db->prefix.'posts SET poster=\''.$db->escape($username).'\' WHERE poster_id='.$id);
  1726. // TODO: The following must compare using collation utf8_bin otherwise we will accidently update posts/topics/etc belonging to both of the duplicate users, not just the one we renamed!
  1727. $db->query('UPDATE '.$db->prefix.'posts SET edited_by=\''.$db->escape($username).'\' WHERE edited_by=\''.$db->escape($old_username).'\' COLLATE utf8_bin');
  1728. $db->query('UPDATE '.$db->prefix.'topics SET poster=\''.$db->escape($username).'\' WHERE poster=\''.$db->escape($old_username).'\' COLLATE utf8_bin');
  1729. $db->query('UPDATE '.$db->prefix.'topics SET last_poster=\''.$db->escape($username).'\' WHERE last_poster=\''.$db->escape($old_username).'\' COLLATE utf8_bin');
  1730. $db->query('UPDATE '.$db->prefix.'forums SET last_poster=\''.$db->escape($username).'\' WHERE last_poster=\''.$db->escape($old_username).'\' COLLATE utf8_bin');
  1731. $db->query('UPDATE '.$db->prefix.'online SET ident=\''.$db->escape($username).'\' WHERE ident=\''.$db->escape($old_username).'\' COLLATE utf8_bin');
  1732. // If the user is a moderator or an administrator we have to update the moderator lists
  1733. $result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$cur_user['group_id']);
  1734. $group_mod = $db->result($result);
  1735. if ($cur_user['group_id'] == PUN_ADMIN || $group_mod == '1')
  1736. {
  1737. $result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums');
  1738. while ($cur_forum = $db->fetch_assoc($result))
  1739. {
  1740. $cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
  1741. if (in_array($id, $cur_moderators))
  1742. {
  1743. unset($cur_moderators[$old_username]);
  1744. $cur_moderators[$username] = $id;
  1745. uksort($cur_moderators, 'utf8_strcasecmp');
  1746. $db->query('UPDATE '.$db->prefix.'forums SET moderators=\''.$db->escape(serialize($cur_moderators)).'\' WHERE id='.$cur_forum['id']);
  1747. }
  1748. }
  1749. }
  1750. // Email the user alerting them of the change
  1751. if (file_exists(PUN_ROOT.'lang/'.$cur_user['language'].'/mail_templates/rename.tpl'))
  1752. $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_user['language'].'/mail_templates/rename.tpl'));
  1753. else if (file_exists(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/mail_templates/rename.tpl'))
  1754. $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/mail_templates/rename.tpl'));
  1755. else
  1756. $mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/English/mail_templates/rename.tpl'));
  1757. // The first row contains the subject
  1758. $first_crlf = strpos($mail_tpl, "\n");
  1759. $mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
  1760. $mail_message = trim(substr($mail_tpl, $first_crlf));
  1761. $mail_subject = str_replace('<board_title>', $pun_config['o_board_title'], $mail_subject);
  1762. $mail_message = str_replace('<base_url>', Pe::get('urlUtils')->getBaseUrl().'/', $mail_message);
  1763. $mail_message = str_replace('<old_username>', $old_username, $mail_message);
  1764. $mail_message = str_replace('<new_username>', $username, $mail_message);
  1765. $mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' Mailer', $mail_message);
  1766. Pe::get('email')->punMail($cur_user['email'], $mail_subject, $mail_message);
  1767. unset($_SESSION['dupe_users'][$id]);
  1768. }
  1769. }
  1770. }
  1771. if (!empty($_SESSION['dupe_users']))
  1772. {
  1773. $query_str = '';
  1774. ?>
  1775. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  1776. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
  1777. <head>
  1778. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  1779. <title><?php echo $lang_update['Update'] ?></title>
  1780. <link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
  1781. </head>
  1782. <body>
  1783. <div id="pundb_update" class="pun">
  1784. <div class="top-box"><div><!-- Top Corners --></div></div>
  1785. <div class="punwrap">
  1786. <div class="blockform">
  1787. <h2><span><?php echo $lang_update['Error converting users'] ?></span></h2>
  1788. <div class="box">
  1789. <form method="post" action="db_update.php?stage=conv_users_dupe&amp;uid=<?php echo $uid ?>">
  1790. <input type="hidden" name="form_sent" value="1" />
  1791. <div class="inform">
  1792. <div class="forminfo">
  1793. <p style="font-size: 1.1em"><?php echo $lang_update['Error info 1'] ?></p>
  1794. <p style="font-size: 1.1em"><?php echo $lang_update['Error info 2'] ?></p>
  1795. </div>
  1796. </div>
  1797. <?php
  1798. foreach ($_SESSION['dupe_users'] as $id => $cur_user)
  1799. {
  1800. ?>
  1801. <div class="inform">
  1802. <fieldset>
  1803. <legend><?php echo pun_htmlspecialchars($cur_user['username']); ?></legend>
  1804. <div class="infldset">
  1805. <label class="required"><strong><?php echo $lang_update['New username'] ?> <span><?php echo $lang_update['Required'] ?></span></strong><br /><input type="text" name="<?php echo 'dupe_users['.$id.']'; ?>" value="<?php if (isset($_POST['dupe_users'][$id])) echo pun_htmlspecialchars($_POST['dupe_users'][$id]); ?>" size="25" maxlength="25" /><br /></label>
  1806. </div>
  1807. </fieldset>
  1808. <?php if (!empty($errors[$id])): ?> <div class="forminfo error-info">
  1809. <h3><?php echo $lang_update['Correct errors'] ?></h3>
  1810. <ul class="error-list">
  1811. <?php
  1812. foreach ($errors[$id] as $cur_error)
  1813. echo "\t\t\t\t\t\t".'<li><strong>'.$cur_error.'</strong></li>'."\n";
  1814. ?>
  1815. </ul>
  1816. </div>
  1817. <?php endif; ?> </div>
  1818. <?php
  1819. }
  1820. ?>
  1821. <p class="buttons"><input type="submit" name="rename" value="<?php echo $lang_update['Rename users'] ?>" /></p>
  1822. </form>
  1823. </div>
  1824. </div>
  1825. </div>
  1826. <div class="end-box"><div><!-- Bottom Corners --></div></div>
  1827. </div>
  1828. </body>
  1829. </html>
  1830. <?php
  1831. }
  1832. break;
  1833. // Preparse posts
  1834. case 'preparse_posts':
  1835. $query_str = '?stage=preparse_sigs';
  1836. // If we don't need to parse the posts, skip this stage
  1837. if (isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION)
  1838. break;
  1839. // Fetch posts to process this cycle
  1840. $result = $db->query('SELECT id, message FROM '.$db->prefix.'posts WHERE id > '.$start_at.' ORDER BY id ASC LIMIT '.PER_PAGE);
  1841. $temp = array();
  1842. $end_at = 0;
  1843. while ($cur_item = $db->fetch_assoc($result))
  1844. {
  1845. echo sprintf($lang_update['Preparsing item'], $lang_update['post'], $cur_item['id']).'<br />'."\n";
  1846. $db->query('UPDATE '.$db->prefix.'posts SET message = \''.$db->escape(Pe::get('parser')->preparseBBcode($cur_item['message'], $temp)).'\' WHERE id = '.$cur_item['id']);
  1847. $end_at = $cur_item['id'];
  1848. }
  1849. // Check if there is more work to do
  1850. if ($end_at > 0)
  1851. {
  1852. $result = $db->query('SELECT 1 FROM '.$db->prefix.'posts WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1');
  1853. if ($db->num_rows($result) > 0)
  1854. $query_str = '?stage=preparse_posts&start_at='.$end_at;
  1855. }
  1856. break;
  1857. // Preparse signatures
  1858. case 'preparse_sigs':
  1859. $query_str = '?stage=rebuild_idx';
  1860. // If we don't need to parse the sigs, skip this stage
  1861. if (isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION)
  1862. break;
  1863. // Fetch users to process this cycle
  1864. $result = $db->query('SELECT id, signature FROM '.$db->prefix.'users WHERE id > '.$start_at.' ORDER BY id ASC LIMIT '.PER_PAGE);
  1865. $temp = array();
  1866. $end_at = 0;
  1867. while ($cur_item = $db->fetch_assoc($result))
  1868. {
  1869. echo sprintf($lang_update['Preparsing item'], $lang_update['signature'], $cur_item['id']).'<br />'."\n";
  1870. $db->query('UPDATE '.$db->prefix.'users SET signature = \''.$db->escape(Pe::get('parser')->preparseBBcode($cur_item['signature'], $temp, true)).'\' WHERE id = '.$cur_item['id']);
  1871. $end_at = $cur_item['id'];
  1872. }
  1873. // Check if there is more work to do
  1874. if ($end_at > 0)
  1875. {
  1876. $result = $db->query('SELECT 1 FROM '.$db->prefix.'users WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1');
  1877. if ($db->num_rows($result) > 0)
  1878. $query_str = '?stage=preparse_sigs&start_at='.$end_at;
  1879. }
  1880. break;
  1881. // Rebuild the search index
  1882. case 'rebuild_idx':
  1883. $query_str = '?stage=finish';
  1884. // If we don't need to update the search index, skip this stage
  1885. if (isset($pun_config['o_searchindex_revision']) && $pun_config['o_searchindex_revision'] >= UPDATE_TO_SI_REVISION)
  1886. break;
  1887. if ($start_at == 0)
  1888. {
  1889. // Truncate the tables just in-case we didn't already (if we are coming directly here without converting the tables)
  1890. $db->truncate_table('search_cache');
  1891. $db->truncate_table('search_matches');
  1892. $db->truncate_table('search_words');
  1893. // Reset the sequence for the search words (not needed for SQLite)
  1894. switch ($db_type)
  1895. {
  1896. case 'mysql':
  1897. case 'mysqli':
  1898. case 'mysql_innodb':
  1899. case 'mysqli_innodb':
  1900. $db->query('ALTER TABLE '.$db->prefix.'search_words auto_increment=1');
  1901. break;
  1902. case 'pgsql';
  1903. $db->query('SELECT setval(\''.$db->prefix.'search_words_id_seq\', 1, false)');
  1904. break;
  1905. }
  1906. }
  1907. // Fetch posts to process this cycle
  1908. $result = $db->query('SELECT p.id, p.message, t.subject, t.first_post_id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id WHERE p.id > '.$start_at.' ORDER BY p.id ASC LIMIT '.PER_PAGE);
  1909. $end_at = 0;
  1910. while ($cur_item = $db->fetch_assoc($result))
  1911. {
  1912. echo sprintf($lang_update['Rebuilding index item'], $lang_update['post'], $cur_item['id']).'<br />'."\n";
  1913. if ($cur_item['id'] == $cur_item['first_post_id'])
  1914. Pe::get('searchIdx')->updateSearchIndex('post', $cur_item['id'], $cur_item['message'], $cur_item['subject']);
  1915. else
  1916. Pe::get('searchIdx')->updateSearchIndex('post', $cur_item['id'], $cur_item['message']);
  1917. $end_at = $cur_item['id'];
  1918. }
  1919. // Check if there is more work to do
  1920. if ($end_at > 0)
  1921. {
  1922. $result = $db->query('SELECT 1 FROM '.$db->prefix.'posts WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1');
  1923. if ($db->num_rows($result) > 0)
  1924. $query_str = '?stage=rebuild_idx&start_at='.$end_at;
  1925. }
  1926. break;
  1927. // Show results page
  1928. case 'finish':
  1929. // We update the version number
  1930. $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO.'\' WHERE conf_name = \'o_cur_version\'');
  1931. // And the PE version number
  1932. $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_PE.'\' WHERE conf_name = \'o_cur_pe_version\'');
  1933. // And the database revision number
  1934. $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_DB_REVISION.'\' WHERE conf_name = \'o_database_revision\'');
  1935. // And the PE database revision number
  1936. $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_PE_DB_REVISION.'\' WHERE conf_name = \'o_pe_database_revision\'');
  1937. // And the search index revision number
  1938. $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_SI_REVISION.'\' WHERE conf_name = \'o_searchindex_revision\'');
  1939. // And the parser revision number
  1940. $db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_PARSER_REVISION.'\' WHERE conf_name = \'o_parser_revision\'');
  1941. // Check the default language still exists!
  1942. if (!file_exists(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/common.php'))
  1943. $db->query('UPDATE '.$db->prefix.'config SET conf_value = \'English\' WHERE conf_name = \'o_default_lang\'');
  1944. // Check the default style still exists!
  1945. if (!file_exists(PUN_ROOT.'style/'.$pun_config['o_default_style'].'.css'))
  1946. $db->query('UPDATE '.$db->prefix.'config SET conf_value = \'Air\' WHERE conf_name = \'o_default_style\'');
  1947. // This feels like a good time to synchronize the forums
  1948. $result = $db->query('SELECT id FROM '.$db->prefix.'forums');
  1949. while ($row = $db->fetch_row($result))
  1950. Pe::get('forum')->updateForum($row[0]);
  1951. // Empty the PHP cache
  1952. Pe::get('cacheGenerator')->clearCache();
  1953. // Delete the update lock file
  1954. @unlink(FORUM_CACHE_DIR.'db_update.lock');
  1955. ?>
  1956. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  1957. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
  1958. <head>
  1959. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  1960. <title><?php echo $lang_update['Update'] ?></title>
  1961. <link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
  1962. </head>
  1963. <body>
  1964. <div id="pundb_update" class="pun">
  1965. <div class="top-box"><div><!-- Top Corners --></div></div>
  1966. <div class="punwrap">
  1967. <div class="blockform">
  1968. <h2><span><?php echo $lang_update['Update'] ?></span></h2>
  1969. <div class="box">
  1970. <div class="fakeform">
  1971. <div class="inform">
  1972. <div class="forminfo">
  1973. <p style="font-size: 1.1em"><?php printf($lang_update['Successfully updated'], sprintf('<a href="index.php">%s</a>', $lang_update['go to index'])) ?></p>
  1974. </div>
  1975. </div>
  1976. </div>
  1977. </div>
  1978. </div>
  1979. </div>
  1980. <div class="end-box"><div><!-- Bottom Corners --></div></div>
  1981. </div>
  1982. </body>
  1983. </html>
  1984. <?php
  1985. break;
  1986. }
  1987. $db->end_transaction();
  1988. $db->close();
  1989. if ($query_str != '')
  1990. exit('<script type="text/javascript">window.location="db_update.php'.$query_str.'&uid='.$uid.'"</script><noscript>'.sprintf($lang_update['JavaScript disabled'], sprintf('<a href="db_update.php'.$query_str.'&uid='.$uid.'">%s</a>', $lang_update['Click here to continue'])).'</noscript>');