PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/noticelist.php

https://github.com/Br3nda/laconica
PHP | 504 lines | 252 code | 56 blank | 196 comment | 26 complexity | 38c93604749473ec09a3e71aafdb503e MD5 | raw file
Possible License(s): AGPL-3.0
  1. <?php
  2. /**
  3. * Laconica, the distributed open-source microblogging tool
  4. *
  5. * widget for displaying a list of notices
  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 UI
  23. * @package Laconica
  24. * @author Evan Prodromou <evan@controlyourself.ca>
  25. * @author Sarven Capadisli <csarven@controlyourself.ca>
  26. * @copyright 2008 Control Yourself, Inc.
  27. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  28. * @link http://laconi.ca/
  29. */
  30. if (!defined('LACONICA')) {
  31. exit(1);
  32. }
  33. require_once INSTALLDIR.'/lib/favorform.php';
  34. require_once INSTALLDIR.'/lib/disfavorform.php';
  35. require_once INSTALLDIR.'/lib/attachmentlist.php';
  36. /**
  37. * widget for displaying a list of notices
  38. *
  39. * There are a number of actions that display a list of notices, in
  40. * reverse chronological order. This widget abstracts out most of the
  41. * code for UI for notice lists. It's overridden to hide some
  42. * data for e.g. the profile page.
  43. *
  44. * @category UI
  45. * @package Laconica
  46. * @author Evan Prodromou <evan@controlyourself.ca>
  47. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  48. * @link http://laconi.ca/
  49. * @see Notice
  50. * @see NoticeListItem
  51. * @see ProfileNoticeList
  52. */
  53. class NoticeList extends Widget
  54. {
  55. /** the current stream of notices being displayed. */
  56. var $notice = null;
  57. /**
  58. * constructor
  59. *
  60. * @param Notice $notice stream of notices from DB_DataObject
  61. */
  62. function __construct($notice, $out=null)
  63. {
  64. parent::__construct($out);
  65. $this->notice = $notice;
  66. }
  67. /**
  68. * show the list of notices
  69. *
  70. * "Uses up" the stream by looping through it. So, probably can't
  71. * be called twice on the same list.
  72. *
  73. * @return int count of notices listed.
  74. */
  75. function show()
  76. {
  77. $this->out->elementStart('div', array('id' =>'notices_primary'));
  78. $this->out->element('h2', null, _('Notices'));
  79. $this->out->elementStart('ol', array('class' => 'notices xoxo'));
  80. $cnt = 0;
  81. while ($this->notice->fetch() && $cnt <= NOTICES_PER_PAGE) {
  82. $cnt++;
  83. if ($cnt > NOTICES_PER_PAGE) {
  84. break;
  85. }
  86. $item = $this->newListItem($this->notice);
  87. $item->show();
  88. }
  89. $this->out->elementEnd('ol');
  90. $this->out->elementEnd('div');
  91. return $cnt;
  92. }
  93. /**
  94. * returns a new list item for the current notice
  95. *
  96. * Recipe (factory?) method; overridden by sub-classes to give
  97. * a different list item class.
  98. *
  99. * @param Notice $notice the current notice
  100. *
  101. * @return NoticeListItem a list item for displaying the notice
  102. */
  103. function newListItem($notice)
  104. {
  105. return new NoticeListItem($notice, $this->out);
  106. }
  107. }
  108. /**
  109. * widget for displaying a single notice
  110. *
  111. * This widget has the core smarts for showing a single notice: what to display,
  112. * where, and under which circumstances. Its key method is show(); this is a recipe
  113. * that calls all the other show*() methods to build up a single notice. The
  114. * ProfileNoticeListItem subclass, for example, overrides showAuthor() to skip
  115. * author info (since that's implicit by the data in the page).
  116. *
  117. * @category UI
  118. * @package Laconica
  119. * @author Evan Prodromou <evan@controlyourself.ca>
  120. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  121. * @link http://laconi.ca/
  122. * @see NoticeList
  123. * @see ProfileNoticeListItem
  124. */
  125. class NoticeListItem extends Widget
  126. {
  127. /** The notice this item will show. */
  128. var $notice = null;
  129. /** The profile of the author of the notice, extracted once for convenience. */
  130. var $profile = null;
  131. /**
  132. * constructor
  133. *
  134. * Also initializes the profile attribute.
  135. *
  136. * @param Notice $notice The notice we'll display
  137. */
  138. function __construct($notice, $out=null)
  139. {
  140. parent::__construct($out);
  141. $this->notice = $notice;
  142. $this->profile = $notice->getProfile();
  143. }
  144. /**
  145. * recipe function for displaying a single notice.
  146. *
  147. * This uses all the other methods to correctly display a notice. Override
  148. * it or one of the others to fine-tune the output.
  149. *
  150. * @return void
  151. */
  152. function show()
  153. {
  154. $this->showStart();
  155. $this->showNotice();
  156. $this->showNoticeInfo();
  157. $this->showNoticeOptions();
  158. $this->showEnd();
  159. }
  160. function showNotice()
  161. {
  162. $this->out->elementStart('div', 'entry-title');
  163. $this->showAuthor();
  164. $this->showContent();
  165. $this->out->elementEnd('div');
  166. }
  167. function showNoticeInfo()
  168. {
  169. $this->out->elementStart('div', 'entry-content');
  170. $this->showNoticeLink();
  171. $this->showNoticeSource();
  172. $this->showContext();
  173. $this->out->elementEnd('div');
  174. }
  175. function showNoticeOptions()
  176. {
  177. $user = common_current_user();
  178. if ($user) {
  179. $this->out->elementStart('div', 'notice-options');
  180. $this->showFaveForm();
  181. $this->showReplyLink();
  182. $this->showDeleteLink();
  183. $this->out->elementEnd('div');
  184. }
  185. }
  186. /**
  187. * start a single notice.
  188. *
  189. * @return void
  190. */
  191. function showStart()
  192. {
  193. // XXX: RDFa
  194. // TODO: add notice_type class e.g., notice_video, notice_image
  195. $this->out->elementStart('li', array('class' => 'hentry notice',
  196. 'id' => 'notice-' . $this->notice->id));
  197. }
  198. /**
  199. * show the "favorite" form
  200. *
  201. * @return void
  202. */
  203. function showFaveForm()
  204. {
  205. $user = common_current_user();
  206. if ($user) {
  207. if ($user->hasFave($this->notice)) {
  208. $disfavor = new DisfavorForm($this->out, $this->notice);
  209. $disfavor->show();
  210. } else {
  211. $favor = new FavorForm($this->out, $this->notice);
  212. $favor->show();
  213. }
  214. }
  215. }
  216. /**
  217. * show the author of a notice
  218. *
  219. * By default, this shows the avatar and (linked) nickname of the author.
  220. *
  221. * @return void
  222. */
  223. function showAuthor()
  224. {
  225. $this->out->elementStart('span', 'vcard author');
  226. $attrs = array('href' => $this->profile->profileurl,
  227. 'class' => 'url');
  228. if (!empty($this->profile->fullname)) {
  229. $attrs['title'] = $this->profile->fullname . ' (' . $this->profile->nickname . ') ';
  230. }
  231. $this->out->elementStart('a', $attrs);
  232. $this->showAvatar();
  233. $this->showNickname();
  234. $this->out->elementEnd('a');
  235. $this->out->elementEnd('span');
  236. }
  237. /**
  238. * show the avatar of the notice's author
  239. *
  240. * This will use the default avatar if no avatar is assigned for the author.
  241. * It makes a link to the author's profile.
  242. *
  243. * @return void
  244. */
  245. function showAvatar()
  246. {
  247. if ('shownotice' === $this->out->trimmed('action')) {
  248. $avatar_size = AVATAR_PROFILE_SIZE;
  249. } else {
  250. $avatar_size = AVATAR_STREAM_SIZE;
  251. }
  252. $avatar = $this->profile->getAvatar($avatar_size);
  253. $this->out->element('img', array('src' => ($avatar) ?
  254. $avatar->displayUrl() :
  255. Avatar::defaultImage($avatar_size),
  256. 'class' => 'avatar photo',
  257. 'width' => $avatar_size,
  258. 'height' => $avatar_size,
  259. 'alt' =>
  260. ($this->profile->fullname) ?
  261. $this->profile->fullname :
  262. $this->profile->nickname));
  263. }
  264. /**
  265. * show the nickname of the author
  266. *
  267. * Links to the author's profile page
  268. *
  269. * @return void
  270. */
  271. function showNickname()
  272. {
  273. $this->out->element('span', array('class' => 'nickname fn'),
  274. $this->profile->nickname);
  275. }
  276. /**
  277. * show the content of the notice
  278. *
  279. * Shows the content of the notice. This is pre-rendered for efficiency
  280. * at save time. Some very old notices might not be pre-rendered, so
  281. * they're rendered on the spot.
  282. *
  283. * @return void
  284. */
  285. function showContent()
  286. {
  287. // FIXME: URL, image, video, audio
  288. $this->out->elementStart('p', array('class' => 'entry-content'));
  289. if ($this->notice->rendered) {
  290. $this->out->raw($this->notice->rendered);
  291. } else {
  292. // XXX: may be some uncooked notices in the DB,
  293. // we cook them right now. This should probably disappear in future
  294. // versions (>> 0.4.x)
  295. $this->out->raw(common_render_content($this->notice->content, $this->notice));
  296. }
  297. $this->out->elementEnd('p');
  298. }
  299. /**
  300. * show the link to the main page for the notice
  301. *
  302. * Displays a link to the page for a notice, with "relative" time. Tries to
  303. * get remote notice URLs correct, but doesn't always succeed.
  304. *
  305. * @return void
  306. */
  307. function showNoticeLink()
  308. {
  309. $noticeurl = common_local_url('shownotice',
  310. array('notice' => $this->notice->id));
  311. // XXX: we need to figure this out better. Is this right?
  312. if (strcmp($this->notice->uri, $noticeurl) != 0 &&
  313. preg_match('/^http/', $this->notice->uri)) {
  314. $noticeurl = $this->notice->uri;
  315. }
  316. $this->out->elementStart('dl', 'timestamp');
  317. $this->out->element('dt', null, _('Published'));
  318. $this->out->elementStart('dd', null);
  319. $this->out->elementStart('a', array('rel' => 'bookmark',
  320. 'href' => $noticeurl));
  321. $dt = common_date_iso8601($this->notice->created);
  322. $this->out->element('abbr', array('class' => 'published',
  323. 'title' => $dt),
  324. common_date_string($this->notice->created));
  325. $this->out->elementEnd('a');
  326. $this->out->elementEnd('dd');
  327. $this->out->elementEnd('dl');
  328. }
  329. /**
  330. * Show the source of the notice
  331. *
  332. * Either the name (and link) of the API client that posted the notice,
  333. * or one of other other channels.
  334. *
  335. * @return void
  336. */
  337. function showNoticeSource()
  338. {
  339. if ($this->notice->source) {
  340. $this->out->elementStart('dl', 'device');
  341. $this->out->element('dt', null, _('From'));
  342. $source_name = _($this->notice->source);
  343. switch ($this->notice->source) {
  344. case 'web':
  345. case 'xmpp':
  346. case 'mail':
  347. case 'omb':
  348. case 'system':
  349. case 'api':
  350. $this->out->element('dd', null, $source_name);
  351. break;
  352. default:
  353. $ns = Notice_source::staticGet($this->notice->source);
  354. if ($ns) {
  355. $this->out->elementStart('dd', null);
  356. $this->out->element('a', array('href' => $ns->url,
  357. 'rel' => 'external'),
  358. $ns->name);
  359. $this->out->elementEnd('dd');
  360. } else {
  361. $this->out->element('dd', null, $source_name);
  362. }
  363. break;
  364. }
  365. $this->out->elementEnd('dl');
  366. }
  367. }
  368. /**
  369. * show link to notice this notice is a reply to
  370. *
  371. * If this notice is a reply, show a link to the notice it is replying to. The
  372. * heavy lifting for figuring out replies happens at save time.
  373. *
  374. * @return void
  375. */
  376. function showContext()
  377. {
  378. // XXX: also show context if there are replies to this notice
  379. if (!empty($this->notice->conversation)
  380. && $this->notice->conversation != $this->notice->id) {
  381. $convurl = common_local_url('conversation',
  382. array('id' => $this->notice->conversation));
  383. $this->out->elementStart('dl', 'response');
  384. $this->out->element('dt', null, _('To'));
  385. $this->out->elementStart('dd');
  386. $this->out->element('a', array('href' => $convurl.'#notice-'.$this->notice->id),
  387. _('in context'));
  388. $this->out->elementEnd('dd');
  389. $this->out->elementEnd('dl');
  390. }
  391. }
  392. /**
  393. * show a link to reply to the current notice
  394. *
  395. * Should either do the reply in the current notice form (if available), or
  396. * link out to the notice-posting form. A little flakey, doesn't always work.
  397. *
  398. * @return void
  399. */
  400. function showReplyLink()
  401. {
  402. if (common_logged_in()) {
  403. $reply_url = common_local_url('newnotice',
  404. array('replyto' => $this->profile->nickname));
  405. $this->out->elementStart('dl', 'notice_reply');
  406. $this->out->element('dt', null, _('Reply to this notice'));
  407. $this->out->elementStart('dd');
  408. $this->out->elementStart('a', array('href' => $reply_url,
  409. 'title' => _('Reply to this notice')));
  410. $this->out->text(_('Reply'));
  411. $this->out->element('span', 'notice_id', $this->notice->id);
  412. $this->out->elementEnd('a');
  413. $this->out->elementEnd('dd');
  414. $this->out->elementEnd('dl');
  415. }
  416. }
  417. /**
  418. * if the user is the author, let them delete the notice
  419. *
  420. * @return void
  421. */
  422. function showDeleteLink()
  423. {
  424. $user = common_current_user();
  425. if ($user && $this->notice->profile_id == $user->id) {
  426. $deleteurl = common_local_url('deletenotice',
  427. array('notice' => $this->notice->id));
  428. $this->out->elementStart('dl', 'notice_delete');
  429. $this->out->element('dt', null, _('Delete this notice'));
  430. $this->out->elementStart('dd');
  431. $this->out->element('a', array('href' => $deleteurl,
  432. 'title' => _('Delete this notice')), _('Delete'));
  433. $this->out->elementEnd('dd');
  434. $this->out->elementEnd('dl');
  435. }
  436. }
  437. /**
  438. * finish the notice
  439. *
  440. * Close the last elements in the notice list item
  441. *
  442. * @return void
  443. */
  444. function showEnd()
  445. {
  446. $this->out->elementEnd('li');
  447. }
  448. }