]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - actions/profilesettings.php
Work in progress: subscription approval policy field in place on user, hooked up...
[quix0rs-gnu-social.git] / actions / profilesettings.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * Change profile settings
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  Settings
23  * @package   StatusNet
24  * @author    Evan Prodromou <evan@status.net>
25  * @author    Zach Copley <zach@status.net>
26  * @author    Sarven Capadisli <csarven@status.net>
27  * @copyright 2008-2009 StatusNet, Inc.
28  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
29  * @link      http://status.net/
30  */
31
32 if (!defined('STATUSNET') && !defined('LACONICA')) {
33     exit(1);
34 }
35
36
37
38 /**
39  * Change profile settings
40  *
41  * @category Settings
42  * @package  StatusNet
43  * @author   Evan Prodromou <evan@status.net>
44  * @author   Zach Copley <zach@status.net>
45  * @author   Sarven Capadisli <csarven@status.net>
46  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
47  * @link     http://status.net/
48  */
49
50 class ProfilesettingsAction extends SettingsAction
51 {
52     /**
53      * Title of the page
54      *
55      * @return string Title of the page
56      */
57     function title()
58     {
59         // TRANS: Page title for profile settings.
60         return _('Profile settings');
61     }
62
63     /**
64      * Instructions for use
65      *
66      * @return instructions for use
67      */
68     function getInstructions()
69     {
70         // TRANS: Usage instructions for profile settings.
71         return _('You can update your personal profile info here '.
72                  'so people know more about you.');
73     }
74
75     function showScripts()
76     {
77         parent::showScripts();
78         $this->autofocus('nickname');
79     }
80
81     /**
82      * Content area of the page
83      *
84      * Shows a form for uploading an avatar.
85      *
86      * @return void
87      */
88     function showContent()
89     {
90         $user = common_current_user();
91         $profile = $user->getProfile();
92
93         $this->elementStart('form', array('method' => 'post',
94                                           'id' => 'form_settings_profile',
95                                           'class' => 'form_settings',
96                                           'action' => common_local_url('profilesettings')));
97         $this->elementStart('fieldset');
98         // TRANS: Profile settings form legend.
99         $this->element('legend', null, _('Profile information'));
100         $this->hidden('token', common_session_token());
101
102         // too much common patterns here... abstractable?
103         $this->elementStart('ul', 'form_data');
104         if (Event::handle('StartProfileFormData', array($this))) {
105             $this->elementStart('li');
106             // TRANS: Field label in form for profile settings.
107             $this->input('nickname', _('Nickname'),
108                          ($this->arg('nickname')) ? $this->arg('nickname') : $profile->nickname,
109                          // TRANS: Tooltip for field label in form for profile settings.
110                          _('1-64 lowercase letters or numbers, no punctuation or spaces.'));
111             $this->elementEnd('li');
112             $this->elementStart('li');
113             // TRANS: Field label in form for profile settings.
114             $this->input('fullname', _('Full name'),
115                          ($this->arg('fullname')) ? $this->arg('fullname') : $profile->fullname);
116             $this->elementEnd('li');
117             $this->elementStart('li');
118             // TRANS: Field label in form for profile settings.
119             $this->input('homepage', _('Homepage'),
120                          ($this->arg('homepage')) ? $this->arg('homepage') : $profile->homepage,
121                          // TRANS: Tooltip for field label in form for profile settings.
122                          _('URL of your homepage, blog, or profile on another site.'));
123             $this->elementEnd('li');
124             $this->elementStart('li');
125             $maxBio = Profile::maxBio();
126             if ($maxBio > 0) {
127                 // TRANS: Tooltip for field label in form for profile settings. Plural
128                 // TRANS: is decided by the number of characters available for the
129                 // TRANS: biography (%d).
130                 $bioInstr = sprintf(_m('Describe yourself and your interests in %d character',
131                                        'Describe yourself and your interests in %d characters',
132                                        $maxBio),
133                                     $maxBio);
134             } else {
135                 // TRANS: Tooltip for field label in form for profile settings.
136                 $bioInstr = _('Describe yourself and your interests');
137             }
138             // TRANS: Text area label in form for profile settings where users can provide.
139             // TRANS: their biography.
140             $this->textarea('bio', _('Bio'),
141                             ($this->arg('bio')) ? $this->arg('bio') : $profile->bio,
142                             $bioInstr);
143             $this->elementEnd('li');
144             $this->elementStart('li');
145             // TRANS: Field label in form for profile settings.
146             $this->input('location', _('Location'),
147                          ($this->arg('location')) ? $this->arg('location') : $profile->location,
148                          // TRANS: Tooltip for field label in form for profile settings.
149                          _('Where you are, like "City, State (or Region), Country"'));
150             $this->elementEnd('li');
151             if (common_config('location', 'share') == 'user') {
152                 $this->elementStart('li');
153                 // TRANS: Checkbox label in form for profile settings.
154                 $this->checkbox('sharelocation', _('Share my current location when posting notices'),
155                                 ($this->arg('sharelocation')) ?
156                                 $this->arg('sharelocation') : $user->shareLocation());
157                 $this->elementEnd('li');
158             }
159             Event::handle('EndProfileFormData', array($this));
160             $this->elementStart('li');
161             // TRANS: Field label in form for profile settings.
162             $this->input('tags', _('Tags'),
163                          ($this->arg('tags')) ? $this->arg('tags') : implode(' ', $user->getSelfTags()),
164                          // TRANS: Tooltip for field label in form for profile settings.
165                          _('Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated.'));
166             $this->elementEnd('li');
167             $this->elementStart('li');
168             $language = common_language();
169             // TRANS: Dropdownlist label in form for profile settings.
170             $this->dropdown('language', _('Language'),
171                          // TRANS: Tooltip for dropdown list label in form for profile settings.
172                             get_nice_language_list(), _('Preferred language.'),
173                             false, $language);
174             $this->elementEnd('li');
175             $timezone = common_timezone();
176             $timezones = array();
177             foreach(DateTimeZone::listIdentifiers() as $k => $v) {
178                 $timezones[$v] = $v;
179             }
180             $this->elementStart('li');
181             // TRANS: Dropdownlist label in form for profile settings.
182             $this->dropdown('timezone', _('Timezone'),
183                          // TRANS: Tooltip for dropdown list label in form for profile settings.
184                             $timezones, _('What timezone are you normally in?'),
185                             true, $timezone);
186             $this->elementEnd('li');
187             $this->elementStart('li');
188             $this->checkbox('autosubscribe',
189                             // TRANS: Checkbox label in form for profile settings.
190                             _('Automatically subscribe to whoever '.
191                               'subscribes to me (best for non-humans).'),
192                             ($this->arg('autosubscribe')) ?
193                             $this->boolean('autosubscribe') : $user->autosubscribe);
194             $this->elementEnd('li');
195             $this->elementStart('li');
196             $this->dropdown('subscribe_policy',
197                             // TRANS: Dropdown field label on profile settings, for what policies to apply when someone else tries to subscribe to your updates.
198                             _('Subscription policy'),
199                             array(User::SUBSCRIBE_POLICY_OPEN     => _('Let anyone follow me'),
200                                   User::SUBSCRIBE_POLICY_MODERATE => _('Ask me first')),
201                             // TRANS: Dropdown field title on group edit form.
202                             _('Whether other users need your permission to follow your updates.'),
203                             false,
204                             (empty($user->subscribe_policy)) ? User::SUBSCRIBE_POLICY_OPEN : $user->subscribe_policy);
205             $this->elementEnd('li');
206         }
207         $this->elementEnd('ul');
208         // TRANS: Button to save input in profile settings.
209         $this->submit('save', _m('BUTTON','Save'));
210
211         $this->elementEnd('fieldset');
212         $this->elementEnd('form');
213     }
214
215     /**
216      * Handle a post
217      *
218      * Validate input and save changes. Reload the form with a success
219      * or error message.
220      *
221      * @return void
222      */
223     function handlePost()
224     {
225         // CSRF protection
226         $token = $this->trimmed('token');
227         if (!$token || $token != common_session_token()) {
228             // TRANS: Form validation error.
229             $this->showForm(_('There was a problem with your session token. '.
230                               'Try again, please.'));
231             return;
232         }
233
234         if (Event::handle('StartProfileSaveForm', array($this))) {
235
236             try {
237                 $nickname = Nickname::normalize($this->trimmed('nickname'));
238             } catch (NicknameException $e) {
239                 $this->showForm($e->getMessage());
240                 return;
241             }
242
243             $fullname = $this->trimmed('fullname');
244             $homepage = $this->trimmed('homepage');
245             $bio = $this->trimmed('bio');
246             $location = $this->trimmed('location');
247             $autosubscribe = $this->boolean('autosubscribe');
248             $subscribe_policy = $this->trimmed('subscribe_policy');
249             $language = $this->trimmed('language');
250             $timezone = $this->trimmed('timezone');
251             $tagstring = $this->trimmed('tags');
252
253             // Some validation
254             if (!User::allowed_nickname($nickname)) {
255                 // TRANS: Validation error in form for profile settings.
256                 $this->showForm(_('Not a valid nickname.'));
257                 return;
258             } else if (!is_null($homepage) && (strlen($homepage) > 0) &&
259                        !Validate::uri($homepage, array('allowed_schemes' => array('http', 'https')))) {
260                 // TRANS: Validation error in form for profile settings.
261                 $this->showForm(_('Homepage is not a valid URL.'));
262                 return;
263             } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
264                 // TRANS: Validation error in form for profile settings.
265                 $this->showForm(_('Full name is too long (maximum 255 characters).'));
266                 return;
267             } else if (Profile::bioTooLong($bio)) {
268                 // TRANS: Validation error in form for profile settings.
269                 // TRANS: Plural form is used based on the maximum number of allowed
270                 // TRANS: characters for the biography (%d).
271                 $this->showForm(sprintf(_m('Bio is too long (maximum %d character).',
272                                            'Bio is too long (maximum %d characters).',
273                                            Profile::maxBio()),
274                                         Profile::maxBio()));
275                 return;
276             } else if (!is_null($location) && mb_strlen($location) > 255) {
277                 // TRANS: Validation error in form for profile settings.
278                 $this->showForm(_('Location is too long (maximum 255 characters).'));
279                 return;
280             }  else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) {
281                 // TRANS: Validation error in form for profile settings.
282                 $this->showForm(_('Timezone not selected.'));
283                 return;
284             } else if ($this->nicknameExists($nickname)) {
285                 // TRANS: Validation error in form for profile settings.
286                 $this->showForm(_('Nickname already in use. Try another one.'));
287                 return;
288             } else if (!is_null($language) && strlen($language) > 50) {
289                 // TRANS: Validation error in form for profile settings.
290                 $this->showForm(_('Language is too long (maximum 50 characters).'));
291                 return;
292             }
293
294             if ($tagstring) {
295                 $tags = array_map('common_canonical_tag', preg_split('/[\s,]+/', $tagstring));
296             } else {
297                 $tags = array();
298             }
299
300             foreach ($tags as $tag) {
301                 if (!common_valid_profile_tag($tag)) {
302                     // TRANS: Validation error in form for profile settings.
303                     // TRANS: %s is an invalid tag.
304                     $this->showForm(sprintf(_('Invalid tag: "%s".'), $tag));
305                     return;
306                 }
307             }
308
309             $user = common_current_user();
310
311             $user->query('BEGIN');
312
313             if ($user->nickname != $nickname ||
314                 $user->language != $language ||
315                 $user->timezone != $timezone) {
316
317                 common_debug('Updating user nickname from ' . $user->nickname . ' to ' . $nickname,
318                              __FILE__);
319                 common_debug('Updating user language from ' . $user->language . ' to ' . $language,
320                              __FILE__);
321                 common_debug('Updating user timezone from ' . $user->timezone . ' to ' . $timezone,
322                              __FILE__);
323
324                 $original = clone($user);
325
326                 $user->nickname = $nickname;
327                 $user->language = $language;
328                 $user->timezone = $timezone;
329
330                 $result = $user->updateKeys($original);
331
332                 if ($result === false) {
333                     common_log_db_error($user, 'UPDATE', __FILE__);
334                     // TRANS: Server error thrown when user profile settings could not be updated.
335                     $this->serverError(_('Could not update user.'));
336                     return;
337                 } else {
338                     // Re-initialize language environment if it changed
339                     common_init_language();
340                     // Clear the site owner, in case nickname changed
341                     if ($user->hasRole(Profile_role::OWNER)) {
342                         User::blow('user:site_owner');
343                     }
344                 }
345             }
346
347             // XXX: XOR
348             if (($user->autosubscribe ^ $autosubscribe) || $user->subscribe_policy != $subscribe_policy) {
349
350                 $original = clone($user);
351
352                 $user->autosubscribe = $autosubscribe;
353                 $user->subscribe_policy = $subscribe_policy;
354
355                 $result = $user->update($original);
356
357                 if ($result === false) {
358                     common_log_db_error($user, 'UPDATE', __FILE__);
359                     // TRANS: Server error thrown when user profile settings could not be updated to
360                     // TRANS: automatically subscribe to any subscriber.
361                     $this->serverError(_('Could not update user for autosubscribe or subscribe_policy.'));
362                     return;
363                 }
364             }
365
366             $profile = $user->getProfile();
367
368             $orig_profile = clone($profile);
369
370             $profile->nickname = $user->nickname;
371             $profile->fullname = $fullname;
372             $profile->homepage = $homepage;
373             $profile->bio = $bio;
374             $profile->location = $location;
375
376             $loc = Location::fromName($location);
377
378             if (empty($loc)) {
379                 $profile->lat         = null;
380                 $profile->lon         = null;
381                 $profile->location_id = null;
382                 $profile->location_ns = null;
383             } else {
384                 $profile->lat         = $loc->lat;
385                 $profile->lon         = $loc->lon;
386                 $profile->location_id = $loc->location_id;
387                 $profile->location_ns = $loc->location_ns;
388             }
389
390             $profile->profileurl = common_profile_url($nickname);
391
392             if (common_config('location', 'share') == 'user') {
393
394                 $exists = false;
395
396                 $prefs = User_location_prefs::staticGet('user_id', $user->id);
397
398                 if (empty($prefs)) {
399                     $prefs = new User_location_prefs();
400
401                     $prefs->user_id = $user->id;
402                     $prefs->created = common_sql_now();
403                 } else {
404                     $exists = true;
405                     $orig = clone($prefs);
406                 }
407
408                 $prefs->share_location = $this->boolean('sharelocation');
409
410                 if ($exists) {
411                     $result = $prefs->update($orig);
412                 } else {
413                     $result = $prefs->insert();
414                 }
415
416                 if ($result === false) {
417                     common_log_db_error($prefs, ($exists) ? 'UPDATE' : 'INSERT', __FILE__);
418                     // TRANS: Server error thrown when user profile location preference settings could not be updated.
419                     $this->serverError(_('Could not save location prefs.'));
420                     return;
421                 }
422             }
423
424             common_debug('Old profile: ' . common_log_objstring($orig_profile), __FILE__);
425             common_debug('New profile: ' . common_log_objstring($profile), __FILE__);
426
427             $result = $profile->update($orig_profile);
428
429             if ($result === false) {
430                 common_log_db_error($profile, 'UPDATE', __FILE__);
431                 // TRANS: Server error thrown when user profile settings could not be saved.
432                 $this->serverError(_('Could not save profile.'));
433                 return;
434             }
435
436             // Set the user tags
437             $result = $user->setSelfTags($tags);
438
439             if (!$result) {
440                 // TRANS: Server error thrown when user profile settings tags could not be saved.
441                 $this->serverError(_('Could not save tags.'));
442                 return;
443             }
444
445             $user->query('COMMIT');
446             Event::handle('EndProfileSaveForm', array($this));
447             common_broadcast_profile($profile);
448
449             // TRANS: Confirmation shown when user profile settings are saved.
450             $this->showForm(_('Settings saved.'), true);
451
452         }
453     }
454
455     function nicknameExists($nickname)
456     {
457         $user = common_current_user();
458         $other = User::staticGet('nickname', $nickname);
459         if (!$other) {
460             return false;
461         } else {
462             return $other->id != $user->id;
463         }
464     }
465
466     function showAside() {
467         $user = common_current_user();
468
469         $this->elementStart('div', array('id' => 'aside_primary',
470                                          'class' => 'aside'));
471
472         $this->elementStart('div', array('id' => 'account_actions',
473                                          'class' => 'section'));
474         $this->elementStart('ul');
475         if (Event::handle('StartProfileSettingsActions', array($this))) {
476             if ($user->hasRight(Right::BACKUPACCOUNT)) {
477                 $this->elementStart('li');
478                 $this->element('a',
479                                array('href' => common_local_url('backupaccount')),
480                                // TRANS: Option in profile settings to create a backup of the account of the currently logged in user.
481                                _('Backup account'));
482                 $this->elementEnd('li');
483             }
484             if ($user->hasRight(Right::DELETEACCOUNT)) {
485                 $this->elementStart('li');
486                 $this->element('a',
487                                array('href' => common_local_url('deleteaccount')),
488                                // TRANS: Option in profile settings to delete the account of the currently logged in user.
489                                _('Delete account'));
490                 $this->elementEnd('li');
491             }
492             if ($user->hasRight(Right::RESTOREACCOUNT)) {
493                 $this->elementStart('li');
494                 $this->element('a',
495                                array('href' => common_local_url('restoreaccount')),
496                                // TRANS: Option in profile settings to restore the account of the currently logged in user from a backup.
497                                _('Restore account'));
498                 $this->elementEnd('li');
499             }
500             Event::handle('EndProfileSettingsActions', array($this));
501         }
502         $this->elementEnd('ul');
503         $this->elementEnd('div');
504         $this->elementEnd('div');
505     }
506 }