showstream.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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. * User profile page
  18. *
  19. * @category Personal
  20. * @package GNUsocial
  21. * @author Evan Prodromou <evan@status.net>
  22. * @author Sarven Capadisli <csarven@status.net>
  23. * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
  24. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  25. */
  26. defined('GNUSOCIAL') || die();
  27. /**
  28. * User profile page
  29. *
  30. * When I created this page, "show stream" seemed like the best name for it.
  31. * Now, it seems like a really bad name.
  32. *
  33. * It shows a stream of the user's posts, plus lots of profile info, links
  34. * to subscriptions and stuff, etc.
  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. class ShowstreamAction extends NoticestreamAction
  43. {
  44. public function getStream()
  45. {
  46. if (empty($this->tag)) {
  47. $stream = new ProfileNoticeStream($this->target, $this->scoped);
  48. } else {
  49. $stream = new TaggedProfileNoticeStream($this->target, $this->tag, $this->scoped);
  50. }
  51. return $stream;
  52. }
  53. public function title()
  54. {
  55. $base = $this->target->getFancyName();
  56. if (!empty($this->tag)) {
  57. if ($this->page == 1) {
  58. // TRANS: Page title showing tagged notices in one user's timeline.
  59. // TRANS: %1$s is the username, %2$s is the hash tag.
  60. return sprintf(_('Notices by %1$s tagged %2$s'), $base, $this->tag);
  61. } else {
  62. // TRANS: Page title showing tagged notices in one user's timeline.
  63. // TRANS: %1$s is the username, %2$s is the hash tag, %3$d is the page number.
  64. return sprintf(_('Notices by %1$s tagged %2$s, page %3$d'), $base, $this->tag, $this->page);
  65. }
  66. } else {
  67. if ($this->page == 1) {
  68. return sprintf(_('Notices by %s'), $base);
  69. } else {
  70. // TRANS: Extended page title showing tagged notices in one user's timeline.
  71. // TRANS: %1$s is the username, %2$d is the page number.
  72. return sprintf(
  73. _('Notices by %1$s, page %2$d'),
  74. $base,
  75. $this->page
  76. );
  77. }
  78. }
  79. }
  80. protected function showContent()
  81. {
  82. $this->showNotices();
  83. }
  84. public function showProfileBlock()
  85. {
  86. $block = new AccountProfileBlock($this, $this->target);
  87. $block->show();
  88. }
  89. public function showPageNoticeBlock()
  90. {
  91. return;
  92. }
  93. public function getFeeds()
  94. {
  95. if (!empty($this->tag)) {
  96. return [new Feed(
  97. Feed::RSS1,
  98. common_local_url(
  99. 'userrss',
  100. [
  101. 'nickname' => $this->target->getNickname(),
  102. 'tag' => $this->tag,
  103. ]
  104. ),
  105. // TRANS: Title for link to notice feed.
  106. // TRANS: %1$s is a user nickname, %2$s is a hashtag.
  107. sprintf(
  108. _('Notice feed for %1$s tagged %2$s (RSS 1.0)'),
  109. $this->target->getNickname(),
  110. $this->tag
  111. )
  112. )];
  113. }
  114. if (!$this->target->isLocal()) {
  115. // remote profiles at least have Atom, but we can't guarantee anything else
  116. return [new Feed(
  117. Feed::ATOM,
  118. $this->target->getAtomFeed(),
  119. // TRANS: Title for link to notice feed.
  120. // TRANS: %s is a user nickname.
  121. sprintf(
  122. _('Notice feed for %s (Atom)'),
  123. $this->target->getNickname()
  124. )
  125. )];
  126. }
  127. return [
  128. new Feed(
  129. Feed::JSON,
  130. common_local_url(
  131. 'ApiTimelineUser',
  132. [
  133. 'id' => $this->target->getID(),
  134. 'format' => 'as',
  135. ]
  136. ),
  137. // TRANS: Title for link to notice feed.
  138. // TRANS: %s is a user nickname.
  139. sprintf(
  140. _('Notice feed for %s (Activity Streams JSON)'),
  141. $this->target->getNickname()
  142. )
  143. ),
  144. new Feed(
  145. Feed::RSS1,
  146. common_local_url(
  147. 'userrss',
  148. ['nickname' => $this->target->getNickname()]
  149. ),
  150. // TRANS: Title for link to notice feed.
  151. // TRANS: %s is a user nickname.
  152. sprintf(
  153. _('Notice feed for %s (RSS 1.0)'),
  154. $this->target->getNickname()
  155. )
  156. ),
  157. new Feed(
  158. Feed::RSS2,
  159. common_local_url(
  160. 'ApiTimelineUser',
  161. [
  162. 'id' => $this->target->getID(),
  163. 'format' => 'rss',
  164. ]
  165. ),
  166. // TRANS: Title for link to notice feed.
  167. // TRANS: %s is a user nickname.
  168. sprintf(
  169. _('Notice feed for %s (RSS 2.0)'),
  170. $this->target->getNickname()
  171. )
  172. ),
  173. new Feed(
  174. Feed::ATOM,
  175. $this->target->getAtomFeed(),
  176. // TRANS: Title for link to notice feed.
  177. // TRANS: %s is a user nickname.
  178. sprintf(
  179. _('Notice feed for %s (Atom)'),
  180. $this->target->getNickname()
  181. )
  182. ),
  183. new Feed(
  184. Feed::FOAF,
  185. common_local_url(
  186. 'foaf',
  187. ['nickname' => $this->target->getNickname()]
  188. ),
  189. // TRANS: Title for link to notice feed. FOAF stands for Friend of a Friend.
  190. // TRANS: More information at http://www.foaf-project.org. %s is a user nickname.
  191. sprintf(_('FOAF for %s'), $this->target->getNickname())
  192. )
  193. ];
  194. }
  195. public function extraHeaders()
  196. {
  197. parent::extraHeaders();
  198. // Publish all the rel="me" in the HTTP headers on our main profile page
  199. if (get_class($this) == 'ShowstreamAction') {
  200. foreach ($this->target->getRelMes() as $relMe) {
  201. header('Link: <' . htmlspecialchars($relMe['href']) . '>; rel="me"', false);
  202. }
  203. }
  204. }
  205. public function extraHead()
  206. {
  207. if (!$this->target->isLocal()) {
  208. $this->element(
  209. 'meta',
  210. [
  211. 'name' => 'robots',
  212. 'content' => 'noindex',
  213. ]
  214. );
  215. }
  216. if ($this->target->bio) {
  217. $this->element(
  218. 'meta',
  219. [
  220. 'name' => 'description',
  221. 'content' => $this->target->getDescription(),
  222. ]
  223. );
  224. }
  225. $rsd = common_local_url(
  226. 'rsd',
  227. ['nickname' => $this->target->getNickname()]
  228. );
  229. // RSD, http://tales.phrasewise.com/rfc/rsd
  230. $this->element(
  231. 'link',
  232. [
  233. 'rel' => 'EditURI',
  234. 'type' => 'application/rsd+xml',
  235. 'href' => $rsd,
  236. ]
  237. );
  238. if ($this->page != 1) {
  239. $this->element(
  240. 'link',
  241. [
  242. 'rel' => 'canonical',
  243. 'href' => $this->target->getUrl(),
  244. ]
  245. );
  246. }
  247. }
  248. public function showEmptyListMessage()
  249. {
  250. // TRANS: First sentence of empty list message for a timeline. $1%s is a user nickname.
  251. $message = sprintf(_('This is the timeline for %1$s, but %1$s hasn\'t posted anything yet.'), $this->target->getNickname()) . ' ';
  252. if ($this->scoped instanceof Profile) {
  253. if ($this->target->getID() === $this->scoped->getID()) {
  254. // TRANS: Second sentence of empty list message for a stream for the user themselves.
  255. $message .= _('Seen anything interesting recently? You haven\'t posted any notices yet, now would be a good time to start :)');
  256. } else {
  257. // TRANS: Second sentence of empty list message for a non-self timeline. %1$s is a user nickname, %2$s is a part of a URL.
  258. // TRANS: This message contains a Markdown link. Keep "](" together.
  259. $message .= sprintf(_('You can try to nudge %1$s or [post something to them](%%%%action.newnotice%%%%?status_textarea=%2$s).'), $this->target->getNickname(), '@' . $this->target->getNickname());
  260. }
  261. } else {
  262. // TRANS: Second sentence of empty message for anonymous users. %s is a user nickname.
  263. // TRANS: This message contains a Markdown link. Keep "](" together.
  264. $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->getNickname());
  265. }
  266. $this->elementStart('div', 'guide');
  267. $this->raw(common_markup_to_html($message));
  268. $this->elementEnd('div');
  269. }
  270. public function showNotices()
  271. {
  272. $pnl = new PrimaryNoticeList($this->notice, $this);
  273. $cnt = $pnl->show();
  274. if (0 == $cnt) {
  275. $this->showEmptyListMessage();
  276. }
  277. // either nickname or id will be used, depending on which action (showstream, userbyid...)
  278. $args = array('nickname' => $this->target->getNickname(), 'id' => $this->target->getID());
  279. if (!empty($this->tag)) {
  280. $args['tag'] = $this->tag;
  281. }
  282. $this->pagination(
  283. $this->page > 1,
  284. $cnt > NOTICES_PER_PAGE,
  285. $this->page,
  286. $this->getActionName(),
  287. $args
  288. );
  289. }
  290. public function showAnonymousMessage()
  291. {
  292. if (!(common_config('site', 'closed') || common_config('site', 'inviteonly'))) {
  293. // TRANS: Announcement for anonymous users showing a timeline if site registrations are open.
  294. // TRANS: This message contains a Markdown link. Keep "](" together.
  295. $m = sprintf(
  296. _('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
  297. 'based on the Free Software [StatusNet](http://status.net/) tool. ' .
  298. '[Join now](%%%%action.register%%%%) to follow **%s**\'s notices and many more! ([Read more](%%%%doc.help%%%%))'),
  299. $this->target->getNickname(),
  300. $this->target->getNickname()
  301. );
  302. } else {
  303. // TRANS: Announcement for anonymous users showing a timeline if site registrations are closed or invite only.
  304. // TRANS: This message contains a Markdown link. Keep "](" together.
  305. $m = sprintf(
  306. _('**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' .
  307. 'based on the Free Software [StatusNet](http://status.net/) tool.'),
  308. $this->target->getNickname(),
  309. $this->target->getNickname()
  310. );
  311. }
  312. $this->elementStart('div', array('id' => 'anon_notice'));
  313. $this->raw(common_markup_to_html($m));
  314. $this->elementEnd('div');
  315. }
  316. public function noticeFormOptions()
  317. {
  318. $options = parent::noticeFormOptions();
  319. if (!$this->scoped instanceof Profile || !$this->scoped->sameAs($this->target)) {
  320. $options['to_profile'] = $this->target;
  321. }
  322. return $options;
  323. }
  324. }