showstream.php 13 KB

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