PageRenderTime 58ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/upgrade.php

https://github.com/misterbrandt/chyrp
PHP | 1233 lines | 798 code | 197 blank | 238 comment | 101 complexity | 2026829c4cc3bab0ce6f4d1b7794eaa0 MD5 | raw file
  1. <?php
  2. /**
  3. * File: Upgrader
  4. * A task-based general-purpose Chyrp upgrader.
  5. *
  6. * Performs upgrade functions based on individual tasks, and checks whether or not they need to be done.
  7. *
  8. * Version-agnostic. Completely safe to be run at all times, by anyone.
  9. */
  10. header("Content-type: text/html; charset=UTF-8");
  11. define('DEBUG', true);
  12. define('CACHE_TWIG', false);
  13. define('JAVASCRIPT', false);
  14. define('ADMIN', false);
  15. define('AJAX', false);
  16. define('XML_RPC', false);
  17. define('TRACKBACK', false);
  18. define('UPGRADING', true);
  19. define('INSTALLING', false);
  20. define('TESTER', true);
  21. define('INDEX', false);
  22. define('MAIN_DIR', dirname(__FILE__));
  23. define('INCLUDES_DIR', dirname(__FILE__)."/includes");
  24. define('MODULES_DIR', MAIN_DIR."/modules");
  25. define('FEATHERS_DIR', MAIN_DIR."/feathers");
  26. define('THEMES_DIR', MAIN_DIR."/themes");
  27. if (!AJAX and
  28. extension_loaded("zlib") and
  29. !ini_get("zlib.output_compression") and
  30. isset($_SERVER['HTTP_ACCEPT_ENCODING']) and
  31. substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], "gzip")) {
  32. ob_start("ob_gzhandler");
  33. header("Content-Encoding: gzip");
  34. } else
  35. ob_start();
  36. /**
  37. * Function: config_file
  38. * Returns what config file their install is set up for.
  39. */
  40. function config_file() {
  41. if (file_exists(INCLUDES_DIR."/config.yaml.php"))
  42. return INCLUDES_DIR."/config.yaml.php";
  43. if (file_exists(INCLUDES_DIR."/config.yml.php"))
  44. return INCLUDES_DIR."/config.yml.php";
  45. if (file_exists(INCLUDES_DIR."/config.php"))
  46. return INCLUDES_DIR."/config.php";
  47. exit("Config file not found.");
  48. }
  49. /**
  50. * Function: database_file
  51. * Returns what database config file their install is set up for.
  52. */
  53. function database_file() {
  54. if (file_exists(INCLUDES_DIR."/database.yaml.php"))
  55. return INCLUDES_DIR."/database.yaml.php";
  56. if (file_exists(INCLUDES_DIR."/database.yml.php"))
  57. return INCLUDES_DIR."/database.yml.php";
  58. if (file_exists(INCLUDES_DIR."/database.php"))
  59. return INCLUDES_DIR."/database.php";
  60. return false;
  61. }
  62. /**
  63. * Function: using_yaml
  64. * Are they using YAML config storage?
  65. */
  66. function using_yaml() {
  67. return (basename(config_file()) != "config.php" and basename(database_file()) != "database.php") or !database_file();
  68. }
  69. # Evaluate the code in their config files, but with the classes renamed, so we can safely retrieve the values.
  70. if (!using_yaml()) {
  71. eval(str_replace(array("<?php", "?>", "Config"),
  72. array("", "", "OldConfig"),
  73. file_get_contents(config_file())));
  74. if (database_file())
  75. eval(str_replace(array("<?php", "?>", "SQL"),
  76. array("", "", "OldSQL"),
  77. file_get_contents(database_file())));
  78. }
  79. # File: Helpers
  80. # Various functions used throughout Chyrp's code.
  81. require_once INCLUDES_DIR."/helpers.php";
  82. # File: Gettext
  83. # Gettext library.
  84. require_once INCLUDES_DIR."/lib/gettext/gettext.php";
  85. # File: Streams
  86. # Streams library.
  87. require_once INCLUDES_DIR."/lib/gettext/streams.php";
  88. # File: YAML
  89. # Horde YAML parsing library.
  90. require_once INCLUDES_DIR."/lib/YAML.php";
  91. # File: SQL
  92. # See Also:
  93. # <SQL>
  94. require INCLUDES_DIR."/class/SQL.php";
  95. /**
  96. * Class: Config
  97. * Handles writing to whichever config file they're using.
  98. */
  99. class Config {
  100. # Array: $yaml
  101. # Stores all of the YAML data.
  102. static $yaml = array("config" => array(),
  103. "database" => array());
  104. /**
  105. * Function: get
  106. * Returns a config setting.
  107. *
  108. * Parameters:
  109. * $setting - The setting to return.
  110. */
  111. static function get($setting) {
  112. return (isset(Config::$yaml["config"][$setting])) ? Config::$yaml["config"][$setting] : false ;
  113. }
  114. /**
  115. * Function: set
  116. * Sets a config setting.
  117. *
  118. * Parameters:
  119. * $setting - The config setting to set.
  120. * $value - The value for the setting.
  121. * $message - The message to display with test().
  122. */
  123. static function set($setting, $value, $message = null) {
  124. if (self::get($setting) == $value) return;
  125. if (!isset($message))
  126. $message = _f("Setting %s to %s...", array($setting, normalize(print_r($value, true))));
  127. Config::$yaml["config"][$setting] = $value;
  128. $protection = "<?php header(\"Status: 403\"); exit(\"Access denied.\"); ?>\n";
  129. $dump = $protection.YAML::dump(Config::$yaml["config"]);
  130. echo $message.test(@file_put_contents(INCLUDES_DIR."/config.yaml.php", $dump));
  131. }
  132. /**
  133. * Function: check
  134. * Goes a config exist?
  135. *
  136. * Parameters:
  137. * $setting - Name of the config to check.
  138. */
  139. static function check($setting) {
  140. return (isset(Config::$yaml["config"][$setting]));
  141. }
  142. /**
  143. * Function: fallback
  144. * Sets a config setting to $value if it does not exist.
  145. *
  146. * Parameters:
  147. * $setting - The config setting to set.
  148. * $value - The value for the setting.
  149. * $message - The message to display with test().
  150. */
  151. static function fallback($setting, $value, $message = null) {
  152. if (!isset($message))
  153. $message = _f("Adding %s setting...", array($setting));
  154. if (!self::check($setting))
  155. echo self::set($setting, $value, $message);
  156. }
  157. /**
  158. * Function: remove
  159. * Removes a setting if it exists.
  160. *
  161. * Parameters:
  162. * $setting - The setting to remove.
  163. */
  164. static function remove($setting) {
  165. if (!self::check($setting)) return;
  166. unset(Config::$yaml["config"][$setting]);
  167. $protection = "<?php header(\"Status: 403\"); exit(\"Access denied.\"); ?>\n";
  168. $dump = $protection.YAML::dump(Config::$yaml["config"]);
  169. echo _f("Removing %s setting...", array($setting)).
  170. test(@file_put_contents(INCLUDES_DIR."/config.yaml.php", $dump));
  171. }
  172. }
  173. if (using_yaml()) {
  174. Config::$yaml["config"] = YAML::load(preg_replace("/<\?php(.+)\?>\n?/s", "", file_get_contents(config_file())));
  175. if (database_file())
  176. Config::$yaml["database"] = YAML::load(preg_replace("/<\?php(.+)\?>\n?/s",
  177. "",
  178. file_get_contents(database_file())));
  179. else
  180. Config::$yaml["database"] = oneof(@Config::$yaml["config"]["sql"], array());
  181. } else {
  182. # $config and $sql here are loaded from the eval()'s above.
  183. foreach ($config as $name => $val)
  184. Config::$yaml["config"][$name] = $val;
  185. foreach ($sql as $name => $val)
  186. Config::$yaml["database"][$name] = $val;
  187. }
  188. load_translator("chyrp", INCLUDES_DIR."/locale/".Config::get("locale").".mo");
  189. /**
  190. * Function: test
  191. * Attempts to perform a task, and displays a "success" or "failed" message determined by the outcome.
  192. *
  193. * Parameters:
  194. * $try - The task to attempt. Should return something that evaluates to true or false.
  195. * $message - Message to display for the test.
  196. */
  197. function test($try, $message = "") {
  198. $sql = SQL::current();
  199. if (!empty($sql->error)) {
  200. $message.= "\n".$sql->error."\n\n";
  201. $sql->error = "";
  202. }
  203. $info = $message;
  204. if ($try)
  205. return " <span class=\"yay\">".__("success!")."</span>\n";
  206. else
  207. return " <span class=\"boo\">".__("failed!")."</span>\n".$info;
  208. }
  209. #---------------------------------------------
  210. # Upgrading Actions
  211. #---------------------------------------------
  212. /**
  213. * Function: fix_htaccess
  214. * Repairs their .htaccess file.
  215. */
  216. function fix_htaccess() {
  217. $url = "http://".$_SERVER['HTTP_HOST'].str_replace("/upgrade.php", "", $_SERVER['REQUEST_URI']);
  218. $index = (parse_url($url, PHP_URL_PATH)) ? "/".trim(parse_url($url, PHP_URL_PATH), "/")."/" : "/" ;
  219. $path = preg_quote($index, "/");
  220. $htaccess_has_chyrp = (file_exists(MAIN_DIR."/.htaccess") and preg_match("/<IfModule mod_rewrite\.c>\n([\s]*)RewriteEngine On\n([\s]*)RewriteBase {$path}\n([\s]*)RewriteCond %\{REQUEST_FILENAME\} !-f\n([\s]*)RewriteCond %\{REQUEST_FILENAME\} !-d\n([\s]*)RewriteRule (\^\.\+\\$|\!\\.\(gif\|jpg\|png\|css\)) index\.php \[L\]\n([\s]*)RewriteRule \^\.\+\\\.twig\\$ index\.php \[L\]\n([\s]*)<\/IfModule>/", file_get_contents(MAIN_DIR."/.htaccess")));
  221. if ($htaccess_has_chyrp)
  222. return;
  223. $htaccess = "<IfModule mod_rewrite.c>\nRewriteEngine On\nRewriteBase {$index}\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteRule ^.+\$ index.php [L]\nRewriteRule ^.+\\.twig\$ index.php [L]\n</IfModule>";
  224. if (!file_exists(MAIN_DIR."/.htaccess"))
  225. echo __("Generating .htaccess file...").
  226. test(@file_put_contents(MAIN_DIR."/.htaccess", $htaccess), __("Try creating the file and/or CHMODding it to 777 temporarily."));
  227. else
  228. echo __("Appending to .htaccess file...").
  229. test(@file_put_contents(MAIN_DIR."/.htaccess", "\n\n".$htaccess, FILE_APPEND), __("Try creating the file and/or CHMODding it to 777 temporarily."));
  230. }
  231. /**
  232. * Function: tweets_to_posts
  233. * Enacts the "tweet" to "post" rename.
  234. *
  235. * Versions: 1.0.2 => 1.0.3
  236. */
  237. function tweets_to_posts() {
  238. if (SQL::current()->query("SELECT * FROM __tweets"))
  239. echo __("Renaming tweets table to posts...").
  240. test(SQL::current()->query("RENAME TABLE __tweets TO __posts"));
  241. if (SQL::current()->query("SELECT add_tweet FROM __groups"))
  242. echo __("Renaming add_tweet permission to add_post...").
  243. test(SQL::current()->query("ALTER TABLE __groups CHANGE add_tweet add_post TINYINT(1) NOT NULL DEFAULT '0'"));
  244. if (SQL::current()->query("SELECT edit_tweet FROM __groups"))
  245. echo __("Renaming edit_tweet permission to edit_post...").
  246. test(SQL::current()->query("ALTER TABLE __groups CHANGE edit_tweet edit_post TINYINT(1) NOT NULL DEFAULT '0'"));
  247. if (SQL::current()->query("SELECT delete_tweet FROM __groups"))
  248. echo __("Renaming delete_tweet permission to delete_post...").
  249. test(SQL::current()->query("ALTER TABLE __groups CHANGE delete_tweet delete_post TINYINT(1) NOT NULL DEFAULT '0'"));
  250. if (Config::check("tweets_per_page")) {
  251. Config::fallback("posts_per_page", Config::get("tweets_per_page"));
  252. Config::remove("tweets_per_page");
  253. }
  254. if (Config::check("tweet_url")) {
  255. Config::fallback("post_url", Config::get("tweet_url"));
  256. Config::remove("tweet_url");
  257. }
  258. if (Config::check("rss_tweets")) {
  259. Config::fallback("rss_posts", Config::get("rss_posts"));
  260. Config::remove("rss_tweets");
  261. }
  262. }
  263. /**
  264. * Function: pages_parent_id_column
  265. * Adds the @parent_id@ column to the "pages" table.
  266. *
  267. * Versions: 1.0.3 => 1.0.4
  268. */
  269. function pages_parent_id_column() {
  270. if (SQL::current()->query("SELECT parent_id FROM __pages"))
  271. return;
  272. echo __("Adding parent_id column to pages table...").
  273. test(SQL::current()->query("ALTER TABLE __pages ADD parent_id INT(11) NOT NULL DEFAULT '0' AFTER user_id"));
  274. }
  275. /**
  276. * Function: pages_list_order_column
  277. * Adds the @list_order@ column to the "pages" table.
  278. *
  279. * Versions: 1.0.4 => 1.1.0
  280. */
  281. function pages_list_order_column() {
  282. if (SQL::current()->query("SELECT list_order FROM __pages"))
  283. return;
  284. echo __("Adding list_order column to pages table...").
  285. test(SQL::current()->query("ALTER TABLE __pages ADD list_order INT(11) NOT NULL DEFAULT '0' AFTER show_in_list"));
  286. }
  287. /**
  288. * Function: remove_beginning_slash_from_post_url
  289. * Removes the slash at the beginning of the post URL setting.
  290. */
  291. function remove_beginning_slash_from_post_url() {
  292. if (substr(Config::get("post_url"), 0, 1) == "/")
  293. Config::set("post_url", ltrim(Config::get("post_url"), "/"));
  294. }
  295. /**
  296. * Function: move_yml_yaml
  297. * Renames config.yml.php to config.yaml.php.
  298. *
  299. * Versions: 1.1.2 => 1.1.3
  300. */
  301. function move_yml_yaml() {
  302. if (file_exists(INCLUDES_DIR."/config.yml.php"))
  303. echo __("Moving /includes/config.yml.php to /includes/config.yaml.php...").
  304. test(@rename(INCLUDES_DIR."/config.yml.php", INCLUDES_DIR."/config.yaml.php"), __("Try CHMODding the file to 777."));
  305. }
  306. /**
  307. * Function: update_protection
  308. * Updates the PHP protection code in the config file.
  309. */
  310. function update_protection() {
  311. if (!file_exists(INCLUDES_DIR."/config.yaml.php") or
  312. substr_count(file_get_contents(INCLUDES_DIR."/config.yaml.php"),
  313. "<?php header(\"Status: 403\"); exit(\"Access denied.\"); ?>"))
  314. return;
  315. $contents = file_get_contents(INCLUDES_DIR."/config.yaml.php");
  316. $new_error = preg_replace("/<\?php (.+) \?>/",
  317. "<?php header(\"Status: 403\"); exit(\"Access denied.\"); ?>",
  318. $contents);
  319. echo __("Updating protection code in config.yaml.php...").
  320. test(@file_put_contents(INCLUDES_DIR."/config.yaml.php", $new_error), __("Try CHMODding the file to 777."));
  321. }
  322. /**
  323. * Function: theme_default_to_stardust
  324. * Changes their theme from "default" to "stardust", or leaves it alone if they're not using "default".
  325. *
  326. * Versions: 1.1.3.2 => 2.0
  327. */
  328. function theme_default_to_stardust() {
  329. if (Config::get("theme") != "default") return;
  330. Config::set("theme", "stardust");
  331. }
  332. /**
  333. * Function: default_db_adapter_to_mysql
  334. * Adds an "adapter" SQL setting if it doesn't exist, and sets it to "mysql".
  335. *
  336. * Versions: 1.1.3.2 => 2.0
  337. */
  338. function default_db_adapter_to_mysql() {
  339. $sql = SQL::current();
  340. if (isset($sql->adapter)) return;
  341. $sql->set("adapter", "mysql");
  342. }
  343. /**
  344. * Function: move_upload
  345. * Renames the "upload" directory to "uploads".
  346. */
  347. function move_upload() {
  348. if (file_exists(MAIN_DIR."/upload") and !file_exists(MAIN_DIR."/uploads"))
  349. echo __("Renaming /upload directory to /uploads...").test(@rename(MAIN_DIR."/upload", MAIN_DIR."/uploads"), __("Try CHMODding the directory to 777."));
  350. }
  351. /**
  352. * Function: make_posts_xml
  353. * Updates all of the post XML data to well-formed non-CDATAized XML.
  354. *
  355. * Versions: 1.1.3.2 => 2.0
  356. */
  357. function make_posts_safe() {
  358. if (!$posts = SQL::current()->query("SELECT * FROM __posts"))
  359. return;
  360. if (!SQL::current()->query("SELECT xml FROM __posts"))
  361. return;
  362. function clean_xml(&$input) {
  363. $input = trim($input);
  364. }
  365. while ($post = $posts->fetchObject()) {
  366. if (!substr_count($post->xml, "<![CDATA["))
  367. continue;
  368. $post->xml = str_replace("<![CDATA[]]>", "", $post->xml);
  369. $xml = simplexml_load_string($post->xml, "SimpleXMLElement", LIBXML_NOCDATA);
  370. $parse = xml2arr($xml);
  371. array_walk_recursive($parse, "clean_xml");
  372. $new_xml = new SimpleXMLElement("<post></post>");
  373. arr2xml($new_xml, $parse);
  374. echo _f("Sanitizing XML data of post #%d...", array($post->id)).
  375. test(SQL::current()->update("posts",
  376. array("id" => $post->id),
  377. array("xml" => $new_xml->asXML())));
  378. }
  379. }
  380. /**
  381. * Function: rss_posts_to_feed_items
  382. * Rename the feed items setting.
  383. *
  384. * Versions: 1.1.3.2 => 2.0
  385. */
  386. function rss_posts_to_feed_items() {
  387. if (!Config::check("rss_posts"))
  388. return;
  389. Config::fallback("feed_items", Config::get("rss_posts"));
  390. Config::remove("rss_posts");
  391. }
  392. /**
  393. * Function: update_groups_to_yaml
  394. * Updates the groups to use YAML-based permissions instead of table columns.
  395. *
  396. * Versions: 1.1.3.2 => 2.0
  397. */
  398. function update_groups_to_yaml() {
  399. if (!SQL::current()->query("SELECT view_site FROM __groups")) return;
  400. $get_groups = SQL::current()->query("SELECT * FROM __groups");
  401. echo __("Backing up current groups table...").test($get_groups);
  402. if (!$get_groups) return;
  403. $groups = array();
  404. # Generate an array of groups, name => permissions.
  405. while ($group = $get_groups->fetchObject()) {
  406. $groups[$group->name] = array("permissions" => array());
  407. foreach ($group as $key => $val)
  408. if ($key != "name" and $key != "id" and $val)
  409. $groups[$group->name]["permissions"][] = $key;
  410. elseif ($key == "id")
  411. $groups[$group->name]["id"] = $val;
  412. }
  413. # Convert permissions array to a YAML dump.
  414. foreach ($groups as $key => &$val)
  415. $val["permissions"] = YAML::dump($val["permissions"]);
  416. $drop_groups = SQL::current()->query("DROP TABLE __groups");
  417. echo __("Dropping old groups table...").test($drop_groups);
  418. if (!$drop_groups) return;
  419. $groups_table = SQL::current()->query("CREATE TABLE __groups (
  420. id INTEGER PRIMARY KEY AUTO_INCREMENT,
  421. name VARCHAR(100) DEFAULT '',
  422. permissions LONGTEXT,
  423. UNIQUE (name)
  424. ) DEFAULT CHARSET=utf8");
  425. echo __("Creating new groups table...").test($groups_table);
  426. if (!$groups_table) return;
  427. foreach($groups as $name => $values)
  428. echo _f("Restoring group \"%s\"...", array($name)).
  429. test(SQL::current()->insert("groups",
  430. array("id" => $values["id"],
  431. "name" => $name,
  432. "permissions" => $values["permissions"])));
  433. }
  434. /**
  435. * Function: add_permissions_table
  436. * Creates the "permissions" table and fills it in with the default set.
  437. *
  438. * Versions: 1.1.3.2 => 2.0
  439. */
  440. function add_permissions_table() {
  441. if (SQL::current()->query("SELECT * FROM __permissions")) return;
  442. $permissions_table = SQL::current()->query("CREATE TABLE __permissions (
  443. id VARCHAR(100) DEFAULT '' PRIMARY KEY,
  444. name VARCHAR(100) DEFAULT ''
  445. ) DEFAULT CHARSET=utf8");
  446. echo __("Creating new permissions table...").test($permissions_table);
  447. if (!$permissions_table) return;
  448. $permissions = array("change_settings" => "Change Settings",
  449. "toggle_extensions" => "Toggle Extensions",
  450. "view_site" => "View Site",
  451. "view_private" => "View Private Posts",
  452. "view_draft" => "View Drafts",
  453. "view_own_draft" => "View Own Drafts",
  454. "add_post" => "Add Posts",
  455. "add_draft" => "Add Drafts",
  456. "edit_post" => "Edit Posts",
  457. "edit_draft" => "Edit Drafts",
  458. "edit_own_post" => "Edit Own Posts",
  459. "edit_own_draft" => "Edit Own Drafts",
  460. "delete_post" => "Delete Posts",
  461. "delete_draft" => "Delete Drafts",
  462. "delete_own_post" => "Delete Own Posts",
  463. "delete_own_draft" => "Delete Own Drafts",
  464. "add_page" => "Add Pages",
  465. "edit_page" => "Edit Pages",
  466. "delete_page" => "Delete Pages",
  467. "add_user" => "Add Users",
  468. "edit_user" => "Edit Users",
  469. "delete_user" => "Delete Users",
  470. "add_group" => "Add Groups",
  471. "edit_group" => "Edit Groups",
  472. "delete_group" => "Delete Groups");
  473. foreach ($permissions as $id => $name)
  474. echo _f("Inserting permission \"%s\"...", array($name)).
  475. test(SQL::current()->insert("permissions",
  476. array("id" => $id,
  477. "name" => $name)));
  478. }
  479. /**
  480. * Function: add_sessions_table
  481. * Creates the "sessions" table.
  482. *
  483. * Versions: 1.1.3.2 => 2.0
  484. */
  485. function add_sessions_table() {
  486. if (SQL::current()->query("SELECT * FROM __sessions")) return;
  487. echo __("Creating `sessions` table...").
  488. test(SQL::current()->query("CREATE TABLE __sessions (
  489. id VARCHAR(40) DEFAULT '',
  490. data LONGTEXT,
  491. user_id INTEGER DEFAULT '0',
  492. created_at DATETIME DEFAULT '0000-00-00 00:00:00',
  493. updated_at DATETIME DEFAULT '0000-00-00 00:00:00',
  494. PRIMARY KEY (id)
  495. ) DEFAULT CHARSET=utf8") or die(mysql_error()));
  496. }
  497. /**
  498. * Function: update_permissions_table
  499. * Updates the "permissions" table from ## (id) => foo_bar (name) to foo_bar (id) => Foo Bar (name).
  500. *
  501. * Versions: 2.0b => 2.0
  502. */
  503. function update_permissions_table() {
  504. # If there are any non-numeric IDs in the permissions database, assume this is already done.
  505. $check = SQL::current()->query("SELECT * FROM __permissions");
  506. while ($row = $check->fetchObject())
  507. if (!is_numeric($row->id))
  508. return;
  509. $permissions_backup = array();
  510. $get_permissions = SQL::current()->query("SELECT * FROM __permissions");
  511. echo __("Backing up current permissions table...").test($get_permissions);
  512. if (!$get_permissions) return;
  513. while ($permission = $get_permissions->fetchObject())
  514. $permissions_backup[] = $permission->name;
  515. $drop_permissions = SQL::current()->query("DROP TABLE __permissions");
  516. echo __("Dropping old permissions table...").test($drop_permissions);
  517. if (!$drop_permissions) return;
  518. echo __("Creating new permissions table...").
  519. test(SQL::current()->query("CREATE TABLE IF NOT EXISTS __permissions (
  520. id VARCHAR(100) DEFAULT '' PRIMARY KEY,
  521. name VARCHAR(100) DEFAULT ''
  522. ) DEFAULT CHARSET=utf8"));
  523. $permissions = array("change_settings" => "Change Settings",
  524. "toggle_extensions" => "Toggle Extensions",
  525. "view_site" => "View Site",
  526. "view_private" => "View Private Posts",
  527. "view_draft" => "View Drafts",
  528. "view_own_draft" => "View Own Drafts",
  529. "add_post" => "Add Posts",
  530. "add_draft" => "Add Drafts",
  531. "edit_post" => "Edit Posts",
  532. "edit_draft" => "Edit Drafts",
  533. "edit_own_post" => "Edit Own Posts",
  534. "edit_own_draft" => "Edit Own Drafts",
  535. "delete_post" => "Delete Posts",
  536. "delete_draft" => "Delete Drafts",
  537. "delete_own_post" => "Delete Own Posts",
  538. "delete_own_draft" => "Delete Own Drafts",
  539. "add_page" => "Add Pages",
  540. "edit_page" => "Edit Pages",
  541. "delete_page" => "Delete Pages",
  542. "add_user" => "Add Users",
  543. "edit_user" => "Edit Users",
  544. "delete_user" => "Delete Users",
  545. "add_group" => "Add Groups",
  546. "edit_group" => "Edit Groups",
  547. "delete_group" => "Delete Groups");
  548. foreach ($permissions_backup as $id) {
  549. $name = isset($permissions[$id]) ? $permissions[$id] : camelize($id, true);
  550. echo _f("Restoring permission \"%s\"...", array($name)).
  551. test(SQL::current()->insert("permissions",
  552. array("id" => $id,
  553. "name" => $name)));
  554. }
  555. }
  556. /**
  557. * Function: update_custom_routes
  558. * Updates the custom routes to be path => action instead of # => path.
  559. *
  560. * Versions: 2.0rc1 => 2.0rc2
  561. */
  562. function update_custom_routes() {
  563. $custom_routes = Config::get("routes");
  564. if (empty($custom_routes)) return;
  565. $new_routes = array();
  566. foreach ($custom_routes as $key => $route) {
  567. if (!is_int($key))
  568. return;
  569. $split = array_filter(explode("/", $route));
  570. if (!isset($split[0]))
  571. return;
  572. echo _f("Updating custom route %s to new format...", array($route)).
  573. test(isset($split[0]) and $new_routes[$route] = $split[0]);
  574. }
  575. Config::set("routes", $new_routes, "Setting new custom routes configuration...");
  576. }
  577. /**
  578. * Function: remove_database_config_file
  579. * Removes the database.yaml.php file, which is merged into config.yaml.php.
  580. *
  581. * Versions: 2.0rc1 => 2.0rc2
  582. */
  583. function remove_database_config_file() {
  584. if (file_exists(INCLUDES_DIR."/database.yaml.php"))
  585. echo __("Removing database.yaml.php file...").
  586. test(@unlink(INCLUDES_DIR."/database.yaml.php"), __("Try deleting it manually."));
  587. }
  588. /**
  589. * Function: rename_database_setting_to_sql
  590. * Renames the "database" config setting to "sql".
  591. */
  592. function rename_database_setting_to_sql() {
  593. if (Config::check("sql")) return;
  594. Config::set("sql", Config::get("database"));
  595. Config::remove("database");
  596. }
  597. /**
  598. * Function: update_post_status_column
  599. * Updates the @status@ column on the "posts" table to be a generic varchar field instead of enum.
  600. *
  601. * Versions: 2.0rc1 => 2.0rc2
  602. */
  603. function update_post_status_column() {
  604. $sql = SQL::current();
  605. if (!$column = $sql->query("SHOW COLUMNS FROM __posts WHERE Field = 'status'"))
  606. return;
  607. if ($column->fetchObject()->Type == "varchar(32)")
  608. return;
  609. echo __("Updating `status` column on `posts` table...")."\n";
  610. echo " - ".__("Backing up `posts` table...").
  611. test($backup = $sql->select("posts"));
  612. if (!$backup)
  613. return;
  614. $backups = $backup->fetchAll();
  615. echo " - ".__("Dropping `posts` table...").
  616. test($drop = $sql->query("DROP TABLE __posts"));
  617. if (!$drop)
  618. return;
  619. echo " - ".__("Creating `posts` table...").
  620. test($create = $sql->query("CREATE TABLE IF NOT EXISTS __posts (
  621. id INTEGER PRIMARY KEY AUTO_INCREMENT,
  622. xml LONGTEXT,
  623. feather VARCHAR(32) DEFAULT '',
  624. clean VARCHAR(128) DEFAULT '',
  625. url VARCHAR(128) DEFAULT '',
  626. pinned TINYINT(1) DEFAULT 0,
  627. status VARCHAR(32) DEFAULT 'public',
  628. user_id INTEGER DEFAULT 0,
  629. created_at DATETIME DEFAULT '0000-00-00 00:00:00',
  630. updated_at DATETIME DEFAULT '0000-00-00 00:00:00'
  631. ) DEFAULT CHARSET=utf8"));
  632. if (!$create) {
  633. echo " -".test(false, _f("Backup written to %s.", array("./_posts.bak.txt")));
  634. return file_put_contents("./_posts.bak.txt", var_export($backups, true));
  635. }
  636. foreach ($backups as $backup) {
  637. echo " - "._f("Restoring post #%d...", array($backup["id"])).
  638. test($insert = $sql->insert("posts", $backup), _f("Backup written to %s.", array("./_posts.bak.txt")));
  639. if (!$insert)
  640. return file_put_contents("./_posts.bak.txt", var_export($backups, true));
  641. }
  642. echo " -".test(true);
  643. }
  644. /**
  645. * Function: add_post_attributes_table
  646. * Adds the "post_attributes" table.
  647. *
  648. * Versions: 2.0rc1 => 2.0rc2
  649. */
  650. function add_post_attributes_table() {
  651. $sql = SQL::current();
  652. if ($sql->select("post_attributes"))
  653. return;
  654. echo __("Creating `post_attributes` table...").
  655. test($sql->query("CREATE TABLE __post_attributes (
  656. post_id INTEGER NOT NULL ,
  657. name VARCHAR(100) DEFAULT '',
  658. value LONGTEXT,
  659. PRIMARY KEY (post_id, name)
  660. ) DEFAULT CHARSET=utf8"));
  661. }
  662. /**
  663. * Function: post_xml_to_db
  664. * Migrates the XML post attributes to the "post_attributes" table.
  665. *
  666. * Versions: 2.0rc1 => 2.0rc2
  667. */
  668. function post_xml_to_db() {
  669. $sql = SQL::current();
  670. if (!$rows = $sql->query("SELECT id, xml FROM __posts"))
  671. return;
  672. function insert_attributes($sql, $row, $xml, &$inserts) {
  673. foreach ($xml as $name => $value) {
  674. if (is_array($value))
  675. $value = YAML::dump($value);
  676. if (!$sql->insert("post_attributes",
  677. array("post_id" => $row["id"],
  678. "name" => $name,
  679. "value" => $value))) {
  680. # Clear successful attribute insertions so the
  681. # user can try again without primary key conflicts.
  682. foreach ($inserts as $insertion)
  683. $sql->delete("post_attributes",
  684. array("post_id" => $insertion["id"],
  685. "name" => $insertion["name"]));
  686. return false;
  687. } else
  688. $inserts[] = array("id" => $row["id"],
  689. "name" => $name);
  690. }
  691. return true;
  692. }
  693. $results = array();
  694. foreach ($rows->fetchAll() as $row) {
  695. if (empty($row["xml"]))
  696. continue;
  697. $xml = xml2arr(new SimpleXMLElement($row["xml"]));
  698. $inserts = array();
  699. echo _f("Migrating attributes of post #%d...", array($row["id"])).
  700. test($results[] = insert_attributes($sql, $row, $xml, $inserts));
  701. }
  702. if (!in_array(false, $results)) {
  703. echo __("Removing `xml` column from `posts` table...")."\n";
  704. echo " - ".__("Backing up `posts` table...").
  705. test($backup = $sql->select("posts"));
  706. if (!$backup)
  707. return;
  708. $backups = $backup->fetchAll();
  709. echo " - ".__("Dropping `posts` table...").
  710. test($drop = $sql->query("DROP TABLE __posts"));
  711. if (!$drop)
  712. return;
  713. echo " - ".__("Creating `posts` table...").
  714. test($create = $sql->query("CREATE TABLE IF NOT EXISTS __posts (
  715. id INTEGER PRIMARY KEY AUTO_INCREMENT,
  716. feather VARCHAR(32) DEFAULT '',
  717. clean VARCHAR(128) DEFAULT '',
  718. url VARCHAR(128) DEFAULT '',
  719. pinned TINYINT(1) DEFAULT 0,
  720. status VARCHAR(32) DEFAULT 'public',
  721. user_id INTEGER DEFAULT 0,
  722. created_at DATETIME DEFAULT '0000-00-00 00:00:00',
  723. updated_at DATETIME DEFAULT '0000-00-00 00:00:00'
  724. ) DEFAULT CHARSET=utf8"));
  725. if (!$create)
  726. return file_put_contents("./_posts.bak.txt", var_export($backups, true));
  727. foreach ($backups as $backup) {
  728. unset($backup["xml"]);
  729. echo " - "._f("Restoring post #%d...", array($backup["id"])).
  730. test($insert = $sql->insert("posts", $backup));
  731. if (!$insert)
  732. return file_put_contents("./_posts.bak.txt", var_export($backups, true));
  733. }
  734. echo " -".test(true);
  735. }
  736. }
  737. /**
  738. * Function: add_group_id_to_permissions
  739. * Adds the @group_id@ column to the "permissions" table.
  740. *
  741. * Versions: 2.0rc1 => 2.0rc2
  742. */
  743. function add_group_id_to_permissions() {
  744. $sql = SQL::current();
  745. if ($sql->select("permissions", "group_id"))
  746. return;
  747. echo __("Backing up permissions...").
  748. test($permissions = $sql->select("permissions"));
  749. if (!$permissions)
  750. return;
  751. $backup = $permissions->fetchAll();
  752. echo __("Dropping `permissions` table...").
  753. test($sql->query("DROP TABLE __permissions"));
  754. echo __("Creating `permissions` table...").
  755. test($sql->query("CREATE TABLE __permissions (
  756. id VARCHAR(100) DEFAULT '',
  757. name VARCHAR(100) DEFAULT '',
  758. group_id INTEGER DEFAULT 0,
  759. PRIMARY KEY (id, group_id)
  760. ) DEFAULT CHARSET=utf8"));
  761. foreach ($backup as $permission)
  762. echo _f("Restoring permission `%s`...", array($permission["name"])).
  763. test($sql->insert("permissions",
  764. array("id" => $permission["id"],
  765. "name" => $permission["name"],
  766. "group_id" => 0)));
  767. }
  768. /**
  769. * Function: group_permissions_to_db
  770. * Migrates the group permissions from a YAML column to the "permissions" table.
  771. *
  772. * Versions: 2.0rc1 => 2.0rc2
  773. */
  774. function group_permissions_to_db() {
  775. $sql = SQL::current();
  776. if (!$sql->select("groups", "permissions"))
  777. return;
  778. echo __("Backing up groups...").
  779. test($groups = $sql->select("groups"));
  780. if (!$groups)
  781. return;
  782. $backup = $groups->fetchAll();
  783. $names = array();
  784. foreach($backup as $group) {
  785. $names[$group["id"]] = $group["name"];
  786. $permissions[$group["id"]] = empty($group["permissions"]) ? array() : YAML::load($group["permissions"]) ;
  787. }
  788. echo __("Dropping `groups` table...").
  789. test($sql->query("DROP TABLE __groups"));
  790. echo __("Creating `groups` table...").
  791. test($sql->query("CREATE TABLE __groups (
  792. id INTEGER PRIMARY KEY AUTO_INCREMENT,
  793. name VARCHAR(100) DEFAULT '',
  794. UNIQUE (name)
  795. ) DEFAULT CHARSET=utf8"));
  796. foreach ($names as $id => $name)
  797. echo _f("Restoring group `%s`...", array($name)).
  798. test($sql->insert("groups",
  799. array("id" => $id,
  800. "name" => $name)));
  801. foreach ($permissions as $id => $permissions)
  802. foreach ($permissions as $permission)
  803. echo _f("Restoring permission `%s` on group `%s`...", array($permission, $names[$id])).
  804. test($sql->insert("permissions",
  805. array("id" => $permission,
  806. "name" => $sql->select("permissions", "name", array("id" => $permission))->fetchColumn(),
  807. "group_id" => $id)));
  808. }
  809. /**
  810. * Function: remove_old_files
  811. * Removes old/unused files from previous installs.
  812. */
  813. function remove_old_files() {
  814. if (file_exists(INCLUDES_DIR."/config.php"))
  815. echo __("Removing `includes/config.php` file...").
  816. test(@unlink(INCLUDES_DIR."/config.php"));
  817. if (file_exists(INCLUDES_DIR."/database.php"))
  818. echo __("Removing `includes/database.php` file...").
  819. test(@unlink(INCLUDES_DIR."/database.php"));
  820. if (file_exists(INCLUDES_DIR."/rss.php"))
  821. echo __("Removing `includes/rss.php` file...").
  822. test(@unlink(INCLUDES_DIR."/rss.php"));
  823. if (file_exists(INCLUDES_DIR."/bookmarklet.php"))
  824. echo __("Removing `includes/bookmarklet.php` file...").
  825. test(@unlink(INCLUDES_DIR."/bookmarklet.php"));
  826. }
  827. ?>
  828. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  829. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  830. <html xmlns="http://www.w3.org/1999/xhtml">
  831. <head>
  832. <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
  833. <title><?php echo __("Chyrp Upgrader"); ?></title>
  834. <style type="text/css" media="screen">
  835. html, body, ul, ol, li,
  836. h1, h2, h3, h4, h5, h6,
  837. form, fieldset, a, p {
  838. margin: 0;
  839. padding: 0;
  840. border: 0;
  841. }
  842. html {
  843. font-size: 62.5%;
  844. }
  845. body {
  846. font: 1.25em/1.5em normal Verdana, Helvetica, Arial, sans-serif;
  847. color: #626262;
  848. background: #e8e8e8;
  849. padding: 0 0 5em;
  850. }
  851. .window {
  852. width: 30em;
  853. background: #fff;
  854. padding: 2em;
  855. margin: 5em auto 0;
  856. -webkit-border-radius: 2em;
  857. -moz-border-radius: 2em;
  858. }
  859. h1 {
  860. color: #ccc;
  861. font-size: 3em;
  862. margin: 1em 0 .5em;
  863. text-align: center;
  864. }
  865. h1.first {
  866. margin-top: .25em;
  867. }
  868. h1.what_now {
  869. margin-top: .5em;
  870. }
  871. code {
  872. color: #06B;
  873. font-family: Monaco, monospace;
  874. }
  875. a:link, a:visited {
  876. color: #6B0;
  877. }
  878. pre.pane {
  879. height: 15em;
  880. overflow-y: auto;
  881. margin: -2.68em -2.68em 4em;
  882. padding: 2.5em;
  883. background: #333;
  884. color: #fff;
  885. -webkit-border-top-left-radius: 2.5em;
  886. -webkit-border-top-right-radius: 2.5em;
  887. -moz-border-radius-topleft: 2.5em;
  888. -moz-border-radius-topright: 2.5em;
  889. }
  890. span.yay { color: #0f0; }
  891. span.boo { color: #f00; }
  892. a.big,
  893. button {
  894. background: #eee;
  895. display: block;
  896. text-align: center;
  897. margin-top: 2em;
  898. padding: .75em 1em;
  899. color: #777;
  900. text-shadow: #fff .1em .1em 0;
  901. font: 1em normal "Lucida Grande", Verdana, Helvetica, Arial, sans-serif;
  902. text-decoration: none;
  903. border: 0;
  904. cursor: pointer;
  905. -webkit-border-radius: .5em;
  906. -moz-border-radius: .5em;
  907. }
  908. button {
  909. width: 100%;
  910. }
  911. a.big:hover,
  912. button:hover {
  913. background: #f5f5f5;
  914. }
  915. a.big:active,
  916. button:active {
  917. background: #e0e0e0;
  918. }
  919. ul, ol {
  920. margin: 0 0 1em 2em;
  921. }
  922. li {
  923. margin-bottom: .5em;
  924. }
  925. ul {
  926. margin-bottom: 1.5em;
  927. }
  928. p {
  929. margin-bottom: 1em;
  930. }
  931. </style>
  932. </head>
  933. <body>
  934. <div class="window">
  935. <?php if (!empty($_POST) and $_POST['upgrade'] == "yes"): ?>
  936. <pre class="pane"><?php
  937. # Begin with file/config upgrade tasks.
  938. fix_htaccess();
  939. remove_beginning_slash_from_post_url();
  940. move_yml_yaml();
  941. update_protection();
  942. theme_default_to_stardust();
  943. Config::fallback("routes", array());
  944. Config::fallback("secure_hashkey", md5(random(32, true)));
  945. Config::fallback("enable_xmlrpc", true);
  946. Config::fallback("enable_ajax", true);
  947. Config::fallback("uploads_path", "/uploads/");
  948. Config::fallback("chyrp_url", Config::get("url"));
  949. Config::fallback("sql", Config::$yaml["database"]);
  950. Config::fallback("timezone", "America/Indiana/Indianapolis");
  951. Config::remove("rss_posts");
  952. Config::remove("time_offset");
  953. move_upload();
  954. remove_database_config_file();
  955. rename_database_setting_to_sql();
  956. update_custom_routes();
  957. default_db_adapter_to_mysql();
  958. # Perform database upgrade tasks after all the files/config upgrade tasks are done.
  959. # Prepare the SQL interface.
  960. $sql = SQL::current();
  961. # Set the SQL info.
  962. foreach (Config::$yaml["config"]["sql"] as $name => $value)
  963. $sql->$name = $value;
  964. # Initialize connection to SQL server.
  965. $sql->connect();
  966. tweets_to_posts();
  967. pages_parent_id_column();
  968. pages_list_order_column();
  969. make_posts_safe();
  970. rss_posts_to_feed_items();
  971. update_groups_to_yaml();
  972. add_permissions_table();
  973. add_sessions_table();
  974. update_permissions_table();
  975. update_post_status_column();
  976. add_post_attributes_table();
  977. post_xml_to_db();
  978. add_group_id_to_permissions();
  979. group_permissions_to_db();
  980. remove_old_files();
  981. # Perform Module/Feather upgrades.
  982. foreach ((array) Config::get("enabled_modules") as $module)
  983. if (file_exists(MAIN_DIR."/modules/".$module."/upgrades.php")) {
  984. ob_start();
  985. echo $begin = _f("Calling <span class=\"yay\">%s</span> Module's upgrader...", array($module))."\n";
  986. require MAIN_DIR."/modules/".$module."/upgrades.php";
  987. $buf = ob_get_contents();
  988. if (ob_get_contents() == $begin)
  989. ob_end_clean();
  990. else
  991. ob_end_flush();
  992. }
  993. foreach ((array) Config::get("enabled_feathers") as $feather)
  994. if (file_exists(MAIN_DIR."/feathers/".$feather."/upgrades.php")) {
  995. ob_start();
  996. echo $begin = _f("Calling <span class=\"yay\">%s</span> Feather's upgrader...", array($feather))."\n";
  997. require MAIN_DIR."/feathers/".$feather."/upgrades.php";
  998. $buf = ob_get_contents();
  999. if (ob_get_contents() == $begin)
  1000. ob_end_clean();
  1001. else
  1002. ob_end_flush();
  1003. }
  1004. ?>
  1005. <?php echo __("Done!"); ?>
  1006. </pre>
  1007. <h1 class="what_now"><?php echo __("What now?"); ?></h1>
  1008. <ol>
  1009. <li><?php echo __("Look through the results up there for any failed tasks. If you see any and you can't figure out why, you can ask for help at the <a href=\"http://chyrp.net/community/\">Chyrp Community</a>."); ?></li>
  1010. <li><?php echo __("If any of your Modules or Feathers have new versions available for this release, check if an <code>upgrades.php</code> file exists in their main directory. If that file exists, run this upgrader again after enabling the Module or Feather and it will run the upgrade tasks."); ?></li>
  1011. <li><?php echo __("When you are done, you can delete this file. It doesn't pose any real threat on its own, but you should delete it anyway, just to be sure."); ?></li>
  1012. </ol>
  1013. <h1 class="tips"><?php echo __("Tips"); ?></h1>
  1014. <ul>
  1015. <li><?php echo __("If the admin area looks weird, try clearing your cache."); ?></li>
  1016. <li><?php echo __("As of v2.0, Chyrp uses time zones to determine timestamps. Please set your installation to the correct timezone at <a href=\"admin/index.php?action=general_settings\">General Settings</a>."); ?></li>
  1017. <li><?php echo __("Check the group permissions &ndash; they might have changed, and certain Admin functionality would be disabled until you enabled the permissions for the particular groups. <a href=\"admin/index.php?action=manage_groups\">Manage Groups &rarr;</a>"); ?></li>
  1018. </ul>
  1019. <a class="big" href="<?php echo (Config::check("url") ? Config::get("url") : Config::get("chyrp_url")); ?>"><?php echo __("All done!"); ?></a>
  1020. <?php else: ?>
  1021. <h1 class="first"><?php echo __("Halt!"); ?></h1>
  1022. <p><?php echo __("That button may look ready for a-clickin&rsquo;, but please take these preemptive measures before indulging:"); ?></p>
  1023. <ol>
  1024. <li><?php echo __("<strong>Make a backup of your installation.</strong> You never know."); ?></li>
  1025. <li><?php echo __("Disable any third-party Modules and Feathers."); ?></li>
  1026. <li><?php echo __("Ensure that the Chyrp installation directory is writable by the server."); ?></li>
  1027. </ol>
  1028. <p><?php echo __("If any of the upgrade processes fail, you can safely keep refreshing &ndash; it will only attempt to do tasks that are not already successfully completed. If you cannot figure something out, please make a topic (with details!) at the <a href=\"http://chyrp.net/community/\">Chyrp Community</a>."); ?></p>
  1029. <form action="upgrade.php" method="post">
  1030. <button type="submit" name="upgrade" value="yes"><?php echo __("Upgrade me!"); ?></button>
  1031. </form>
  1032. <?php endif; ?>
  1033. </div>
  1034. </body>
  1035. </html>