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.

308 lines
8.4KB

  1. <?php
  2. /**
  3. * @file src/Model/Term
  4. */
  5. namespace Friendica\Model;
  6. use Friendica\Core\System;
  7. use Friendica\Database\DBA;
  8. class Term
  9. {
  10. public static function tagTextFromItemId($itemid)
  11. {
  12. $tag_text = '';
  13. $condition = ['otype' => TERM_OBJ_POST, 'oid' => $itemid, 'type' => [TERM_HASHTAG, TERM_MENTION]];
  14. $tags = DBA::select('term', [], $condition);
  15. while ($tag = DBA::fetch($tags)) {
  16. if ($tag_text != '') {
  17. $tag_text .= ',';
  18. }
  19. if ($tag['type'] == 1) {
  20. $tag_text .= '#';
  21. } else {
  22. $tag_text .= '@';
  23. }
  24. $tag_text .= '[url=' . $tag['url'] . ']' . $tag['term'] . '[/url]';
  25. }
  26. return $tag_text;
  27. }
  28. public static function tagArrayFromItemId($itemid, $type = [TERM_HASHTAG, TERM_MENTION])
  29. {
  30. $condition = ['otype' => TERM_OBJ_POST, 'oid' => $itemid, 'type' => $type];
  31. $tags = DBA::select('term', ['type', 'term', 'url'], $condition);
  32. if (!DBA::isResult($tags)) {
  33. return [];
  34. }
  35. return DBA::toArray($tags);
  36. }
  37. public static function fileTextFromItemId($itemid)
  38. {
  39. $file_text = '';
  40. $condition = ['otype' => TERM_OBJ_POST, 'oid' => $itemid, 'type' => [TERM_FILE, TERM_CATEGORY]];
  41. $tags = DBA::select('term', [], $condition);
  42. while ($tag = DBA::fetch($tags)) {
  43. if ($tag['type'] == TERM_CATEGORY) {
  44. $file_text .= '<' . $tag['term'] . '>';
  45. } else {
  46. $file_text .= '[' . $tag['term'] . ']';
  47. }
  48. }
  49. return $file_text;
  50. }
  51. public static function insertFromTagFieldByItemId($itemid, $tags)
  52. {
  53. $profile_base = System::baseUrl();
  54. $profile_data = parse_url($profile_base);
  55. $profile_path = defaults($profile_data, 'path', '');
  56. $profile_base_friendica = $profile_data['host'] . $profile_path . '/profile/';
  57. $profile_base_diaspora = $profile_data['host'] . $profile_path . '/u/';
  58. $fields = ['guid', 'uid', 'id', 'edited', 'deleted', 'created', 'received', 'title', 'body', 'parent'];
  59. $message = Item::selectFirst($fields, ['id' => $itemid]);
  60. if (!DBA::isResult($message)) {
  61. return;
  62. }
  63. $message['tag'] = $tags;
  64. // Clean up all tags
  65. self::deleteByItemId($itemid);
  66. if ($message['deleted']) {
  67. return;
  68. }
  69. $taglist = explode(',', $message['tag']);
  70. $tags_string = '';
  71. foreach ($taglist as $tag) {
  72. if ((substr(trim($tag), 0, 1) == '#') || (substr(trim($tag), 0, 1) == '@') || (substr(trim($tag), 0, 1) == '!')) {
  73. $tags_string .= ' ' . trim($tag);
  74. } else {
  75. $tags_string .= ' #' . trim($tag);
  76. }
  77. }
  78. $data = ' ' . $message['title'] . ' ' . $message['body'] . ' ' . $tags_string . ' ';
  79. // ignore anything in a code block
  80. $data = preg_replace('/\[code\](.*?)\[\/code\]/sm', '', $data);
  81. $tags = [];
  82. $pattern = '/\W\#([^\[].*?)[\s\'".,:;\?!\[\]\/]/ism';
  83. if (preg_match_all($pattern, $data, $matches)) {
  84. foreach ($matches[1] as $match) {
  85. $tags['#' . $match] = '';
  86. }
  87. }
  88. $pattern = '/\W([\#@!])\[url\=(.*?)\](.*?)\[\/url\]/ism';
  89. if (preg_match_all($pattern, $data, $matches, PREG_SET_ORDER)) {
  90. foreach ($matches as $match) {
  91. if (($match[1] == '@') || ($match[1] == '!')) {
  92. $contact = Contact::getDetailsByURL($match[2], 0);
  93. if (!empty($contact['addr'])) {
  94. $match[3] = $contact['addr'];
  95. }
  96. if (!empty($contact['url'])) {
  97. $match[2] = $contact['url'];
  98. }
  99. }
  100. $tags[$match[1] . trim($match[3], ',.:;[]/\"?!')] = $match[2];
  101. }
  102. }
  103. foreach ($tags as $tag => $link) {
  104. if (substr(trim($tag), 0, 1) == '#') {
  105. // try to ignore #039 or #1 or anything like that
  106. if (ctype_digit(substr(trim($tag), 1))) {
  107. continue;
  108. }
  109. // try to ignore html hex escapes, e.g. #x2317
  110. if ((substr(trim($tag), 1, 1) == 'x' || substr(trim($tag), 1, 1) == 'X') && ctype_digit(substr(trim($tag), 2))) {
  111. continue;
  112. }
  113. $type = TERM_HASHTAG;
  114. $term = substr($tag, 1);
  115. $link = '';
  116. } elseif ((substr(trim($tag), 0, 1) == '@') || (substr(trim($tag), 0, 1) == '!')) {
  117. $type = TERM_MENTION;
  118. $contact = Contact::getDetailsByURL($link, 0);
  119. if (!empty($contact['name'])) {
  120. $term = $contact['name'];
  121. } else {
  122. $term = substr($tag, 1);
  123. }
  124. } else { // This shouldn't happen
  125. $type = TERM_HASHTAG;
  126. $term = $tag;
  127. $link = '';
  128. }
  129. if (DBA::exists('term', ['uid' => $message['uid'], 'otype' => TERM_OBJ_POST, 'oid' => $itemid, 'term' => $term])) {
  130. continue;
  131. }
  132. if ($message['uid'] == 0) {
  133. $global = true;
  134. DBA::update('term', ['global' => true], ['otype' => TERM_OBJ_POST, 'guid' => $message['guid']]);
  135. } else {
  136. $global = DBA::exists('term', ['uid' => 0, 'otype' => TERM_OBJ_POST, 'guid' => $message['guid']]);
  137. }
  138. DBA::insert('term', [
  139. 'uid' => $message['uid'],
  140. 'oid' => $itemid,
  141. 'otype' => TERM_OBJ_POST,
  142. 'type' => $type,
  143. 'term' => $term,
  144. 'url' => $link,
  145. 'guid' => $message['guid'],
  146. 'created' => $message['created'],
  147. 'received' => $message['received'],
  148. 'global' => $global
  149. ]);
  150. // Search for mentions
  151. if (((substr($tag, 0, 1) == '@') || (substr($tag, 0, 1) == '!')) && (strpos($link, $profile_base_friendica) || strpos($link, $profile_base_diaspora))) {
  152. $users = q("SELECT `uid` FROM `contact` WHERE self AND (`url` = '%s' OR `nurl` = '%s')", $link, $link);
  153. foreach ($users AS $user) {
  154. if ($user['uid'] == $message['uid']) {
  155. /// @todo This function is called frim Item::update - so we mustn't call that function here
  156. DBA::update('item', ['mention' => true], ['id' => $itemid]);
  157. DBA::update('thread', ['mention' => true], ['iid' => $message['parent']]);
  158. }
  159. }
  160. }
  161. }
  162. }
  163. /**
  164. * @param integer $itemid item id
  165. * @return void
  166. */
  167. public static function insertFromFileFieldByItemId($itemid, $files)
  168. {
  169. $message = Item::selectFirst(['uid', 'deleted'], ['id' => $itemid]);
  170. if (!DBA::isResult($message)) {
  171. return;
  172. }
  173. // Clean up all tags
  174. DBA::delete('term', ['otype' => TERM_OBJ_POST, 'oid' => $itemid, 'type' => [TERM_FILE, TERM_CATEGORY]]);
  175. if ($message["deleted"]) {
  176. return;
  177. }
  178. $message['file'] = $files;
  179. if (preg_match_all("/\[(.*?)\]/ism", $message["file"], $files)) {
  180. foreach ($files[1] as $file) {
  181. DBA::insert('term', [
  182. 'uid' => $message["uid"],
  183. 'oid' => $itemid,
  184. 'otype' => TERM_OBJ_POST,
  185. 'type' => TERM_FILE,
  186. 'term' => $file
  187. ]);
  188. }
  189. }
  190. if (preg_match_all("/\<(.*?)\>/ism", $message["file"], $files)) {
  191. foreach ($files[1] as $file) {
  192. DBA::insert('term', [
  193. 'uid' => $message["uid"],
  194. 'oid' => $itemid,
  195. 'otype' => TERM_OBJ_POST,
  196. 'type' => TERM_CATEGORY,
  197. 'term' => $file
  198. ]);
  199. }
  200. }
  201. }
  202. /**
  203. * Sorts an item's tags into mentions, hashtags and other tags. Generate personalized URLs by user and modify the
  204. * provided item's body with them.
  205. *
  206. * @param array $item
  207. * @return array
  208. */
  209. public static function populateTagsFromItem(&$item)
  210. {
  211. $return = [
  212. 'tags' => [],
  213. 'hashtags' => [],
  214. 'mentions' => [],
  215. ];
  216. $searchpath = System::baseUrl() . "/search?tag=";
  217. $taglist = DBA::select(
  218. 'term',
  219. ['type', 'term', 'url'],
  220. ["`otype` = ? AND `oid` = ? AND `type` IN (?, ?)", TERM_OBJ_POST, $item['id'], TERM_HASHTAG, TERM_MENTION],
  221. ['order' => ['tid']]
  222. );
  223. while ($tag = DBA::fetch($taglist)) {
  224. if ($tag['url'] == '') {
  225. $tag['url'] = $searchpath . rawurlencode($tag['term']);
  226. }
  227. $orig_tag = $tag['url'];
  228. $author = ['uid' => 0, 'id' => $item['author-id'],
  229. 'network' => $item['author-network'], 'url' => $item['author-link']];
  230. $tag['url'] = Contact::magicLinkByContact($author, $tag['url']);
  231. if ($tag['type'] == TERM_HASHTAG) {
  232. if ($orig_tag != $tag['url']) {
  233. $item['body'] = str_replace($orig_tag, $tag['url'], $item['body']);
  234. }
  235. $return['hashtags'][] = '#<a href="' . $tag['url'] . '" target="_blank">' . $tag['term'] . '</a>';
  236. $prefix = '#';
  237. } elseif ($tag['type'] == TERM_MENTION) {
  238. $return['mentions'][] = '@<a href="' . $tag['url'] . '" target="_blank">' . $tag['term'] . '</a>';
  239. $prefix = '@';
  240. }
  241. $return['tags'][] = $prefix . '<a href="' . $tag['url'] . '" target="_blank">' . $tag['term'] . '</a>';
  242. }
  243. DBA::close($taglist);
  244. return $return;
  245. }
  246. /**
  247. * Delete all tags from an item
  248. * @param int itemid - choose from which item the tags will be removed
  249. * @param array type - items type. default is [TERM_HASHTAG, TERM_MENTION]
  250. */
  251. public static function deleteByItemId($itemid, $type = [TERM_HASHTAG, TERM_MENTION])
  252. {
  253. if (empty($itemid)) {
  254. return;
  255. }
  256. // Clean up all tags
  257. DBA::delete('term', ['otype' => TERM_OBJ_POST, 'oid' => $itemid, 'type' => $type]);
  258. }
  259. }