KXStudio Website https://kx.studio/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1295 lines
49KB

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