Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
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.

377 lines
11KB

  1. <?php
  2. /**
  3. * @file src/Database/PostUpdate.php
  4. */
  5. namespace Friendica\Database;
  6. use Friendica\Core\Config;
  7. use Friendica\Core\Logger;
  8. use Friendica\Core\Protocol;
  9. use Friendica\Model\Contact;
  10. use Friendica\Model\Item;
  11. use Friendica\Model\ItemURI;
  12. use Friendica\Model\PermissionSet;
  13. use Friendica\Database\DBA;
  14. /**
  15. * Post update functions
  16. */
  17. class PostUpdate
  18. {
  19. /**
  20. * @brief Calls the post update functions
  21. */
  22. public static function update()
  23. {
  24. if (!self::update1194()) {
  25. return false;
  26. }
  27. if (!self::update1206()) {
  28. return false;
  29. }
  30. if (!self::update1279()) {
  31. return false;
  32. }
  33. if (!self::update1281()) {
  34. return false;
  35. }
  36. return true;
  37. }
  38. /**
  39. * @brief Updates the "global" field in the item table
  40. *
  41. * @return bool "true" when the job is done
  42. */
  43. private static function update1194()
  44. {
  45. // Was the script completed?
  46. if (Config::get("system", "post_update_version") >= 1194) {
  47. return true;
  48. }
  49. Logger::log("Start", Logger::DEBUG);
  50. $end_id = Config::get("system", "post_update_1194_end");
  51. if (!$end_id) {
  52. $r = q("SELECT `id` FROM `item` WHERE `uid` != 0 ORDER BY `id` DESC LIMIT 1");
  53. if ($r) {
  54. Config::set("system", "post_update_1194_end", $r[0]["id"]);
  55. $end_id = Config::get("system", "post_update_1194_end");
  56. }
  57. }
  58. Logger::log("End ID: ".$end_id, Logger::DEBUG);
  59. $start_id = Config::get("system", "post_update_1194_start");
  60. $query1 = "SELECT `item`.`id` FROM `item` ";
  61. $query2 = "INNER JOIN `item` AS `shadow` ON `item`.`uri` = `shadow`.`uri` AND `shadow`.`uid` = 0 ";
  62. $query3 = "WHERE `item`.`uid` != 0 AND `item`.`id` >= %d AND `item`.`id` <= %d
  63. AND `item`.`visible` AND NOT `item`.`private`
  64. AND NOT `item`.`deleted` AND NOT `item`.`moderated`
  65. AND `item`.`network` IN ('%s', '%s', '%s', '')
  66. AND NOT `item`.`global`";
  67. $r = q($query1.$query2.$query3." ORDER BY `item`.`id` LIMIT 1",
  68. intval($start_id), intval($end_id),
  69. DBA::escape(Protocol::DFRN), DBA::escape(Protocol::DIASPORA), DBA::escape(Protocol::OSTATUS));
  70. if (!$r) {
  71. Config::set("system", "post_update_version", 1194);
  72. Logger::log("Update is done", Logger::DEBUG);
  73. return true;
  74. } else {
  75. Config::set("system", "post_update_1194_start", $r[0]["id"]);
  76. $start_id = Config::get("system", "post_update_1194_start");
  77. }
  78. Logger::log("Start ID: ".$start_id, Logger::DEBUG);
  79. $r = q($query1.$query2.$query3." ORDER BY `item`.`id` LIMIT 1000,1",
  80. intval($start_id), intval($end_id),
  81. DBA::escape(Protocol::DFRN), DBA::escape(Protocol::DIASPORA), DBA::escape(Protocol::OSTATUS));
  82. if ($r) {
  83. $pos_id = $r[0]["id"];
  84. } else {
  85. $pos_id = $end_id;
  86. }
  87. Logger::log("Progress: Start: ".$start_id." position: ".$pos_id." end: ".$end_id, Logger::DEBUG);
  88. q("UPDATE `item` ".$query2." SET `item`.`global` = 1 ".$query3,
  89. intval($start_id), intval($pos_id),
  90. DBA::escape(Protocol::DFRN), DBA::escape(Protocol::DIASPORA), DBA::escape(Protocol::OSTATUS));
  91. Logger::log("Done", Logger::DEBUG);
  92. }
  93. /**
  94. * @brief update the "last-item" field in the "self" contact
  95. *
  96. * This field avoids cost intensive calls in the admin panel and in "nodeinfo"
  97. *
  98. * @return bool "true" when the job is done
  99. */
  100. private static function update1206()
  101. {
  102. // Was the script completed?
  103. if (Config::get("system", "post_update_version") >= 1206) {
  104. return true;
  105. }
  106. Logger::log("Start", Logger::DEBUG);
  107. $r = q("SELECT `contact`.`id`, `contact`.`last-item`,
  108. (SELECT MAX(`changed`) FROM `item` USE INDEX (`uid_wall_changed`) WHERE `wall` AND `uid` = `user`.`uid`) AS `lastitem_date`
  109. FROM `user`
  110. INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self`");
  111. if (!DBA::isResult($r)) {
  112. return false;
  113. }
  114. foreach ($r as $user) {
  115. if (!empty($user["lastitem_date"]) && ($user["lastitem_date"] > $user["last-item"])) {
  116. DBA::update('contact', ['last-item' => $user['lastitem_date']], ['id' => $user['id']]);
  117. }
  118. }
  119. Config::set("system", "post_update_version", 1206);
  120. Logger::log("Done", Logger::DEBUG);
  121. return true;
  122. }
  123. /**
  124. * @brief update the item related tables
  125. *
  126. * @return bool "true" when the job is done
  127. */
  128. private static function update1279()
  129. {
  130. // Was the script completed?
  131. if (Config::get("system", "post_update_version") >= 1279) {
  132. return true;
  133. }
  134. $id = Config::get("system", "post_update_version_1279_id", 0);
  135. Logger::log("Start from item " . $id, Logger::DEBUG);
  136. $fields = array_merge(Item::MIXED_CONTENT_FIELDLIST, ['network', 'author-id', 'owner-id', 'tag', 'file',
  137. 'author-name', 'author-avatar', 'author-link', 'owner-name', 'owner-avatar', 'owner-link', 'id',
  138. 'uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'psid', 'post-type', 'bookmark', 'type',
  139. 'inform', 'postopts', 'icid']);
  140. $start_id = $id;
  141. $rows = 0;
  142. $condition = ["`id` > ?", $id];
  143. $params = ['order' => ['id'], 'limit' => 10000];
  144. $items = Item::select($fields, $condition, $params);
  145. if (DBA::errorNo() != 0) {
  146. Logger::log('Database error ' . DBA::errorNo() . ':' . DBA::errorMessage());
  147. return false;
  148. }
  149. while ($item = Item::fetch($items)) {
  150. $id = $item['id'];
  151. if (empty($item['author-id'])) {
  152. $default = ['url' => $item['author-link'], 'name' => $item['author-name'],
  153. 'photo' => $item['author-avatar'], 'network' => $item['network']];
  154. $item['author-id'] = Contact::getIdForURL($item["author-link"], 0, false, $default);
  155. }
  156. if (empty($item['owner-id'])) {
  157. $default = ['url' => $item['owner-link'], 'name' => $item['owner-name'],
  158. 'photo' => $item['owner-avatar'], 'network' => $item['network']];
  159. $item['owner-id'] = Contact::getIdForURL($item["owner-link"], 0, false, $default);
  160. }
  161. if (empty($item['psid'])) {
  162. $item['psid'] = PermissionSet::fetchIDForPost($item);
  163. } else {
  164. $item['allow_cid'] = null;
  165. $item['allow_gid'] = null;
  166. $item['deny_cid'] = null;
  167. $item['deny_gid'] = null;
  168. }
  169. if ($item['post-type'] == 0) {
  170. if (!empty($item['type']) && ($item['type'] == 'note')) {
  171. $item['post-type'] = Item::PT_PERSONAL_NOTE;
  172. } elseif (!empty($item['type']) && ($item['type'] == 'photo')) {
  173. $item['post-type'] = Item::PT_IMAGE;
  174. } elseif (!empty($item['bookmark']) && $item['bookmark']) {
  175. $item['post-type'] = Item::PT_PAGE;
  176. }
  177. }
  178. self::createLanguage($item);
  179. if (!empty($item['icid']) && !empty($item['language'])) {
  180. DBA::update('item-content', ['language' => $item['language']], ['id' => $item['icid']]);
  181. }
  182. unset($item['language']);
  183. Item::update($item, ['id' => $id]);
  184. ++$rows;
  185. }
  186. DBA::close($items);
  187. Config::set("system", "post_update_version_1279_id", $id);
  188. Logger::log("Processed rows: " . $rows . " - last processed item: " . $id, Logger::DEBUG);
  189. if ($start_id == $id) {
  190. // Set all deprecated fields to "null" if they contain an empty string
  191. $nullfields = ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'postopts', 'inform', 'type',
  192. 'bookmark', 'file', 'location', 'coord', 'tag', 'plink', 'title', 'content-warning',
  193. 'body', 'app', 'verb', 'object-type', 'object', 'target-type', 'target',
  194. 'author-name', 'author-link', 'author-avatar', 'owner-name', 'owner-link', 'owner-avatar',
  195. 'rendered-hash', 'rendered-html'];
  196. foreach ($nullfields as $field) {
  197. $fields = [$field => null];
  198. $condition = [$field => ''];
  199. Logger::log("Setting '" . $field . "' to null if empty.", Logger::DEBUG);
  200. // Important: This has to be a "DBA::update", not a "Item::update"
  201. DBA::update('item', $fields, $condition);
  202. }
  203. Config::set("system", "post_update_version", 1279);
  204. Logger::log("Done", Logger::DEBUG);
  205. return true;
  206. }
  207. return false;
  208. }
  209. private static function createLanguage(&$item)
  210. {
  211. if (empty($item['postopts'])) {
  212. return;
  213. }
  214. $opts = explode(',', $item['postopts']);
  215. $postopts = [];
  216. foreach ($opts as $opt) {
  217. if (strstr($opt, 'lang=')) {
  218. $language = substr($opt, 5);
  219. } else {
  220. $postopts[] = $opt;
  221. }
  222. }
  223. if (empty($language)) {
  224. return;
  225. }
  226. if (!empty($postopts)) {
  227. $item['postopts'] = implode(',', $postopts);
  228. } else {
  229. $item['postopts'] = null;
  230. }
  231. $lang_pairs = explode(':', $language);
  232. $lang_arr = [];
  233. foreach ($lang_pairs as $pair) {
  234. $lang_pair_arr = explode(';', $pair);
  235. if (count($lang_pair_arr) == 2) {
  236. $lang_arr[$lang_pair_arr[0]] = $lang_pair_arr[1];
  237. }
  238. }
  239. $item['language'] = json_encode($lang_arr);
  240. }
  241. /**
  242. * @brief update item-uri data. Prerequisite for the next item structure update.
  243. *
  244. * @return bool "true" when the job is done
  245. */
  246. private static function update1281()
  247. {
  248. // Was the script completed?
  249. if (Config::get("system", "post_update_version") >= 1281) {
  250. return true;
  251. }
  252. $id = Config::get("system", "post_update_version_1281_id", 0);
  253. Logger::log("Start from item " . $id, Logger::DEBUG);
  254. $fields = ['id', 'guid', 'uri', 'uri-id', 'parent-uri', 'parent-uri-id', 'thr-parent', 'thr-parent-id'];
  255. $start_id = $id;
  256. $rows = 0;
  257. $condition = ["`id` > ?", $id];
  258. $params = ['order' => ['id'], 'limit' => 10000];
  259. $items = DBA::select('item', $fields, $condition, $params);
  260. if (DBA::errorNo() != 0) {
  261. Logger::log('Database error ' . DBA::errorNo() . ':' . DBA::errorMessage());
  262. return false;
  263. }
  264. while ($item = DBA::fetch($items)) {
  265. $id = $item['id'];
  266. if (empty($item['uri'])) {
  267. // Should not happen
  268. continue;
  269. } elseif (empty($item['uri-id'])) {
  270. $item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
  271. }
  272. if (empty($item['parent-uri'])) {
  273. $item['parent-uri-id'] = $item['uri-id'];
  274. } elseif (empty($item['parent-uri-id'])) {
  275. $item['parent-uri-id'] = ItemURI::getIdByURI($item['parent-uri']);
  276. }
  277. // Very old items don't have this field
  278. if (empty($item['thr-parent'])) {
  279. $item['thr-parent-id'] = $item['parent-uri-id'];
  280. } elseif (empty($item['thr-parent-id'])) {
  281. $item['thr-parent-id'] = ItemURI::getIdByURI($item['thr-parent']);
  282. }
  283. unset($item['id']);
  284. unset($item['guid']);
  285. unset($item['uri']);
  286. unset($item['parent-uri']);
  287. unset($item['thr-parent']);
  288. DBA::update('item', $item, ['id' => $id]);
  289. ++$rows;
  290. }
  291. DBA::close($items);
  292. Config::set("system", "post_update_version_1281_id", $id);
  293. Logger::log("Processed rows: " . $rows . " - last processed item: " . $id, Logger::DEBUG);
  294. if ($start_id == $id) {
  295. Logger::log("Updating item-uri in item-activity", Logger::DEBUG);
  296. DBA::e("UPDATE `item-activity` INNER JOIN `item-uri` ON `item-uri`.`uri` = `item-activity`.`uri` SET `item-activity`.`uri-id` = `item-uri`.`id` WHERE `item-activity`.`uri-id` IS NULL");
  297. Logger::log("Updating item-uri in item-content", Logger::DEBUG);
  298. DBA::e("UPDATE `item-content` INNER JOIN `item-uri` ON `item-uri`.`uri` = `item-content`.`uri` SET `item-content`.`uri-id` = `item-uri`.`id` WHERE `item-content`.`uri-id` IS NULL");
  299. Config::set("system", "post_update_version", 1281);
  300. Logger::log("Done", Logger::DEBUG);
  301. return true;
  302. }
  303. return false;
  304. }
  305. }