diff --git a/EVENTS.txt b/EVENTS.txt
index f7cc7df67c..6719ba737a 100644
--- a/EVENTS.txt
+++ b/EVENTS.txt
@@ -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
of a notice list element
+- $nli: The notice list item being shown
+
+EndOpenNoticeListItemElement: After the opening
of a notice list element
+- $nli: The notice list item being shown
+
+StartCloseNoticeListItemElement: Before the closing
of a notice list element
+- $nli: The notice list item being shown
+
+EndCloseNoticeListItemElement: After the closing of a notice list element
+- $nli: The notice list item being shown
diff --git a/README b/README
index e2e4c580ef..d972bf5676 100644
--- a/README
+++ b/README
@@ -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 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
=======
diff --git a/lib/default.php b/lib/default.php
index 6d57c4ef02..ce61de5ea5 100644
--- a/lib/default.php
+++ b/lib/default.php
@@ -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
diff --git a/lib/httpclient.php b/lib/httpclient.php
index 514a5afeb2..04e2b9ac65 100644
--- a/lib/httpclient.php
+++ b/lib/httpclient.php
@@ -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());
}
diff --git a/lib/noticelist.php b/lib/noticelist.php
index c6f964662f..7b2fbb1e7c 100644
--- a/lib/noticelist.php
+++ b/lib/noticelist.php
@@ -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));
+ }
}
}
diff --git a/lib/uuid.php b/lib/uuid.php
new file mode 100644
index 0000000000..93153504f2
--- /dev/null
+++ b/lib/uuid.php
@@ -0,0 +1,109 @@
+.
+ *
+ * @category UUID
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @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
+ * @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));
+ }
+}
diff --git a/plugins/Bookmark/Bookmark.php b/plugins/Bookmark/Bookmark.php
index 61fe3c5b97..4ee287fb65 100644
--- a/plugins/Bookmark/Bookmark.php
+++ b/plugins/Bookmark/Bookmark.php
@@ -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();
diff --git a/plugins/Bookmark/BookmarkPlugin.php b/plugins/Bookmark/BookmarkPlugin.php
index 4f31349801..8eef609751 100644
--- a/plugins/Bookmark/BookmarkPlugin.php
+++ b/plugins/Bookmark/BookmarkPlugin.php
@@ -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)) {
+ if (!empty($replies) || !empty($tags)) {
+
+ $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(' ');
}
+
+ 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');
}
- $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(' ');
+ if (!empty($nb->description)) {
+ $out->element('p',
+ array('class' => 'bookmark-description'),
+ $nb->description);
}
- $out->elementEnd('ul');
-
- $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(' ');
- $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);
}
}
-
diff --git a/plugins/Bookmark/bookmark.css b/plugins/Bookmark/bookmark.css
index b86e749fd9..6743622d2f 100644
--- a/plugins/Bookmark/bookmark.css
+++ b/plugins/Bookmark/bookmark.css
@@ -1,4 +1,6 @@
-.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 }
diff --git a/plugins/Bookmark/bookmarkpopup.js b/plugins/Bookmark/bookmarkpopup.js
index 29f314ed06..4904b07e24 100644
--- a/plugins/Bookmark/bookmarkpopup.js
+++ b/plugins/Bookmark/bookmarkpopup.js
@@ -2,6 +2,13 @@ $(document).ready(
function() {
var form = $('#form_new_bookmark');
form.append('');
+ 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();
}});
}
diff --git a/plugins/Bookmark/deliciousbackupimporter.php b/plugins/Bookmark/deliciousbackupimporter.php
index 1b55115d6d..197c7a143b 100644
--- a/plugins/Bookmark/deliciousbackupimporter.php
+++ b/plugins/Bookmark/deliciousbackupimporter.php
@@ -65,7 +65,7 @@ class DeliciousBackupImporter extends QueueHandler
* and import to StatusNet as Bookmark activities.
*
* The document format is terrible. It consists of a
with
- * a bunch of
's, occasionally with
's.
+ * a bunch of
's, occasionally with
's adding descriptions.
* There are sometimes
's lost inside.
*
* @param array $data pair of user, text
@@ -99,6 +99,9 @@ class DeliciousBackupImporter extends QueueHandler
}
switch (strtolower($child->tagName)) {
case '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
.
if (!empty($dt)) {
// No DD provided
$this->importBookmark($user, $dt);
@@ -109,10 +112,13 @@ class DeliciousBackupImporter extends QueueHandler
case 'dd':
$dd = $child;
+ // This
contains a description for the bookmark in
+ // the preceding
node.
$saved = $this->importBookmark($user, $dt, $dd);
$dt = null;
$dd = null;
+ break;
case 'p':
common_log(LOG_INFO, 'Skipping the
in the
.');
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
- // as a child.
+ $as = $dt->getElementsByTagName('a');
- for ($i = 0; $i < $dt->childNodes->length; $i++) {
- $child = $dt->childNodes->item($i);
- if ($child->nodeType == XML_ELEMENT_NODE) {
- if ($child->tagName == 'dt' && !is_null($dd)) {
- $this->importBookmark($user, $dt);
- $this->importBookmark($user, $child, $dd);
- return;
- }
- }
+ if ($as->length == 0) {
+ throw new ClientException(_("No tag in a