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