PageRenderTime 60ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/facebookaction.php

https://github.com/Br3nda/laconica
PHP | 697 lines | 404 code | 136 blank | 157 comment | 24 complexity | 8a75903dcf5a39b778d61bc199e84fb9 MD5 | raw file
Possible License(s): AGPL-3.0
  1. <?php
  2. /**
  3. * Laconica, the distributed open-source microblogging tool
  4. *
  5. * Low-level generator for HTML
  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 Faceboook
  23. * @package Laconica
  24. * @author Zach Copley <zach@controlyourself.ca>
  25. * @copyright 2008 Control Yourself, Inc.
  26. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  27. * @link http://laconi.ca/
  28. */
  29. if (!defined('LACONICA'))
  30. {
  31. exit(1);
  32. }
  33. require_once INSTALLDIR.'/lib/facebookutil.php';
  34. require_once INSTALLDIR.'/lib/noticeform.php';
  35. class FacebookAction extends Action
  36. {
  37. var $facebook = null;
  38. var $fbuid = null;
  39. var $flink = null;
  40. var $action = null;
  41. var $app_uri = null;
  42. var $app_name = null;
  43. /**
  44. * Constructor
  45. *
  46. * Just wraps the HTMLOutputter constructor.
  47. *
  48. * @param string $output URI to output to, default = stdout
  49. * @param boolean $indent Whether to indent output, default true
  50. *
  51. * @see XMLOutputter::__construct
  52. * @see HTMLOutputter::__construct
  53. */
  54. function __construct($output='php://output', $indent=true, $facebook=null, $flink=null)
  55. {
  56. parent::__construct($output, $indent);
  57. $this->facebook = $facebook;
  58. $this->flink = $flink;
  59. if ($this->flink) {
  60. $this->fbuid = $flink->foreign_id;
  61. $this->user = $flink->getUser();
  62. }
  63. $this->args = array();
  64. }
  65. function prepare($argarray)
  66. {
  67. parent::prepare($argarray);
  68. $this->facebook = getFacebook();
  69. $this->fbuid = $this->facebook->require_login();
  70. $this->action = $this->trimmed('action');
  71. $app_props = $this->facebook->api_client->Admin_getAppProperties(
  72. array('canvas_name', 'application_name'));
  73. $this->app_uri = 'http://apps.facebook.com/' . $app_props['canvas_name'];
  74. $this->app_name = $app_props['application_name'];
  75. $this->flink = Foreign_link::getByForeignID($this->fbuid, FACEBOOK_SERVICE);
  76. return true;
  77. }
  78. function showStylesheets()
  79. {
  80. // Add a timestamp to the file so Facebook cache wont ignore our changes
  81. $ts = filemtime(INSTALLDIR.'/theme/base/css/display.css');
  82. $this->element('link', array('rel' => 'stylesheet',
  83. 'type' => 'text/css',
  84. 'href' => theme_path('css/display.css', 'base') . '?ts=' . $ts));
  85. $theme = common_config('site', 'theme');
  86. $ts = filemtime(INSTALLDIR. '/theme/' . $theme .'/css/display.css');
  87. $this->element('link', array('rel' => 'stylesheet',
  88. 'type' => 'text/css',
  89. 'href' => theme_path('css/display.css', null) . '?ts=' . $ts));
  90. $ts = filemtime(INSTALLDIR.'/theme/base/css/facebookapp.css');
  91. $this->element('link', array('rel' => 'stylesheet',
  92. 'type' => 'text/css',
  93. 'href' => theme_path('css/facebookapp.css', 'base') . '?ts=' . $ts));
  94. }
  95. function showScripts()
  96. {
  97. // Add a timestamp to the file so Facebook cache wont ignore our changes
  98. $ts = filemtime(INSTALLDIR.'/js/facebookapp.js');
  99. $this->element('script', array('src' => common_path('js/facebookapp.js') . '?ts=' . $ts));
  100. }
  101. /**
  102. * Start an Facebook ready HTML document
  103. *
  104. * For Facebook we don't want to actually output any headers,
  105. * DTD info, etc. Just Stylesheet and JavaScript links.
  106. *
  107. * If $type isn't specified, will attempt to do content negotiation.
  108. *
  109. * @param string $type MIME type to use; default is to do negotation.
  110. *
  111. * @return void
  112. */
  113. function startHTML($type=null)
  114. {
  115. $this->showStylesheets();
  116. $this->showScripts();
  117. $this->elementStart('div', array('class' => 'facebook-page'));
  118. }
  119. /**
  120. * Ends a Facebook ready HTML document
  121. *
  122. * @return void
  123. */
  124. function endHTML()
  125. {
  126. $this->elementEnd('div');
  127. $this->endXML();
  128. }
  129. /**
  130. * Show notice form.
  131. *
  132. * MAY overload if no notice form needed... or direct message box????
  133. *
  134. * @return nothing
  135. */
  136. function showNoticeForm()
  137. {
  138. // don't do it for most of the Facebook pages
  139. }
  140. function showBody()
  141. {
  142. $this->elementStart('div', array('id' => 'wrap'));
  143. $this->showHeader();
  144. $this->showCore();
  145. $this->showFooter();
  146. $this->elementEnd('div');
  147. }
  148. function showAside()
  149. {
  150. }
  151. function showHead($error, $success)
  152. {
  153. if ($error) {
  154. $this->element("h1", null, $error);
  155. }
  156. if ($success) {
  157. $this->element("h1", null, $success);
  158. }
  159. $this->elementStart('fb:if-section-not-added', array('section' => 'profile'));
  160. $this->elementStart('span', array('id' => 'add_to_profile'));
  161. $this->element('fb:add-section-button', array('section' => 'profile'));
  162. $this->elementEnd('span');
  163. $this->elementEnd('fb:if-section-not-added');
  164. }
  165. // Make this into a widget later
  166. function showLocalNav()
  167. {
  168. $this->elementStart('ul', array('class' => 'nav'));
  169. $this->elementStart('li', array('class' =>
  170. ($this->action == 'facebookhome') ? 'current' : 'facebook_home'));
  171. $this->element('a',
  172. array('href' => 'index.php', 'title' => _('Home')), _('Home'));
  173. $this->elementEnd('li');
  174. if (common_config('invite', 'enabled')) {
  175. $this->elementStart('li',
  176. array('class' =>
  177. ($this->action == 'facebookinvite') ? 'current' : 'facebook_invite'));
  178. $this->element('a',
  179. array('href' => 'invite.php', 'title' => _('Invite')), _('Invite'));
  180. $this->elementEnd('li');
  181. }
  182. $this->elementStart('li',
  183. array('class' =>
  184. ($this->action == 'facebooksettings') ? 'current' : 'facebook_settings'));
  185. $this->element('a',
  186. array('href' => 'settings.php',
  187. 'title' => _('Settings')), _('Settings'));
  188. $this->elementEnd('li');
  189. $this->elementEnd('ul');
  190. }
  191. /**
  192. * Show header of the page.
  193. *
  194. * Calls template methods
  195. *
  196. * @return nothing
  197. */
  198. function showHeader()
  199. {
  200. $this->elementStart('div', array('id' => 'header'));
  201. $this->showLogo();
  202. $this->showNoticeForm();
  203. $this->elementEnd('div');
  204. }
  205. /**
  206. * Show page, a template method.
  207. *
  208. * @return nothing
  209. */
  210. function showPage($error = null, $success = null)
  211. {
  212. $this->startHTML();
  213. $this->showHead($error, $success);
  214. $this->showBody();
  215. $this->endHTML();
  216. }
  217. function showInstructions()
  218. {
  219. $this->elementStart('div', array('class' => 'facebook_guide'));
  220. $this->elementStart('dl', array('class' => 'system_notice'));
  221. $this->element('dt', null, 'Page Notice');
  222. $loginmsg_part1 = _('To use the %s Facebook Application you need to login ' .
  223. 'with your username and password. Don\'t have a username yet? ');
  224. $loginmsg_part2 = _(' a new account.');
  225. $this->elementStart('dd');
  226. $this->elementStart('p');
  227. $this->text(sprintf($loginmsg_part1, common_config('site', 'name')));
  228. $this->element('a',
  229. array('href' => common_local_url('register')), _('Register'));
  230. $this->text($loginmsg_part2);
  231. $this->elementEnd('p');
  232. $this->elementEnd('dd');
  233. $this->elementEnd('dl');
  234. $this->elementEnd('div');
  235. }
  236. function showLoginForm($msg = null)
  237. {
  238. $this->elementStart('div', array('id' => 'content'));
  239. $this->element('h1', null, _('Login'));
  240. if ($msg) {
  241. $this->element('fb:error', array('message' => $msg));
  242. }
  243. $this->showInstructions();
  244. $this->elementStart('div', array('id' => 'content_inner'));
  245. $this->elementStart('form', array('method' => 'post',
  246. 'class' => 'form_settings',
  247. 'id' => 'login',
  248. 'action' => 'index.php'));
  249. $this->elementStart('fieldset');
  250. $this->elementStart('ul', array('class' => 'form_datas'));
  251. $this->elementStart('li');
  252. $this->input('nickname', _('Nickname'));
  253. $this->elementEnd('li');
  254. $this->elementStart('li');
  255. $this->password('password', _('Password'));
  256. $this->elementEnd('li');
  257. $this->elementEnd('ul');
  258. $this->submit('submit', _('Login'));
  259. $this->elementEnd('fieldset');
  260. $this->elementEnd('form');
  261. $this->elementStart('p');
  262. $this->element('a', array('href' => common_local_url('recoverpassword')),
  263. _('Lost or forgotten password?'));
  264. $this->elementEnd('p');
  265. $this->elementEnd('div');
  266. $this->elementEnd('div');
  267. }
  268. function updateProfileBox($notice)
  269. {
  270. // Need to include inline CSS for styling the Profile box
  271. $app_props = $this->facebook->api_client->Admin_getAppProperties(array('icon_url'));
  272. $icon_url = $app_props['icon_url'];
  273. $style = '<style>
  274. .entry-title *,
  275. .entry-content * {
  276. font-size:14px;
  277. font-family:"Lucida Sans Unicode", "Lucida Grande", sans-serif;
  278. }
  279. .entry-title a,
  280. .entry-content a {
  281. color:#002E6E;
  282. }
  283. .entry-title .vcard .photo {
  284. float:left;
  285. display:inline;
  286. margin-right:11px;
  287. margin-bottom:11px
  288. }
  289. .entry-title {
  290. margin-bottom:11px;
  291. }
  292. .entry-title p.entry-content {
  293. display:inline;
  294. margin-left:5px;
  295. }
  296. div.entry-content {
  297. clear:both;
  298. }
  299. div.entry-content dl,
  300. div.entry-content dt,
  301. div.entry-content dd {
  302. display:inline;
  303. text-transform:lowercase;
  304. }
  305. div.entry-content dd,
  306. div.entry-content .device dt {
  307. margin-left:0;
  308. margin-right:5px;
  309. }
  310. div.entry-content dl.timestamp dt,
  311. div.entry-content dl.response dt {
  312. display:none;
  313. }
  314. div.entry-content dd a {
  315. display:inline-block;
  316. }
  317. #facebook_laconica_app {
  318. text-indent:-9999px;
  319. height:16px;
  320. width:16px;
  321. display:block;
  322. background:url('.$icon_url.') no-repeat 0 0;
  323. float:right;
  324. }
  325. </style>';
  326. $this->xw->openMemory();
  327. $item = new FacebookProfileBoxNotice($notice, $this);
  328. $item->show();
  329. $fbml = "<fb:wide>$style " . $this->xw->outputMemory(false) . "</fb:wide>";
  330. $fbml .= "<fb:narrow>$style " . $this->xw->outputMemory(false) . "</fb:narrow>";
  331. $fbml_main = "<fb:narrow>$style " . $this->xw->outputMemory(false) . "</fb:narrow>";
  332. $this->facebook->api_client->profile_setFBML(null, $this->fbuid, $fbml, null, null, $fbml_main);
  333. $this->xw->openURI('php://output');
  334. }
  335. /**
  336. * Generate pagination links
  337. *
  338. * @param boolean $have_before is there something before?
  339. * @param boolean $have_after is there something after?
  340. * @param integer $page current page
  341. * @param string $action current action
  342. * @param array $args rest of query arguments
  343. *
  344. * @return nothing
  345. */
  346. function pagination($have_before, $have_after, $page, $action, $args=null)
  347. {
  348. // Does a little before-after block for next/prev page
  349. if ($have_before || $have_after) {
  350. $this->elementStart('div', array('class' => 'pagination'));
  351. $this->elementStart('dl', null);
  352. $this->element('dt', null, _('Pagination'));
  353. $this->elementStart('dd', null);
  354. $this->elementStart('ul', array('class' => 'nav'));
  355. }
  356. if ($have_before) {
  357. $pargs = array('page' => $page-1);
  358. $newargs = $args ? array_merge($args, $pargs) : $pargs;
  359. $this->elementStart('li', array('class' => 'nav_prev'));
  360. $this->element('a', array('href' => "$this->app_uri/$action?page=$newargs[page]", 'rel' => 'prev'),
  361. _('After'));
  362. $this->elementEnd('li');
  363. }
  364. if ($have_after) {
  365. $pargs = array('page' => $page+1);
  366. $newargs = $args ? array_merge($args, $pargs) : $pargs;
  367. $this->elementStart('li', array('class' => 'nav_next'));
  368. $this->element('a', array('href' => "$this->app_uri/$action?page=$newargs[page]", 'rel' => 'next'),
  369. _('Before'));
  370. $this->elementEnd('li');
  371. }
  372. if ($have_before || $have_after) {
  373. $this->elementEnd('ul');
  374. $this->elementEnd('dd');
  375. $this->elementEnd('dl');
  376. $this->elementEnd('div');
  377. }
  378. }
  379. function saveNewNotice()
  380. {
  381. $user = $this->flink->getUser();
  382. $content = $this->trimmed('status_textarea');
  383. if (!$content) {
  384. $this->showPage(_('No notice content!'));
  385. return;
  386. } else {
  387. $content_shortened = common_shorten_links($content);
  388. if (mb_strlen($content_shortened) > 140) {
  389. $this->showPage(_('That\'s too long. Max notice size is 140 chars.'));
  390. return;
  391. }
  392. }
  393. $inter = new CommandInterpreter();
  394. $cmd = $inter->handle_command($user, $content_shortened);
  395. if ($cmd) {
  396. // XXX fix this
  397. $cmd->execute(new WebChannel());
  398. return;
  399. }
  400. $replyto = $this->trimmed('inreplyto');
  401. $notice = Notice::saveNew($user->id, $content,
  402. 'web', 1, ($replyto == 'false') ? null : $replyto);
  403. if (is_string($notice)) {
  404. $this->showPage($notice);
  405. return;
  406. }
  407. common_broadcast_notice($notice);
  408. // Also update the user's Facebook status
  409. facebookBroadcastNotice($notice);
  410. }
  411. }
  412. class FacebookNoticeForm extends NoticeForm
  413. {
  414. var $post_action = null;
  415. /**
  416. * Constructor
  417. *
  418. * @param HTMLOutputter $out output channel
  419. * @param string $action action to return to, if any
  420. * @param string $content content to pre-fill
  421. */
  422. function __construct($out=null, $action=null, $content=null,
  423. $post_action=null, $user=null)
  424. {
  425. parent::__construct($out, $action, $content, $user);
  426. $this->post_action = $post_action;
  427. }
  428. /**
  429. * Action of the form
  430. *
  431. * @return string URL of the action
  432. */
  433. function action()
  434. {
  435. return $this->post_action;
  436. }
  437. }
  438. class FacebookNoticeList extends NoticeList
  439. {
  440. /**
  441. * constructor
  442. *
  443. * @param Notice $notice stream of notices from DB_DataObject
  444. */
  445. function __construct($notice, $out=null)
  446. {
  447. parent::__construct($notice, $out);
  448. }
  449. /**
  450. * show the list of notices
  451. *
  452. * "Uses up" the stream by looping through it. So, probably can't
  453. * be called twice on the same list.
  454. *
  455. * @return int count of notices listed.
  456. */
  457. function show()
  458. {
  459. $this->out->elementStart('div', array('id' =>'notices_primary'));
  460. $this->out->element('h2', null, _('Notices'));
  461. $this->out->elementStart('ul', array('class' => 'notices'));
  462. $cnt = 0;
  463. while ($this->notice->fetch() && $cnt <= NOTICES_PER_PAGE) {
  464. $cnt++;
  465. if ($cnt > NOTICES_PER_PAGE) {
  466. break;
  467. }
  468. $item = $this->newListItem($this->notice);
  469. $item->show();
  470. }
  471. $this->out->elementEnd('ul');
  472. $this->out->elementEnd('div');
  473. return $cnt;
  474. }
  475. /**
  476. * returns a new list item for the current notice
  477. *
  478. * Overridden to return a Facebook specific list item.
  479. *
  480. * @param Notice $notice the current notice
  481. *
  482. * @return FacebookNoticeListItem a list item for displaying the notice
  483. * formatted for display in the Facebook App.
  484. */
  485. function newListItem($notice)
  486. {
  487. return new FacebookNoticeListItem($notice, $this);
  488. }
  489. }
  490. class FacebookNoticeListItem extends NoticeListItem
  491. {
  492. /**
  493. * constructor
  494. *
  495. * Also initializes the profile attribute.
  496. *
  497. * @param Notice $notice The notice we'll display
  498. */
  499. function __construct($notice, $out=null)
  500. {
  501. parent::__construct($notice, $out);
  502. }
  503. /**
  504. * recipe function for displaying a single notice in the Facebook App.
  505. *
  506. * Overridden to strip out some of the controls that we don't
  507. * want to be available.
  508. *
  509. * @return void
  510. */
  511. function show()
  512. {
  513. $this->showStart();
  514. $this->showNotice();
  515. $this->showNoticeInfo();
  516. // XXX: Need to update to show attachements and controls
  517. $this->showEnd();
  518. }
  519. }
  520. class FacebookProfileBoxNotice extends FacebookNoticeListItem
  521. {
  522. /**
  523. * constructor
  524. *
  525. * Also initializes the profile attribute.
  526. *
  527. * @param Notice $notice The notice we'll display
  528. */
  529. function __construct($notice, $out=null)
  530. {
  531. parent::__construct($notice, $out);
  532. }
  533. /**
  534. * Recipe function for displaying a single notice in the
  535. * Facebook App profile notice box
  536. *
  537. * @return void
  538. */
  539. function show()
  540. {
  541. $this->showNotice();
  542. $this->showNoticeInfo();
  543. $this->showAppLink();
  544. }
  545. function showAppLink()
  546. {
  547. $this->facebook = getFacebook();
  548. $app_props = $this->facebook->api_client->Admin_getAppProperties(
  549. array('canvas_name', 'application_name'));
  550. $this->app_uri = 'http://apps.facebook.com/' . $app_props['canvas_name'];
  551. $this->app_name = $app_props['application_name'];
  552. $this->out->elementStart('a', array('id' => 'facebook_laconica_app',
  553. 'href' => $this->app_uri));
  554. $this->out->text($this->app_name);
  555. $this->out->elementEnd('a');
  556. }
  557. }