From b6b9baa9a240db9f10c41dc0c0aae81dad0d744a Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 13 Apr 2011 00:10:10 -0700 Subject: [PATCH] DirectoryPlugin - Group directory mostly working. HTML markup for results needs fixing. --- plugins/Directory/DirectoryPlugin.php | 2 +- plugins/Directory/actions/groupdirectory.php | 411 +++++++++++++++++++ plugins/Directory/lib/alphanav.php | 13 +- plugins/Directory/lib/sortablegrouplist.php | 322 +++++++++++++++ 4 files changed, 743 insertions(+), 5 deletions(-) create mode 100644 plugins/Directory/actions/groupdirectory.php create mode 100644 plugins/Directory/lib/sortablegrouplist.php diff --git a/plugins/Directory/DirectoryPlugin.php b/plugins/Directory/DirectoryPlugin.php index 107eb1a77c..fbc11fbede 100644 --- a/plugins/Directory/DirectoryPlugin.php +++ b/plugins/Directory/DirectoryPlugin.php @@ -94,6 +94,7 @@ class DirectoryPlugin extends Plugin . '/lib/' . strtolower($cls) . '.php'; return false; case 'SortableSubscriptionList': + case 'SortableGroupList': include_once $dir . '/lib/' . strtolower($cls) . '.php'; return false; @@ -149,7 +150,6 @@ class DirectoryPlugin extends Plugin { if (in_array($path, array('group', 'group/', 'groups', 'groups/'))) { $defaults['action'] = 'groupdirectory'; - $rules = array('filter' => 'all'); return true; } return true; diff --git a/plugins/Directory/actions/groupdirectory.php b/plugins/Directory/actions/groupdirectory.php new file mode 100644 index 0000000000..0966dfac98 --- /dev/null +++ b/plugins/Directory/actions/groupdirectory.php @@ -0,0 +1,411 @@ +. + * + * @category Public + * @package StatusNet + * @author Zach Copley + * @copyright 2011 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) +{ + exit(1); +} + +require_once INSTALLDIR . '/lib/publicgroupnav.php'; + +/** + * Group directory + * + * @category Directory + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class GroupdirectoryAction extends Action +{ + /** + * The page we're on + * + * @var integer + */ + public $page; + + /** + * What to filter the search results by + * + * @var string + */ + public $filter; + + /** + * Column to sort by + * + * @var string + */ + public $sort; + + /** + * How to order search results, ascending or descending + * + * @var string + */ + public $reverse; + + /** + * Query + * + * @var string + */ + public $q; + + /** + * Title of the page + * + * @return string Title of the page + */ + function title() + { + // @fixme: This looks kinda gross + + if ($this->filter == 'all') { + if ($this->page != 1) { + return(sprintf(_m('Group Directory, page %d'), $this->page)); + } + return _m('Group directory'); + } else if ($this->page == 1) { + return sprintf( + _m('Group directory - %s'), + strtoupper($this->filter) + ); + } else { + return sprintf( + _m('Group directory - %s, page %d'), + strtoupper($this->filter), + $this->page + ); + } + } + + /** + * Instructions for use + * + * @return instructions for use + */ + function getInstructions() + { + // TRANS: %%site.name%% is the name of the StatusNet site. + return _m( + 'Search for groups on %%site.name%% by their name, ' + . 'location, or interests. Separate the terms by spaces; ' + . ' they must be 3 characters or more.' + ); + } + + /** + * Is this page read-only? + * + * @return boolean true + */ + function isReadOnly($args) + { + return true; + } + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + */ + function prepare($args) + { + parent::prepare($args); + + $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1; + $this->filter = $this->arg('filter', 'all'); + $this->reverse = $this->boolean('reverse'); + $this->q = $this->trimmed('q'); + $this->sort = $this->arg('sort', 'nickname'); + + common_set_returnto($this->selfUrl()); + + return true; + } + + /** + * Handle request + * + * Shows the page + * + * @param array $args $_REQUEST args; handled in prepare() + * + * @return void + */ + function handle($args) + { + parent::handle($args); + $this->showPage(); + } + + /** + * Show the page notice + * + * Shows instructions for the page + * + * @return void + */ + function showPageNotice() + { + $instr = $this->getInstructions(); + $output = common_markup_to_html($instr); + + $this->elementStart('div', 'instructions'); + $this->raw($output); + $this->elementEnd('div'); + } + + + /** + * Content area + * + * Shows the list of popular notices + * + * @return void + */ + function showContent() + { + $this->showForm(); + + $this->elementStart('div', array('id' => 'group_directory')); + + $alphaNav = new AlphaNav($this, false, false, array('0-9', 'All')); + $alphaNav->show(); + + $group = null; + $group = $this->getGroups(); + $cnt = 0; + + if (!empty($group)) { + $groupList = new SortableGroupList( + $group, + common_current_user(), + $this + ); + + $cnt = $groupList->show(); + $group->free(); + + if (0 == $cnt) { + $this->showEmptyListMessage(); + } + } + + $args = array(); + if (isset($this->q)) { + $args['q'] = $this->q; + } else { + $args['filter'] = $this->filter; + } + + $this->pagination( + $this->page > 1, + $cnt > GROUPS_PER_PAGE, + $this->page, + 'groupdirectory', + $args + ); + + $this->elementEnd('div'); + + } + + function showForm($error=null) + { + $this->elementStart( + 'form', + array( + 'method' => 'get', + 'id' => 'form_search', + 'class' => 'form_settings', + 'action' => common_local_url('groupdirectory') + ) + ); + + $this->elementStart('fieldset'); + + $this->element('legend', null, _m('Search groups')); + $this->elementStart('ul', 'form_data'); + $this->elementStart('li'); + + $this->input('q', _m('Keyword(s)'), $this->q); + + $this->submit('search', _m('BUTTON','Search')); + $this->elementEnd('li'); + $this->elementEnd('ul'); + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + } + + /* + * Get groups filtered by the current filter, sort key, + * sort order, and page + */ + function getGroups() + { + $group = new User_group(); + + $offset = ($this->page-1) * GROUPS_PER_PAGE; + $limit = GROUPS_PER_PAGE + 1; + + if (isset($this->q)) { + + $order = 'user_group.created ASC'; + + if ($this->sort == 'nickname') { + if ($this->reverse) { + $order = 'user_group.nickname DESC'; + } else { + $order = 'user_group.nickname ASC'; + } + } else { + if ($this->reverse) { + $order = 'user_group.created DESC'; + } + } + + $sql = <<< GROUP_QUERY_END +SELECT user_group.* +FROM user_group +JOIN local_group ON user_group.id = local_group.group_id +ORDER BY %s +LIMIT %d, %d +GROUP_QUERY_END; + + $cnt = 0; + $group->query(sprintf($sql, $order, $limit, $offset)); + $group->find(); + + } else { + // User is browsing via AlphaNav + $sort = $this->getSortKey(); + + $sql = <<< GROUP_QUERY_END +SELECT user_group.* +FROM user_group +JOIN local_group ON user_group.id = local_group.group_id +GROUP_QUERY_END; + + switch($this->filter) + { + case 'all': + // NOOP + break; + case '0-9': + $sql .= + ' AND LEFT(user_group.nickname, 1) BETWEEN \'0\' AND \'9\''; + break; + default: + $sql .= sprintf( + ' AND LEFT(LOWER(user_group.nickname), 1) = \'%s\'', + $this->filter + ); + } + + $sql .= sprintf( + ' ORDER BY user_group.%s %s, user_group.nickname ASC LIMIT %d, %d', + $sort, + $this->reverse ? 'DESC' : 'ASC', + $offset, + $limit + ); + + $group->query($sql); + } + + return $group; + } + + /** + * Filter the sort parameter + * + * @return string a column name for sorting + */ + function getSortKey() + { + switch ($this->sort) { + case 'nickname': + return $this->sort; + break; + case 'created': + return $this->sort; + break; + default: + return 'nickname'; + } + } + + /** + * Show a nice message when there's no search results + */ + function showEmptyListMessage() + { + if (!empty($this->filter) && ($this->filter != 'all')) { + $this->element( + 'p', + 'error', + sprintf( + _m('No groups starting with %s'), + $this->filter + ) + ); + } else { + $this->element('p', 'error', _m('No results.')); + $message = _m(<<elementStart('div', 'help instructions'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + } + + // XXX This needs some adjustment + +/* + function showSections() + { + $gbp = new GroupsByPostsSection($this); + $gbp->show(); + $gbm = new GroupsByMembersSection($this); + $gbm->show(); + } +*/ + +} diff --git a/plugins/Directory/lib/alphanav.php b/plugins/Directory/lib/alphanav.php index 87e2f18f18..60a15ee942 100644 --- a/plugins/Directory/lib/alphanav.php +++ b/plugins/Directory/lib/alphanav.php @@ -108,10 +108,15 @@ class AlphaNav extends Widget $classes .= 'last '; // last filter in the list } - $href = common_local_url( - $actionName, - array('filter' => strtolower($filter)) - ); + // hack to get around $m->connect(array('action' => 'all, 'nickname' => $nickname)); + if (strtolower($filter) == 'all') { + $href = common_local_url($actionName); + } else { + $href = common_local_url( + $actionName, + array('filter' => strtolower($filter)) + ); + } $params = array('href' => $href); diff --git a/plugins/Directory/lib/sortablegrouplist.php b/plugins/Directory/lib/sortablegrouplist.php new file mode 100644 index 0000000000..4f875ce2c3 --- /dev/null +++ b/plugins/Directory/lib/sortablegrouplist.php @@ -0,0 +1,322 @@ +. + * + * @category Public + * @package StatusNet + * @author Zach Copley + * @copyright 2011 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/subscriptionlist.php'; + +/** + * Widget to show a sortable list of subscriptions + * + * @category Public + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class SortableGroupList extends SortableSubscriptionList +{ + /** Owner of this list */ + var $owner = null; + + function __construct($profile, $owner=null, $action=null) + { + parent::__construct($profile, $owner, $action); + + $this->owner = $owner; + } + + function startList() + { + $this->out->elementStart('table', array('class' => 'profile_list xoxo', 'border' => '1')); + $this->out->elementStart('thead'); + $this->out->elementStart('tr'); + + $tableHeaders = array( + 'nickname' => _m('Nickname'), + 'created' => _m('Created') + ); + + foreach ($tableHeaders as $id => $label) { + + $attrs = array('id' => $id); + $current = (!empty($this->action->sort) && $this->action->sort == $id); + + if ($current || empty($this->action->sort) && $id == 'nickname') { + $attrs['class'] = 'current'; + } + + if ($current && $this->action->reverse) { + $attrs['class'] .= ' reverse'; + $attrs['class'] = trim($attrs['class']); + } + + $this->out->elementStart('th', $attrs); + + $linkAttrs = array(); + $params = array('sort' => $id); + + if (!empty($this->action->q)) { + $params['q'] = $this->action->q; + } + + if ($current && !$this->action->reverse) { + $params['reverse'] = 'true'; + } + + $args = array(); + + $filter = $this->action->arg('filter'); + + if (!empty($filter)) { + $args['filter'] = $filter; + } + + $linkAttrs['href'] = common_local_url( + $this->action->arg('action'), $args, $params + ); + + $this->out->element('a', $linkAttrs, $label); + $this->out->elementEnd('th'); + } + + $this->out->element('th', array('id' => 'Members'), _m('Members')); + $this->out->element('th', array('id' => 'Admins'), _m('Admins')); + $this->out->element('th', array('id' => 'controls'), null); + + $this->out->elementEnd('tr'); + $this->out->elementEnd('thead'); + + $this->out->elementStart('tbody'); + } + + function showProfiles() + { + $cnt = 0; + + while ($this->profile->fetch()) { + $cnt++; + if($cnt > PROFILES_PER_PAGE) { + break; + } + + $odd = ($cnt % 2 == 0); // for zebra striping + + $pli = $this->newListItem($this->profile, $odd); + $pli->show(); + } + + return $cnt; + } + + function newListItem($profile, $odd) + { + return new SortableGroupListItem($profile, $this->owner, $this->action, $odd); + } +} + +class SortableGroupListItem extends SortableSubscriptionListItem +{ + /** Owner of this list */ + var $owner = null; + + function __construct($profile, $owner, $action, $alt) + { + parent::__construct($profile, $owner, $action, $alt); + + $this->alt = $alt; // is this row alternate? + $this->owner = $owner; + } + + function startItem() + { + $attr = array( + 'class' => 'profile', + 'id' => 'profile-' . $this->profile->id + ); + + if ($this->alt) { + $attr['class'] .= ' alt'; + } + + $this->out->elementStart('tr', $attr); + } + + function showHomepage() + { + if (!empty($this->profile->homepage)) { + $this->out->text(' '); + $aAttrs = $this->homepageAttributes(); + $this->out->elementStart('a', $aAttrs); + $this->out->raw($this->highlight($this->profile->homeUrl())); + $this->out->elementEnd('a'); + } + } + + function showAvatar() + { + $avatar = $this->profile->stream_logo; + $aAttrs = $this->linkAttributes(); + $this->out->elementStart('a', $aAttrs); + $this->out->element('img', array('src' => ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_STREAM_SIZE), + 'class' => 'photo avatar', + 'width' => AVATAR_STREAM_SIZE, + 'height' => AVATAR_STREAM_SIZE, + 'alt' => + ($this->profile->fullname) ? $this->profile->fullname : + $this->profile->nickname)); + $this->out->text(' '); + $hasFN = (!empty($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'); + } + + + function endItem() + { + $this->out->elementEnd('tr'); + } + + function startProfile() + { + $this->out->elementStart('td', 'entity_profile vcard entry-content'); + } + + function endProfile() + { + $this->out->elementEnd('td'); + } + + function startActions() + { + $this->out->elementStart('td', 'entity_actions'); + $this->out->elementStart('ul'); + } + + function endActions() + { + $this->out->elementEnd('ul'); + $this->out->elementEnd('td'); + } + + function show() + { + if (Event::handle('StartProfileListItem', array($this))) { + $this->startItem(); + if (Event::handle('StartProfileListItemProfile', array($this))) { + $this->showProfile(); + Event::handle('EndProfileListItemProfile', array($this)); + } + + // XXX Add events? + $this->showCreatedDate(); + $this->showMemberCount(); + $this->showAdmins(); + + if (Event::handle('StartProfileListItemActions', array($this))) { + $this->showActions(); + Event::handle('EndProfileListItemActions', array($this)); + } + $this->endItem(); + Event::handle('EndProfileListItem', array($this)); + } + } + + function showActions() + { + $this->startActions(); + if (Event::handle('StartProfileListItemActionElements', array($this))) { + $this->showJoinButton(); + Event::handle('EndProfileListItemActionElements', array($this)); + } + $this->endActions(); + } + + function showJoinButton() + { + $this->out->elementStart('td', 'entry_controls'); + + $user = $this->owner; + if ($user) { + + $this->out->elementStart('li', 'entity_subscribe'); + // XXX: special-case for user looking at own + // subscriptions page + if ($user->isMember($this->profile)) { + $lf = new LeaveForm($this->out, $this->profile); + $lf->show(); + } else if (!Group_block::isBlocked($this->profile, $user->getProfile())) { + $jf = new JoinForm($this->out, $this->profile); + $jf->show(); + } + } + $this->out->elementEnd('td'); + + } + + function showMemberCount() + { + $this->out->elementStart('td', 'entry_subscriber_count'); + $this->out->raw($this->profile->getMemberCount()); + $this->out->elementEnd('td'); + } + + function showCreatedDate() + { + $this->out->elementStart('td', 'entry_created'); + $this->out->raw(date('j M Y', strtotime($this->profile->created))); + $this->out->elementEnd('td'); + } + + function showAdmins() + { + $this->out->elementStart('td', 'entry_notice_count'); + // @todo + $this->out->raw('gargargar'); + $this->out->elementEnd('td'); + } + + /** + * Only show the tags if we're logged in + */ + function showTags() + { + if (common_logged_in()) { + parent::showTags(); + } + + } + +} -- 2.39.5