291 lines
10 KiB
PHP
291 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2009-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file COPYING for license information (LGPL-2). If you
|
|
* did not receive this file, see http://www.horde.org/licenses/lgpl.
|
|
*
|
|
* @author Michael J Rubinsky <mrubinsk.horde.org>
|
|
* @category Horde
|
|
* @license http://www.horde.org/licenses/lgpl LGPL-2
|
|
* @package Horde
|
|
*/
|
|
|
|
/**
|
|
* Defines the AJAX actions used in the Twitter client.
|
|
*
|
|
* @author Michael J Rubinsky <mrubinsk.horde.org>
|
|
* @category Horde
|
|
* @license http://www.horde.org/licenses/lgpl LGPL-2
|
|
* @package Horde
|
|
*/
|
|
class Horde_Ajax_Application_TwitterHandler extends Horde_Core_Ajax_Application_Handler
|
|
{
|
|
/**
|
|
* Update the twitter timeline.
|
|
*
|
|
* @return array An hash containing the following keys:
|
|
* - o: The id of the oldest tweet
|
|
* - n: The id of the newest tweet
|
|
* - c: The HTML content
|
|
*/
|
|
public function twitterUpdate()
|
|
{
|
|
global $conf;
|
|
|
|
if (empty($conf['twitter']['enabled'])) {
|
|
return _("Twitter not enabled.");
|
|
}
|
|
|
|
switch ($this->vars->actionID) {
|
|
case 'getPage':
|
|
return $this->_doTwitterGetPage();
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Retweet a tweet. Expects the following in $this->vars:
|
|
* - tweetId: The tweet id to retweet.
|
|
* - i:
|
|
*
|
|
* @return string The HTML to render the newly retweeted tweet.
|
|
*/
|
|
public function retweet()
|
|
{
|
|
$twitter = $this->_getTwitterObject();
|
|
try {
|
|
$tweet = json_decode($twitter->statuses->retweet($this->vars->tweetId));
|
|
$html = $this->_buildTweet($tweet)->render('twitter_tweet');
|
|
return $html;
|
|
} catch (Horde_Service_Twitter_Exception $e) {
|
|
$this->_twitterError($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Favorite a tweet. Expects:
|
|
* - tweetId:
|
|
*
|
|
* @return stdClass
|
|
*/
|
|
public function favorite()
|
|
{
|
|
$twitter = $this->_getTwitterObject();
|
|
try {
|
|
return json_decode($twitter->favorites->create($this->vars->tweetId));
|
|
} catch (Horde_Service_Twitter_Exception $e) {
|
|
$this->_twitterError($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unfavorite a tweet. Expects:
|
|
* - tweetId:
|
|
*/
|
|
public function unfavorite()
|
|
{
|
|
$twitter = $this->_getTwitterObject();
|
|
try {
|
|
return json_decode($twitter->favorites->destroy($this->vars->tweetId));
|
|
} catch (Horde_Service_Twitter_Exception $e) {
|
|
$this->_twitterError($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update twitter status. Expects:
|
|
* - inReplyTo:
|
|
* - statusText:
|
|
*
|
|
* @return string The HTML text of the new tweet.
|
|
*/
|
|
public function updateStatus()
|
|
{
|
|
$twitter = $this->_getTwitterObject();
|
|
if ($inreplyTo = $this->vars->inReplyTo) {
|
|
$params = array('in_reply_to_status_id', $inreplyTo);
|
|
} else {
|
|
$params = array();
|
|
}
|
|
try {
|
|
$tweet = json_decode($twitter->statuses->update($this->vars->statusText, $params));
|
|
return $this->_buildTweet($tweet)->render('twitter_tweet');
|
|
} catch (Horde_Service_Twitter_Exception $e) {
|
|
$this->_twitterError($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @return Horde_Service_Twitter
|
|
*/
|
|
protected function _getTwitterObject()
|
|
{
|
|
$twitter = $GLOBALS['injector']->getInstance('Horde_Service_Twitter');
|
|
$token = unserialize($GLOBALS['prefs']->getValue('twitter'));
|
|
if (!empty($token['key']) && !empty($token['secret'])) {
|
|
$auth_token = new Horde_Oauth_Token($token['key'], $token['secret']);
|
|
$twitter->auth->setToken($auth_token);
|
|
}
|
|
|
|
return $twitter;
|
|
}
|
|
|
|
/**
|
|
* Helper method to build a view object for a tweet.
|
|
*
|
|
* @param stdClass $tweet The tweet object.
|
|
*
|
|
* @return Horde_View The view object, populated with tweet data.
|
|
*/
|
|
protected function _buildTweet($tweet)
|
|
{
|
|
global $injector, $registry;
|
|
|
|
$view = new Horde_View(array('templatePath' => HORDE_TEMPLATES . '/block'));
|
|
$view->addHelper('Tag');
|
|
$view->ajax_uri = $registry->getServiceLink('ajax', $registry->getApp());
|
|
$filter = $injector->getInstance('Horde_Core_Factory_TextFilter');
|
|
$instance = $this->vars->i;
|
|
|
|
// Links and media
|
|
$map = $previews = array();
|
|
foreach ($tweet->entities->urls as $link) {
|
|
$replace = '<a target="_blank" href="' . $link->url . '" title="' . $link->expanded_url . '">' . htmlspecialchars($link->display_url) . '</a>';
|
|
$map[$link->indices[0]] = array($link->indices[1], $replace);
|
|
}
|
|
if (!empty($tweet->entities->media)) {
|
|
foreach ($tweet->entities->media as $picture) {
|
|
$replace = '<a target="_blank" href="' . $picture->url . '" title="' . $picture->expanded_url . '">' . htmlentities($picture->display_url, ENT_COMPAT, 'UTF-8') . '</a>';
|
|
$map[$picture->indices[0]] = array($picture->indices[1], $replace);
|
|
$previews[] = ' <a href="#" onclick="return Horde[\'twitter' . $instance . '\'].showPreview(\'' . $picture->media_url . ':small\');"><img src="' . Horde_Themes::img('mime/image.png') . '" /></a>';
|
|
}
|
|
}
|
|
if (!empty($tweet->entities->user_mentions)) {
|
|
foreach ($tweet->entities->user_mentions as $user) {
|
|
$replace = ' <a target="_blank" title="' . $user->name . '" href="http://twitter.com/' . $user->screen_name . '">@' . htmlentities($user->screen_name, ENT_COMPAT, 'UTF-8') . '</a>';
|
|
$map[$user->indices[0]] = array($user->indices[1], $replace);
|
|
}
|
|
}
|
|
if (!empty($tweet->entities->hashtags)) {
|
|
foreach ($tweet->entities->hashtags as $hashtag) {
|
|
$replace = ' <a target="_blank" href="http://twitter.com/search?q=#' . urlencode($hashtag->text) . '">#' . htmlentities($hashtag->text, ENT_COMPAT, 'UTF-8') . '</a>';
|
|
$map[$hashtag->indices[0]] = array($hashtag->indices[1], $replace);
|
|
}
|
|
}
|
|
$body = '';
|
|
$pos = 0;
|
|
while ($pos <= Horde_String::length($tweet->text) - 1) {
|
|
if (!empty($map[$pos])) {
|
|
$entity = $map[$pos];
|
|
$body .= $entity[1];
|
|
$pos = $entity[0];
|
|
} else {
|
|
$body .= Horde_String::substr($tweet->text, $pos, 1);
|
|
++$pos;
|
|
}
|
|
}
|
|
foreach ($previews as $preview) {
|
|
$body .= $preview;
|
|
}
|
|
$view->body = $body;
|
|
|
|
/* If this is a retweet, use the original author's profile info */
|
|
if (!empty($tweet->retweeted_status)) {
|
|
$tweetObj = $tweet->retweeted_status;
|
|
} else {
|
|
$tweetObj = $tweet;
|
|
}
|
|
|
|
/* These are all referencing the *original* tweet */
|
|
$view->profileLink = Horde::externalUrl('http://twitter.com/' . htmlspecialchars($tweetObj->user->screen_name), true);
|
|
$view->profileImg = $GLOBALS['browser']->usingSSLConnection() ? $tweetObj->user->profile_image_url_https : $tweetObj->user->profile_image_url;
|
|
$view->authorName = '@' . htmlspecialchars($tweetObj->user->screen_name);
|
|
$view->authorFullname = htmlspecialchars($tweetObj->user->name);
|
|
$view->createdAt = $tweetObj->created_at;
|
|
$view->clientText = $filter->filter($tweet->source, 'xss');
|
|
$view->tweet = $tweet;
|
|
$view->instanceid = $instance;
|
|
|
|
return $view;
|
|
}
|
|
|
|
/**
|
|
* Helper method for getting a slice of tweets.
|
|
*
|
|
* Expects the following in $this->vars:
|
|
* - max_id:
|
|
* - since_id:
|
|
* - i:
|
|
* - mentions:
|
|
*
|
|
* @return [type] [description]
|
|
*/
|
|
protected function _doTwitterGetPage()
|
|
{
|
|
$twitter = $this->_getTwitterObject();
|
|
try {
|
|
$params = array('include_entities' => 1);
|
|
if ($max = $this->vars->max_id) {
|
|
$params['max_id'] = $max;
|
|
} elseif ($since = $this->vars->since_id) {
|
|
$params['since_id'] = $since;
|
|
}
|
|
if ($this->vars->mentions) {
|
|
$stream = Horde_Serialize::unserialize($twitter->statuses->mentions($params), Horde_Serialize::JSON);
|
|
} else {
|
|
$stream = Horde_Serialize::unserialize($twitter->statuses->homeTimeline($params), Horde_Serialize::JSON);
|
|
}
|
|
} catch (Horde_Service_Twitter_Exception $e) {
|
|
$this->_twitterError($e);
|
|
return;
|
|
}
|
|
if (count($stream)) {
|
|
$newest = $stream[0]->id_str;
|
|
} else {
|
|
$newest = $params['since_id'];
|
|
$oldest = 0;
|
|
}
|
|
|
|
$view = new Horde_View(array('templatePath' => HORDE_TEMPLATES . '/block'));
|
|
$view->addHelper('Tag');
|
|
$html = '';
|
|
foreach ($stream as $tweet) {
|
|
/* Don't return the max_id tweet, since we already have it */
|
|
if (!empty($params['max_id']) && $params['max_id'] == $tweet->id_str) {
|
|
continue;
|
|
}
|
|
$view = $this->_buildTweet($tweet);
|
|
$oldest = $tweet->id_str;
|
|
$html .= $view->render('twitter_tweet');
|
|
}
|
|
|
|
$result = array(
|
|
'o' => $oldest,
|
|
'n' => $newest,
|
|
'c' => $html
|
|
);
|
|
|
|
return $result;
|
|
}
|
|
|
|
protected function _twitterError($e)
|
|
{
|
|
global $notification;
|
|
|
|
Horde::log($e, 'INFO');
|
|
$body = ($e instanceof Exception) ? $e->getMessage() : $e;
|
|
if (($errors = json_decode($body, true)) && isset($errors['errors'])) {
|
|
$errors = $errors['errors'];
|
|
} else {
|
|
$errors = array(array('message' => $body));
|
|
}
|
|
$notification->push(_("Error connecting to Twitter. Details have been logged for the administrator."), 'horde.error', array('sticky'));
|
|
foreach ($errors as $error) {
|
|
$notification->push($error['message'], 'horde.error', array('sticky'));
|
|
}
|
|
}
|
|
|
|
}
|