From f64103447188f71101ce90fdb2f76b1c6e5889a0 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 18 Dec 2010 02:27:14 -0500 Subject: [PATCH] First pass at storing bookmarks Form for saving bookmarks that looks like the delicious.com form. Save a new notice with the right text, but attach a new notice_bookmark table which marks this as a bookmark. Tags, URLs are kept the same. --- plugins/Bookmark/BookmarkPlugin.php | 130 +++++++++++++++ plugins/Bookmark/Notice_bookmark.php | 114 +++++++++++++ plugins/Bookmark/bookmarkform.php | 151 +++++++++++++++++ plugins/Bookmark/newbookmark.php | 238 +++++++++++++++++++++++++++ 4 files changed, 633 insertions(+) create mode 100644 plugins/Bookmark/BookmarkPlugin.php create mode 100644 plugins/Bookmark/Notice_bookmark.php create mode 100644 plugins/Bookmark/bookmarkform.php create mode 100644 plugins/Bookmark/newbookmark.php diff --git a/plugins/Bookmark/BookmarkPlugin.php b/plugins/Bookmark/BookmarkPlugin.php new file mode 100644 index 0000000000..cf014cc82a --- /dev/null +++ b/plugins/Bookmark/BookmarkPlugin.php @@ -0,0 +1,130 @@ +. + * + * @category SocialBookmark + * @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')) { + exit(1); +} + +/** + * Bookmark plugin main class + * + * @category Bookmark + * @package StatusNet + * @author Brion Vibber + * @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 BookmarkPlugin extends Plugin +{ + /** + * Database schema setup + * + * @see Schema + * @see ColumnDef + * + * @return boolean hook value; true means continue processing, false means stop. + */ + + function onCheckSchema() + { + $schema = Schema::get(); + + // For storing user-submitted flags on profiles + + $schema->ensureTable('notice_bookmark', + array(new ColumnDef('notice_id', + 'integer', + null, + true, + 'PRI'))); + + return true; + } + + /** + * Load related modules when needed + * + * @param string $cls Name of the class to be loaded + * + * @return boolean hook value; true means continue processing, false means stop. + */ + + function onAutoload($cls) + { + $dir = dirname(__FILE__); + + switch ($cls) + { + case 'NewbookmarkAction': + include_once $dir.'/newbookmark.php'; + return false; + case 'Notice_bookmark': + include_once $dir.'/'.$cls.'.php'; + return false; + case 'BookmarkForm': + include_once $dir.'/'.strtolower($cls).'.php'; + return false; + default: + return true; + } + } + + /** + * Map URLs to actions + * + * @param Net_URL_Mapper $m path-to-action mapper + * + * @return boolean hook value; true means continue processing, false means stop. + */ + + function onRouterInitialized($m) + { + $m->connect('main/bookmark/new', + array('action' => 'newbookmark'), + array('id' => '[0-9]+')); + + return true; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'Sample', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:Bookmark', + 'rawdescription' => + _m('Simple extension for supporting bookmarks.')); + return true; + } +} + diff --git a/plugins/Bookmark/Notice_bookmark.php b/plugins/Bookmark/Notice_bookmark.php new file mode 100644 index 0000000000..772fad528b --- /dev/null +++ b/plugins/Bookmark/Notice_bookmark.php @@ -0,0 +1,114 @@ + + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2009, StatusNet, Inc. + * + * 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 . + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * For storing the fact that a notice is a bookmark + * + * @category Bookmark + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * @see DB_DataObject + */ + +class Notice_bookmark extends Memcached_DataObject +{ + public $__table = 'notice_bookmark'; // table name + public $notice_id; // int(4) primary_key not_null + + /** + * Get an instance by key + * + * This is a utility method to get a single instance with a given key value. + * + * @param string $k Key to use to lookup (usually 'user_id' for this class) + * @param mixed $v Value to lookup + * + * @return User_greeting_count object found, or null for no hits + * + */ + + function staticGet($k, $v=null) + { + return Memcached_DataObject::staticGet('Notice_bookmark', $k, $v); + } + + /** + * return table definition for DB_DataObject + * + * DB_DataObject needs to know something about the table to manipulate + * instances. This method provides all the DB_DataObject needs to know. + * + * @return array array of column definitions + */ + + function table() + { + return array('notice_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL); + } + + /** + * return key definitions for DB_DataObject + * + * @return array list of key field names + */ + + function keys() + { + return array_keys($this->keyTypes()); + } + + /** + * return key definitions for Memcached_DataObject + * + * @return array associative array of key definitions + */ + + function keyTypes() + { + return array('notice_id' => 'K'); + } + + /** + * Magic formula for non-autoincrementing integer primary keys + * + * @return array magic three-false array that stops auto-incrementing. + */ + + function sequenceKey() + { + return array(false, false, false); + } +} diff --git a/plugins/Bookmark/bookmarkform.php b/plugins/Bookmark/bookmarkform.php new file mode 100644 index 0000000000..deeed84829 --- /dev/null +++ b/plugins/Bookmark/bookmarkform.php @@ -0,0 +1,151 @@ +. + * + * @category Bookmark + * @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); +} + +/** + * Form to add a new bookmark + * + * @category Bookmark + * @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 BookmarkForm extends Form +{ + private $_title = null; + private $_url = null; + private $_tags = null; + private $_description = null; + + function __construct($out=null, $title=null, $url=null, $tags=null, $description=null) + { + parent::__construct($out); + + $this->_title = $title; + $this->_url = $url; + $this->_tags = $tags; + $this->_description = $description; + } + + /** + * ID of the form + * + * @return int ID of the form + */ + + function id() + { + return 'form_new_bookmark'; + } + + /** + * class of the form + * + * @return string class of the form + */ + + function formClass() + { + return 'form_new_bookmark'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('newbookmark'); + } + + /** + * Data elements of the form + * + * @return void + */ + + function formData() + { + $this->out->elementStart('fieldset', array('id' => 'new_bookmark_data')); + $this->out->elementStart('ul', 'form_data'); + + $this->li(); + $this->out->input('title', + _('Title'), + $this->_title, + _('Title of the bookmark')); + $this->unli(); + + $this->li(); + $this->out->input('url', + _('URL'), + $this->_url, + _('URL to bookmark')); + $this->unli(); + + $this->li(); + $this->out->input('tags', + _('Tags'), + $this->_tags, + _('Comma- or space-separated list of tags')); + $this->unli(); + + $this->li(); + $this->out->input('description', + _('Description'), + $this->_description, + _('Description of the URL')); + $this->unli(); + + $this->out->elementEnd('ul'); + $this->out->elementEnd('fieldset'); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit('submit', _m('BUTTON', 'Save')); + } +} diff --git a/plugins/Bookmark/newbookmark.php b/plugins/Bookmark/newbookmark.php new file mode 100644 index 0000000000..034e73b429 --- /dev/null +++ b/plugins/Bookmark/newbookmark.php @@ -0,0 +1,238 @@ +. + * + * @category Bookmark + * @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); +} + +/** + * Add a new bookmark + * + * @category Bookmark + * @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 NewbookmarkAction extends Action +{ + private $_user = null; + private $_error = null; + private $_complete = null; + private $_title = null; + private $_url = null; + private $_tags = null; + private $_description = null; + + function title() + { + return _('New bookmark'); + } + + /** + * For initializing members of the class. + * + * @param array $argarray misc. arguments + * + * @return boolean true + */ + + function prepare($argarray) + { + parent::prepare($argarray); + + $this->_user = common_current_user(); + + if (empty($this->_user)) { + throw new ClientException(_("Must be logged in to post a bookmark."), 403); + } + + if ($this->isPost()) { + $this->checkSessionToken(); + } + + $this->_title = $this->trimmed('title'); + $this->_url = $this->trimmed('url'); + $this->_tags = $this->trimmed('tags'); + $this->_description = $this->trimmed('description'); + + return true; + } + + /** + * Handler method + * + * @param array $argarray is ignored since it's now passed in in prepare() + * + * @return void + */ + + function handle($argarray=null) + { + parent::handle($argarray); + + if ($this->isPost()) { + $this->newBookmark(); + } else { + $this->showPage(); + } + + return; + } + + /** + * Add a new bookmark + * + * @return void + */ + + function newBookmark() + { + try { + if (empty($this->_title)) { + throw new ClientException(_('Bookmark must have a title.')); + } + + if (empty($this->_url)) { + throw new ClientException(_('Bookmark must have an URL.')); + } + + // XXX: filter "for:nickname" tags + + $tags = array_map('common_canonical_tag', + preg_split('/[\s,]+/', $this->_tags)); + + $hashtags = array(); + $taglinks = array(); + + foreach ($tags as $tag) { + $hashtags[] = '#'.$tag; + if (common_config('singleuser', 'enabled')) { + // regular TagAction isn't set up in 1user mode + $nickname = User::singleUserNickname(); + $url = common_local_url('showstream', + array('nickname' => $nickname, + 'tag' => $tag)); + } else { + $url = common_local_url('tag', array('tag' => $tag)); + } + $attrs = array('href' => $url, + 'rel' => $tag, + 'class' => 'tag'); + $taglinks[] = XMLStringer::estring('a', $attrs, $tag); + } + + $content = sprintf(_('"%s" %s %s %s'), + $this->_title, + File_redirection::makeShort($this->_url, $this->_user), + $this->_description, + implode(' ', $hashtags)); + + $rendered = sprintf(_(''. + '%s '. + '%s '. + '%s'. + ''), + htmlspecialchars($this->_url), + htmlspecialchars($this->_title), + htmlspecialchars($this->_description), + implode(' ', $taglinks)); + + $options = array('urls' => array($this->_url), + 'rendered' => $rendered, + 'tags' => $tags); + + $saved = Notice::saveNew($this->_user->id, + $content, + 'web', + $options); + + if (!empty($saved)) { + $nb = new Notice_bookmark(); + $nb->notice_id = $saved->id; + $nb->insert(); + } + + } catch (ClientException $ce) { + $this->_error = $ce->getMessage(); + $this->showPage(); + return; + } + + common_redirect($saved->bestUrl(), 303); + } + + /** + * Show the bookmark form + * + * @return void + */ + + function showContent() + { + if (!empty($this->_error)) { + $this->element('p', 'error', $this->_error); + } + + $form = new BookmarkForm($this, + $this->_title, + $this->_url, + $this->_tags, + $this->_description); + + $form->show(); + + return; + } + + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + + function isReadOnly($args) + { + if ($_SERVER['REQUEST_METHOD'] == 'GET' || + $_SERVER['REQUEST_METHOD'] == 'HEAD') { + return true; + } else { + return false; + } + } +} -- 2.39.5