profileaction.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. <?php
  2. /**
  3. * StatusNet, the distributed open-source microblogging tool
  4. *
  5. * Common parent of Personal and Profile actions
  6. *
  7. * PHP version 5
  8. *
  9. * LICENCE: This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Affero General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. * @category Personal
  23. * @package StatusNet
  24. * @author Evan Prodromou <evan@status.net>
  25. * @author Sarven Capadisli <csarven@status.net>
  26. * @copyright 2008-2011 StatusNet, Inc.
  27. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  28. * @link http://status.net/
  29. */
  30. if (!defined('GNUSOCIAL')) { exit(1); }
  31. /**
  32. * Profile action common superclass
  33. *
  34. * Abstracts out common code from profile and personal tabs
  35. *
  36. * @category Personal
  37. * @package StatusNet
  38. * @author Evan Prodromou <evan@status.net>
  39. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  40. * @link http://status.net/
  41. */
  42. abstract class ProfileAction extends ManagedAction
  43. {
  44. var $page = null;
  45. var $tag = null;
  46. protected $target = null; // Profile that we're showing
  47. protected function doPreparation()
  48. {
  49. // showstream requires a nickname
  50. $nickname_arg = $this->trimmed('nickname');
  51. $nickname = common_canonical_nickname($nickname_arg);
  52. // Permanent redirect on non-canonical nickname
  53. if ($nickname_arg !== $nickname) {
  54. $args = array('nickname' => $nickname);
  55. if ($this->arg('page') && $this->arg('page') != 1) {
  56. $args['page'] = $this->arg['page'];
  57. }
  58. common_redirect(common_local_url($this->getActionName(), $args), 301);
  59. }
  60. try {
  61. $user = User::getByNickname($nickname);
  62. } catch (NoSuchUserException $e) {
  63. $group = Local_group::getKV('nickname', $nickname);
  64. if ($group instanceof Local_group) {
  65. common_redirect($group->getProfile()->getUrl());
  66. }
  67. // No user nor group found, throw the NoSuchUserException again
  68. throw $e;
  69. }
  70. $this->target = $user->getProfile();
  71. }
  72. protected function prepare(array $args=array())
  73. {
  74. // this will call ->doPreparation() which child classes use to set $this->target
  75. parent::prepare($args);
  76. if ($this->target->isPerson() && $this->target->hasRole(Profile_role::SILENCED)
  77. && (!$this->scoped instanceof Profile || !$this->scoped->hasRight(Right::SILENCEUSER))) {
  78. throw new ClientException(_('This profile has been silenced by site moderators'), 403);
  79. }
  80. $this->tag = $this->trimmed('tag');
  81. $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
  82. common_set_returnto($this->selfUrl());
  83. return true;
  84. }
  85. public function getTarget()
  86. {
  87. if (!$this->target instanceof Profile) {
  88. throw new ServerException('No target profile in ProfileAction class');
  89. }
  90. return $this->target;
  91. }
  92. function isReadOnly($args)
  93. {
  94. return true;
  95. }
  96. function showSections()
  97. {
  98. $this->showSubscriptions();
  99. $this->showSubscribers();
  100. $this->showGroups();
  101. $this->showLists();
  102. $this->showStatistics();
  103. }
  104. /**
  105. * Convenience function for common pattern of links to subscription/groups sections.
  106. *
  107. * @param string $actionClass
  108. * @param string $title
  109. * @param string $cssClass
  110. */
  111. private function statsSectionLink($actionClass, $title, $cssClass='')
  112. {
  113. $this->element('a', array('href' => common_local_url($actionClass,
  114. array('nickname' => $this->target->getNickname())),
  115. 'class' => $cssClass),
  116. $title);
  117. }
  118. function showSubscriptions()
  119. {
  120. $this->elementStart('div', array('id' => 'entity_subscriptions',
  121. 'class' => 'section'));
  122. if (Event::handle('StartShowSubscriptionsMiniList', array($this))) {
  123. $this->elementStart('h2');
  124. // TRANS: H2 text for user subscription statistics.
  125. $this->statsSectionLink('subscriptions', _('Following'));
  126. $this->text(' ');
  127. $this->text($this->target->subscriptionCount());
  128. $this->elementEnd('h2');
  129. try {
  130. $profile = $this->target->getSubscribed(0, PROFILES_PER_MINILIST + 1);
  131. $pml = new ProfileMiniList($profile, $this);
  132. $pml->show();
  133. } catch (NoResultException $e) {
  134. // TRANS: Text for user subscription statistics if the user has no subscription
  135. $this->element('p', null, _('(None)'));
  136. }
  137. Event::handle('EndShowSubscriptionsMiniList', array($this));
  138. }
  139. $this->elementEnd('div');
  140. }
  141. function showSubscribers()
  142. {
  143. $this->elementStart('div', array('id' => 'entity_subscribers',
  144. 'class' => 'section'));
  145. if (Event::handle('StartShowSubscribersMiniList', array($this))) {
  146. $this->elementStart('h2');
  147. // TRANS: H2 text for user subscriber statistics.
  148. $this->statsSectionLink('subscribers', _('Followers'));
  149. $this->text(' ');
  150. $this->text($this->target->subscriberCount());
  151. $this->elementEnd('h2');
  152. try {
  153. $profile = $this->target->getSubscribers(0, PROFILES_PER_MINILIST + 1);
  154. $sml = new SubscribersMiniList($profile, $this);
  155. $sml->show();
  156. } catch (NoResultException $e) {
  157. // TRANS: Text for user subscriber statistics if user has no subscribers.
  158. $this->element('p', null, _('(None)'));
  159. }
  160. Event::handle('EndShowSubscribersMiniList', array($this));
  161. }
  162. $this->elementEnd('div');
  163. }
  164. function showStatistics()
  165. {
  166. $notice_count = $this->target->noticeCount();
  167. $age_days = (time() - strtotime($this->target->created)) / 86400;
  168. if ($age_days < 1) {
  169. // Rather than extrapolating out to a bajillion...
  170. $age_days = 1;
  171. }
  172. $daily_count = round($notice_count / $age_days);
  173. $this->elementStart('div', array('id' => 'entity_statistics',
  174. 'class' => 'section'));
  175. // TRANS: H2 text for user statistics.
  176. $this->element('h2', null, _('Statistics'));
  177. $profile = $this->target;
  178. $actionParams = array('nickname' => $profile->nickname);
  179. $stats = array(
  180. array(
  181. 'id' => 'user-id',
  182. // TRANS: Label for user statistics.
  183. 'label' => _('User ID'),
  184. 'value' => $profile->id,
  185. ),
  186. array(
  187. 'id' => 'member-since',
  188. // TRANS: Label for user statistics.
  189. 'label' => _('Member since'),
  190. 'value' => date('j M Y', strtotime($profile->created))
  191. ),
  192. array(
  193. 'id' => 'notices',
  194. // TRANS: Label for user statistics.
  195. 'label' => _('Notices'),
  196. 'value' => $notice_count,
  197. ),
  198. array(
  199. 'id' => 'daily_notices',
  200. // TRANS: Label for user statistics.
  201. // TRANS: Average count of posts made per day since account registration.
  202. 'label' => _('Daily average'),
  203. 'value' => $daily_count
  204. )
  205. );
  206. // Give plugins a chance to add stats entries
  207. Event::handle('ProfileStats', array($profile, &$stats));
  208. foreach ($stats as $row) {
  209. $this->showStatsRow($row);
  210. }
  211. $this->elementEnd('div');
  212. }
  213. private function showStatsRow($row)
  214. {
  215. $this->elementStart('dl', 'entity_' . $row['id']);
  216. $this->elementStart('dt');
  217. if (!empty($row['link'])) {
  218. $this->element('a', array('href' => $row['link']), $row['label']);
  219. } else {
  220. $this->text($row['label']);
  221. }
  222. $this->elementEnd('dt');
  223. $this->element('dd', null, $row['value']);
  224. $this->elementEnd('dl');
  225. }
  226. function showGroups()
  227. {
  228. $groups = $this->target->getGroups(0, GROUPS_PER_MINILIST + 1);
  229. $this->elementStart('div', array('id' => 'entity_groups',
  230. 'class' => 'section'));
  231. if (Event::handle('StartShowGroupsMiniList', array($this))) {
  232. $this->elementStart('h2');
  233. // TRANS: H2 text for user group membership statistics.
  234. $this->statsSectionLink('usergroups', _('Groups'));
  235. $this->text(' ');
  236. $this->text($this->target->getGroupCount());
  237. $this->elementEnd('h2');
  238. if ($groups instanceof User_group) {
  239. $gml = new GroupMiniList($groups, $this->target, $this);
  240. $cnt = $gml->show();
  241. } else {
  242. // TRANS: Text for user user group membership statistics if user is not a member of any group.
  243. $this->element('p', null, _('(None)'));
  244. }
  245. Event::handle('EndShowGroupsMiniList', array($this));
  246. }
  247. $this->elementEnd('div');
  248. }
  249. function showLists()
  250. {
  251. $lists = $this->target->getLists($this->scoped);
  252. if ($lists->N > 0) {
  253. $this->elementStart('div', array('id' => 'entity_lists',
  254. 'class' => 'section'));
  255. if (Event::handle('StartShowListsMiniList', array($this))) {
  256. $url = common_local_url('peopletagsbyuser',
  257. array('nickname' => $this->target->getNickname()));
  258. $this->elementStart('h2');
  259. $this->element('a',
  260. array('href' => $url),
  261. // TRANS: H2 text for user list membership statistics.
  262. _('Lists'));
  263. $this->text(' ');
  264. $this->text($lists->N);
  265. $this->elementEnd('h2');
  266. $this->elementStart('ul');
  267. $first = true;
  268. while ($lists->fetch()) {
  269. if (!empty($lists->mainpage)) {
  270. $url = $lists->mainpage;
  271. } else {
  272. $url = common_local_url('showprofiletag',
  273. array('nickname' => $this->target->getNickname(),
  274. 'tag' => $lists->tag));
  275. }
  276. if (!$first) {
  277. $this->text(', ');
  278. } else {
  279. $first = false;
  280. }
  281. $this->element('a', array('href' => $url),
  282. $lists->tag);
  283. }
  284. $this->elementEnd('ul');
  285. Event::handle('EndShowListsMiniList', array($this));
  286. }
  287. $this->elementEnd('div');
  288. }
  289. }
  290. }