]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Directory/actions/userdirectory.php
XSS vulnerability when remote-subscribing
[quix0rs-gnu-social.git] / plugins / Directory / actions / userdirectory.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * Output a user directory
6  *
7  * PHP version 5
8  *
9  * LICENCE: This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Affero General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Affero General Public License for more details.
18  *
19  * You should have received a copy of the GNU Affero General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * @category  Public
23  * @package   StatusNet
24  * @author    Zach Copley <zach@status.net>
25  * @copyright 2011 StatusNet, Inc.
26  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
27  * @link      http://status.net/
28  */
29
30 if (!defined('GNUSOCIAL')) { exit(1); }
31
32 /**
33  * User directory
34  *
35  * @category Personal
36  * @package  StatusNet
37  * @author   Zach Copley <zach@status.net>
38  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
39  * @link     http://status.net/
40  */
41 class UserdirectoryAction extends ManagedAction
42 {
43     /**
44      * The page we're on
45      *
46      * @var integer
47      */
48     public $page;
49
50     /**
51      * What to filter the search results by
52      *
53      * @var string
54      */
55     public $filter;
56
57     /**
58      * Column to sort by
59      *
60      * @var string
61      */
62     public $sort;
63
64     /**
65      * How to order search results, ascending or descending
66      *
67      * @var string
68      */
69     public $reverse;
70
71     /**
72      * Query
73      *
74      * @var string
75      */
76     public $q;
77
78     /**
79      * Title of the page
80      *
81      * @return string Title of the page
82      */
83     function title()
84     {
85         // @todo fixme: This looks kinda gross
86
87         if ($this->filter == 'all') {
88             if ($this->page != 1) {
89                 // TRANS: Page title for user directory. %d is a page number.
90                 return(sprintf(_m('User Directory, page %d'), $this->page));
91             }
92             // TRANS: Page title for user directory.
93             return _m('User directory');
94         } else if ($this->page == 1) {
95             return sprintf(
96                 // TRANS: Page title for user directory. %s is the applied filter.
97                 _m('User directory - %s'),
98                 strtoupper($this->filter)
99             );
100         } else {
101             return sprintf(
102                 // TRANS: Page title for user directory.
103                 // TRANS: %1$s is the applied filter, %2$d is a page number.
104                 _m('User directory - %1$s, page %2$d'),
105                 strtoupper($this->filter),
106                 $this->page
107             );
108         }
109     }
110
111     /**
112      * Instructions for use
113      *
114      * @return instructions for use
115      */
116     function getInstructions()
117     {
118         // TRANS: %%site.name%% is the name of the StatusNet site.
119         return _m('Search for people on %%site.name%% by their name, '
120             . 'location, or interests. Separate the terms by spaces; '
121             . ' they must be 3 characters or more.'
122         );
123     }
124
125     /**
126      * Is this page read-only?
127      *
128      * @return boolean true
129      */
130     function isReadOnly($args)
131     {
132         return true;
133     }
134
135     protected function doPreparation()
136     {
137         $this->page    = ($this->arg('page')) ? ($this->arg('page') + 0) : 1;
138         $this->filter  = $this->arg('filter', 'all');
139         $this->reverse = $this->boolean('reverse');
140         $this->q       = $this->trimmed('q');
141         $this->sort    = $this->arg('sort', 'nickname');
142
143         common_set_returnto($this->selfUrl());
144     }
145
146     /**
147      * Show the page notice
148      *
149      * Shows instructions for the page
150      *
151      * @return void
152      */
153     function showPageNotice()
154     {
155         $instr  = $this->getInstructions();
156         $output = common_markup_to_html($instr);
157
158         $this->elementStart('div', 'instructions');
159         $this->raw($output);
160         $this->elementEnd('div');
161     }
162
163
164     /**
165      * Content area
166      *
167      * Shows the list of popular notices
168      *
169      * @return void
170      */
171     function showContent()
172     {
173         $this->showForm();
174
175         $this->elementStart('div', array('id' => 'profile_directory'));
176
177         $alphaNav = new AlphaNav($this, false, false, array('0-9', 'All'));
178         $alphaNav->show();
179
180         $profile = null;
181         $profile = $this->getUsers();
182         $cnt     = 0;
183
184         if (!empty($profile)) {
185             $profileList = new SortableSubscriptionList(
186                 $profile,
187                 common_current_user(),
188                 $this
189             );
190
191             $cnt = $profileList->show();
192             $profile->free();
193
194             if (0 == $cnt) {
195                 $this->showEmptyListMessage();
196             }
197         }
198
199         $args = array();
200         if (isset($this->q)) {
201             $args['q'] = $this->q;
202         } elseif (isset($this->filter) && $this->filter != 'all') {
203             $args['filter'] = $this->filter;
204         }
205         
206         if (isset($this->sort)) {
207             $args['sort'] = $this->sort;
208         }        
209         if (!empty($this->reverse)) {
210             $args['reverse'] = $this->reverse;
211         }
212
213         $this->pagination(
214             $this->page > 1,
215             $cnt > PROFILES_PER_PAGE,
216             $this->page,
217             'userdirectory',
218             $args
219         );
220
221         $this->elementEnd('div');
222
223     }
224
225     function showForm($error=null)
226     {
227         $this->elementStart(
228             'form',
229             array(
230                 'method' => 'get',
231                 'id'     => 'form_search',
232                 'class'  => 'form_settings',
233                 'action' => common_local_url('userdirectory')
234             )
235         );
236
237         $this->elementStart('fieldset');
238
239         // TRANS: Fieldset legend.
240         $this->element('legend', null, _m('Search site'));
241         $this->elementStart('ul', 'form_data');
242         $this->elementStart('li');
243
244         // TRANS: Field label for user directory filter.
245         $this->input('q', _m('Keyword(s)'), $this->q);
246
247         // TRANS: Button text.
248         $this->submit('search', _m('BUTTON','Search'));
249         $this->elementEnd('li');
250         $this->elementEnd('ul');
251         $this->elementEnd('fieldset');
252         $this->elementEnd('form');
253     }
254
255     /*
256      * Get users filtered by the current filter, sort key,
257      * sort order, and page
258      */
259     function getUsers()
260     {
261         $profile = new Profile();
262
263         // Comment this out or disable to get global profile searches
264         $profile->joinAdd(array('id', 'user:id'));
265
266         $offset = ($this->page - 1) * PROFILES_PER_PAGE;
267         $limit  = PROFILES_PER_PAGE + 1;
268
269         if (!empty($this->q)) {
270              // User is searching via query
271              $search_engine = $profile->getSearchEngine('profile');
272
273              $mode = 'reverse_chron';
274
275              if ($this->sort == 'nickname') {
276                  if ($this->reverse) {
277                      $mode = 'nickname_desc';
278                  } else {
279                      $mode = 'nickname_asc';
280                  }
281              } else {
282                  if ($this->reverse) {
283                      $mode = 'chron';
284                  }
285              }
286
287              $search_engine->set_sort_mode($mode);
288              $search_engine->limit($offset, $limit);
289              $search_engine->query($this->q);
290
291              $profile->find();
292         } else {
293             // User is browsing via AlphaNav
294
295             switch ($this->filter) {
296             case 'all':
297                 // NOOP
298                 break;
299             case '0-9':
300                 $profile->whereAdd(sprintf('LEFT(%1$s.%2$s, 1) BETWEEN %3$s AND %4$s',
301                                             $profile->escapedTableName(),
302                                             'nickname',
303                                             $profile->_quote("0"),
304                                             $profile->_quote("9")));
305                 break;
306             default:
307                 $profile->whereAdd(sprintf('LEFT(LOWER(%1$s.%2$s), 1) = %3$s',
308                                             $profile->escapedTableName(),
309                                             'nickname',
310                                             $profile->_quote($this->filter)));
311             }
312
313             $order = sprintf('%1$s.%2$s %3$s, %1$s.%4$s ASC',
314                             $profile->escapedTableName(),
315                             $this->getSortKey('nickname'),
316                             $this->reverse ? 'DESC' : 'ASC',
317                             'nickname');
318             $profile->orderBy($order);
319             $profile->limit($offset, $limit);
320
321             $profile->find();
322         }
323
324         return $profile;
325     }
326
327     /**
328      * Filter the sort parameter
329      *
330      * @return string   a column name for sorting
331      */
332     function getSortKey($def='nickname')
333     {
334         switch ($this->sort) {
335         case 'nickname':
336         case 'created':
337             return $this->sort;
338         default:
339             return 'nickname';
340         }
341     }
342
343     /**
344      * Show a nice message when there's no search results
345      */
346     function showEmptyListMessage()
347     {
348         if (!empty($this->filter) && ($this->filter != 'all')) {
349             $this->element(
350                 'p',
351                 'error',
352                 sprintf(
353                     // TRANS: Empty list message for user directory.
354                     _m('No users starting with %s'),
355                     $this->filter
356                 )
357             );
358         } else {
359             // TRANS: Empty list message for user directory.
360             $this->element('p', 'error', _m('No results.'));
361             // TRANS: Standard search suggestions shown when a search does not give any results.
362             $message = _m("* Make sure all words are spelled correctly.
363 * Try different keywords.
364 * Try more general keywords.
365 * Try fewer keywords.");
366             $message .= "\n";
367
368             $this->elementStart('div', 'help instructions');
369             $this->raw(common_markup_to_html($message));
370             $this->elementEnd('div');
371         }
372     }
373 }