if (!empty($r->file_id)) {
return $r;
}
- }
- $redir->httpcode = $redir_info['code'];
- $redir->redirections = intval($redir_info['redirects']);
- $redir->file = new File();
- $redir->file->url = $redir_info ? $redir_info['url'] : $in_url;
- $redir->file->mimetype = $redir_info['type'];
- $redir->file->size = $redir_info['size'];
- $redir->file->date = $redir_info['time'];
- if($redir_info['protected']) $redir->file->protected = true;
+ $redir->httpcode = $redir_info['code'];
+ $redir->redirections = intval($redir_info['redirects']);
+ $redir->file = new File();
+ $redir->file->url = $redir_info ? $redir_info['url'] : $in_url;
+ $redir->file->mimetype = $redir_info['type'];
+ $redir->file->size = $redir_info['size'];
+ $redir->file->date = $redir_info['time'];
+ if ($redir_info['protected']) {
+ $redir->file->protected = true;
+ }
+ }
return $redir;
}
// Use the local user's shortening preferences, if applicable.
$stored->rendered = $actor->isLocal()
? $actor->shortenLinks($act->content)
- : $act->content;
+ : common_purify($act->content);
$stored->content = common_strip_html($stored->rendered);
// Maybe a missing act-time should be fatal if the actor is not local?
}
$this->initDocument('json');
$error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
- $this->text(json_encode($error_array));
+ print(json_encode($error_array));
$this->endDocument('json');
break;
case 'text':
'maxpeople' => 500, // maximum no. of people with the same tag by the same user
'allow_tagging' => array('all' => true), // equivalent to array('local' => true, 'remote' => true)
'desclimit' => null),
- 'oembed' =>
- array('endpoint' => null, // 'https://noembed.com/embed/' for proxied oEmbed data
- 'order' => array('built-in', 'well-known', 'service', 'discovery'),
- ),
'search' =>
array('type' => 'like'),
'sessions' =>
return 'form';
}
- function li()
+ function li($class=null)
{
- $this->out->elementStart('li');
+ $this->out->elementStart('li', $class);
}
function unli()
define('GNUSOCIAL_ENGINE_URL', 'https://www.gnu.org/software/social/');
define('GNUSOCIAL_BASE_VERSION', '1.2.0');
-define('GNUSOCIAL_LIFECYCLE', 'beta1'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
+define('GNUSOCIAL_LIFECYCLE', 'beta2'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
define('GNUSOCIAL_VERSION', GNUSOCIAL_BASE_VERSION . '-' . GNUSOCIAL_LIFECYCLE);
while ($this->groups instanceof User_group && $this->groups->fetch()) {
$items[] = array('placeholder',
- array('nickname' => $this->groups->nickname,
+ array('nickname' => $this->groups->getNickname(),
'mainpage' => $this->groups->homeUrl()),
- $this->groups->getBestName(),
+ $this->groups->getNickname(),
$this->groups->getBestName()
);
}
/**
* Quick static function to GET a URL
*/
- public static function quickGet($url, $accept=null)
+ public static function quickGet($url, $accept=null, $params=array())
{
+ if (!empty($params)) {
+ $params = http_build_query($params, null, '&');
+ if (strpos($url, '?') === false) {
+ $url .= '?' . $params;
+ } else {
+ $url .= '&' . $params;
+ }
+ }
+
$client = new HTTPClient();
if (!is_null($accept)) {
$client->setHeader('Accept', $accept);
return $response->getBody();
}
+ public static function quickGetJson($url, $params=array())
+ {
+ $data = json_decode(self::quickGet($url, null, $params));
+ if (is_null($data)) {
+ common_debug('Could not decode JSON data from URL: '.$url);
+ throw new ServerException('Could not decode JSON data from URL');
+ }
+ return $data;
+ }
+
/**
* Convenience function to run a GET request.
*
'(?:'.
'(?:'. //Known protocols
'(?:'.
- '(?:(?:https?|ftps?|mms|rtsp|gopher|news|nntp|telnet|wais|file|prospero|webcal|irc)://)'.
+ '(?:(?:https?|ftps?|mms|rtsp|gopher|news|nntp|telnet|wais|file|prospero|webcal|ircs?)://)'.
'|'.
'(?:(?:mailto|aim|tel|xmpp):)'.
')'.
$results = array();
foreach($this->profiles as $profile){
$avatarUrl = $profile->avatarUrl(AVATAR_MINI_SIZE);
+ $acct = $profile->getAcctUri();
+ $identifier = split(':', $profile->getAcctUri(), 2)[1];
$results[] = array(
- 'value' => '@'.$profile->nickname,
- 'nickname' => $profile->nickname,
- 'label'=> $profile->getFancyName(),
+ 'value' => '@'.$identifier,
+ 'nickname' => $profile->getNickname(),
+ 'acct_uri' => $acct,
+ 'label'=> "${identifier} (".$profile->getFullname().")",
'avatar' => $avatarUrl,
'type' => 'user'
);
} else {
$avatarUrl = User_group::defaultLogo(AVATAR_MINI_SIZE);
}
+ $acct = $profile->getAcctUri();
+ $identifier = split(':', $profile->getAcctUri(), 2)[1];
$results[] = array(
- 'value' => '!'.$group->nickname,
- 'nickname' => $group->nickname,
- 'label'=> $group->getFancyName(),
+ 'value' => '!'.$group->getNickname(),
+ 'nickname' => $group->getNickname(),
+ 'acct_uri' => $acct,
+ 'label'=> "${identifier} (".$group->getFullname().")",
'avatar' => $avatarUrl,
'type' => 'group');
}
array('id' => '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'));
$m->connect('main/event/updatetimes',
array('action' => 'timelist'));
+
+ $m->connect(':nickname/events',
+ array('action' => 'events'),
+ array('nickname' => Nickname::DISPLAY_FMT));
return true;
}
$out->raw($rsvp->asHTML());
$out->elementEnd('div');
}
+
+ function onEndPersonalGroupNav(Menu $menu, Profile $target, Profile $scoped=null)
+ {
+ $menu->menuItem(common_local_url('events', array('nickname' => $target->getNickname())),
+ // TRANS: Menu item in sample plugin.
+ _m('Happenings'),
+ // TRANS: Menu item title in sample plugin.
+ _m('A list of your events'), false, 'nav_timeline_events');
+ return true;
+ }
}
--- /dev/null
+<?php
+/**
+ * List events
+ */
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+class EventsAction extends ShowstreamAction
+{
+ public function getStream()
+ {
+ /* whose events */ /* are these the user's own events? */
+ $stream = new EventsNoticeStream($this->target, $this->scoped);
+ return $stream;
+ }
+
+ function title()
+ {
+ // TRANS: Page title for sample plugin. %s is a user nickname.
+ return sprintf(_m('%s\'s happenings'), $this->target->getNickname());
+ }
+
+ function getFeeds()
+ {
+ return array(
+ );
+ }
+
+ function showEmptyList() {
+ $message = sprintf(_('This is %1$s\'s event stream, but %1$s hasn\'t received any events yet.'), $this->target->getNickname()) . ' ';
+
+ $this->elementStart('div', 'guide');
+ $this->raw(common_markup_to_html($message));
+ $this->elementEnd('div');
+ }
+
+ /**
+ * Return true if read only.
+ *
+ * Some actions only read from the database; others read and write.
+ * The simple database load-balancer built into StatusNet will
+ * direct read-only actions to database mirrors (if they are configured),
+ * and read-write actions to the master database.
+ *
+ * This defaults to false to avoid data integrity issues, but you
+ * should make sure to overload it for performance gains.
+ *
+ * @param array $args other arguments, if RO/RW status depends on them.
+ *
+ * @return boolean is read only action?
+ */
+ function isReadOnly($args)
+ {
+ return true;
+ }
+}
--- /dev/null
+<?php
+
+class RawEventsNoticeStream extends NoticeStream
+{
+ protected $target;
+ protected $own;
+
+ function __construct(Profile $target)
+ {
+ $this->target = $target;
+ }
+
+ function getNoticeIds($offset, $limit, $since_id, $max_id)
+ {
+ $notice = new Notice();
+ $qry = null;
+
+ $qry = 'SELECT notice.* FROM notice ';
+ $qry .= 'INNER JOIN happening ON happening.uri = notice.uri ';
+ $qry .= 'WHERE happening.profile_id = ' . $this->target->getID() . ' ';
+ $qry .= 'AND notice.is_local != ' . Notice::GATEWAY . ' ';
+
+ if ($since_id != 0) {
+ $qry .= 'AND notice.id > ' . $since_id . ' ';
+ }
+
+ if ($max_id != 0) {
+ $qry .= 'AND notice.id <= ' . $max_id . ' ';
+ }
+
+ // NOTE: we sort by bookmark time, not by notice time!
+ $qry .= 'ORDER BY created DESC ';
+ if (!is_null($offset)) {
+ $qry .= "LIMIT $limit OFFSET $offset";
+ }
+
+ $notice->query($qry);
+ $ids = array();
+ while ($notice->fetch()) {
+ $ids[] = $notice->id;
+ }
+
+ $notice->free();
+ unset($notice);
+ return $ids;
+ }
+}
+
+class EventsNoticeStream extends ScopingNoticeStream
+{
+ function __construct(Profile $target, Profile $scoped=null)
+ {
+ $stream = new RawEventsNoticeStream($target);
+
+ if ($target->sameAs($scoped)) {
+ $key = 'bookmark:ids_by_user_own:'.$target->getID();
+ } else {
+ $key = 'bookmark:ids_by_user:'.$target->getID();
+ }
+
+ parent::__construct(new CachingNoticeStream($stream, $key), $scoped);
+ }
+}
' modified = "%s" '.
'WHERE user_id = %d '.
'AND notice_id = %d',
- Fave::newUri($fave->user_id, $fave->notice_id, $fave->modified),
+ Fave::newUri($fave->getActor(), $fave->getTarget(), $fave->modified),
common_sql_date(strtotime($fave->modified)),
$fave->user_id,
$fave->notice_id));
$m->connect('main/oembed', array('action' => 'oembed'));
}
+ public function onGetRemoteUrlMetadataFromDom($url, DOMDocument $dom, stdClass &$metadata)
+ {
+ try {
+ common_log(LOG_INFO, 'Trying to discover an oEmbed endpoint using link headers.');
+ $api = oEmbedHelper::oEmbedEndpointFromHTML($dom);
+ common_log(LOG_INFO, 'Found API endpoint ' . $api . ' for URL ' . $url);
+ $params = array(
+ 'maxwidth' => common_config('thumbnail', 'width'),
+ 'maxheight' => common_config('thumbnail', 'height'),
+ );
+ $metadata = oEmbedHelper::getOembedFrom($api, $url, $params);
+
+ } catch (Exception $e) {
+ common_log(LOG_INFO, 'Could not find an oEmbed endpoint using link headers.');
+ // Just ignore it!
+ }
+ }
+
public function onEndShowHeadElements(Action $action)
{
switch ($action->getActionName()) {
}
static function _getOembed($url) {
- $parameters = array(
- 'maxwidth' => common_config('thumbnail', 'width'),
- 'maxheight' => common_config('thumbnail', 'height'),
- );
try {
- return oEmbedHelper::getObject($url, $parameters);
+ return oEmbedHelper::getObject($url);
} catch (Exception $e) {
common_log(LOG_INFO, "Error during oembed lookup for $url - " . $e->getMessage());
return false;
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
'revision3.com' => 'https://revision3.com/api/oembed/',
'vimeo.com' => 'https://vimeo.com/api/oembed.json',
);
- protected static $functionMap = array(
- );
/**
* Perform or fake an oEmbed lookup for the given resource.
*/
public static function getObject($url, $params=array())
{
- $host = parse_url($url, PHP_URL_HOST);
- if (substr($host, 0, 4) == 'www.') {
- $host = substr($host, 4);
- }
+ common_log(LOG_INFO, 'Checking for remote URL metadata for ' . $url);
- common_log(LOG_INFO, 'Checking for oEmbed data for ' . $url);
+ // TODO: Make this class something like UrlMetadata, or use a dataobject?
+ $metadata = new stdClass();
- // You can fiddle with the order of discovery -- either skipping
- // some types or re-ordering them.
+ if (Event::handle('GetRemoteUrlMetadata', array($url, &$metadata))) {
+ // If that event didn't return anything, try downloading the body and parse it
+ $body = HTTPClient::quickGet($url);
- $order = common_config('oembed', 'order');
+ // DOMDocument::loadHTML may throw warnings on unrecognized elements,
+ // and notices on unrecognized namespaces.
+ $old = error_reporting(error_reporting() & ~(E_WARNING | E_NOTICE));
+ $dom = new DOMDocument();
+ $ok = $dom->loadHTML($body);
+ unset($body); // storing the DOM in memory is enough...
+ error_reporting($old);
- foreach ($order as $method) {
-
- switch ($method) {
- case 'built-in':
- common_log(LOG_INFO, 'Considering built-in oEmbed methods...');
- // Blacklist: systems with no oEmbed API of their own, which are
- // either missing from or broken on noembed.com's proxy.
- // we know how to look data up in another way...
- if (array_key_exists($host, self::$functionMap)) {
- common_log(LOG_INFO, 'We have a built-in method for ' . $host);
- $func = self::$functionMap[$host];
- return call_user_func($func, $url, $params);
- }
- break;
- case 'well-known':
- common_log(LOG_INFO, 'Considering well-known oEmbed endpoints...');
- // Whitelist: known API endpoints for sites that don't provide discovery...
- if (array_key_exists($host, self::$apiMap)) {
- $api = self::$apiMap[$host];
- common_log(LOG_INFO, 'Using well-known endpoint "' . $api . '" for "' . $host . '"');
- break 2;
- }
- break;
- case 'discovery':
- try {
- common_log(LOG_INFO, 'Trying to discover an oEmbed endpoint using link headers.');
- $api = self::discover($url);
- common_log(LOG_INFO, 'Found API endpoint ' . $api . ' for URL ' . $url);
- break 2;
- } catch (Exception $e) {
- common_log(LOG_INFO, 'Could not find an oEmbed endpoint using link headers.');
- // Just ignore it!
- }
- break;
- case 'service':
- $api = common_config('oembed', 'endpoint');
- common_log(LOG_INFO, 'Using service API endpoint ' . $api);
- break;
+ if (!$ok) {
+ throw new oEmbedHelper_BadHtmlException();
}
- }
- if (empty($api)) {
- // TRANS: Server exception thrown in oEmbed action if no API endpoint is available.
- throw new ServerException(_('No oEmbed API endpoint available.'));
+ Event::handle('GetRemoteUrlMetadataFromDom', array($url, $dom, &$metadata));
}
- return self::getObjectFrom($api, $url, $params);
- }
-
- /**
- * Perform basic discovery.
- * @return string
- */
- static function discover($url)
- {
- // @fixme ideally skip this for non-HTML stuff!
- $body = self::http($url);
- return self::discoverFromHTML($url, $body);
+ return self::normalize($metadata);
}
/**
* @param string $body HTML body text
* @return mixed string with URL or false if no target found
*/
- static function discoverFromHTML($url, $body)
+ static function oEmbedEndpointFromHTML(DOMDocument $dom)
{
- // DOMDocument::loadHTML may throw warnings on unrecognized elements,
- // and notices on unrecognized namespaces.
- $old = error_reporting(error_reporting() & ~(E_WARNING | E_NOTICE));
- $dom = new DOMDocument();
- $ok = $dom->loadHTML($body);
- error_reporting($old);
-
- if (!$ok) {
- throw new oEmbedHelper_BadHtmlException();
- }
-
// Ok... now on to the links!
$feeds = array(
'application/json+oembed' => false,
* @param array $params
* @return object
*/
- static function getObjectFrom($api, $url, $params=array())
+ static function getOembedFrom($api, $url, $params=array())
{
+ if (empty($api)) {
+ // TRANS: Server exception thrown in oEmbed action if no API endpoint is available.
+ throw new ServerException(_('No oEmbed API endpoint available.'));
+ }
$params['url'] = $url;
$params['format'] = 'json';
$key=common_config('oembed','apikey');
if(isset($key)) {
$params['key'] = common_config('oembed','apikey');
}
- $data = self::json($api, $params);
- return self::normalize($data);
+ return HTTPClient::quickGetJson($api, $params);
}
/**
* @param object $orig
* @return object
*/
- static function normalize($orig)
+ static function normalize(stdClass $data)
{
- $data = clone($orig);
-
if (empty($data->type)) {
throw new Exception('Invalid oEmbed data: no type field.');
}
-
if ($data->type == 'image') {
// YFrog does this.
$data->type = 'photo';
return $data;
}
-
- /**
- * Fetch some URL and return JSON data.
- *
- * @param string $url
- * @param array $params query-string params
- * @return object
- */
- static protected function json($url, $params=array())
- {
- $data = self::http($url, $params);
- return json_decode($data);
- }
-
- /**
- * Hit some web API and return data on success.
- * @param string $url
- * @param array $params
- * @return string
- */
- static protected function http($url, $params=array())
- {
- $client = HTTPClient::start();
- if ($params) {
- $query = http_build_query($params, null, '&');
- if (strpos($url, '?') === false) {
- $url .= '?' . $query;
- } else {
- $url .= '&' . $query;
- }
- }
- $response = $client->get($url);
- if ($response->isOk()) {
- return $response->getBody();
- } else {
- throw new Exception('Bad HTTP response code: ' . $response->getStatus());
- }
- }
}
class oEmbedHelper_Exception extends Exception
$url = get_option_value('u', 'url');
-print "Contacting URL";
+print "Contacting URL\n";
$oEmbed = oEmbedHelper::getObject($url);
var_dump($oEmbed);
'set uri = "%s" '.
'where subscriber = %d '.
'and subscribed = %d',
- Subscription::newURI($sub->subscriber, $sub->subscribed, $sub->created),
+ $sub->escape(Subscription::newUri($sub->getSubscriber(), $sub->getSubscribed(), $sub->created)),
$sub->subscriber,
$sub->subscribed));
} catch (Exception $e) {
/* site nav local views */
-#site_nav_local_views H3 {
- margin-bottom: 10px;
- padding-bottom: 6px;
- background: url('../images/brdr_black_dot.png') repeat-x bottom left;
+#site_nav_local_views h3 {
+ border-bottom: 1px dashed gray;
+ margin-bottom: 1ex;
+ padding-bottom: 1ex;
color: #7a7c87;
font-size: 1.1em;
letter-spacing: 2px;
#site_nav_local_views a {
display: block;
- width: 118px;
- padding: 0px 10px 1px 10px;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
+ line-height: 1em;
+ padding: 1ex;
+ border-radius: 0.5ex;
text-transform: uppercase;
text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.5);
word-wrap: break-word;