adminprofileflag.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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. * Show latest and greatest profile flags
  18. *
  19. * @category Action
  20. * @package GNUsocial
  21. * @author Evan Prodromou <evan@status.net>
  22. * @copyright 2009 StatusNet, Inc.
  23. * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
  24. */
  25. defined('GNUSOCIAL') || die();
  26. /**
  27. * Show the latest and greatest profile flags
  28. *
  29. * @category Action
  30. * @package GNUsocial
  31. * @author Evan Prodromou <evan@status.net>
  32. * @copyright 2009 StatusNet, Inc.
  33. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  34. */
  35. class AdminprofileflagAction extends Action
  36. {
  37. public $page;
  38. public $profiles;
  39. /**
  40. * Take arguments for running
  41. *
  42. * @param array $args $_REQUEST args
  43. *
  44. * @return bool success flag
  45. */
  46. public function prepare(array $args = [])
  47. {
  48. parent::prepare($args);
  49. $user = common_current_user();
  50. // User must be logged in.
  51. if (!common_logged_in()) {
  52. // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
  53. $this->clientError(_m('Not logged in.'));
  54. }
  55. $user = common_current_user();
  56. // ...because they're logged in
  57. assert(!empty($user));
  58. // It must be a "real" login, not saved cookie login
  59. if (!common_is_real_login()) {
  60. // Cookie theft is too easy; we require automatic
  61. // logins to re-authenticate before admining the site
  62. common_set_returnto($this->selfUrl());
  63. if (Event::handle('RedirectToLogin', [$this, $user])) {
  64. common_redirect(common_local_url('login'), 303);
  65. }
  66. }
  67. // User must have the right to review flags
  68. if (!$user->hasRight(UserFlagPlugin::REVIEWFLAGS)) {
  69. // TRANS: Error message displayed when trying to review profile flags while not authorised.
  70. $this->clientError(_m('You cannot review profile flags.'));
  71. }
  72. $this->page = $this->trimmed('page');
  73. if (empty($this->page)) {
  74. $this->page = 1;
  75. }
  76. $this->profiles = $this->getProfiles();
  77. return true;
  78. }
  79. /**
  80. * Handle request
  81. *
  82. * @param array $args $_REQUEST args; handled in prepare()
  83. *
  84. * @return void
  85. */
  86. public function handle()
  87. {
  88. parent::handle();
  89. $this->showPage();
  90. }
  91. /**
  92. * Title of this page
  93. *
  94. * @return string Title of the page
  95. */
  96. public function title()
  97. {
  98. // TRANS: Title for page with a list of profiles that were flagged for review.
  99. return _m('Flagged profiles');
  100. }
  101. /**
  102. * save the profile flag
  103. *
  104. * @return void
  105. */
  106. public function showContent()
  107. {
  108. $pl = new FlaggedProfileList($this->profiles, $this);
  109. $cnt = $pl->show();
  110. $this->pagination(
  111. $this->page > 1,
  112. $cnt > PROFILES_PER_PAGE,
  113. $this->page,
  114. 'adminprofileflag'
  115. );
  116. }
  117. /**
  118. * Retrieve this action's profiles
  119. *
  120. * @return Profile $profile Profile query results
  121. */
  122. public function getProfiles()
  123. {
  124. $ufp = new User_flag_profile();
  125. $ufp->selectAdd();
  126. $ufp->selectAdd('profile_id');
  127. $ufp->selectAdd('count(*) as flag_count');
  128. $ufp->whereAdd('cleared is NULL');
  129. $ufp->groupBy('profile_id');
  130. $ufp->orderBy('flag_count DESC, profile_id DESC');
  131. $offset = ($this->page - 1) * PROFILES_PER_PAGE;
  132. $limit = PROFILES_PER_PAGE + 1;
  133. $ufp->limit($offset, $limit);
  134. $profiles = [];
  135. if ($ufp->find()) {
  136. while ($ufp->fetch()) {
  137. $profile = Profile::getKV('id', $ufp->profile_id);
  138. if (!empty($profile)) {
  139. $profiles[] = $profile;
  140. }
  141. }
  142. }
  143. $ufp->free();
  144. return new ArrayWrapper($profiles);
  145. }
  146. }
  147. /**
  148. * Specialization of ProfileList to show flagging information
  149. *
  150. * Most of the hard part is done in FlaggedProfileListItem.
  151. *
  152. * @category Widget
  153. * @package GNUsocial
  154. * @author Evan Prodromou <evan@status.net>
  155. * @copyright 2009 StatusNet, Inc.
  156. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  157. */
  158. class FlaggedProfileList extends ProfileList
  159. {
  160. /**
  161. * Factory method for creating new list items
  162. *
  163. * @param Profile $profile Profile to create an item for
  164. *
  165. * @return ProfileListItem newly-created item
  166. */
  167. public function newListItem(Profile $profile)
  168. {
  169. return new FlaggedProfileListItem($profile, $this->action);
  170. }
  171. }
  172. /**
  173. * Specialization of ProfileListItem to show flagging information
  174. *
  175. * @category Widget
  176. * @package GNUsocial
  177. * @author Evan Prodromou <evan@status.net>
  178. * @copyright 2009 StatusNet, Inc.
  179. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  180. */
  181. class FlaggedProfileListItem extends ProfileListItem
  182. {
  183. const MAX_FLAGGERS = 5;
  184. public $user;
  185. public $r2args;
  186. /**
  187. * Overload parent's action list with our own moderation-oriented buttons
  188. *
  189. * @return void
  190. */
  191. public function showActions()
  192. {
  193. $this->user = common_current_user();
  194. list($action, $this->r2args) = $this->out->returnToArgs();
  195. $this->r2args['action'] = $action;
  196. $this->startActions();
  197. if (Event::handle('StartProfileListItemActionElements', [$this])) {
  198. $this->out->elementStart('li', 'entity_moderation');
  199. // TRANS: Header for moderation menu with action buttons for flagged profiles (like 'sandbox', 'silence', ...).
  200. $this->out->element('p', null, _m('Moderate'));
  201. $this->out->elementStart('ul');
  202. $this->showSandboxButton();
  203. $this->showSilenceButton();
  204. $this->showDeleteButton();
  205. $this->showClearButton();
  206. $this->out->elementEnd('ul');
  207. $this->out->elementEnd('li');
  208. Event::handle('EndProfileListItemActionElements', [$this]);
  209. }
  210. $this->endActions();
  211. }
  212. /**
  213. * Show a button to sandbox the profile
  214. *
  215. * @return void
  216. */
  217. public function showSandboxButton()
  218. {
  219. if ($this->user->hasRight(Right::SANDBOXUSER)) {
  220. $this->out->elementStart('li', 'entity_sandbox');
  221. if ($this->profile->isSandboxed()) {
  222. $usf = new UnSandboxForm($this->out, $this->profile, $this->r2args);
  223. $usf->show();
  224. } else {
  225. $sf = new SandboxForm($this->out, $this->profile, $this->r2args);
  226. $sf->show();
  227. }
  228. $this->out->elementEnd('li');
  229. }
  230. }
  231. /**
  232. * Show a button to silence the profile
  233. *
  234. * @return void
  235. */
  236. public function showSilenceButton()
  237. {
  238. if ($this->user->hasRight(Right::SILENCEUSER)) {
  239. $this->out->elementStart('li', 'entity_silence');
  240. if ($this->profile->isSilenced()) {
  241. $usf = new UnSilenceForm($this->out, $this->profile, $this->r2args);
  242. $usf->show();
  243. } else {
  244. $sf = new SilenceForm($this->out, $this->profile, $this->r2args);
  245. $sf->show();
  246. }
  247. $this->out->elementEnd('li');
  248. }
  249. }
  250. /**
  251. * Show a button to delete user and profile
  252. *
  253. * @return void
  254. */
  255. public function showDeleteButton()
  256. {
  257. if ($this->user->hasRight(Right::DELETEUSER)) {
  258. $this->out->elementStart('li', 'entity_delete');
  259. $df = new DeleteUserForm($this->out, $this->profile, $this->r2args);
  260. $df->show();
  261. $this->out->elementEnd('li');
  262. }
  263. }
  264. /**
  265. * Show a button to clear flags
  266. *
  267. * @return void
  268. */
  269. public function showClearButton()
  270. {
  271. if ($this->user->hasRight(UserFlagPlugin::CLEARFLAGS)) {
  272. $this->out->elementStart('li', 'entity_clear');
  273. $cf = new ClearFlagForm($this->out, $this->profile, $this->r2args);
  274. $cf->show();
  275. $this->out->elementEnd('li');
  276. }
  277. }
  278. /**
  279. * Overload parent function to add flaggers list
  280. *
  281. * @return void
  282. */
  283. public function endProfile()
  284. {
  285. $this->showFlaggersList();
  286. parent::endProfile();
  287. }
  288. /**
  289. * Show a list of people who've flagged this profile
  290. *
  291. * @return void
  292. */
  293. public function showFlaggersList()
  294. {
  295. $flaggers = [];
  296. $ufp = new User_flag_profile();
  297. $ufp->selectAdd();
  298. $ufp->selectAdd('user_id');
  299. $ufp->profile_id = $this->profile->id;
  300. $ufp->orderBy('created, user_id');
  301. if ($ufp->find()) { // XXX: this should always happen
  302. while ($ufp->fetch()) {
  303. $user = User::getKV('id', $ufp->user_id);
  304. if (!empty($user)) { // XXX: this would also be unusual
  305. $flaggers[] = clone $user;
  306. }
  307. }
  308. }
  309. $cnt = count($flaggers);
  310. $others = 0;
  311. if ($cnt > self::MAX_FLAGGERS) {
  312. $flaggers = array_slice($flaggers, 0, self::MAX_FLAGGERS);
  313. $others = $cnt - self::MAX_FLAGGERS;
  314. }
  315. $lnks = [];
  316. foreach ($flaggers as $flagger) {
  317. $url = common_local_url(
  318. 'showstream',
  319. ['nickname' => $flagger->nickname]
  320. );
  321. $lnks[] = XMLStringer::estring(
  322. 'a',
  323. ['href' => $url, 'class' => 'flagger'],
  324. $flagger->nickname
  325. );
  326. }
  327. if ($cnt > 0) {
  328. if ($others > 0) {
  329. $flagging_users = implode(', ', $lnks);
  330. // TRANS: Message displayed on a profile if it has been flagged.
  331. // TRANS: %1$s is a comma separated list of at most 5 user nicknames that flagged.
  332. // TRANS: %2$d is a positive integer of additional flagging users. Also used for plural.
  333. $text .= sprintf(_m('Flagged by %1$s and %2$d other', 'Flagged by %1$s and %2$d others', $others), $flagging_users, $others);
  334. } else {
  335. // TRANS: Message displayed on a profile if it has been flagged.
  336. // TRANS: %s is a comma separated list of at most 5 user nicknames that flagged.
  337. $text .= sprintf(_m('Flagged by %s'), $flagging_users);
  338. }
  339. $this->out->elementStart('p', ['class' => 'flaggers']);
  340. $this->out->raw($text);
  341. $this->out->elementEnd('p');
  342. }
  343. }
  344. }