groupdirectory.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. <?php
  2. // This file is part of GNU social - https://www.gnu.org/software/social
  3. //
  4. // GNU social is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // GNU social is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with GNU social. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Output a group directory
  18. *
  19. * @category Public
  20. * @package GNUsocial
  21. * @author Zach Copley <zach@status.net>
  22. * @copyright 2011 StatusNet, Inc.
  23. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or late
  24. */
  25. defined('GNUSOCIAL') || die();
  26. /**
  27. * Group directory
  28. *
  29. * @category Directory
  30. * @package GNUsocial
  31. * @author Zach Copley <zach@status.net>
  32. * @author Mikael Nordfeldth <mmn@hethane.se>
  33. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or late
  34. */
  35. class GroupdirectoryAction extends ManagedAction
  36. {
  37. protected $redirectAfterLogin = true;
  38. /**
  39. * The page we're on
  40. *
  41. * @var integer
  42. */
  43. public $page;
  44. /**
  45. * What to filter the search results by
  46. *
  47. * @var string
  48. */
  49. public $filter;
  50. /**
  51. * Column to sort by
  52. *
  53. * @var string
  54. */
  55. public $sort;
  56. /**
  57. * How to order search results, ascending or descending
  58. *
  59. * @var string
  60. */
  61. public $reverse;
  62. /**
  63. * Query
  64. *
  65. * @var string
  66. */
  67. public $q;
  68. /**
  69. * Title of the page
  70. *
  71. * @return string Title of the page
  72. */
  73. public function title()
  74. {
  75. // @fixme: This looks kinda gross
  76. if ($this->filter == 'all') {
  77. if ($this->page != 1) {
  78. // TRANS: Title for group directory page. %d is a page number.
  79. return(sprintf(_m('Group Directory, page %d'), $this->page));
  80. }
  81. // TRANS: Title for group directory page.
  82. return _m('Group directory');
  83. } elseif ($this->page == 1) {
  84. return sprintf(
  85. // TRANS: Title for group directory page when it is filtered.
  86. // TRANS: %s is the filter string.
  87. _m('Group directory - %s'),
  88. strtoupper($this->filter)
  89. );
  90. } else {
  91. return sprintf(
  92. // TRANS: Title for group directory page when it is filtered.
  93. // TRANS: %1$s is the filter string, %2$d is a page number.
  94. _m('Group directory - %1$s, page %2$d'),
  95. strtoupper($this->filter),
  96. $this->page
  97. );
  98. }
  99. }
  100. /**
  101. * Instructions for use
  102. *
  103. * @return instructions for use
  104. */
  105. public function getInstructions()
  106. {
  107. // TRANS: Page instructions.
  108. return _m("After you join a group you can send messages to all other members\n".
  109. "using the syntax \"!groupname\".\n\n".
  110. "Browse groups, or search for groups by their name, location or topic.\n".
  111. "Separate the terms by spaces; they must be three characters or more.") . "\n";
  112. }
  113. /**
  114. * Is this page read-only?
  115. *
  116. * @return boolean true
  117. */
  118. public function isReadOnly($args)
  119. {
  120. return true;
  121. }
  122. protected function doPreparation()
  123. {
  124. $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1;
  125. $this->filter = $this->arg('filter', 'all');
  126. $this->reverse = $this->boolean('reverse');
  127. $this->q = $this->trimmed('q');
  128. $this->sort = $this->arg('sort', 'nickname');
  129. }
  130. /**
  131. * Show the page notice
  132. *
  133. * Shows instructions for the page
  134. *
  135. * @return void
  136. */
  137. public function showPageNotice()
  138. {
  139. $instr = $this->getInstructions();
  140. $output = common_markup_to_html($instr);
  141. $this->elementStart('div', 'instructions');
  142. $this->raw($output);
  143. $this->elementEnd('div');
  144. }
  145. /**
  146. * Content area
  147. *
  148. * Shows the groups
  149. *
  150. * @return void
  151. */
  152. public function showContent()
  153. {
  154. if (common_logged_in()) {
  155. $this->elementStart('p', ['id' => 'new_group']);
  156. $this->element(
  157. 'a',
  158. [
  159. 'href' => common_local_url('newgroup'),
  160. 'class' => 'more',
  161. ],
  162. // TRANS: Link to create a new group on the group list page.
  163. _m('Create a new group')
  164. );
  165. $this->elementEnd('p');
  166. }
  167. $this->showForm();
  168. $this->elementStart('div', ['id' => 'profile_directory']);
  169. // @todo FIXME: Does "All" need i18n here?
  170. $alphaNav = new AlphaNav($this, false, false, ['0-9', 'All']);
  171. $alphaNav->show();
  172. $group = null;
  173. $group = $this->getGroups();
  174. $cnt = 0;
  175. if (!empty($group)) {
  176. $groupList = new SortableGroupList(
  177. $group,
  178. common_current_user(),
  179. $this
  180. );
  181. $cnt = $groupList->show();
  182. $group->free();
  183. if (0 == $cnt) {
  184. $this->showEmptyListMessage();
  185. }
  186. }
  187. $args = [];
  188. if (isset($this->q)) {
  189. $args['q'] = $this->q;
  190. } else {
  191. $args['filter'] = $this->filter;
  192. }
  193. $this->pagination(
  194. $this->page > 1,
  195. $cnt > PROFILES_PER_PAGE,
  196. $this->page,
  197. 'groupdirectory',
  198. $args
  199. );
  200. $this->elementEnd('div');
  201. }
  202. public function showForm($error=null)
  203. {
  204. $this->elementStart('form', [
  205. 'method' => 'get',
  206. 'id' => 'form_search',
  207. 'class' => 'form_settings',
  208. 'action' => common_local_url('groupdirectory'),
  209. ]);
  210. $this->elementStart('fieldset');
  211. // TRANS: Fieldset legend.
  212. $this->element('legend', null, _m('Search groups'));
  213. $this->elementStart('ul', 'form_data');
  214. $this->elementStart('li');
  215. // TRANS: Field label for input of one or more keywords.
  216. $this->input('q', _m('Keyword(s)'), $this->q);
  217. // TRANS: Button text for searching group directory.
  218. $this->submit('search', _m('BUTTON', 'Search'));
  219. $this->elementEnd('li');
  220. $this->elementEnd('ul');
  221. $this->elementEnd('fieldset');
  222. $this->elementEnd('form');
  223. }
  224. /*
  225. * Get groups filtered by the current filter, sort key,
  226. * sort order, and page
  227. */
  228. public function getGroups()
  229. {
  230. $group = new User_group();
  231. // Disable this to get global group searches
  232. $group->joinAdd(['id', 'local_group:group_id']);
  233. $order = false;
  234. if (!empty($this->q)) {
  235. $wheres = ['nickname', 'fullname', 'homepage', 'description', 'location'];
  236. foreach ($wheres as $where) {
  237. // Double % because of sprintf
  238. $group->whereAdd(sprintf(
  239. 'LOWER(%1$s.%2$s) LIKE LOWER(\'%%%3$s%%\')',
  240. $group->escapedTableName(),
  241. $where,
  242. $group->escape($this->q)
  243. ), 'OR');
  244. }
  245. $order = sprintf(
  246. '%1$s.%2$s %3$s',
  247. $group->escapedTableName(),
  248. $this->getSortKey('created'),
  249. $this->reverse ? 'DESC' : 'ASC'
  250. );
  251. } else {
  252. // User is browsing via AlphaNav
  253. switch ($this->filter) {
  254. case 'all':
  255. // NOOP
  256. break;
  257. case '0-9':
  258. $group->whereAdd(sprintf(
  259. 'LEFT(%1$s.%2$s, 1) BETWEEN %3$s AND %4$s',
  260. $group->escapedTableName(),
  261. 'nickname',
  262. $group->_quote('0'),
  263. $group->_quote('9')
  264. ));
  265. break;
  266. default:
  267. $group->whereAdd(sprintf(
  268. 'LEFT(LOWER(%1$s.%2$s), 1) = %3$s',
  269. $group->escapedTableName(),
  270. 'nickname',
  271. $group->_quote($this->filter)
  272. ));
  273. }
  274. $order = sprintf(
  275. '%1$s.%2$s %3$s, %1$s.%4$s ASC',
  276. $group->escapedTableName(),
  277. $this->getSortKey('nickname'),
  278. $this->reverse ? 'DESC' : 'ASC',
  279. 'nickname'
  280. );
  281. }
  282. $offset = ($this->page-1) * PROFILES_PER_PAGE;
  283. $limit = PROFILES_PER_PAGE + 1;
  284. $group->selectAdd();
  285. $group->selectAdd('profile_id');
  286. $group->orderBy($order);
  287. $group->limit($offset, $limit);
  288. $group->find();
  289. return Profile::multiGet('id', $group->fetchAll('profile_id'));
  290. }
  291. /**
  292. * Filter the sort parameter
  293. *
  294. * @return string a column name for sorting
  295. */
  296. public function getSortKey($def='created')
  297. {
  298. switch ($this->sort) {
  299. case 'nickname':
  300. case 'created':
  301. return $this->sort;
  302. default:
  303. return $def;
  304. }
  305. }
  306. /**
  307. * Show a nice message when there's no search results
  308. */
  309. public function showEmptyListMessage()
  310. {
  311. if (!empty($this->filter) && ($this->filter != 'all')) {
  312. $this->element('p', 'error', sprintf(
  313. // TRANS: Empty list message for searching group directory.
  314. // TRANS: %s is the search string.
  315. _m('No groups starting with %s.'),
  316. $this->filter
  317. ));
  318. } else {
  319. // TRANS: Empty list message for searching group directory.
  320. $this->element('p', 'error', _m('No results.'));
  321. // TRANS: Help text for searching group directory.
  322. $message = _m("* Make sure all words are spelled correctly.\n".
  323. "* Try different keywords.\n".
  324. "* Try more general keywords.\n".
  325. "* Try fewer keywords.");
  326. $this->elementStart('div', 'help instructions');
  327. $this->raw(common_markup_to_html($message));
  328. $this->elementEnd('div');
  329. }
  330. }
  331. public function showSections()
  332. {
  333. $gbp = new GroupsByPostsSection($this);
  334. $gbp->show();
  335. $gbm = new GroupsByMembersSection($this);
  336. $gbm->show();
  337. }
  338. }