function set_sort_mode($mode)
{
- if ('chron' === $mode)
- return $this->target->orderBy('created desc');
+ switch ($mode) {
+ case 'chron':
+ return $this->target->orderBy('created DESC');
+ break;
+ case 'reverse_chron':
+ return $this->target->orderBy('created ASC');
+ break;
+ case 'nickname_desc':
+ if ($this->table != 'profile') {
+ throw new Exception(
+ 'nickname_desc sort mode can only be use when searching profile.'
+ );
+ } else {
+ return $this->target->orderBy('nickname DESC');
+ }
+ break;
+ case 'nickname_asc':
+ if ($this->table != 'profile') {
+ throw new Exception(
+ 'nickname_desc sort mode can only be use when searching profile.'
+ );
+ } else {
+ return $this->target->orderBy('nickname ASC');
+ }
+ break;
+ default:
+ return $this->target->orderBy('created DESC');
+ break;
+ }
}
}
--- /dev/null
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * Adds a user directory
+ *
+ * PHP version 5
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Zach Copely <zach@status.net>
+ * @copyright 2011 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);
+}
+
+/**
+ * Directory plugin main class
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+class DirectoryPlugin extends Plugin
+{
+
+ private $dir = null;
+
+ /**
+ * Initializer for this plugin
+ *
+ * @return boolean hook value; true means continue processing,
+ * false means stop.
+ */
+ function initialize()
+ {
+ return true;
+ }
+
+ /**
+ * Cleanup for this plugin.
+ *
+ * @return boolean hook value; true means continue processing,
+ * false means stop.
+ */
+ function cleanup()
+ {
+ 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)
+ {
+ // common_debug("class = $cls");
+
+ $dir = dirname(__FILE__);
+
+ switch ($cls)
+ {
+ case 'UserdirectoryAction':
+ include_once $dir
+ . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
+ return false;
+ case 'AlphaNav':
+ include_once $dir
+ . '/lib/' . strtolower($cls) . '.php';
+ return false;
+ case 'SortableSubscriptionList':
+ include_once $dir
+ . '/lib/' . 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(
+ 'directory/users',
+ array('action' => 'userdirectory'),
+ array('filter' => 'all')
+ );
+
+ $m->connect(
+ 'directory/users/:filter',
+ array('action' => 'userdirectory'),
+ array('filter' => '[0-9a-zA-Z_]{1,64}')
+ );
+
+ return true;
+ }
+
+ /**
+ * Link in a styelsheet for the onboarding actions
+ *
+ * @param Action $action Action being shown
+ *
+ * @return boolean hook flag
+ */
+ function onEndShowStatusNetStyles($action)
+ {
+ if (in_array(
+ $action->trimmed('action'),
+ array('userdirectory'))
+ ) {
+ $action->cssLink($this->path('css/directory.css'));
+ }
+
+ return true;
+ }
+
+ /**
+ * Modify the public local nav to add a link to the user directory
+ *
+ * @param Action $action The current action handler. Use this to
+ * do any output.
+ *
+ * @return boolean hook value; true means continue processing,
+ * false means stop.
+ *
+ * @see Action
+ */
+ function onEndPublicGroupNav($nav)
+ {
+ // XXX: Maybe this should go under search instead?
+
+ $actionName = $nav->action->trimmed('action');
+
+ $nav->out->menuItem(
+ common_local_url('userdirectory'),
+ _('Directory'),
+ _('User Directory'),
+ $actionName == 'userdirectory',
+ 'nav_directory'
+ );
+
+ return true;
+ }
+
+ /*
+ * Version info
+ */
+ function onPluginVersion(&$versions)
+ {
+ $versions[] = array(
+ 'name' => 'Directory',
+ 'version' => STATUSNET_VERSION,
+ 'author' => 'Zach Copley',
+ 'homepage' => 'http://status.net/wiki/Plugin:Directory',
+ 'rawdescription' => _m('Add a user directory.')
+ );
+
+ return true;
+ }
+}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Output a user directory
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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 <http://www.gnu.org/licenses/>.
+ *
+ * @category Public
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @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';
+
+/**
+ * User directory
+ *
+ * @category Personal
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+class UserdirectoryAction 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('User Directory, page %d'), $this->page));
+ }
+ return _m('User directory');
+ } else if ($this->page == 1) {
+ return sprintf(
+ _m('User directory - %s'),
+ strtoupper($this->filter)
+ );
+ } else {
+ return sprintf(
+ _m('User 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 _(
+ 'Search for people 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');
+ }
+
+ /**
+ * Local navigation
+ *
+ * This page is part of the public group, so show that.
+ *
+ * @return void
+ */
+ function showLocalNav()
+ {
+ $nav = new PublicGroupNav($this);
+ $nav->show();
+ }
+
+ /**
+ * Content area
+ *
+ * Shows the list of popular notices
+ *
+ * @return void
+ */
+ function showContent()
+ {
+ $this->showForm();
+
+ $this->elementStart('div', array('id' => 'user_directory'));
+
+ $alphaNav = new AlphaNav($this, true, array('All'));
+ $alphaNav->show();
+
+ $profile = null;
+ $profile = $this->getUsers();
+ $cnt = 0;
+
+ if (!empty($profile)) {
+ $profileList = new SortableSubscriptionList(
+ $profile,
+ common_current_user(),
+ $this
+ );
+
+ $cnt = $profileList->show();
+ $profile->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 > PROFILES_PER_PAGE,
+ $this->page,
+ 'userdirectory',
+ $args
+ );
+
+ $this->elementEnd('div');
+
+ }
+
+ function showForm($error=null)
+ {
+ $this->elementStart(
+ 'form',
+ array(
+ 'method' => 'get',
+ 'id' => 'form_search',
+ 'class' => 'form_settings',
+ 'action' => common_local_url('userdirectory')
+ )
+ );
+
+ $this->elementStart('fieldset');
+
+ $this->element('legend', null, _('Search site'));
+ $this->elementStart('ul', 'form_data');
+ $this->elementStart('li');
+
+ $this->input('q', _('Keyword(s)'), $this->q);
+
+ $this->submit('search', _m('BUTTON','Search'));
+ $this->elementEnd('li');
+ $this->elementEnd('ul');
+ $this->elementEnd('fieldset');
+ $this->elementEnd('form');
+ }
+
+ /*
+ * Get users filtered by the current filter, sort key,
+ * sort order, and page
+ */
+ function getUsers()
+ {
+ $profile = new Profile();
+
+ $offset = ($this->page - 1) * PROFILES_PER_PAGE;
+ $limit = PROFILES_PER_PAGE + 1;
+
+ if (isset($this->q)) {
+ // User is searching via query
+ $search_engine = $profile->getSearchEngine('profile');
+
+ $mode = 'reverse_chron';
+
+ if ($this->sort == 'nickname') {
+ if ($this->reverse) {
+ $mode = 'nickname_desc';
+ } else {
+ $mode = 'nickname_asc';
+ }
+ } else {
+ if ($this->reverse) {
+ $mode = 'chron';
+ }
+ }
+
+ $search_engine->set_sort_mode($mode);
+ $search_engine->limit($offset, $limit);
+ $search_engine->query($this->q);
+
+ $profile->find();
+ } else {
+ // User is browsing via AlphaNav
+ $sort = $this->getSortKey();
+ $sql = 'SELECT profile.* FROM profile, user WHERE profile.id = user.id';
+
+ if ($this->filter != 'all') {
+ $sql .= sprintf(
+ ' AND LEFT(LOWER(profile.nickname), 1) = \'%s\'',
+ $this->filter
+ );
+ }
+
+ $sql .= sprintf(
+ ' ORDER BY profile.%s %s, profile.nickname ASC LIMIT %d, %d',
+ $sort,
+ $this->reverse ? 'DESC' : 'ASC',
+ $offset,
+ $limit
+ );
+
+ $profile->query($sql);
+ }
+
+ return $profile;
+ }
+
+ /**
+ * 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 users starting with %s'),
+ $this->filter
+ )
+ );
+ } else {
+ $this->element('p', 'error', _('No results.'));
+ $message = _m(<<<E_O_T
+* Make sure all words are spelled correctly.
+* Try different keywords.
+* Try more general keywords.
+* Try fewer keywords.
+E_O_T
+);
+ $this->elementStart('div', 'help instructions');
+ $this->raw(common_markup_to_html($message));
+ $this->elementEnd('div');
+ }
+ }
+
+}
--- /dev/null
+/* CSS file for the Directory plugin */
+
+div#user_directory div.alpha_nav {
+ overflow: hidden;
+ width: 100%;
+ text-align: center;
+}
+
+/* XXX: this needs serious CSS foo */
+div#user_directory div.alpha_nav > a {
+ border-left: 1px solid #000;
+ padding-left: 2px;
+}
+div#user_directory div.alpha_nav > a.first {
+ border-left: none;
+}
+
+div#user_directory div.alpha_nav a:link {
+ text-decoration: none;
+}
+
+div#user_directory div.alpha_nav a:visited {
+ text-decoration: none;
+}
+div#user_directory div.alpha_nav a:active {
+ text-decoration: none;
+}
+div#user_directory div.alpha_nav a:hover {
+ text-decoration: underline; color: blue;
+}
+
+div#user_directory div.alpha_nav a.current {
+ background-color:#9BB43E;
+}
+
+table.profile_list {
+ width: 100%;
+}
+
+table.profile_list tr {
+ float: none;
+}
+
+table.profile_list tr.alt {
+ background-color: #def; /* zebra stripe */
+}
+
+table.profie_list td {
+ width: 100%;
+ padding: 0;
+}
+
+
+th.current {
+ background-image: url(../images/control_arrow_down.gif);
+ background-repeat: no-repeat;
+ background-position: 60% 2px;
+}
+
+th.current.reverse {
+ background-image: url(../images/control_arrow_up.gif);
+ background-repeat: no-repeat;
+ background-position: 60% 2px;
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Widget to display an alphabet menu
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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 <http://www.gnu.org/licenses/>.
+ *
+ * @category Widget
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @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);
+}
+
+/**
+ * Outputs a fancy alphabet letter navigation menu
+ *
+ * @category Widget
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ *
+ * @see HTMLOutputter
+ */
+
+class AlphaNav extends Widget
+{
+ protected $action = null;
+ protected $filters = array();
+
+ /**
+ * Prepare the widget for use
+ *
+ * @param Action $action the current action
+ * @param boolean $numbers whether to output 0..9
+ * @param Array $prepend array of filters to prepend
+ * @param Array $append array of filters to append
+ */
+ function __construct(
+ $action = null,
+ $numbers = false,
+ $prepend = false,
+ $append = false
+ )
+ {
+ parent::__construct($action);
+
+ $this->action = $action;
+
+ if ($prepend) {
+ $this->filters = array_merge($prepend, $this->filters);
+ }
+
+ if ($numbers) {
+ $this->filters = array_merge($this->filters, range(0, 9));
+ }
+
+ if ($append) {
+ $this->filters = array_merge($this->filters, $append);
+ }
+
+ $this->filters = array_merge($this->filters, range('A', 'Z'));
+ }
+
+ /**
+ * Show the widget
+ *
+ * Emit the HTML for the widget, using the configured outputter.
+ *
+ * @return void
+ */
+
+ function show()
+ {
+ $actionName = $this->action->trimmed('action');
+
+ $this->action->elementStart('div', array('class' => 'alpha_nav'));
+
+ for ($i = 0, $size = sizeof($this->filters); $i < $size; $i++) {
+
+ $filter = $this->filters[$i];
+ $classes = '';
+
+ // Add some classes for styling
+ if ($i == 0) {
+ $classes .= 'first '; // first filter in the list
+ } elseif ($i == $size - 1) {
+ $classes .= 'last '; // last filter in the list
+ }
+
+ $href = common_local_url(
+ $actionName,
+ array('filter' => strtolower($filter))
+ );
+
+ $params = array('href' => $href);
+
+ // sort column
+ if (!empty($this->action->sort)) {
+ $params['sort'] = $this->action->sort;
+ }
+
+ // sort order
+ if ($this->action->reverse) {
+ $params['reverse'] = 'true';
+ }
+
+ $current = $this->action->arg('filter');
+
+ // Highlight the selected filter. If there is no selected
+ // filter, highlight the first filter in the list
+ if (!isset($current) && $i == 0
+ || $current === strtolower($filter)) {
+ $classes .= 'current ';
+ }
+
+ if (!empty($classes)) {
+ $params['class'] = trim($classes);
+ }
+
+ $this->action->element('a', $params, $filter);
+ }
+
+ $this->action->elementEnd('div');
+ }
+
+}
--- /dev/null
+<?php
+
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Widget to show a sortable list of profiles
+ *
+ * PHP version 5
+ *
+ * LICENCE: 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 <http://www.gnu.org/licenses/>.
+ *
+ * @category Public
+ * @package StatusNet
+ * @author Zach Copley <zach@status.net>
+ * @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 <zach@status.net>
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class SortableSubscriptionList extends SubscriptionList
+{
+ /** 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'));
+ $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' => 'subscriptions'), 'Subscriptions');
+ $this->out->element('th', array('id' => 'notices'), 'Notices');
+ $this->out->element('th', array('id' => 'controls'), null);
+
+ $this->out->elementEnd('tr');
+ $this->out->elementEnd('thead');
+
+ $this->out->elementStart('tbody');
+ }
+
+ function endList()
+ {
+ $this->out->elementEnd('tbody');
+ $this->out->elementEnd('table');
+ }
+
+ 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 SortableSubscriptionListItem($profile, $this->owner, $this->action, $odd);
+ }
+}
+
+class SortableSubscriptionListItem extends SubscriptionListItem
+{
+ /** Owner of this list */
+ var $owner = null;
+
+ function __construct($profile, $owner, $action, $alt)
+ {
+ parent::__construct($profile, $owner, $action);
+
+ $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 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->showSubscriberCount();
+ $this->showNoticeCount();
+
+ if (Event::handle('StartProfileListItemActions', array($this))) {
+ $this->showActions();
+ Event::handle('EndProfileListItemActions', array($this));
+ }
+ $this->endItem();
+ Event::handle('EndProfileListItem', array($this));
+ }
+ }
+
+ function showSubscriberCount()
+ {
+ $this->out->elementStart('td', 'entry_subscriber_count');
+ $this->out->raw($this->profile->subscriberCount());
+ $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 showNoticeCount()
+ {
+ $this->out->elementStart('td', 'entry_notice_count');
+ $this->out->raw($this->profile->noticeCount());
+ $this->out->elementEnd('td');
+ }
+
+ /**
+ * Only show the tags if we're logged in
+ */
+ function showTags()
+ {
+ if (common_logged_in()) {
+ parent::showTags();
+ }
+
+ }
+
+}