]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - actions/avatarsettings.php
Merge branch 'avatar-folder-perms' into 'nightly'
[quix0rs-gnu-social.git] / actions / avatarsettings.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * Upload an avatar
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  * @copyright 2008-2009 StatusNet, Inc.
27  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
28  * @link      http://status.net/
29  */
30
31 if (!defined('GNUSOCIAL')) { exit(1); }
32
33 /**
34  * Upload an avatar
35  *
36  * We use jCrop plugin for jQuery to crop the image after upload.
37  *
38  * @category Settings
39  * @package  StatusNet
40  * @author   Evan Prodromou <evan@status.net>
41  * @author   Zach Copley <zach@status.net>
42  * @author   Sarven Capadisli <csarven@status.net>
43  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
44  * @link     http://status.net/
45  */
46 class AvatarsettingsAction extends SettingsAction
47 {
48     var $mode = null;
49     var $imagefile = null;
50     var $filename = null;
51
52     function prepare(array $args=array())
53     {
54         $avatarpath = Avatar::path('');
55
56         if (!is_writable($avatarpath)) {
57             throw new Exception(_("The administrator of your site needs to
58                 add write permissions on the avatar upload folder before
59                 you're able to set one."));
60         }
61
62         parent::prepare($args);
63         return true;
64     }
65
66     /**
67      * Title of the page
68      *
69      * @return string Title of the page
70      */
71     function title()
72     {
73         // TRANS: Title for avatar upload page.
74         return _('Avatar');
75     }
76
77     /**
78      * Instructions for use
79      *
80      * @return instructions for use
81      */
82     function getInstructions()
83     {
84         // TRANS: Instruction for avatar upload page.
85         // TRANS: %s is the maximum file size, for example "500b", "10kB" or "2MB".
86         return sprintf(_('You can upload your personal avatar. The maximum file size is %s.'),
87                        ImageFile::maxFileSize());
88     }
89
90     /**
91      * Content area of the page
92      *
93      * Shows a form for uploading an avatar. Currently overrides FormAction's showContent
94      * since we haven't made classes out of AvatarCropForm and AvatarUploadForm.
95      *
96      * @return void
97      */
98     function showContent()
99     {
100         if ($this->mode == 'crop') {
101             $this->showCropForm();
102         } else {
103             $this->showUploadForm();
104         }
105     }
106
107     function showUploadForm()
108     {
109         $this->elementStart('form', array('enctype' => 'multipart/form-data',
110                                           'method' => 'post',
111                                           'id' => 'form_settings_avatar',
112                                           'class' => 'form_settings',
113                                           'action' =>
114                                           common_local_url('avatarsettings')));
115         $this->elementStart('fieldset');
116         // TRANS: Avatar upload page form legend.
117         $this->element('legend', null, _('Avatar settings'));
118         $this->hidden('token', common_session_token());
119
120         if (Event::handle('StartAvatarFormData', array($this))) {
121             $this->elementStart('ul', 'form_data');
122             try {
123                 $original = Avatar::getUploaded($this->scoped);
124
125                 $this->elementStart('li', array('id' => 'avatar_original',
126                                                 'class' => 'avatar_view'));
127                 // TRANS: Header on avatar upload page for thumbnail of originally uploaded avatar (h2).
128                 $this->element('h2', null, _("Original"));
129                 $this->elementStart('div', array('id'=>'avatar_original_view'));
130                 $this->element('img', array('src' => $original->displayUrl(),
131                                             'width' => $original->width,
132                                             'height' => $original->height,
133                                             'alt' => $this->scoped->getNickname()));
134                 $this->elementEnd('div');
135                 $this->elementEnd('li');
136             } catch (NoAvatarException $e) {
137                 // No original avatar found!
138             }
139
140             try {
141                 $avatar = $this->scoped->getAvatar(AVATAR_PROFILE_SIZE);
142                 $this->elementStart('li', array('id' => 'avatar_preview',
143                                                 'class' => 'avatar_view'));
144                 // TRANS: Header on avatar upload page for thumbnail of to be used rendition of uploaded avatar (h2).
145                 $this->element('h2', null, _("Preview"));
146                 $this->elementStart('div', array('id'=>'avatar_preview_view'));
147                 $this->element('img', array('src' => $avatar->displayUrl(),
148                                             'width' => AVATAR_PROFILE_SIZE,
149                                             'height' => AVATAR_PROFILE_SIZE,
150                                             'alt' => $this->scoped->getNickname()));
151                 $this->elementEnd('div');
152                 if (!empty($avatar->filename)) {
153                     // TRANS: Button on avatar upload page to delete current avatar.
154                     $this->submit('delete', _m('BUTTON','Delete'));
155                 }
156                 $this->elementEnd('li');
157             } catch (NoAvatarException $e) {
158                 // No previously uploaded avatar to preview.
159             }
160
161             $this->elementStart('li', array ('id' => 'settings_attach'));
162             $this->element('input', array('name' => 'MAX_FILE_SIZE',
163                                           'type' => 'hidden',
164                                           'id' => 'MAX_FILE_SIZE',
165                                           'value' => ImageFile::maxFileSizeInt()));
166             $this->element('input', array('name' => 'avatarfile',
167                                           'type' => 'file',
168                                           'id' => 'avatarfile'));
169             $this->elementEnd('li');
170             $this->elementEnd('ul');
171
172             $this->elementStart('ul', 'form_actions');
173             $this->elementStart('li');
174                 // TRANS: Button on avatar upload page to upload an avatar.
175             $this->submit('upload', _m('BUTTON','Upload'));
176             $this->elementEnd('li');
177             $this->elementEnd('ul');
178         }
179         Event::handle('EndAvatarFormData', array($this));
180
181         $this->elementEnd('fieldset');
182         $this->elementEnd('form');
183     }
184
185     function showCropForm()
186     {
187         $this->elementStart('form', array('method' => 'post',
188                                           'id' => 'form_settings_avatar',
189                                           'class' => 'form_settings',
190                                           'action' =>
191                                           common_local_url('avatarsettings')));
192         $this->elementStart('fieldset');
193         // TRANS: Avatar upload page crop form legend.
194         $this->element('legend', null, _('Avatar settings'));
195         $this->hidden('token', common_session_token());
196
197         $this->elementStart('ul', 'form_data');
198
199         $this->elementStart('li',
200                             array('id' => 'avatar_original',
201                                   'class' => 'avatar_view'));
202         // TRANS: Header on avatar upload crop form for thumbnail of originally uploaded avatar (h2).
203         $this->element('h2', null, _('Original'));
204         $this->elementStart('div', array('id'=>'avatar_original_view'));
205         $this->element('img', array('src' => Avatar::url($this->filedata['filename']),
206                                     'width' => $this->filedata['width'],
207                                     'height' => $this->filedata['height'],
208                                     'alt' => $this->scoped->getNickname()));
209         $this->elementEnd('div');
210         $this->elementEnd('li');
211
212         $this->elementStart('li',
213                             array('id' => 'avatar_preview',
214                                   'class' => 'avatar_view'));
215         // TRANS: Header on avatar upload crop form for thumbnail of to be used rendition of uploaded avatar (h2).
216         $this->element('h2', null, _('Preview'));
217         $this->elementStart('div', array('id'=>'avatar_preview_view'));
218         $this->element('img', array('src' => Avatar::url($this->filedata['filename']),
219                                     'width' => AVATAR_PROFILE_SIZE,
220                                     'height' => AVATAR_PROFILE_SIZE,
221                                     'alt' => $this->scoped->getNickname()));
222         $this->elementEnd('div');
223
224         foreach (array('avatar_crop_x', 'avatar_crop_y',
225                        'avatar_crop_w', 'avatar_crop_h') as $crop_info) {
226             $this->element('input', array('name' => $crop_info,
227                                           'type' => 'hidden',
228                                           'id' => $crop_info));
229         }
230
231         // TRANS: Button on avatar upload crop form to confirm a selected crop as avatar.
232         $this->submit('crop', _m('BUTTON','Crop'));
233
234         $this->elementEnd('li');
235         $this->elementEnd('ul');
236         $this->elementEnd('fieldset');
237         $this->elementEnd('form');
238     }
239
240     protected function doPost()
241     {
242         if (Event::handle('StartAvatarSaveForm', array($this))) {
243             if ($this->trimmed('upload')) {
244                 return $this->uploadAvatar();
245             } else if ($this->trimmed('crop')) {
246                 return $this->cropAvatar();
247             } else if ($this->trimmed('delete')) {
248                 return $this->deleteAvatar();
249             } else {
250                 // TRANS: Unexpected validation error on avatar upload form.
251                 throw new ClientException(_('Unexpected form submission.'));
252             }
253             Event::handle('EndAvatarSaveForm', array($this));
254         }
255     }
256
257     /**
258      * Handle an image upload
259      *
260      * Does all the magic for handling an image upload, and crops the
261      * image by default.
262      *
263      * @return void
264      */
265     function uploadAvatar()
266     {
267         // ImageFile throws exception if something goes wrong, which we'll
268         // pick up and show as an error message above the form.
269         $imagefile = ImageFile::fromUpload('avatarfile');
270
271         $type = $imagefile->preferredType();
272         $filename = Avatar::filename($this->scoped->getID(),
273                                      image_type_to_extension($type),
274                                      null,
275                                      'tmp'.common_timestamp());
276
277         $filepath = Avatar::path($filename);
278         $imagefile = $imagefile->copyTo($filepath);
279
280         $filedata = array('filename' => $filename,
281                           'filepath' => $filepath,
282                           'width' => $imagefile->width,
283                           'height' => $imagefile->height,
284                           'type' => $type);
285
286         $_SESSION['FILEDATA'] = $filedata;
287
288         $this->filedata = $filedata;
289
290         $this->mode = 'crop';
291
292         // TRANS: Avatar upload form instruction after uploading a file.
293         return _('Pick a square area of the image to be your avatar.');
294     }
295
296     /**
297      * Handle the results of jcrop.
298      *
299      * @return void
300      */
301     public function cropAvatar()
302     {
303         $filedata = $_SESSION['FILEDATA'];
304
305         if (empty($filedata)) {
306             // TRANS: Server error displayed if an avatar upload went wrong somehow server side.
307             throw new ServerException(_('Lost our file data.'));
308         }
309
310         $file_d = min($filedata['width'],  $filedata['height']);
311
312         $dest_x = $this->arg('avatar_crop_x') ? $this->arg('avatar_crop_x'):0;
313         $dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0;
314         $dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$file_d;
315         $dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$file_d;
316         $size = intval(min($dest_w, $dest_h, common_config('avatar', 'maxsize')));
317
318         $box = array('width' => $size, 'height' => $size,
319                      'x' => $dest_x,   'y' => $dest_y,
320                      'w' => $dest_w,   'h' => $dest_h);
321
322         $imagefile = new ImageFile(null, $filedata['filepath']);
323         $filename = Avatar::filename($this->scoped->getID(), image_type_to_extension($imagefile->preferredType()),
324                                      $size, common_timestamp());
325         try {
326             $imagefile->resizeTo(Avatar::path($filename), $box);
327         } catch (UseFileAsThumbnailException $e) {
328             common_debug('Using uploaded avatar directly without resizing, copying it to: '.$filename);
329             if (!copy($filedata['filepath'], Avatar::path($filename))) {
330                 common_debug('Tried to copy image file '.$filedata['filepath'].' to destination '.Avatar::path($filename));
331                 throw new ServerException('Could not copy file to destination.');
332             }
333         }
334
335         if ($this->scoped->setOriginal($filename)) {
336             @unlink($filedata['filepath']);
337             unset($_SESSION['FILEDATA']);
338             $this->mode = 'upload';
339             // TRANS: Success message for having updated a user avatar.
340             return _('Avatar updated.');
341         }
342
343         // TRANS: Error displayed on the avatar upload page if the avatar could not be updated for an unknown reason.
344         throw new ServerException(_('Failed updating avatar.'));
345     }
346
347     /**
348      * Get rid of the current avatar.
349      *
350      * @return void
351      */
352     function deleteAvatar()
353     {
354         Avatar::deleteFromProfile($this->scoped);
355
356         // TRANS: Success message for deleting a user avatar.
357         return _('Avatar deleted.');
358     }
359
360     /**
361      * Add the jCrop stylesheet
362      *
363      * @return void
364      */
365
366     function showStylesheets()
367     {
368         parent::showStylesheets();
369         $this->cssLink('js/extlib/jquery-jcrop/css/jcrop.css','base','screen, projection, tv');
370     }
371
372     /**
373      * Add the jCrop scripts
374      *
375      * @return void
376      */
377     function showScripts()
378     {
379         parent::showScripts();
380
381         if ($this->mode == 'crop') {
382             $this->script('extlib/jquery-jcrop/jcrop.js');
383             $this->script('jcrop.go.js');
384         }
385
386         $this->autofocus('avatarfile');
387     }
388 }