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.

254 lines
6.7KB

  1. <?php
  2. /**
  3. * @file mod/search.php
  4. */
  5. use Friendica\App;
  6. use Friendica\Content\Feature;
  7. use Friendica\Content\Nav;
  8. use Friendica\Content\Pager;
  9. use Friendica\Content\Text\HTML;
  10. use Friendica\Core\Cache;
  11. use Friendica\Core\Config;
  12. use Friendica\Core\L10n;
  13. use Friendica\Core\Logger;
  14. use Friendica\Core\Renderer;
  15. use Friendica\Core\System;
  16. use Friendica\Database\DBA;
  17. use Friendica\Model\Item;
  18. use Friendica\Util\Strings;
  19. require_once 'mod/dirfind.php';
  20. function search_saved_searches() {
  21. $o = '';
  22. $search = (!empty($_GET['search']) ? Strings::escapeTags(trim(rawurldecode($_GET['search']))) : '');
  23. $r = q("SELECT `id`,`term` FROM `search` WHERE `uid` = %d",
  24. intval(local_user())
  25. );
  26. if (DBA::isResult($r)) {
  27. $saved = [];
  28. foreach ($r as $rr) {
  29. $saved[] = [
  30. 'id' => $rr['id'],
  31. 'term' => $rr['term'],
  32. 'encodedterm' => urlencode($rr['term']),
  33. 'delete' => L10n::t('Remove term'),
  34. 'selected' => ($search==$rr['term']),
  35. ];
  36. }
  37. $tpl = Renderer::getMarkupTemplate("saved_searches_aside.tpl");
  38. $o .= Renderer::replaceMacros($tpl, [
  39. '$title' => L10n::t('Saved Searches'),
  40. '$add' => '',
  41. '$searchbox' => '',
  42. '$saved' => $saved,
  43. ]);
  44. }
  45. return $o;
  46. }
  47. function search_init(App $a) {
  48. $search = (!empty($_GET['search']) ? Strings::escapeTags(trim(rawurldecode($_GET['search']))) : '');
  49. if (local_user()) {
  50. if (!empty($_GET['save']) && $search) {
  51. $r = q("SELECT * FROM `search` WHERE `uid` = %d AND `term` = '%s' LIMIT 1",
  52. intval(local_user()),
  53. DBA::escape($search)
  54. );
  55. if (!DBA::isResult($r)) {
  56. DBA::insert('search', ['uid' => local_user(), 'term' => $search]);
  57. }
  58. }
  59. if (!empty($_GET['remove']) && $search) {
  60. DBA::delete('search', ['uid' => local_user(), 'term' => $search]);
  61. }
  62. /// @todo Check if there is a case at all that "aside" is prefilled here
  63. if (!isset($a->page['aside'])) {
  64. $a->page['aside'] = '';
  65. }
  66. $a->page['aside'] .= search_saved_searches();
  67. } else {
  68. unset($_SESSION['theme']);
  69. unset($_SESSION['mobile-theme']);
  70. }
  71. }
  72. function search_content(App $a) {
  73. if (Config::get('system','block_public') && !local_user() && !remote_user()) {
  74. notice(L10n::t('Public access denied.') . EOL);
  75. return;
  76. }
  77. if (Config::get('system','local_search') && !local_user() && !remote_user()) {
  78. System::httpExit(403,
  79. ["title" => L10n::t("Public access denied."),
  80. "description" => L10n::t("Only logged in users are permitted to perform a search.")]);
  81. killme();
  82. //notice(L10n::t('Public access denied.').EOL);
  83. //return;
  84. }
  85. if (Config::get('system','permit_crawling') && !local_user() && !remote_user()) {
  86. // Default values:
  87. // 10 requests are "free", after the 11th only a call per minute is allowed
  88. $free_crawls = intval(Config::get('system','free_crawls'));
  89. if ($free_crawls == 0)
  90. $free_crawls = 10;
  91. $crawl_permit_period = intval(Config::get('system','crawl_permit_period'));
  92. if ($crawl_permit_period == 0)
  93. $crawl_permit_period = 10;
  94. $remote = $_SERVER["REMOTE_ADDR"];
  95. $result = Cache::get("remote_search:".$remote);
  96. if (!is_null($result)) {
  97. $resultdata = json_decode($result);
  98. if (($resultdata->time > (time() - $crawl_permit_period)) && ($resultdata->accesses > $free_crawls)) {
  99. System::httpExit(429,
  100. ["title" => L10n::t("Too Many Requests"),
  101. "description" => L10n::t("Only one search per minute is permitted for not logged in users.")]);
  102. killme();
  103. }
  104. Cache::set("remote_search:".$remote, json_encode(["time" => time(), "accesses" => $resultdata->accesses + 1]), Cache::HOUR);
  105. } else
  106. Cache::set("remote_search:".$remote, json_encode(["time" => time(), "accesses" => 1]), Cache::HOUR);
  107. }
  108. Nav::setSelected('search');
  109. $search = (!empty($_REQUEST['search']) ? Strings::escapeTags(trim(rawurldecode($_REQUEST['search']))) : '');
  110. $tag = false;
  111. if (!empty($_GET['tag'])) {
  112. $tag = true;
  113. $search = (!empty($_GET['tag']) ? '#' . Strings::escapeTags(trim(rawurldecode($_GET['tag']))) : '');
  114. }
  115. // contruct a wrapper for the search header
  116. $o = Renderer::replaceMacros(Renderer::getMarkupTemplate("content_wrapper.tpl"),[
  117. 'name' => "search-header",
  118. '$title' => L10n::t("Search"),
  119. '$title_size' => 3,
  120. '$content' => HTML::search($search,'search-box','search', false)
  121. ]);
  122. if (strpos($search,'#') === 0) {
  123. $tag = true;
  124. $search = substr($search,1);
  125. }
  126. if (strpos($search,'@') === 0) {
  127. return dirfind_content($a);
  128. }
  129. if (strpos($search,'!') === 0) {
  130. return dirfind_content($a);
  131. }
  132. if (!empty($_GET['search-option']))
  133. switch($_GET['search-option']) {
  134. case 'fulltext':
  135. break;
  136. case 'tags':
  137. $tag = true;
  138. break;
  139. case 'contacts':
  140. return dirfind_content($a, "@");
  141. break;
  142. case 'forums':
  143. return dirfind_content($a, "!");
  144. break;
  145. }
  146. if (!$search)
  147. return $o;
  148. if (Config::get('system','only_tag_search'))
  149. $tag = true;
  150. // Here is the way permissions work in the search module...
  151. // Only public posts can be shown
  152. // OR your own posts if you are a logged in member
  153. // No items will be shown if the member has a blocked profile wall.
  154. $pager = new Pager($a->query_string);
  155. if ($tag) {
  156. Logger::log("Start tag search for '".$search."'", Logger::DEBUG);
  157. $condition = ["(`uid` = 0 OR (`uid` = ? AND NOT `global`))
  158. AND `otype` = ? AND `type` = ? AND `term` = ?",
  159. local_user(), TERM_OBJ_POST, TERM_HASHTAG, $search];
  160. $params = ['order' => ['created' => true],
  161. 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
  162. $terms = DBA::select('term', ['oid'], $condition, $params);
  163. $itemids = [];
  164. while ($term = DBA::fetch($terms)) {
  165. $itemids[] = $term['oid'];
  166. }
  167. DBA::close($terms);
  168. if (!empty($itemids)) {
  169. $params = ['order' => ['id' => true]];
  170. $items = Item::selectForUser(local_user(), [], ['id' => $itemids], $params);
  171. $r = Item::inArray($items);
  172. } else {
  173. $r = [];
  174. }
  175. } else {
  176. Logger::log("Start fulltext search for '".$search."'", Logger::DEBUG);
  177. $condition = ["(`uid` = 0 OR (`uid` = ? AND NOT `global`))
  178. AND `body` LIKE CONCAT('%',?,'%')",
  179. local_user(), $search];
  180. $params = ['order' => ['id' => true],
  181. 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
  182. $items = Item::selectForUser(local_user(), [], $condition, $params);
  183. $r = Item::inArray($items);
  184. }
  185. if (!DBA::isResult($r)) {
  186. info(L10n::t('No results.') . EOL);
  187. return $o;
  188. }
  189. if ($tag) {
  190. $title = L10n::t('Items tagged with: %s', $search);
  191. } else {
  192. $title = L10n::t('Results for: %s', $search);
  193. }
  194. $o .= Renderer::replaceMacros(Renderer::getMarkupTemplate("section_title.tpl"),[
  195. '$title' => $title
  196. ]);
  197. Logger::log("Start Conversation for '".$search."'", Logger::DEBUG);
  198. $o .= conversation($a, $r, $pager, 'search', false, false, 'commented', local_user());
  199. $o .= $pager->renderMinimal(count($r));
  200. Logger::log("Done '".$search."'", Logger::DEBUG);
  201. return $o;
  202. }