From e36404dac1a7be2e2a7a1e1dd1c5d1a0498caf82 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 16:11:44 +0100 Subject: [PATCH 01/30] Add a menu for the subscriptions group of actions --- lib/subgroupnav.php | 111 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 lib/subgroupnav.php diff --git a/lib/subgroupnav.php b/lib/subgroupnav.php new file mode 100644 index 0000000000..5fd8a72a25 --- /dev/null +++ b/lib/subgroupnav.php @@ -0,0 +1,111 @@ +. + * + * @category Subs + * @package Laconica + * @author Evan Prodromou + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/widget.php'; + +/** + * Local nav menu for subscriptions, subscribers + * + * @category Subs + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class SubGroupNav extends Widget +{ + var $action = null; + var $user = null; + + /** + * Construction + * + * @param Action $action current action, used for output + */ + + function __construct($action=null, $user=null) + { + parent::__construct($action); + $this->action = $action; + $this->user = $user; + } + + /** + * Show the menu + * + * @return void + */ + + function show() + { + $cur = common_current_user(); + $action = $this->action->trimmed('action'); + + $this->out->elementStart('ul', array('class' => 'nav')); + + $this->out->menuItem(common_local_url('subscriptions', + array('nickname' => + $this->user->nickname)), + _('Subscriptions'), + sprintf(_('People %s subscribes to'), + $this->user->nickname), + $action == 'subscriptions', + 'nav_subscriptions'); + $this->out->menuItem(common_local_url('subscribers', + array('nickname' => + $this->user->nickname)), + _('Subscribers'), + sprintf(_('People subscribed to %s'), + $this->user->nickname), + $action == 'subscribers', + 'nav_subscribers'); + $this->out->menuItem(common_local_url('usergroups', + array('nickname' => + $this->user->nickname)), + _('Groups'), + sprintf(_('Groups %s is a member of'), + $this->user->nickname), + $action == 'usergroups', + 'nav_usergroups'); + if ($this->user->id == $cur->id) { + $this->out->menuItem(common_local_url('invite'), + _('Invite'), + sprintf(_('Invite friends and colleagues to join you on %s'), + common_config('site', 'name')), + $action == 'invite', + 'nav_invite'); + } + $this->out->elementEnd('ul'); + } +} From a30d405328d60edde01032b1770418a0021651e6 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 16:12:00 +0100 Subject: [PATCH 02/30] Update subscriptions action --- actions/subscriptions.php | 158 ++++++++++++++++++++++++++++++-------- 1 file changed, 128 insertions(+), 30 deletions(-) diff --git a/actions/subscriptions.php b/actions/subscriptions.php index 7a87a144f7..604428cf35 100644 --- a/actions/subscriptions.php +++ b/actions/subscriptions.php @@ -1,9 +1,12 @@ . + * + * @category Personal + * @package Laconica + * @author Evan Prodromou + * @author Sarven Capadisli + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/subsgroupnav.php'; + +/** + * User profile page + * + * When I created this page, "show stream" seemed like the best name for it. + * Now, it seems like a really bad name. + * + * It shows a stream of the user's posts, plus lots of profile info, links + * to subscriptions and stuff, etc. + * + * @category Personal + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ */ if (!defined('LACONICA')) { exit(1); } -require_once(INSTALLDIR.'/lib/gallery.php'); - -class SubscriptionsAction extends GalleryAction +class SubscriptionsAction extends Action { + var $profile = null; + var $user = null; + var $page = null; - function gallery_type() + function prepare($args) { - return _('Subscriptions'); + parent::prepare($args); + + // FIXME very similar code below + + $nickname_arg = $this->arg('nickname'); + $nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $nickname) { + $args = array('nickname' => $nickname); + if ($this->arg('page') && $this->arg('page') != 1) { + $args['page'] = $this->arg['page']; + } + common_redirect(common_local_url('subscriptions', $args), 301); + return false; + } + + $this->user = User::staticGet('nickname', $nickname); + + if (!$this->user) { + $this->clientError(_('No such user.'), 404); + return false; + } + + $this->profile = $this->user->getProfile(); + + if (!$this->profile) { + $this->serverError(_('User has no profile.')); + return false; + } + + $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + + return true; } - function get_instructions(&$profile) + function isReadOnly() { - $user =& common_current_user(); - if ($user && ($user->id == $profile->id)) { - return _('These are the people whose notices you listen to.'); + return true; + } + + function handle($args) + { + parent::handle($args); + $this->showPage(); + } + + function title() + { + if ($this->page == 1) { + return sprintf(_('%s subscriptions'), $this->user->nickname); } else { - return sprintf(_('These are the people whose notices %s listens to.'), $profile->nickname); + return sprintf(_('%s subscriptions, page %d'), + $this->user->nickname, + $this->page); } } - function fields() + function showPageNotice() { - return array('subscribed', 'subscriber'); + $user =& common_current_user(); + if ($user && ($user->id == $this->profile->id)) { + $this->element('p', null, + _('These are the people whose notices '. + 'you listen to.')); + } else { + $this->element('p', null, + sprintf(_('These are the people whose '. + 'notices %s listens to.'), + $this->profile->nickname)); + } } - function div_class() + function showLocalNav() { - return 'subscriptions'; + $nav = new SubGroupNav($this, $this->user); + $nav->show(); } - function get_other(&$subs) + function showContent() { - return $subs->subscribed; - } + $offset = ($this->page-1) * PROFILES_PER_PAGE; + $limit = PROFILES_PER_PAGE + 1; - function profile_list_class() - { - return 'SubscriptionsList'; + $subscriptions = $this->user->getSubscriptions($offset, $limit); + + if ($subs) { + $subscriptions_list = new SubscriptionsList($subscriptions, null, $this); + $subscriptions_list->show(); + } + + $subscriptions->free(); + + $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE, + $this->page, 'subscriptions', + array('nickname' => $this->user->nickname)); } } class SubscriptionsList extends ProfileList { - - function show_owner_controls($profile) + function showOwnerControls($profile) { - $sub = Subscription::pkeyGet(array('subscriber' => $this->owner->id, 'subscribed' => $profile->id)); if (!$sub) { @@ -73,9 +171,9 @@ class SubscriptionsList extends ProfileList } $this->elementStart('form', array('id' => 'subedit-' . $profile->id, - 'method' => 'post', - 'class' => 'subedit', - 'action' => common_local_url('subedit'))); + 'method' => 'post', + 'class' => 'subedit', + 'action' => common_local_url('subedit'))); $this->hidden('token', common_session_token()); $this->hidden('profile', $profile->id); $this->checkbox('jabber', _('Jabber'), $sub->jabber); From b8887ef4f668ff3e7f7217f47f998a8b6a04b0b4 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 17:06:06 +0100 Subject: [PATCH 03/30] reformat profilelist code --- lib/gallery.php | 346 ------------------------------------------ lib/galleryaction.php | 168 ++++++++++++++++++++ lib/profilelist.php | 27 ++-- 3 files changed, 181 insertions(+), 360 deletions(-) delete mode 100644 lib/gallery.php create mode 100644 lib/galleryaction.php diff --git a/lib/gallery.php b/lib/gallery.php deleted file mode 100644 index 34b58518c6..0000000000 --- a/lib/gallery.php +++ /dev/null @@ -1,346 +0,0 @@ -. - */ - -if (!defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR.'/lib/profilelist.php'; - -// 10x8 - -define('AVATARS_PER_PAGE', 80); - -class GalleryAction extends Action -{ - function is_readonly() - { - return true; - } - - function handle($args) - { - parent::handle($args); - - // Post from the tag dropdown; redirect to a GET - - if ($_SERVER['REQUEST_METHOD'] == 'POST') { - common_redirect($this->self_url(), 307); - } - - $nickname = common_canonical_nickname($this->arg('nickname')); - - $user = User::staticGet('nickname', $nickname); - - if (!$user) { - $this->no_such_user(); - return; - } - - $profile = $user->getProfile(); - - if (!$profile) { - $this->server_error(_('User without matching profile in system.')); - return; - } - - $page = $this->arg('page'); - - if (!$page) { - $page = 1; - } - - $display = $this->arg('display'); - - if (!$display) { - $display = 'list'; - } - - $tag = $this->arg('tag'); - - common_show_header($profile->nickname . ": " . $this->gallery_type(), - null, $profile, - array($this, 'show_top')); - - $this->display_links($profile, $page, $display); - $this->show_tags_dropdown($profile); - - $this->show_gallery($profile, $page, $display, $tag); - common_show_footer(); - } - - function no_such_user() - { - $this->client_error(_('No such user.')); - } - - function show_tags_dropdown($profile) - { - $tag = $this->trimmed('tag'); - - list($lst, $usr) = $this->fields(); - - $tags = $this->get_all_tags($profile, $lst, $usr); - - $content = array(); - - foreach ($tags as $t) { - $content[$t] = $t; - } - if ($tags) { - common_element_start('dl', array('id'=>'filter_tags')); - common_element('dt', null, _('Filter tags')); - common_element_start('dd'); - common_element_start('ul'); - common_element_start('li', array('id' => 'filter_tags_all', - 'class' => 'child_1')); - common_element('a', - array('href' => - common_local_url($this->trimmed('action'), - array('nickname' => - $profile->nickname))), - _('All')); - common_element_end('li'); - common_element_start('li', array('id'=>'filter_tags_item')); - common_element_start('form', array('name' => 'bytag', - 'id' => 'bytag', - 'method' => 'post')); - common_dropdown('tag', _('Tag'), $content, - _('Choose a tag to narrow list'), false, $tag); - common_submit('go', _('Go')); - common_element_end('form'); - common_element_end('li'); - common_element_end('ul'); - common_element_end('dd'); - common_element_end('dl'); - } - } - - function show_top($profile) - { - common_element('div', 'instructions', - $this->get_instructions($profile)); - $this->show_menu(); - } - - function show_menu() - { - // action => array('prompt', 'title', $args) - $action = $this->trimmed('action'); - $nickname = $this->trimmed('nickname'); - - $menu = - array('subscriptions' => - array( _('Subscriptions'), - _('Subscriptions'), - array('nickname' => $nickname)), - 'subscribers' => - array( - _('Subscribers'), - _('Subscribers'), - array('nickname' => $nickname)), - ); - $this->nav_menu($menu); - } - - function show_gallery($profile, $page, $display='list', $tag=null) - { - $other = new Profile(); - - list($lst, $usr) = $this->fields(); - - $per_page = ($display == 'list') ? PROFILES_PER_PAGE : AVATARS_PER_PAGE; - - $offset = ($page-1)*$per_page; - $limit = $per_page + 1; - - if (common_config('db', 'type') == 'pgsql') { - $lim = ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $lim = ' LIMIT ' . $offset . ', ' . $limit; - } - - // XXX: memcached results - // FIXME: SQL injection on $tag - - $other->query('SELECT profile.* ' . - 'FROM profile JOIN subscription ' . - 'ON profile.id = subscription.' . $lst . ' ' . - (($tag) ? 'JOIN profile_tag ON (profile.id = profile_tag.tagged AND subscription.'.$usr.'= profile_tag.tagger) ' : '') . - 'WHERE ' . $usr . ' = ' . $profile->id . ' ' . - 'AND subscriber != subscribed ' . - (($tag) ? 'AND profile_tag.tag= "' . $tag . '" ': '') . - 'ORDER BY subscription.created DESC, profile.id DESC ' . - $lim); - - if ($display == 'list') { - $cls = $this->profile_list_class(); - $profile_list = new $cls($other, $profile, $this->trimmed('action')); - $cnt = $profile_list->show_list(); - } else { - $cnt = $this->icon_list($other); - } - - // For building the pagination URLs - - $args = array('nickname' => $profile->nickname); - - if ($display != 'list') { - $args['display'] = $display; - } - - common_pagination($page > 1, - $cnt > $per_page, - $page, - $this->trimmed('action'), - $args); - } - - function profile_list_class() - { - return 'ProfileList'; - } - - function icon_list($other) - { - common_element_start('ul', $this->div_class()); - - $cnt = 0; - - while ($other->fetch()) { - - $cnt++; - - if ($cnt > AVATARS_PER_PAGE) { - break; - } - - common_element_start('li'); - - common_element_start('a', array('title' => ($other->fullname) ? - $other->fullname : - $other->nickname, - 'href' => $other->profileurl, - 'class' => 'subscription')); - $avatar = $other->getAvatar(AVATAR_STREAM_SIZE); - common_element('img', - array('src' => - (($avatar) ? common_avatar_display_url($avatar) : - common_default_avatar(AVATAR_STREAM_SIZE)), - 'width' => AVATAR_STREAM_SIZE, - 'height' => AVATAR_STREAM_SIZE, - 'class' => 'avatar stream', - 'alt' => ($other->fullname) ? - $other->fullname : - $other->nickname)); - common_element_end('a'); - - // XXX: subscribe form here - - common_element_end('li'); - } - - common_element_end('ul'); - - return $cnt; - } - - function gallery_type() - { - return null; - } - - function get_instructions(&$profile) - { - return null; - } - - function fields() - { - return null; - } - - function div_class() - { - return ''; - } - - function display_links($profile, $page, $display) - { - $tag = $this->trimmed('tag'); - - common_element_start('dl', array('id'=>'subscriptions_nav')); - common_element('dt', null, _('Subscriptions navigation')); - common_element_start('dd'); - common_element_start('ul', array('class'=>'nav')); - - switch ($display) { - case 'list': - common_element('li', array('class'=>'child_1'), _('List')); - common_element_start('li'); - $url_args = array('display' => 'icons', - 'nickname' => $profile->nickname, - 'page' => 1 + floor((($page - 1) * PROFILES_PER_PAGE) / AVATARS_PER_PAGE)); - if ($tag) { - $url_args['tag'] = $tag; - } - $url = common_local_url($this->trimmed('action'), $url_args); - common_element('a', array('href' => $url), - _('Icons')); - common_element_end('li'); - break; - default: - common_element_start('li', array('class'=>'child_1')); - $url_args = array('nickname' => $profile->nickname, - 'page' => 1 + floor((($page - 1) * AVATARS_PER_PAGE) / PROFILES_PER_PAGE)); - if ($tag) { - $url_args['tag'] = $tag; - } - $url = common_local_url($this->trimmed('action'), $url_args); - common_element('a', array('href' => $url), - _('List')); - common_element_end('li'); - common_element('li', null, _('Icons')); - break; - } - - common_element_end('ul'); - common_element_end('dd'); - common_element_end('dl'); - } - - // Get list of tags we tagged other users with - - function get_all_tags($profile, $lst, $usr) - { - $profile_tag = new Notice_tag(); - $profile_tag->query('SELECT DISTINCT(tag) ' . - 'FROM profile_tag, subscription ' . - 'WHERE tagger = ' . $profile->id . ' ' . - 'AND ' . $usr . ' = ' . $profile->id . ' ' . - 'AND ' . $lst . ' = tagged ' . - 'AND tagger != tagged'); - $tags = array(); - while ($profile_tag->fetch()) { - $tags[] = $profile_tag->tag; - } - $profile_tag->free(); - return $tags; - } -} \ No newline at end of file diff --git a/lib/galleryaction.php b/lib/galleryaction.php new file mode 100644 index 0000000000..b36f1ea34b --- /dev/null +++ b/lib/galleryaction.php @@ -0,0 +1,168 @@ +. + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/profilelist.php'; + +// 10x8 + +define('AVATARS_PER_PAGE', 80); + +class GalleryAction extends Action +{ + var $profile = null; + var $user = null; + var $page = null; + + function prepare($args) + { + parent::prepare($args); + + // FIXME very similar code below + + $nickname_arg = $this->arg('nickname'); + $nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $nickname) { + $args = array('nickname' => $nickname); + if ($this->arg('page') && $this->arg('page') != 1) { + $args['page'] = $this->arg['page']; + } + common_redirect(common_local_url('subscriptions', $args), 301); + return false; + } + + $this->user = User::staticGet('nickname', $nickname); + + if (!$this->user) { + $this->clientError(_('No such user.'), 404); + return false; + } + + $this->profile = $this->user->getProfile(); + + if (!$this->profile) { + $this->serverError(_('User has no profile.')); + return false; + } + + $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + + return true; + } + + function isReadOnly() + { + return true; + } + + function handle($args) + { + parent::handle($args); + + # Post from the tag dropdown; redirect to a GET + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + common_redirect($this->self_url(), 307); + return; + } + + $this->showPage(); + } + + function showLocalNav() + { + $nav = new SubGroupNav($this, $this->user); + $nav->show(); + } + + function showContent() + { + $this->showTagsDropdown(); + } + + function showTagsDropdown() + { + $tag = $this->trimmed('tag'); + + $tags = $this->getAllTags(); + + $content = array(); + + foreach ($tags as $t) { + $content[$t] = $t; + } + if ($tags) { + $this->elementStart('dl', array('id'=>'filter_tags')); + $this->element('dt', null, _('Filter tags')); + $this->elementStart('dd'); + $this->elementStart('ul'); + $this->elementStart('li', array('id' => 'filter_tags_all', + 'class' => 'child_1')); + $this->element('a', + array('href' => + common_local_url($this->trimmed('action'), + array('nickname' => + $profile->nickname))), + _('All')); + $this->elementEnd('li'); + $this->elementStart('li', array('id'=>'filter_tags_item')); + $this->elementStart('form', array('name' => 'bytag', + 'id' => 'bytag', + 'method' => 'post')); + $this->dropdown('tag', _('Tag'), $content, + _('Choose a tag to narrow list'), false, $tag); + $this->submit('go', _('Go')); + $this->elementEnd('form'); + $this->elementEnd('li'); + $this->elementEnd('ul'); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + } + } + + // Get list of tags we tagged other users with + + function getTags($lst, $usr) + { + $profile_tag = new Notice_tag(); + $profile_tag->query('SELECT DISTINCT(tag) ' . + 'FROM profile_tag, subscription ' . + 'WHERE tagger = ' . $profile->id . ' ' . + 'AND ' . $usr . ' = ' . $profile->id . ' ' . + 'AND ' . $lst . ' = tagged ' . + 'AND tagger != tagged'); + $tags = array(); + while ($profile_tag->fetch()) { + $tags[] = $profile_tag->tag; + } + $profile_tag->free(); + return $tags; + } + + function getAllTags() + { + return array(); + } +} \ No newline at end of file diff --git a/lib/profilelist.php b/lib/profilelist.php index 73c129efe8..a510c518c5 100644 --- a/lib/profilelist.php +++ b/lib/profilelist.php @@ -92,25 +92,24 @@ class ProfileList extends Widget $user = common_current_user(); - $this->out->elementStart('div', 'entity_profile vcard'); $avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE); $this->out->elementStart('a', array('href' => $this->profile->profileurl, 'class' => 'url')); $this->out->element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_STREAM_SIZE), - 'class' => 'photo avatar', - 'width' => AVATAR_STREAM_SIZE, - 'height' => AVATAR_STREAM_SIZE, - 'alt' => - ($this->profile->fullname) ? $this->profile->fullname : - $this->profile->nickname)); + 'class' => 'photo avatar', + 'width' => AVATAR_STREAM_SIZE, + 'height' => AVATAR_STREAM_SIZE, + 'alt' => + ($this->profile->fullname) ? $this->profile->fullname : + $this->profile->nickname)); $hasFN = ($this->profile->fullname) ? 'nickname' : 'fn nickname'; $this->out->elementStart('span', $hasFN); $this->out->raw($this->highlight($this->profile->nickname)); $this->out->elementEnd('span'); $this->out->elementEnd('a'); - + if ($this->profile->fullname) { $this->out->elementStart('dl', 'entity_fn'); $this->out->element('dt', null, 'Full name'); @@ -159,8 +158,8 @@ class ProfileList extends Widget $this->out->elementStart('dt'); if ($user->id == $this->owner->id) { $this->out->element('a', array('href' => common_local_url('tagother', - array('id' => $this->profile->id))), - _('Tags')); + array('id' => $this->profile->id))), + _('Tags')); } else { $this->out->text(_('Tags')); } @@ -172,10 +171,10 @@ class ProfileList extends Widget $this->out->elementStart('li'); $this->element('span', 'mark_hash', '#'); $this->out->element('a', array('rel' => 'tag', - 'href' => common_local_url($this->action, - array('nickname' => $this->owner->nickname, - 'tag' => $tag))), - $tag); + 'href' => common_local_url($this->action, + array('nickname' => $this->owner->nickname, + 'tag' => $tag))), + $tag); $this->out->elementEnd('li'); } $this->out->elementEnd('ul'); From 30dac77caa4a42fbfbf1dedcf34f80d59ed500b8 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 17:06:38 +0100 Subject: [PATCH 04/30] add nav menu to usergroups --- actions/usergroups.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/actions/usergroups.php b/actions/usergroups.php index 62ad3b3a2e..f56f9c6b29 100644 --- a/actions/usergroups.php +++ b/actions/usergroups.php @@ -108,7 +108,8 @@ class UsergroupsAction extends Action function showLocalNav() { - // XXX: Add to the subscriptions tabset + $nav = new SubGroupNav($this, $this->user); + $nav->show(); } function showContent() From 9d913150d4c3b967207837eb07afe890638944ac Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 17:12:03 +0100 Subject: [PATCH 05/30] use profile attr in GalleryAction --- lib/galleryaction.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/galleryaction.php b/lib/galleryaction.php index b36f1ea34b..a277762a65 100644 --- a/lib/galleryaction.php +++ b/lib/galleryaction.php @@ -149,8 +149,8 @@ class GalleryAction extends Action $profile_tag = new Notice_tag(); $profile_tag->query('SELECT DISTINCT(tag) ' . 'FROM profile_tag, subscription ' . - 'WHERE tagger = ' . $profile->id . ' ' . - 'AND ' . $usr . ' = ' . $profile->id . ' ' . + 'WHERE tagger = ' . $this->profile->id . ' ' . + 'AND ' . $usr . ' = ' . $this->profile->id . ' ' . 'AND ' . $lst . ' = tagged ' . 'AND tagger != tagged'); $tags = array(); From 01e7859a81b8f3f1aef75aae92607beabd2c9390 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 17:12:23 +0100 Subject: [PATCH 06/30] Remove common code from Subscriptions, make tags link visible --- actions/subscriptions.php | 97 +++++++------------------------------- theme/base/css/display.css | 84 +++------------------------------ 2 files changed, 24 insertions(+), 157 deletions(-) diff --git a/actions/subscriptions.php b/actions/subscriptions.php index 604428cf35..8d1462d494 100644 --- a/actions/subscriptions.php +++ b/actions/subscriptions.php @@ -2,7 +2,7 @@ /** * Laconica, the distributed open-source microblogging tool * - * User profile page + * List of a user's subscriptions * * PHP version 5 * @@ -19,7 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * - * @category Personal + * @category Social * @package Laconica * @author Evan Prodromou * @author Sarven Capadisli @@ -32,18 +32,10 @@ if (!defined('LACONICA')) { exit(1); } -require_once INSTALLDIR.'/lib/subsgroupnav.php'; - /** - * User profile page + * A list of the user's subscriptions * - * When I created this page, "show stream" seemed like the best name for it. - * Now, it seems like a really bad name. - * - * It shows a stream of the user's posts, plus lots of profile info, links - * to subscriptions and stuff, etc. - * - * @category Personal + * @category Social * @package Laconica * @author Evan Prodromou * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 @@ -52,62 +44,8 @@ require_once INSTALLDIR.'/lib/subsgroupnav.php'; if (!defined('LACONICA')) { exit(1); } -class SubscriptionsAction extends Action +class SubscriptionsAction extends GalleryAction { - var $profile = null; - var $user = null; - var $page = null; - - function prepare($args) - { - parent::prepare($args); - - // FIXME very similar code below - - $nickname_arg = $this->arg('nickname'); - $nickname = common_canonical_nickname($nickname_arg); - - // Permanent redirect on non-canonical nickname - - if ($nickname_arg != $nickname) { - $args = array('nickname' => $nickname); - if ($this->arg('page') && $this->arg('page') != 1) { - $args['page'] = $this->arg['page']; - } - common_redirect(common_local_url('subscriptions', $args), 301); - return false; - } - - $this->user = User::staticGet('nickname', $nickname); - - if (!$this->user) { - $this->clientError(_('No such user.'), 404); - return false; - } - - $this->profile = $this->user->getProfile(); - - if (!$this->profile) { - $this->serverError(_('User has no profile.')); - return false; - } - - $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; - - return true; - } - - function isReadOnly() - { - return true; - } - - function handle($args) - { - parent::handle($args); - $this->showPage(); - } - function title() { if ($this->page == 1) { @@ -134,21 +72,22 @@ class SubscriptionsAction extends Action } } - function showLocalNav() + function getAllTags() { - $nav = new SubGroupNav($this, $this->user); - $nav->show(); + return $this->getTags('subscribed', 'subscriber'); } function showContent() { + parent::showContent(); + $offset = ($this->page-1) * PROFILES_PER_PAGE; $limit = PROFILES_PER_PAGE + 1; $subscriptions = $this->user->getSubscriptions($offset, $limit); - if ($subs) { - $subscriptions_list = new SubscriptionsList($subscriptions, null, $this); + if ($subscriptions) { + $subscriptions_list = new SubscriptionsList($subscriptions, $this->user, $this); $subscriptions_list->show(); } @@ -170,16 +109,16 @@ class SubscriptionsList extends ProfileList return; } - $this->elementStart('form', array('id' => 'subedit-' . $profile->id, + $this->out->elementStart('form', array('id' => 'subedit-' . $profile->id, 'method' => 'post', 'class' => 'subedit', 'action' => common_local_url('subedit'))); - $this->hidden('token', common_session_token()); - $this->hidden('profile', $profile->id); - $this->checkbox('jabber', _('Jabber'), $sub->jabber); - $this->checkbox('sms', _('SMS'), $sub->sms); - $this->submit('save', _('Save')); - $this->elementEnd('form'); + $this->out->hidden('token', common_session_token()); + $this->out->hidden('profile', $profile->id); + $this->out->checkbox('jabber', _('Jabber'), $sub->jabber); + $this->out->checkbox('sms', _('SMS'), $sub->sms); + $this->out->submit('save', _('Save')); + $this->out->elementEnd('form'); return; } } diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 91789f4f18..45affbaf6a 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -36,7 +36,7 @@ font-weight:bold; } legend { -font-weight:bold; +font-weight:bold; font-size:1.3em; text-transform:uppercase; } @@ -99,9 +99,6 @@ display:none; margin-left:11px; } - - - /* FORM SETTINGS */ .form_settings fieldset { margin-bottom:29px; @@ -111,7 +108,6 @@ margin-bottom:29px; font-style:italic; } - .form_settings .form_data li { width:100%; float:left; @@ -179,7 +175,6 @@ width:90%; margin-left:0; } - .form_settings .form_note { border-radius:4px; -moz-border-radius:4px; @@ -187,13 +182,8 @@ border-radius:4px; padding:0 7px; } - - /* FORM SETTINGS */ - - - address { float:left; margin-bottom:18px; @@ -206,7 +196,6 @@ address .fn { font-weight:bold; } - #header { width:100%; position:relative; @@ -225,7 +214,6 @@ display:inline; margin-left:11px; } - .system_notice dt { font-weight:bold; text-transform:uppercase; @@ -244,17 +232,12 @@ clear:both; margin-bottom:18px; } - - - #footer { float:left; width:64%; padding:18px; } - - #site_nav_local_views { width:100%; float:left; @@ -286,7 +269,6 @@ float:left; width:100%; } - #site_nav_global_primary dt, #site_nav_global_secondary dt { display:none; @@ -315,8 +297,6 @@ padding-left:30px; padding-left:28px; } - - #export_data ul { display:inline; } @@ -329,8 +309,6 @@ margin-left:11px; margin-left:0; } - - #licenses { font-size:0.9em; } @@ -353,7 +331,6 @@ vertical-align:top; margin-right:4px; } - #wrap { float:left; margin:0 auto; @@ -361,7 +338,6 @@ margin-right:4px; width:71.714em; } - #core { position:relative; width:100%; @@ -396,9 +372,6 @@ border-radius:7px; -webkit-border-radius:7px; } - - - /*Start: FORM NOTICE*/ #form_notice { width:384px; @@ -501,10 +474,6 @@ float:left; /*end FORM NOTICE*/ - - - - /* entity_profile */ .entity_profile { position:relative; @@ -575,10 +544,6 @@ display:none; /* entity_profile */ - - - - /*entity_actions*/ .entity_actions { float:right; @@ -617,8 +582,6 @@ font-weight:bold; display:block; } - - .form_user_block input.submit, .form_user_unblock input.submit, #entity_send-a-message a, @@ -633,8 +596,6 @@ padding-left:20px; padding:4px 4px 4px 23px; } - - .entity_tags ul { list-style-type:none; } @@ -644,8 +605,6 @@ margin-right:1em; float:left; } - - .aside .section { margin-bottom:29px; clear:both; @@ -665,7 +624,6 @@ display:inline; content: ":"; } - #user_subscriptions, #user_subscribers, #user_groups, { @@ -692,8 +650,6 @@ display:none; clear:both; } - - .profile .entity_profile { margin-bottom:0; min-height:60px; @@ -708,7 +664,6 @@ float:right; display:none; } - .profiles { list-style-type:none; } @@ -730,8 +685,6 @@ display:block; width:auto; } - - /* NOTICE */ .notice, .profile { @@ -756,7 +709,6 @@ border-radius:4px; -webkit-border-radius:4px; } - /* NOTICES */ #notices_primary { float:left; @@ -773,7 +725,6 @@ display:block; padding-left:28px; } - .notice .author { margin-right:11px; } @@ -802,7 +753,6 @@ font-style:italic; } - .notice .entry-title { float:left; width:100%; @@ -875,8 +825,6 @@ text-transform:lowercase; .notice div.entry-content a:hover { } - - .notice-data { position:absolute; top:18px; @@ -966,10 +914,6 @@ padding:0; /*END: NOTICES */ - - - - .pagination dt { font-weight:bold; display:none; @@ -1010,11 +954,8 @@ padding-right:20px; border-right:0; } - /* END: NOTICE */ - - /*If there is hentry on #content_inner, then this doesn't need to be specific to #doc or any other section */ .hentry .entry-content p { margin-bottom:18px; @@ -1030,7 +971,6 @@ margin-bottom:18px; margin-left:18px; } - /*START: LOAD ALONG WITH JS*/ .notice .in-reply-to { width:98%; @@ -1046,8 +986,6 @@ background-color:#E4E9F0; background-color:#D1D9E4; } - - .entity_actions #user_subscribe .form_note, .entity_actions #user_subscribe .form_data, .entity_actions #user_subscribe .form_actions label { @@ -1079,8 +1017,6 @@ background-color:#fff; /*END: LOAD ALONG WITH JS*/ - - /* TOP_POSTERS */ #top-posters caption { text-align:left; @@ -1106,8 +1042,6 @@ height:24px; width:24px; } - - /* tagcloud */ .tag-cloud { list-style-type:none; @@ -1150,20 +1084,15 @@ font-size:4em; display:none; } - - - #form_settings_photo .form_data { clear:both; } - - #form_settings_avatar li { width:auto; } #form_settings_avatar input { -margin-left:0; +margin-left:0; } #avatar_original, #avatar_preview { @@ -1188,8 +1117,6 @@ clear:both; margin-bottom:0; } - - #groups_related ul, #users_featured ul { list-style-type:none; @@ -1223,8 +1150,6 @@ display:none; font-weight:bold; } - - #home.logged_out h1 { display:none; } @@ -1267,7 +1192,10 @@ font-weight:bold; text-align:center; } - #testimonials { clear:both; } + +.entity_tags dt { +display:block; +} \ No newline at end of file From 0df954aa89ed33a64bd519222a0d79c884025ac6 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 17:19:27 +0100 Subject: [PATCH 07/30] Make subscribers work --- actions/subscribers.php | 96 +++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 32 deletions(-) diff --git a/actions/subscribers.php b/actions/subscribers.php index 31d0468d90..408829b545 100644 --- a/actions/subscribers.php +++ b/actions/subscribers.php @@ -1,9 +1,12 @@ . + * + * @category Social + * @package Laconica + * @author Evan Prodromou + * @author Sarven Capadisli + * @copyright 2008-2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ */ -if (!defined('LACONICA')) { exit(1); } +if (!defined('LACONICA')) { + exit(1); +} -require_once(INSTALLDIR.'/lib/gallery.php'); +/** + * List a user's subscribers + * + * @category Social + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ class SubscribersAction extends GalleryAction { - - function gallery_type() + function title() { - return _('Subscribers'); - } - - function get_instructions(&$profile) - { - $user =& common_current_user(); - if ($user && ($user->id == $profile->id)) { - return _('These are the people who listen to your notices.'); + if ($this->page == 1) { + return sprintf(_('%s subscribers'), $this->user->nickname); } else { - return sprintf(_('These are the people who listen to %s\'s notices.'), $profile->nickname); + return sprintf(_('%s subscribers, page %d'), + $this->user->nickname, + $this->page); } } - function fields() + function showPageNotice() { - return array('subscriber', 'subscribed'); + $user =& common_current_user(); + if ($user && ($user->id == $this->profile->id)) { + $this->element('p', null, + _('These are the people who listen to '. + 'your notices.')); + } else { + $this->element('p', null, + sprintf(_('These are the people who '. + 'listen to %s\'s notices.'), + $this->profile->nickname)); + } } - function div_class() + function showContent() { - return 'subscribers'; - } + $offset = ($this->page-1) * PROFILES_PER_PAGE; + $limit = PROFILES_PER_PAGE + 1; - function get_other(&$subs) - { - return $subs->subscriber; - } + $subscribers = $this->user->getSubscribers($offset, $limit); - function profile_list_class() - { - return 'SubscribersList'; + if ($subscribers) { + $subscribers_list = new SubscribersList($subscribers, $this->user, $this); + $subscribers_list->show(); + } + + $subscribers->free(); + + $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE, + $this->page, 'subscribers', + array('nickname' => $this->user->nickname)); } } class SubscribersList extends ProfileList { - function show_owner_controls($profile) + function showOwnerControls($profile) { - common_block_form($profile, array('action' => 'subscribers', - 'nickname' => $this->owner->nickname)); + $bf = new BlockForm($this->out, $profile, + array('action' => 'subscribers', + 'nickname' => $this->owner->nickname)); + $bf->show(); } } From e6211c4c22eae51d78142056180f7b9e706179e4 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 17:29:10 +0100 Subject: [PATCH 08/30] Add a superclass for all sections --- lib/section.php | 108 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 lib/section.php diff --git a/lib/section.php b/lib/section.php new file mode 100644 index 0000000000..d64095a3eb --- /dev/null +++ b/lib/section.php @@ -0,0 +1,108 @@ +. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @copyright 2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/widget.php'; + +/** + * Base class for sections + * + * These are the widgets that show interesting data about a person + * group, or site. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class Section extends Widget +{ + /** + * Show the form + * + * Uses a recipe to output the form. + * + * @return void + * @see Widget::show() + */ + + function show() + { + $this->out->elementStart('div', + array('id' => $this->divId(), + 'class' => 'section')); + + $this->out->element('h2', null, + $this->title()); + + $have_more = $this->showContent(); + + if ($have_more) { + $this->elementStart('p'); + $this->element('a', array('href' => $this->moreUrl(), + 'class' => 'more'), + $this->moreTitle()); + $this->elementEnd('p'); + } + + $this->elementEnd('div'); + } + + function divId() + { + return 'generic_section'; + } + + function title() + { + return _('Untitled section'); + } + + function showContent() + { + $this->out->element('p', null, + _('(None)')); + return false; + } + + function moreUrl() + { + return null; + } + + function moreTitle() + { + return null; + } +} From 93925b8505f428f961ecf546e63744927bf63ad3 Mon Sep 17 00:00:00 2001 From: sarven Date: Thu, 22 Jan 2009 16:50:35 +0000 Subject: [PATCH 09/30] Mathching markup to user profilelist (entities) --- lib/grouplist.php | 22 +++++++++++----------- theme/base/css/display.css | 12 +++++++----- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/grouplist.php b/lib/grouplist.php index dd10a27535..869e448970 100644 --- a/lib/grouplist.php +++ b/lib/grouplist.php @@ -66,7 +66,7 @@ class GroupList extends Widget function show() { - $this->out->elementStart('ul', 'groups'); + $this->out->elementStart('ul', 'profiles groups xoxo'); $cnt = 0; @@ -85,19 +85,19 @@ class GroupList extends Widget function showGroup() { - $this->out->elementStart('li', array('class' => 'group', + $this->out->elementStart('li', array('class' => 'profile', 'id' => 'group-' . $this->group->id)); $user = common_current_user(); - $this->out->elementStart('div', array('id' => 'group_group', - 'class' => 'vcard')); + $this->out->elementStart('div', 'entity_profile vcard'); $logo = ($this->group->stream_logo) ? $this->group->stream_logo : User_group::defaultLogo(AVATAR_STREAM_SIZE); $this->out->elementStart('a', array('href' => $this->group->homeUrl(), - 'class' => 'url')); + 'class' => 'url', + 'rel' => 'group')); $this->out->element('img', array('src' => $logo, 'class' => 'photo avatar', 'width' => AVATAR_STREAM_SIZE, @@ -105,24 +105,24 @@ class GroupList extends Widget 'alt' => ($this->group->fullname) ? $this->group->fullname : $this->group->nickname)); - $hasFN = ($this->group->fullname) ? 'nickname' : 'fn nickname'; + $hasFN = ($this->group->fullname) ? 'nickname url uid' : 'fn org nickname url uid'; $this->out->elementStart('span', $hasFN); $this->out->raw($this->highlight($this->group->nickname)); $this->out->elementEnd('span'); $this->out->elementEnd('a'); if ($this->group->fullname) { - $this->out->elementStart('dl', 'group_fn'); + $this->out->elementStart('dl', 'entity_fn'); $this->out->element('dt', null, 'Full name'); $this->out->elementStart('dd'); - $this->out->elementStart('span', 'fn'); + $this->out->elementStart('span', 'fn org'); $this->out->raw($this->highlight($this->group->fullname)); $this->out->elementEnd('span'); $this->out->elementEnd('dd'); $this->out->elementEnd('dl'); } if ($this->group->location) { - $this->out->elementStart('dl', 'group_location'); + $this->out->elementStart('dl', 'entity_location'); $this->out->element('dt', null, _('Location')); $this->out->elementStart('dd', 'location'); $this->out->raw($this->highlight($this->group->location)); @@ -130,7 +130,7 @@ class GroupList extends Widget $this->out->elementEnd('dl'); } if ($this->group->homepage) { - $this->out->elementStart('dl', 'group_url'); + $this->out->elementStart('dl', 'entity_url'); $this->out->element('dt', null, _('URL')); $this->out->elementStart('dd'); $this->out->elementStart('a', array('href' => $this->group->homepage, @@ -141,7 +141,7 @@ class GroupList extends Widget $this->out->elementEnd('dl'); } if ($this->group->description) { - $this->out->elementStart('dl', 'group_note'); + $this->out->elementStart('dl', 'entity_note'); $this->out->element('dt', null, _('Note')); $this->out->elementStart('dd', 'note'); $this->out->raw($this->highlight($this->group->description)); diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 45affbaf6a..727fd94c47 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -655,10 +655,15 @@ margin-bottom:0; min-height:60px; } + +.profile .form_group_join, +.profile .form_group_leave, .profile .form_user_subscribe, .profile .form_user_unsubscribe { float:right; } +.profile .form_group_join legend, +.profile .form_group_leave legend, .profile .form_user_subscribe legend, .profile .form_user_unsubscribe legend { display:none; @@ -678,7 +683,8 @@ display:inline; float:none; } .profile .entity_profile .entity_note, -.profile .entity_profile .entity_url { +.profile .entity_profile .entity_url, +.profile .entity_profile .entity_tags { margin-left:59px; clear:none; display:block; @@ -1195,7 +1201,3 @@ text-align:center; #testimonials { clear:both; } - -.entity_tags dt { -display:block; -} \ No newline at end of file From ead13846505be3e183ecf05f5897636f0d9e5755 Mon Sep 17 00:00:00 2001 From: sarven Date: Thu, 22 Jan 2009 16:57:37 +0000 Subject: [PATCH 10/30] .profile form.form_subscriptions_edit style --- actions/subscriptions.php | 2 +- theme/base/css/display.css | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/actions/subscriptions.php b/actions/subscriptions.php index 8d1462d494..bcc5578917 100644 --- a/actions/subscriptions.php +++ b/actions/subscriptions.php @@ -111,7 +111,7 @@ class SubscriptionsList extends ProfileList $this->out->elementStart('form', array('id' => 'subedit-' . $profile->id, 'method' => 'post', - 'class' => 'subedit', + 'class' => 'form_subcription_edit', 'action' => common_local_url('subedit'))); $this->out->hidden('token', common_session_token()); $this->out->hidden('profile', $profile->id); diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 727fd94c47..23f04f761b 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -684,13 +684,22 @@ float:none; } .profile .entity_profile .entity_note, .profile .entity_profile .entity_url, -.profile .entity_profile .entity_tags { +.profile .entity_profile .entity_tags, +.profile .entity_profile .form_subcription_edit { margin-left:59px; clear:none; display:block; width:auto; } +.profile .entity_profile .form_subcription_edit label { +font-weight:normal; +margin-right:11px; +} + + + + /* NOTICE */ .notice, .profile { From 8daec3c245fd1127ea96e0ef1312fcad91b53a3f Mon Sep 17 00:00:00 2001 From: sarven Date: Thu, 22 Jan 2009 17:01:57 +0000 Subject: [PATCH 11/30] Colour fix for Subscribe input.submit --- theme/identica/css/display.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/theme/identica/css/display.css b/theme/identica/css/display.css index 1df14a7f14..66ee3c25d2 100644 --- a/theme/identica/css/display.css +++ b/theme/identica/css/display.css @@ -58,8 +58,7 @@ background-color:#fcfffc; #aside_primary, #entity_subscribe a, -#TB_window input.submit, -.form_user_subscribe input.submit { +#TB_window input.submit { background-color:#CEE1E9; } From e2869fcf0a4f64132b989fd6223e3bbd3e7c78d3 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 18:48:52 +0100 Subject: [PATCH 12/30] Move NoticeWrapper to a generalized ArrayWrapper class We need to use array wrappers for other kinds of queries, so I generalized the NoticeWrapper and tested it in the Notice class. --- classes/Notice.php | 6 +-- classes/NoticeWrapper.php | 62 ------------------------------ lib/arraywrapper.php | 79 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 65 deletions(-) delete mode 100644 classes/NoticeWrapper.php create mode 100644 lib/arraywrapper.php diff --git a/classes/Notice.php b/classes/Notice.php index 5fa0782057..de7540705a 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -443,7 +443,7 @@ class Notice extends Memcached_DataObject # On a cache hit, return a DB-object-like wrapper if ($notices !== false) { - $wrapper = new NoticeWrapper(array_slice($notices, $offset, $limit)); + $wrapper = new ArrayWrapper(array_slice($notices, $offset, $limit)); return $wrapper; } @@ -483,7 +483,7 @@ class Notice extends Memcached_DataObject # return a wrapper of the array for use now - return new NoticeWrapper(array_slice($notices, $offset, $limit)); + return new ArrayWrapper(array_slice($notices, $offset, $limit)); } } @@ -514,7 +514,7 @@ class Notice extends Memcached_DataObject # return a wrapper of the array for use now - $wrapper = new NoticeWrapper(array_slice($notices, $offset, $limit)); + $wrapper = new ArrayWrapper(array_slice($notices, $offset, $limit)); return $wrapper; } diff --git a/classes/NoticeWrapper.php b/classes/NoticeWrapper.php deleted file mode 100644 index 233340ccd6..0000000000 --- a/classes/NoticeWrapper.php +++ /dev/null @@ -1,62 +0,0 @@ -. - */ - -if (!defined('LACONICA')) { exit(1); } - -require_once(INSTALLDIR.'/classes/Notice.php'); - -class NoticeWrapper extends Notice -{ - - public $id; // int(4) primary_key not_null - public $profile_id; // int(4) not_null - public $uri; // varchar(255) unique_key - public $content; // varchar(140) - public $rendered; // text() - public $url; // varchar(255) - public $created; // datetime() not_null - public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP - public $reply_to; // int(4) - public $is_local; // tinyint(1) - public $source; // varchar(32) - - var $notices = null; - var $i = -1; - - function __construct($arr) - { - $this->notices = $arr; - } - - function fetch() - { - static $fields = array('id', 'profile_id', 'uri', 'content', 'rendered', - 'url', 'created', 'modified', 'reply_to', 'is_local', 'source'); - $this->i++; - if ($this->i >= count($this->notices)) { - return false; - } else { - $n = $this->notices[$this->i]; - foreach ($fields as $f) { - $this->$f = $n->$f; - } - return true; - } - } -} \ No newline at end of file diff --git a/lib/arraywrapper.php b/lib/arraywrapper.php new file mode 100644 index 0000000000..ef0eeffa5e --- /dev/null +++ b/lib/arraywrapper.php @@ -0,0 +1,79 @@ +. + */ + +if (!defined('LACONICA')) { + exit(1); +} + +class ArrayWrapper +{ + var $_items = null; + var $_count = 0; + var $_i = -1; + + function __construct($items) + { + $this->_items = $items; + $this->_count = count($this->_items); + } + + function fetch() + { + if (!$this->_items) { + return false; + } + $this->_i++; + if ($this->_i < $this->_count) { + return true; + } else { + return false; + } + } + + function __set($name, $value) + { + $item =& $this->_items[$this->_i]; + $item->$name = $value; + return $item->$name; + } + + function __get($name) + { + $item =& $this->_items[$this->_i]; + return $item->$name; + } + + function __isset($name) + { + $item =& $this->_items[$this->_i]; + return isset($item->$name); + } + + function __unset($name) + { + $item =& $this->_items[$this->_i]; + unset($item->$name); + } + + function __call($name, $args) + { + $item =& $this->_items[$this->_i]; + return call_user_func_array(array($item, $name), $args); + } +} \ No newline at end of file From 32debdac66eed90e5b93b29eda7cbc264e5dfa4e Mon Sep 17 00:00:00 2001 From: sarven Date: Thu, 22 Jan 2009 17:49:55 +0000 Subject: [PATCH 13/30] Profile tag dt display inline and margin --- theme/base/css/display.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 23f04f761b..e49f27dfb3 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -691,6 +691,11 @@ clear:none; display:block; width:auto; } +.profile .entity_profile .entity_tags dt { +display:inline; +margin-right:11px; +} + .profile .entity_profile .form_subcription_edit label { font-weight:normal; From 8c79646e53b201434d5c8f36e85b2c9cb1026f0f Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 19:13:26 +0100 Subject: [PATCH 14/30] Add a method the memcached_object that lets it cache query results --- classes/Memcached_DataObject.php | 49 +++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index b9f599dbc8..c670e99c8b 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -21,7 +21,7 @@ if (!defined('LACONICA')) { exit(1); } require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; -class Memcached_DataObject extends DB_DataObject +class Memcached_DataObject extends DB_DataObject { function &staticGet($cls, $k, $v=null) { @@ -69,7 +69,7 @@ class Memcached_DataObject extends DB_DataObject $result = parent::insert(); return $result; } - + function update($orig=null) { if (is_object($orig) && $orig instanceof Memcached_DataObject) { @@ -81,21 +81,21 @@ class Memcached_DataObject extends DB_DataObject } return $result; } - + function delete() { $this->decache(); # while we still have the values! return parent::delete(); } - + static function memcache() { return common_memcache(); } - + static function cacheKey($cls, $k, $v) { return common_cache_key(strtolower($cls).':'.$k.':'.$v); } - + static function getcached($cls, $k, $v) { $c = Memcached_DataObject::memcache(); if (!$c) { @@ -114,7 +114,7 @@ class Memcached_DataObject extends DB_DataObject } return $_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"]; } - + function encache() { $c = $this->memcache(); @@ -122,7 +122,7 @@ class Memcached_DataObject extends DB_DataObject return false; } else { $pkey = array(); - $pval = array(); + $pval = array(); $types = $this->keyTypes(); ksort($types); foreach ($types as $key => $type) { @@ -139,7 +139,7 @@ class Memcached_DataObject extends DB_DataObject $c->set($this->cacheKey($this->tableName(), $pkeys, $pvals), $this); } } - + function decache() { $c = $this->memcache(); @@ -147,7 +147,7 @@ class Memcached_DataObject extends DB_DataObject return false; } else { $pkey = array(); - $pval = array(); + $pval = array(); $types = $this->keyTypes(); ksort($types); foreach ($types as $key => $type) { @@ -201,4 +201,33 @@ class Memcached_DataObject extends DB_DataObject } return $search_engine; } + + static function cachedQuery($cls, $qry, $expiry=3600) + { + $c = Memcached_DataObject::memcache(); + if (!$c) { + $inst = new $cls(); + $inst->query($qry); + return $cls; + } + $key_part = common_keyize($cls).':'.md5($qry); + $ckey = common_cache_key($key_part); + $stored = $c->get($ckey); + if ($stored) { + return new ArrayWrapper($stored); + } + + $inst = new $cls(); + $result = $inst->query($qry); + if (!$result) { + return $inst; + } + $cached = array(); + while ($inst->fetch()) { + $cached[] = clone($inst); + } + $inst->free(); + $c->set($ckey, $cached, MEMCACHE_COMPRESSED, $expiry); + return ArrayWrapper($cached); + } } From 55037403c5d9ab7f73d9f8ee454ba8d33cf313dd Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 19:14:18 +0100 Subject: [PATCH 15/30] Use cached query results for favorited. Might cause pagination issues. --- actions/favorited.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/actions/favorited.php b/actions/favorited.php index 0223564f34..4155b3a234 100644 --- a/actions/favorited.php +++ b/actions/favorited.php @@ -181,10 +181,9 @@ class FavoritedAction extends Action $qry .= ' LIMIT ' . $offset . ', ' . $limit; } - // XXX: Figure out how to cache this query - - $notice = new Notice; - $notice->query(sprintf($qry, common_config('popular', 'dropoff'))); + $notice = Memcached_DataObject::cachedQuery('Notice', + sprintf($qry, common_config('popular', 'dropoff')), + 600); $nl = new NoticeList($notice, $this); From 3cfb708dcd407b3d6d34b89171bf599f09205222 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 20:07:13 +0100 Subject: [PATCH 16/30] Fix section widget's output calls --- lib/section.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/section.php b/lib/section.php index d64095a3eb..0c32ddcf84 100644 --- a/lib/section.php +++ b/lib/section.php @@ -69,14 +69,14 @@ class Section extends Widget $have_more = $this->showContent(); if ($have_more) { - $this->elementStart('p'); - $this->element('a', array('href' => $this->moreUrl(), + $this->out->elementStart('p'); + $this->out->element('a', array('href' => $this->moreUrl(), 'class' => 'more'), $this->moreTitle()); - $this->elementEnd('p'); + $this->out->elementEnd('p'); } - $this->elementEnd('div'); + $this->out->elementEnd('div'); } function divId() From 04b164c342bebc8b5973b28fb25ea512161aa0b1 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 20:07:31 +0100 Subject: [PATCH 17/30] A section sub-class for profiles --- lib/profilesection.php | 97 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 lib/profilesection.php diff --git a/lib/profilesection.php b/lib/profilesection.php new file mode 100644 index 0000000000..087adb2ef6 --- /dev/null +++ b/lib/profilesection.php @@ -0,0 +1,97 @@ +. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @copyright 2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +define('PROFILES_PER_SECTION', 6); + +/** + * Base class for sections + * + * These are the widgets that show interesting data about a person + * group, or site. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class ProfileSection extends Section +{ + function showContent() + { + $profiles = $this->getProfiles(); + + $cnt = 0; + + $this->out->elementStart('ul', 'entities users xoxo'); + + while ($profiles->fetch() && ++$cnt <= PROFILES_PER_SECTION) { + $this->showProfile($profiles); + } + + $this->out->elementEnd('ul'); + + return ($cnt > PROFILES_PER_SECTION); + } + + function getProfiles() + { + return null; + } + + function showProfile($profile) + { + $this->out->elementStart('li', 'vcard'); + $this->out->elementStart('a', array('title' => ($profile->fullname) ? + $profile->fullname : + $profile->nickname, + 'href' => $profile->profileurl, + 'rel' => 'contact member', + 'class' => 'url')); + $avatar = $profile->getAvatar(AVATAR_MINI_SIZE); + $this->out->element('img', array('src' => (($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_MINI_SIZE)), + 'width' => AVATAR_MINI_SIZE, + 'height' => AVATAR_MINI_SIZE, + 'class' => 'avatar photo', + 'alt' => ($profile->fullname) ? + $profile->fullname : + $profile->nickname)); + $this->out->element('span', 'fn nickname', $profile->nickname); + $this->out->elementEnd('a'); + if ($profile->value) { + $this->out->element('span', 'value', $profile->value); + } + $this->out->elementEnd('li'); + } +} From 871439ba6735482c9be2fe252037c699bd951585 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 20:07:55 +0100 Subject: [PATCH 18/30] A section for showing the site's top posters --- lib/topposterssection.php | 105 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 lib/topposterssection.php diff --git a/lib/topposterssection.php b/lib/topposterssection.php new file mode 100644 index 0000000000..1163738180 --- /dev/null +++ b/lib/topposterssection.php @@ -0,0 +1,105 @@ +. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @copyright 2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +/** + * Base class for sections + * + * These are the widgets that show interesting data about a person + * group, or site. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class TopPostersSection extends ProfileSection +{ + function getProfiles() + { + $qry = 'SELECT profile.*, count(*) as value ' . + 'FROM profile JOIN notice ON profile.id = notice.profile_id ' . + 'GROUP BY profile.id ' . + 'ORDER BY value DESC '; + + $limit = PROFILES_PER_SECTION; + $offset = 0; + + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + + $profile = Memcached_DataObject::cachedQuery('Profile', + $qry, + 6 * 3600); + return $profile; + } + + function showProfile($profile) + { + $this->out->elementStart('li', 'vcard'); + $this->out->elementStart('a', array('title' => ($profile->fullname) ? + $profile->fullname : + $profile->nickname, + 'href' => $profile->profileurl, + 'rel' => 'contact member', + 'class' => 'url')); + $avatar = $profile->getAvatar(AVATAR_MINI_SIZE); + $this->out->element('img', array('src' => (($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_MINI_SIZE)), + 'width' => AVATAR_MINI_SIZE, + 'height' => AVATAR_MINI_SIZE, + 'class' => 'avatar photo', + 'alt' => ($profile->fullname) ? + $profile->fullname : + $profile->nickname)); + $this->out->element('span', 'fn nickname', $profile->nickname); + $this->out->elementEnd('a'); + if ($profile->value) { + $this->out->element('span', 'value', $profile->value); + } + $this->out->elementEnd('li'); + } + + function title() + { + return _('Top posters'); + } + + function divId() + { + return 'top_posters'; + } +} From 94d3a4b22cfeeeff57be30647df6c59e971bfa52 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 20:08:16 +0100 Subject: [PATCH 19/30] Add top posters section to public page --- actions/public.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/actions/public.php b/actions/public.php index 0ceeef98e8..880b651e15 100644 --- a/actions/public.php +++ b/actions/public.php @@ -197,4 +197,10 @@ class PublicAction extends Action 'version' => 'Atom 1.0', 'item' => 'publicatom'))); } + + function showSections() + { + $top = new TopPostersSection($this); + $top->show(); + } } From 5cc0391093109a99504d27415a53712421c8e95f Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 20:22:06 +0100 Subject: [PATCH 20/30] Change to only show local posters in top posting section --- lib/topposterssection.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/topposterssection.php b/lib/topposterssection.php index 1163738180..7a3d46aa5f 100644 --- a/lib/topposterssection.php +++ b/lib/topposterssection.php @@ -50,6 +50,7 @@ class TopPostersSection extends ProfileSection { $qry = 'SELECT profile.*, count(*) as value ' . 'FROM profile JOIN notice ON profile.id = notice.profile_id ' . + (common_config('public', 'localonly') ? 'WHERE is_local = 1 ' : '') . 'GROUP BY profile.id ' . 'ORDER BY value DESC '; From ebb99f8cb13e2f81de27e3a31aa1a73c75b2e8fb Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 19:42:55 +0000 Subject: [PATCH 21/30] Add a notice section superclass for sections listing notices --- lib/noticesection.php | 107 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 lib/noticesection.php diff --git a/lib/noticesection.php b/lib/noticesection.php new file mode 100644 index 0000000000..9d10790701 --- /dev/null +++ b/lib/noticesection.php @@ -0,0 +1,107 @@ +. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @copyright 2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +define('NOTICES_PER_SECTION', 6); + +/** + * Base class for sections showing lists of notices + * + * These are the widgets that show interesting data about a person + * group, or site. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class NoticeSection extends Section +{ + function showContent() + { + $notices = $this->getNotices(); + + $cnt = 0; + + $this->out->elementStart('table', 'notices'); + + while ($notices->fetch() && ++$cnt <= NOTICES_PER_SECTION) { + $this->showNotice($notices); + } + + $this->out->elementEnd('table'); + + return ($cnt > NOTICES_PER_SECTION); + } + + function getNotices() + { + return null; + } + + function showNotice($notice) + { + $profile = $notice->getProfile(); + $this->out->elementStart('tr'); + $this->out->elementStart('td'); + $avatar = $profile->getAvatar(AVATAR_MINI_SIZE); + $this->out->element('img', array('src' => (($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_MINI_SIZE)), + 'width' => AVATAR_MINI_SIZE, + 'height' => AVATAR_MINI_SIZE, + 'class' => 'avatar photo', + 'alt' => ($profile->fullname) ? + $profile->fullname : + $profile->nickname)); + $this->out->elementEnd('a'); + $this->out->elementEnd('td'); + $this->out->elementStart('td'); + $this->out->elementStart('a', array('title' => ($profile->fullname) ? + $profile->fullname : + $profile->nickname, + 'href' => $profile->noticeurl, + 'rel' => 'contact member', + 'class' => 'url')); + $this->out->element('span', 'fn nickname', $profile->nickname); + $this->out->elementEnd('td'); + $this->out->elementStart('td'); + $this->out->raw($notice->rendered); + $this->out->elementEnd('td'); + if ($notice->value) { + $this->out->elementStart('td'); + $this->out->text($notice->value); + $this->out->elementEnd('td'); + } + $this->out->elementEnd('tr'); + } +} From 690273e20d98f33e43a490dc129a4d05dd06274c Mon Sep 17 00:00:00 2001 From: sarven Date: Thu, 22 Jan 2009 19:43:08 +0000 Subject: [PATCH 22/30] notice stlye updates --- theme/base/css/display.css | 21 ++++++++++++++++++++- theme/identica/css/display.css | 2 ++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index e49f27dfb3..44dbff5dac 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -754,6 +754,14 @@ margin-right:11px; /*content:":";*/ } +.notice .author .fn { +font-weight:bold; +} + +.notice .author .photo { +margin-bottom:0; +} + .vcard .photo { display:inline; margin-right:11px; @@ -806,7 +814,7 @@ border-radius:4px; /*border:1px solid blue;*/ clear:left; float:left; -width:48%; +/*width:48%;*/ font-size:0.95em; } .notice div.entry-content a, @@ -829,6 +837,10 @@ font-size:1.025em; .notice div.entry-content dd { display:inline; } +.notice div.entry-content .timestamp { +margin-left:59px; +} + .notice div.entry-content .timestamp dt, .notice div.entry-content .response dt { display:none; @@ -845,6 +857,11 @@ text-transform:lowercase; .notice div.entry-content a:hover { } + + + + + .notice-data { position:absolute; top:18px; @@ -867,6 +884,8 @@ float:left; width:50%; position:relative; font-size:0.95em; +width:12.5%; +float:right; } .notice-options a { diff --git a/theme/identica/css/display.css b/theme/identica/css/display.css index 66ee3c25d2..5d07cfd9a7 100644 --- a/theme/identica/css/display.css +++ b/theme/identica/css/display.css @@ -214,9 +214,11 @@ background:transparent url(../images/icons/twotone/green/disfavourite.gif) no-re background:transparent url(../images/icons/twotone/green/trash.gif) no-repeat 0 45%; } +div.entry-content, div.notice-options { opacity:0.3; } +.notices li.hover div.entry-content, .notices li.hover div.notice-options { opacity:1; } From 7260153bd123cce0f82836e10f32a9d3812d93ac Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 19:48:17 +0000 Subject: [PATCH 23/30] Add a section for popular notices sitewide --- lib/popularnoticesection.php | 83 ++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 lib/popularnoticesection.php diff --git a/lib/popularnoticesection.php b/lib/popularnoticesection.php new file mode 100644 index 0000000000..89daaa563c --- /dev/null +++ b/lib/popularnoticesection.php @@ -0,0 +1,83 @@ +. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @copyright 2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +define('NOTICES_PER_SECTION', 6); + +/** + * Base class for sections showing lists of notices + * + * These are the widgets that show interesting data about a person + * group, or site. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class PopularNoticeSection extends NoticeSection +{ + function getNotices() + { + $qry = 'SELECT notice.*, '. + 'sum(exp(-(now() - fave.modified) / %s)) as weight ' . + 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . + 'GROUP BY fave.notice_id ' . + 'ORDER BY weight DESC'; + + $offset = 0; + $limit = NOTICES_PER_SECTION + 1; + + if (common_config('db', 'type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + + $notice = Memcached_DataObject::cachedQuery('Notice', + sprintf($qry, common_config('popular', 'dropoff')), + 1200); + return $notice; + } + + function title() + { + return _('Popular notices'); + } + + function divId() + { + return 'popular_notices'; + } +} From b6dad052bdde1ba6d91eac370f17a9af235b9e09 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 19:48:35 +0000 Subject: [PATCH 24/30] Add a popular notices section to the public page --- actions/public.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actions/public.php b/actions/public.php index 880b651e15..8ce7fbee3e 100644 --- a/actions/public.php +++ b/actions/public.php @@ -202,5 +202,7 @@ class PublicAction extends Action { $top = new TopPostersSection($this); $top->show(); + $pop = new PopularNoticeSection($this); + $pop->show(); } } From dde1c00ce727b311c399271d02cb077f6725edb5 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 19:54:05 +0000 Subject: [PATCH 25/30] Return $inst, not $cls, if no cache in Memcached_DataObject --- classes/Memcached_DataObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index c670e99c8b..3ec7613891 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -208,7 +208,7 @@ class Memcached_DataObject extends DB_DataObject if (!$c) { $inst = new $cls(); $inst->query($qry); - return $cls; + return $inst; } $key_part = common_keyize($cls).':'.md5($qry); $ckey = common_cache_key($key_part); From 0029ebad111fd929936698d843556701df0f348c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 19:54:35 +0000 Subject: [PATCH 26/30] Don't tryto show a section if nothing comes back from profiles --- lib/profilesection.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/profilesection.php b/lib/profilesection.php index 087adb2ef6..14068b0827 100644 --- a/lib/profilesection.php +++ b/lib/profilesection.php @@ -52,6 +52,10 @@ class ProfileSection extends Section { $profiles = $this->getProfiles(); + if (!$profiles) { + return false; + } + $cnt = 0; $this->out->elementStart('ul', 'entities users xoxo'); From 0642b6aa634bc37f86fd54441a3c8358e8da1284 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 20:16:19 +0000 Subject: [PATCH 27/30] Fixed bug in caching of queries --- classes/Memcached_DataObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 3ec7613891..97e1ed736e 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -228,6 +228,6 @@ class Memcached_DataObject extends DB_DataObject } $inst->free(); $c->set($ckey, $cached, MEMCACHE_COMPRESSED, $expiry); - return ArrayWrapper($cached); + return new ArrayWrapper($cached); } } From 82e47b2ec1a2cd9e3e3eeac9b35996935403ab8e Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 20:16:43 +0000 Subject: [PATCH 28/30] Add new section type for groups --- lib/groupsection.php | 103 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 lib/groupsection.php diff --git a/lib/groupsection.php b/lib/groupsection.php new file mode 100644 index 0000000000..bf26ab48ca --- /dev/null +++ b/lib/groupsection.php @@ -0,0 +1,103 @@ +. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @copyright 2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +define('GROUPS_PER_SECTION', 6); + +/** + * Base class for sections + * + * These are the widgets that show interesting data about a person + * group, or site. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class GroupSection extends Section +{ + function showContent() + { + $profiles = $this->getGroups(); + + if (!$profiles) { + return false; + } + + $cnt = 0; + + $this->out->elementStart('ul', 'entities group xoxo'); + + while ($profiles->fetch() && ++$cnt <= GROUPS_PER_SECTION) { + $this->showGroup($profiles); + } + + $this->out->elementEnd('ul'); + + return ($cnt > GROUPS_PER_SECTION); + } + + function getGroups() + { + return null; + } + + function showGroup($group) + { + $this->out->elementStart('li', 'vcard'); + $this->out->elementStart('a', array('title' => ($group->fullname) ? + $group->fullname : + $group->nickname, + 'href' => $group->homeUrl(), + 'rel' => 'contact group', + 'class' => 'url')); + $logo = ($group->stream_logo) ? + $group->stream_logo : User_group::defaultLogo(AVATAR_STREAM_SIZE); + + $this->out->element('img', array('src' => $logo, + 'width' => AVATAR_MINI_SIZE, + 'height' => AVATAR_MINI_SIZE, + 'class' => 'avatar photo', + 'alt' => ($group->fullname) ? + $group->fullname : + $group->nickname)); + $this->out->element('span', 'fn org nickname', $group->nickname); + $this->out->elementEnd('a'); + if ($group->value) { + $this->out->element('span', 'value', $group->value); + } + $this->out->elementEnd('li'); + } +} From 06cc977032f47ab6dcc44a4fd4c4d57e3f8497b3 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 20:17:05 +0000 Subject: [PATCH 29/30] Add a section to show popular groups (by posts) --- lib/groupsbypostssection.php | 78 ++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 lib/groupsbypostssection.php diff --git a/lib/groupsbypostssection.php b/lib/groupsbypostssection.php new file mode 100644 index 0000000000..a5e33a93de --- /dev/null +++ b/lib/groupsbypostssection.php @@ -0,0 +1,78 @@ +. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @copyright 2009 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +if (!defined('LACONICA')) { + exit(1); +} + +/** + * Groups with the most posts section + * + * @category Widget + * @package Laconica + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + */ + +class GroupsByPostsSection extends GroupSection +{ + function getGroups() + { + $qry = 'SELECT user_group.*, count(*) as value ' . + 'FROM user_group JOIN group_inbox '. + 'ON user_group.id = group_inbox.group_id ' . + 'GROUP BY user_group.id ' . + 'ORDER BY value DESC '; + + $limit = GROUPS_PER_SECTION; + $offset = 0; + + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + + $group = Memcached_DataObject::cachedQuery('User_group', + $qry, + 3600); + return $group; + } + + function title() + { + return _('Groups with most posts'); + } + + function divId() + { + return 'top_groups_by_post'; + } +} From 248fd763bde70537e5e9a14db435c27a52849c9a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 22 Jan 2009 20:17:32 +0000 Subject: [PATCH 30/30] Add groups by notice to public --- actions/public.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actions/public.php b/actions/public.php index 8ce7fbee3e..b51a95f244 100644 --- a/actions/public.php +++ b/actions/public.php @@ -204,5 +204,7 @@ class PublicAction extends Action $top->show(); $pop = new PopularNoticeSection($this); $pop->show(); + $gbp = new GroupsByPostsSection($this); + $gbp->show(); } }