We are no longer offering accounts on this server. Consider https://gitlab.freedesktop.org/ as a place to host projects.

Commit a7e5c58a authored by Evan Prodromou's avatar Evan Prodromou

Merge branch 'revertversion' into 0.9.x

parents 80e2f4f5 fd205546
......@@ -1045,3 +1045,15 @@ StartProfileSettingsActions: when we're showing account-management action list
EndProfileSettingsActions: when we're showing account-management action list
- $action: Action being shown (use for output)
StartOpenNoticeListItemElement: Before the opening <li> of a notice list element
- $nli: The notice list item being shown
EndOpenNoticeListItemElement: After the opening <li> of a notice list element
- $nli: The notice list item being shown
StartCloseNoticeListItemElement: Before the closing </li> of a notice list element
- $nli: The notice list item being shown
EndCloseNoticeListItemElement: After the closing </li> of a notice list element
- $nli: The notice list item being shown
......@@ -1556,6 +1556,22 @@ cache: whether to cache the router in memcache (or another caching
router cached) or others who see strange behavior. You're unlikely
to need this unless you're a developer.
http
----
Settings for the HTTP client.
ssl_cafile: location of the CA file for SSL. If not set, won't verify
SSL peers. Default unset.
curl: Use cURL <http://curl.haxx.se/> for doing HTTP calls. You must
have the PHP curl extension installed for this to work.
proxy_host: Host to use for proxying HTTP requests. If unset, doesn't
do any HTTP proxy stuff. Default unset.
proxy_port: Port to use to connect to HTTP proxy host. Default null.
proxy_user: Username to use for authenticating to the HTTP proxy. Default null.
proxy_password: Password to use for authenticating to the HTTP proxy. Default null.
proxy_auth_scheme: Scheme to use for authenticating to the HTTP proxy. Default null.
Plugins
=======
......
......@@ -331,6 +331,11 @@ $default =
'http' => // HTTP client settings when contacting other sites
array('ssl_cafile' => false, // To enable SSL cert validation, point to a CA bundle (eg '/usr/lib/ssl/certs/ca-certificates.crt')
'curl' => false, // Use CURL backend for HTTP fetches if available. (If not, PHP's socket streams will be used.)
'proxy_host' => null,
'proxy_port' => null,
'proxy_user' => null,
'proxy_password' => null,
'proxy_auth_scheme' => null,
),
'router' =>
array('cache' => true), // whether to cache the router object. Defaults to true, turn off for devel
......
......@@ -149,6 +149,14 @@ class HTTPClient extends HTTP_Request2
$this->config['adapter'] = 'HTTP_Request2_Adapter_Curl';
}
foreach (array('host', 'port', 'user', 'password', 'auth_scheme') as $cf) {
$k = 'proxy_'.$cf;
$v = common_config('http', $k);
if (!empty($v)) {
$this->config[$k] = $v;
}
}
parent::__construct($url, $method, $config);
$this->setHeader('User-Agent', $this->userAgent());
}
......
......@@ -263,11 +263,12 @@ class NoticeListItem extends Widget
function showStart()
{
// XXX: RDFa
// TODO: add notice_type class e.g., notice_video, notice_image
$id = (empty($this->repeat)) ? $this->notice->id : $this->repeat->id;
$this->out->elementStart('li', array('class' => 'hentry notice',
'id' => 'notice-' . $id));
if (Event::handle('StartOpenNoticeListItemElement', array($this))) {
$id = (empty($this->repeat)) ? $this->notice->id : $this->repeat->id;
$this->out->elementStart('li', array('class' => 'hentry notice',
'id' => 'notice-' . $id));
Event::handle('EndOpenNoticeListItemElement', array($this));
}
}
/**
......@@ -706,6 +707,9 @@ class NoticeListItem extends Widget
function showEnd()
{
$this->out->elementEnd('li');
if (Event::handle('StartCloseNoticeListItemElement', array($this))) {
$this->out->elementEnd('li');
Event::handle('EndCloseNoticeListItemElement', array($this));
}
}
}
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2010, StatusNet, Inc.
*
* UUID generation
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category UUID
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
/**
* UUID generation
*
* @category General
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class UUID
{
protected $str = null;
/**
* Constructor for a UUID
*
* Uses gen() to create a new UUID
*/
function __construct()
{
$this->str = self::gen();
}
/**
* For serializing to a string
*
* @return string version of self
*/
function __toString()
{
return $this->str;
}
/**
* For serializing to a string
*
* @return string version of self
*/
function getString()
{
return $this->str;
}
/**
* Generate a new UUID
*
* @return 36-char v4 (random-ish) UUID
*/
static function gen()
{
return sprintf('%s-%s-%04x-%04x-%s',
// 32 bits for "time_low"
common_good_rand(4),
// 16 bits for "time_mid"
common_good_rand(2),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
(hexdec(common_good_rand(2)) & 0x0fff) | 0x4000,
// 16 bits, 8 bits for "clk_seq_hi_res",
// 8 bits for "clk_seq_low",
// two most significant bits holds zero and one
// for variant DCE1.1
(hexdec(common_good_rand(2)) & 0x3fff) | 0x8000,
// 48 bits for "node"
common_good_rand(6));
}
}
......@@ -46,13 +46,13 @@ if (!defined('STATUSNET')) {
class Bookmark extends Memcached_DataObject
{
public $__table = 'bookmark'; // table name
public $profile_id; // int(4) primary_key not_null
public $url; // varchar(255) primary_key not_null
public $title; // varchar(255)
public $description; // text
public $uri; // varchar(255)
public $url_crc32; // int(4) not_null
public $created; // datetime
public $id; // char(36) primary_key not_null
public $profile_id; // int(4) not_null
public $url; // varchar(255) not_null
public $title; // varchar(255)
public $description; // text
public $uri; // varchar(255)
public $created; // datetime
/**
* Get an instance by key
......@@ -100,12 +100,12 @@ class Bookmark extends Memcached_DataObject
function table()
{
return array('profile_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
return array('id' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
'profile_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
'url' => DB_DATAOBJECT_STR,
'title' => DB_DATAOBJECT_STR,
'description' => DB_DATAOBJECT_STR,
'uri' => DB_DATAOBJECT_STR,
'url_crc32' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE +
DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
}
......@@ -129,8 +129,7 @@ class Bookmark extends Memcached_DataObject
function keyTypes()
{
return array('profile_id' => 'K',
'url' => 'K',
return array('id' => 'K',
'uri' => 'U');
}
......@@ -169,36 +168,16 @@ class Bookmark extends Memcached_DataObject
static function getByURL($profile, $url)
{
return self::pkeyGet(array('profile_id' => $profile->id,
'url' => $url));
return null;
}
/**
* Get the bookmark that a user made for an URL
*
* @param Profile $profile Profile to check for
* @param integer $crc32 CRC-32 of URL to check for
*
* @return array Bookmark objects found (usually 1 or 0)
*/
static function getByCRC32($profile, $crc32)
{
$bookmarks = array();
$nb = new Bookmark();
$nb->profile_id = $profile->id;
$nb->url_crc32 = $crc32;
$nb->url = $url;
if ($nb->find()) {
while ($nb->fetch()) {
$bookmarks[] = clone($nb);
}
if ($nb->find(true)) {
return $nb;
} else {
return null;
}
return $bookmarks;
}
/**
......@@ -240,11 +219,11 @@ class Bookmark extends Memcached_DataObject
$nb = new Bookmark();
$nb->id = UUID::gen();
$nb->profile_id = $profile->id;
$nb->url = $url;
$nb->title = $title;
$nb->description = $description;
$nb->url_crc32 = crc32($nb->url);
if (array_key_exists('created', $options)) {
$nb->created = $options['created'];
......@@ -255,22 +234,8 @@ class Bookmark extends Memcached_DataObject
if (array_key_exists('uri', $options)) {
$nb->uri = $options['uri'];
} else {
$dt = new DateTime($nb->created, new DateTimeZone('UTC'));
// I posit that it's sufficiently impossible
// for the same user to generate two CRC-32-clashing
// URLs in the same second that this is a safe unique identifier.
// If you find a real counterexample, contact me at acct:evan@status.net
// and I will publicly apologize for my hubris.
$created = $dt->format('YmdHis');
$crc32 = sprintf('%08x', $nb->url_crc32);
$nb->uri = common_local_url('showbookmark',
array('user' => $profile->id,
'created' => $created,
'crc32' => $crc32));
array('id' => $nb->id));
}
$nb->insert();
......
......@@ -86,16 +86,21 @@ class BookmarkPlugin extends Plugin
// For storing user-submitted flags on profiles
$schema->ensureTable('bookmark',
array(new ColumnDef('profile_id',
array(new ColumnDef('id',
'char',
36,
false,
'PRI'),
new ColumnDef('profile_id',
'integer',
null,
false,
'PRI'),
'MUL'),
new ColumnDef('url',
'varchar',
255,
false,
'PRI'),
'MUL'),
new ColumnDef('title',
'varchar',
255),
......@@ -106,26 +111,12 @@ class BookmarkPlugin extends Plugin
255,
false,
'UNI'),
new ColumnDef('url_crc32',
'integer unsigned',
null,
false,
'MUL'),
new ColumnDef('created',
'datetime',
null,
false,
'MUL')));
try {
$schema->createIndex('bookmark',
array('profile_id',
'url_crc32'),
'bookmark_profile_url_idx');
} catch (Exception $e) {
common_log(LOG_ERR, $e->getMessage());
}
return true;
}
......@@ -216,11 +207,9 @@ class BookmarkPlugin extends Plugin
$m->connect('main/bookmark/import',
array('action' => 'importdelicious'));
$m->connect('bookmark/:user/:created/:crc32',
$m->connect('bookmark/:id',
array('action' => 'showbookmark'),
array('user' => '[0-9]+',
'created' => '[0-9]{14}',
'crc32' => '[0-9a-f]{8}'));
array('id' => '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'));
$m->connect('notice/by-url/:id',
array('action' => 'noticebyurl'),
......@@ -262,25 +251,28 @@ class BookmarkPlugin extends Plugin
} else {
$out->elementStart('h3');
$out->element('a',
array('href' => $att->url),
array('href' => $att->url,
'class' => 'bookmark-title entry-title'),
$nb->title);
$out->elementEnd('h3');
$countUrl = common_local_url('noticebyurl',
array('id' => $att->id));
$out->element('a', array('class' => 'bookmark_notice_count',
$out->element('a', array('class' => 'bookmark-notice-count',
'href' => $countUrl),
$att->noticeCount());
}
$out->elementStart('ul', array('class' => 'bookmark_tags'));
// Replies look like "for:" tags
$replies = $nli->notice->getReplies();
$tags = $nli->notice->getTags();
if (!empty($replies) || !empty($tags)) {
if (!empty($replies)) {
$out->elementStart('ul', array('class' => 'bookmark-tags'));
foreach ($replies as $reply) {
$other = Profile::staticGet('id', $reply);
$out->elementStart('li');
......@@ -291,45 +283,59 @@ class BookmarkPlugin extends Plugin
$out->elementEnd('li');
$out->text(' ');
}
}
$tags = $nli->notice->getTags();
foreach ($tags as $tag) {
$out->elementStart('li');
$out->element('a',
array('rel' => 'tag',
'href' => Notice_tag::url($tag)),
$tag);
$out->elementEnd('li');
$out->text(' ');
}
foreach ($tags as $tag) {
$out->elementStart('li');
$out->element('a',
array('rel' => 'tag',
'href' => Notice_tag::url($tag)),
$tag);
$out->elementEnd('li');
$out->text(' ');
$out->elementEnd('ul');
}
$out->elementEnd('ul');
$out->element('p',
array('class' => 'bookmark_description'),
$nb->description);
if (!empty($nb->description)) {
$out->element('p',
array('class' => 'bookmark-description'),
$nb->description);
}
if (common_config('attachments', 'show_thumbs')) {
$al = new InlineAttachmentList($notice, $out);
$al->show();
$haveThumbs = false;
foreach ($atts as $check) {
$thumbnail = File_thumbnail::staticGet('file_id', $check->id);
if (!empty($thumbnail)) {
$haveThumbs = true;
break;
}
}
if ($haveThumbs) {
$al = new InlineAttachmentList($notice, $out);
$al->show();
}
}
$out->elementStart('p', array('style' => 'float: left'));
$out->elementStart('p', array('class' => 'bookmark-info'));
$avatar = $profile->getAvatar(AVATAR_MINI_SIZE);
$out->element('img', array('src' => ($avatar) ?
$avatar->displayUrl() :
Avatar::defaultImage(AVATAR_MINI_SIZE),
'class' => 'avatar photo bookmark_avatar',
'width' => AVATAR_MINI_SIZE,
'height' => AVATAR_MINI_SIZE,
'alt' => $profile->getBestName()));
$out->element('img',
array('src' => ($avatar) ?
$avatar->displayUrl() :
Avatar::defaultImage(AVATAR_MINI_SIZE),
'class' => 'avatar photo bookmark-avatar',
'width' => AVATAR_MINI_SIZE,
'height' => AVATAR_MINI_SIZE,
'alt' => $profile->getBestName()));
$out->raw('&nbsp;');
$out->element('a', array('href' => $profile->profileurl,
'title' => $profile->getBestName()),
$out->element('a',
array('href' => $profile->profileurl,
'title' => $profile->getBestName()),
$profile->nickname);
$nli->showNoticeLink();
......@@ -642,6 +648,27 @@ class BookmarkPlugin extends Plugin
return true;
}
/**
* Output our CSS class for bookmark notice list elements
*
* @param NoticeListItem $nli The item being shown
*
* @return boolean hook value
*/
function onStartOpenNoticeListItemElement($nli)
{
$nb = Bookmark::getByNotice($nli->notice);
if (!empty($nb)) {
$id = (empty($nli->repeat)) ? $nli->notice->id : $nli->repeat->id;
$nli->out->elementStart('li', array('class' => 'hentry notice bookmark',
'id' => 'notice-' . $id));
Event::handle('EndOpenNoticeListItemElement', array($nli));
return false;
}
return true;
}
/**
* Save a remote bookmark (from Salmon or PuSH)
*
......@@ -769,4 +796,3 @@ class BookmarkPlugin extends Plugin
$activity->objects[0]->type == ActivityObject::BOOKMARK);
}
}
.bookmark_tags li { display: inline; }
.bookmark_mentions li { display: inline; }
.bookmark_avatar { float: left }
.bookmark_notice_count { float: right }
.bookmark-tags li { display: inline; }
.bookmark-mentions li { display: inline; }
.bookmark-avatar { float: left; }
.bookmark-notice-count { float: right; }
.bookmark-info { float: left; }
.bookmark-title { margin-left: 0px }
......@@ -2,6 +2,13 @@ $(document).ready(
function() {
var form = $('#form_new_bookmark');
form.append('<input type="hidden" name="ajax" value="1"/>');
function doClose() {
self.close();
// If in popup blocker situation, we'll have to redirect back.
setTimeout(function() {
window.location = $('#url').val();
}, 100);
}
form.ajaxForm({dataType: 'xml',
timeout: '60000',
beforeSend: function(formData) {
......@@ -11,12 +18,12 @@ $(document).ready(
error: function (xhr, textStatus, errorThrown) {
form.removeClass('processing');
form.find('#submit').removeClass('disabled');
self.close();
doClose();
},
success: function(data, textStatus) {
form.removeClass('processing');
form.find('#submit').removeClass('disabled');
self.close();
doClose();
}});
}
......
......@@ -65,7 +65,7 @@ class DeliciousBackupImporter extends QueueHandler
* and import to StatusNet as Bookmark activities.
*
* The document format is terrible. It consists of a <dl> with
* a bunch of <dt>'s, occasionally with <dd>'s.
* a bunch of <dt>'s, occasionally with <dd>'s adding descriptions.
* There are sometimes <p>'s lost inside.
*
* @param array $data pair of user, text
......@@ -99,6 +99,9 @@ class DeliciousBackupImporter extends QueueHandler
}
switch (strtolower($child->tagName)) {
case 'dt':
// <dt> nodes contain primary information about a bookmark.
// We can't import the current one just yet though, since
// it may be followed by a <dd>.
if (!empty($dt)) {
// No DD provided
$this->importBookmark($user, $dt);
......@@ -109,10 +112,13 @@ class DeliciousBackupImporter extends QueueHandler
case 'dd':
$dd = $child;
// This <dd> contains a description for the bookmark in
// the preceding <dt> node.
$saved = $this->importBookmark($user, $dt, $dd);
$dt = null;
$dd = null;
break;
case 'p':
common_log(LOG_INFO, 'Skipping the <p> in the <dl>.');
break;
......@@ -126,6 +132,14 @@ class DeliciousBackupImporter extends QueueHandler
$dt = $dd = null;
}
}
if (!empty($dt)) {
// There was a final bookmark without a description.
try {
$this->importBookmark($user, $dt);
} catch (Exception $e) {
common_log(LOG_ERR, $e->getMessage());
}
}
return true;
}
......@@ -148,24 +162,38 @@ class DeliciousBackupImporter extends QueueHandler
function importBookmark($user, $dt, $dd = null)
{
// We have to go squirrelling around in the child nodes
// on the off chance that we've received another <dt>
// as a child.
for ($i = 0; $i < $dt->childNodes->length; $i++) {