From: Zach Copley Date: Tue, 8 Mar 2011 05:35:48 +0000 (-0800) Subject: Merge branch '1.0.x' of gitorious.org:statusnet/mainline into 1.0.x X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=cfe3e832932ace9c5ad028899a7765b206eaab81;hp=3438a78c023d84c344200b5f2794c37ead83e539;p=quix0rs-gnu-social.git Merge branch '1.0.x' of gitorious.org:statusnet/mainline into 1.0.x * '1.0.x' of gitorious.org:statusnet/mainline: Initial checkin of Poll plugin: micro-app to post mini polls/surveys from the notice form. Localisation updates from http://translatewiki.net. More doc comments on MicroApp stuff; some of the show-notice code & the ActivityStreams stuff is a bit wonky and may need smoothing out Doc comments for MicroAppPlugin mailboxes were wrongly overriding global menu --- diff --git a/lib/search_engines.php b/lib/search_engines.php index 19703e03fd..7f1684a3e7 100644 --- a/lib/search_engines.php +++ b/lib/search_engines.php @@ -41,8 +41,35 @@ class SearchEngine 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; + } } } diff --git a/plugins/Directory/DirectoryPlugin.php b/plugins/Directory/DirectoryPlugin.php new file mode 100644 index 0000000000..541ec556bf --- /dev/null +++ b/plugins/Directory/DirectoryPlugin.php @@ -0,0 +1,192 @@ +. + * + * @category Plugin + * @package StatusNet + * @author Zach Copely + * @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 + * @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}|0-9)') + ); + + 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; + } +} diff --git a/plugins/Directory/actions/userdirectory.php b/plugins/Directory/actions/userdirectory.php new file mode 100644 index 0000000000..24d91ad772 --- /dev/null +++ b/plugins/Directory/actions/userdirectory.php @@ -0,0 +1,401 @@ +. + * + * @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'; + +/** + * User directory + * + * @category Personal + * @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 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, false, false, array('0-9', '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'; + + switch($this->filter) + { + case 'all': + // NOOP + break; + case '0-9': + $sql .= + ' AND LEFT(profile.nickname, 1) BETWEEN \'0\' AND \'9\''; + break; + default: + $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(<<elementStart('div', 'help instructions'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + } + +} diff --git a/plugins/Directory/css/directory.css b/plugins/Directory/css/directory.css new file mode 100644 index 0000000000..14fd2ce23b --- /dev/null +++ b/plugins/Directory/css/directory.css @@ -0,0 +1,64 @@ +/* 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 diff --git a/plugins/Directory/images/control_arrow_down.gif b/plugins/Directory/images/control_arrow_down.gif new file mode 100644 index 0000000000..de28df6eb0 Binary files /dev/null and b/plugins/Directory/images/control_arrow_down.gif differ diff --git a/plugins/Directory/images/control_arrow_up.gif b/plugins/Directory/images/control_arrow_up.gif new file mode 100644 index 0000000000..898aa604bb Binary files /dev/null and b/plugins/Directory/images/control_arrow_up.gif differ diff --git a/plugins/Directory/lib/alphanav.php b/plugins/Directory/lib/alphanav.php new file mode 100644 index 0000000000..dadb589094 --- /dev/null +++ b/plugins/Directory/lib/alphanav.php @@ -0,0 +1,147 @@ +. + * + * @category Widget + * @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); +} + +/** + * Outputs a fancy alphabet letter navigation menu + * + * @category Widget + * @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/ + * + * @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)); + } + + $this->filters = array_merge($this->filters, range('A', 'Z')); + + if ($append) { + $this->filters = array_merge($this->filters, $append); + } + } + + /** + * 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'); + } + +} diff --git a/plugins/Directory/lib/sortablesubscriptionlist.php b/plugins/Directory/lib/sortablesubscriptionlist.php new file mode 100644 index 0000000000..8f6e66d20a --- /dev/null +++ b/plugins/Directory/lib/sortablesubscriptionlist.php @@ -0,0 +1,263 @@ +. + * + * @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 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(); + } + + } + +} diff --git a/plugins/SphinxSearch/sphinxsearch.php b/plugins/SphinxSearch/sphinxsearch.php index 1ce9bfd72d..46b5e3e28a 100644 --- a/plugins/SphinxSearch/sphinxsearch.php +++ b/plugins/SphinxSearch/sphinxsearch.php @@ -73,9 +73,39 @@ class SphinxSearch extends SearchEngine function set_sort_mode($mode) { - if ('chron' === $mode) { + switch ($mode) { + case 'chron': $this->sphinx->SetSortMode(SPH_SORT_ATTR_DESC, 'created_ts'); return $this->target->orderBy('id desc'); + break; + case 'reverse_chron': + $this->sphinx->SetSortMode(SPH_SORT_ATTR_ASC, 'created_ts'); + return $this->target->orderBy('id asc'); + break; + case 'nickname_desc': + if ($this->table != 'profile') { + throw new Exception( + 'nickname_desc sort mode can only be use when searching profile.' + ); + } else { + $this->sphinx->SetSortMode(SPH_SORT_ATTR_DESC, 'nickname'); + return $this->target->orderBy('id desc'); + } + break; + case 'nickname_asc': + if ($this->table != 'profile') { + throw new Exception( + 'nickname_desc sort mode can only be use when searching profile.' + ); + } else { + $this->sphinx->SetSortMode(SPH_SORT_ATTR_ASC, 'nickname'); + return $this->target->orderBy('id asc'); + } + break; + default: + $this->sphinx->SetSortMode(SPH_SORT_ATTR_DESC, 'created_ts'); + return $this->target->orderBy('id desc'); + break; } }