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
    .")); } + $a = $as->item(0); + + $private = $a->getAttribute('private'); + + if ($private != 0) { + throw new ClientException(_('Skipping private bookmark.')); + } + + if (!empty($dd)) { + $description = $dd->nodeValue; + } else { + $description = null; + } + $addDate = $a->getAttribute('add_date'); + + $data = array( + 'profile_id' => $user->id, + 'title' => $a->nodeValue, + 'description' => $description, + 'url' => $a->getAttribute('href'), + 'tags' => $a->getAttribute('tags'), + 'created' => common_sql_date(intval($addDate)) + ); + $qm = QueueManager::get(); - - $qm->enqueue(array($user, $dt, $dd), 'dlcsbkmk'); + $qm->enqueue($data, 'dlcsbkmk'); } /** @@ -188,9 +216,95 @@ class DeliciousBackupImporter extends QueueHandler error_reporting($old); if ($ok) { + foreach ($dom->getElementsByTagName('body') as $node) { + $this->fixListsIn($node); + } return $dom; } else { return null; } } + + + function fixListsIn(DOMNode $body) { + $toFix = array(); + + foreach ($body->childNodes as $node) { + if ($node->nodeType == XML_ELEMENT_NODE) { + $el = strtolower($node->nodeName); + if ($el == 'dl') { + $toFix[] = $node; + } + } + } + + foreach ($toFix as $node) { + $this->fixList($node); + } + } + + function fixList(DOMNode $list) { + $toFix = array(); + + foreach ($list->childNodes as $node) { + if ($node->nodeType == XML_ELEMENT_NODE) { + $el = strtolower($node->nodeName); + if ($el == 'dt' || $el == 'dd') { + $toFix[] = $node; + } + if ($el == 'dl') { + // Sublist. + // Technically, these can only appear inside a
    ... + $this->fixList($node); + } + } + } + + foreach ($toFix as $node) { + $this->fixListItem($node); + } + } + + function fixListItem(DOMNode $item) { + // The HTML parser in libxml2 doesn't seem to properly handle + // many cases of implied close tags, apparently because it doesn't + // understand the nesting rules specified in the HTML DTD. + // + // This leads to sequences of adjacent
    s or
    s being incorrectly + // interpreted as parent->child trees instead of siblings: + // + // When parsing this input: "
    aaa
    bbb" + // should be equivalent to: "
    aaa
    bbb
    " + // but we're seeing instead: "
    aaa
    bbb
    " + // + // It does at least know that going from dt to dd, or dd to dt, + // should make a break. + + $toMove = array(); + + foreach ($item->childNodes as $node) { + if ($node->nodeType == XML_ELEMENT_NODE) { + $el = strtolower($node->nodeName); + if ($el == 'dt' || $el == 'dd') { + // dt & dd cannot contain each other; + // This node was incorrectly placed; move it up a level! + $toMove[] = $node; + } + if ($el == 'dl') { + // Sublist. + // Technically, these can only appear inside a
    . + $this->fixList($node); + } + } + } + + $parent = $item->parentNode; + $next = $item->nextSibling; + foreach ($toMove as $node) { + $item->removeChild($node); + $parent->insertBefore($node, $next); + $this->fixListItem($node); + } + } + } diff --git a/plugins/Bookmark/deliciousbookmarkimporter.php b/plugins/Bookmark/deliciousbookmarkimporter.php index 297ef81246..018239f49d 100644 --- a/plugins/Bookmark/deliciousbookmarkimporter.php +++ b/plugins/Bookmark/deliciousbookmarkimporter.php @@ -61,49 +61,29 @@ class DeliciousBookmarkImporter extends QueueHandler /** * Handle the data * - * @param array $data array of user, dt, dd + * @param array $data associative array of user & bookmark info from DeliciousBackupImporter::importBookmark() * * @return boolean success value */ function handle($data) { - list($user, $dt, $dd) = $data; + $profile = Profile::staticGet('id', $data['profile_id']); - $as = $dt->getElementsByTagName('a'); - - if ($as->length == 0) { - throw new ClientException(_("No tag in a
    .")); + try { + $saved = Bookmark::saveNew($profile, + $data['title'], + $data['url'], + $data['tags'], + $data['description'], + array('created' => $data['created'], + 'distribute' => false)); + } catch (ClientException $e) { + // Most likely a duplicate -- continue on with the rest! + common_log(LOG_ERR, "Error importing delicious bookmark to $data[url]: " . $e->getMessage()); + return true; } - $a = $as->item(0); - - $private = $a->getAttribute('private'); - - if ($private != 0) { - throw new ClientException(_('Skipping private bookmark.')); - } - - if (!empty($dd)) { - $description = $dd->nodeValue; - } else { - $description = null; - } - - $title = $a->nodeValue; - $url = $a->getAttribute('href'); - $tags = $a->getAttribute('tags'); - $addDate = $a->getAttribute('add_date'); - $created = common_sql_date(intval($addDate)); - - $saved = Bookmark::saveNew($user->getProfile(), - $title, - $url, - $tags, - $description, - array('created' => $created, - 'distribute' => false)); - return true; } } diff --git a/plugins/Bookmark/importdelicious.php b/plugins/Bookmark/importdelicious.php index f8529cc914..b98b215717 100644 --- a/plugins/Bookmark/importdelicious.php +++ b/plugins/Bookmark/importdelicious.php @@ -48,6 +48,7 @@ if (!defined('STATUSNET')) { class ImportdeliciousAction extends Action { protected $success = false; + private $inprogress = false; /** * Return the title of the page @@ -191,7 +192,13 @@ class ImportdeliciousAction extends Action $qm = QueueManager::get(); $qm->enqueue(array(common_current_user(), $html), 'dlcsback'); - $this->success = true; + if ($qm instanceof UnQueueManager) { + // No active queuing means we've actually just completed the job! + $this->success = true; + } else { + // We've fed data into background queues, and it's probably still running. + $this->inprogress = true; + } $this->showPage(); @@ -212,8 +219,10 @@ class ImportdeliciousAction extends Action { if ($this->success) { $this->element('p', null, - _('Feed will be restored. '. - 'Please wait a few minutes for results.')); + _('Bookmarks have been imported. Your bookmarks should now appear in search and your profile page.')); + } else if ($this->inprogress) { + $this->element('p', null, + _('Bookmarks are being imported. Please wait a few minutes for results.')); } else { $form = new ImportDeliciousForm($this); $form->show(); diff --git a/plugins/Bookmark/showbookmark.php b/plugins/Bookmark/showbookmark.php index e9e656f84c..6bebffb68e 100644 --- a/plugins/Bookmark/showbookmark.php +++ b/plugins/Bookmark/showbookmark.php @@ -61,7 +61,22 @@ class ShowbookmarkAction extends ShownoticeAction { OwnerDesignAction::prepare($argarray); - $this->user = User::staticGet('id', $this->trimmed('user')); + $this->id = $this->trimmed('id'); + + $this->bookmark = Bookmark::staticGet('id', $this->id); + + if (empty($this->bookmark)) { + throw new ClientException(_('No such bookmark.'), 404); + } + + $this->notice = Notice::staticGet('uri', $this->bookmark->uri); + + if (empty($this->notice)) { + // Did we used to have it, and it got deleted? + throw new ClientException(_('No such bookmark.'), 404); + } + + $this->user = User::staticGet('id', $this->bookmark->profile_id); if (empty($this->user)) { throw new ClientException(_('No such user.'), 404); @@ -75,41 +90,6 @@ class ShowbookmarkAction extends ShownoticeAction $this->avatar = $this->profile->getAvatar(AVATAR_PROFILE_SIZE); - sscanf($this->trimmed('crc32'), '%08x', $crc32); - - if (empty($crc32)) { - throw new ClientException(_('No such URL.'), 404); - } - - $dt = new DateTime($this->trimmed('created'), - new DateTimeZone('UTC')); - - if (empty($dt)) { - throw new ClientException(_('No such create date.'), 404); - } - - $bookmarks = Bookmark::getByCRC32($this->profile, - $crc32); - - foreach ($bookmarks as $bookmark) { - $bdt = new DateTime($bookmark->created, new DateTimeZone('UTC')); - if ($bdt->format('U') == $dt->format('U')) { - $this->bookmark = $bookmark; - break; - } - } - - if (empty($this->bookmark)) { - throw new ClientException(_('No such bookmark.'), 404); - } - - $this->notice = Notice::staticGet('uri', $this->bookmark->uri); - - if (empty($this->notice)) { - // Did we used to have it, and it got deleted? - throw new ClientException(_('No such bookmark.'), 404); - } - return true; } diff --git a/tests/UUIDTest.php b/tests/UUIDTest.php new file mode 100644 index 0000000000..e78d5ce1bc --- /dev/null +++ b/tests/UUIDTest.php @@ -0,0 +1,37 @@ +assertRegExp('/^[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$/', + $result); + // Check version number + $this->assertEquals(0x4000, hexdec(substr($result, 14, 4)) & 0xF000); + $this->assertEquals(0x8000, hexdec(substr($result, 19, 4)) & 0xC000); + } + + public function testUnique() + { + $reps = 100; + $ids = array(); + + for ($i = 0; $i < $reps; $i++) { + $ids[] = UUID::gen(); + } + + $this->assertEquals(count($ids), count(array_unique($ids)), "UUIDs must be unique"); + } +} +