Update copyright
[friendica.git/.git] / src / Module / Settings / Profile / Photo / Crop.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2021, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Module\Settings\Profile\Photo;
23
24 use Friendica\Core\Renderer;
25 use Friendica\Core\Session;
26 use Friendica\Core\Worker;
27 use Friendica\Database\DBA;
28 use Friendica\DI;
29 use Friendica\Model\Contact;
30 use Friendica\Model\Photo;
31 use Friendica\Module\BaseSettings;
32 use Friendica\Network\HTTPException;
33
34 class Crop extends BaseSettings
35 {
36         public static function post(array $parameters = [])
37         {
38                 if (!Session::isAuthenticated()) {
39                         return;
40                 }
41
42                 $photo_prefix = $parameters['guid'];
43                 $resource_id = $photo_prefix;
44                 $scale = 0;
45                 if (substr($photo_prefix, -2, 1) == '-') {
46                         list($resource_id, $scale) = explode('-', $photo_prefix);
47                 }
48
49                 self::checkFormSecurityTokenRedirectOnError('settings/profile/photo/crop/' . $photo_prefix, 'settings_profile_photo_crop');
50
51                 $action = $_POST['action'] ?? 'crop';
52
53                 // Image selection origin is top left
54                 $selectionX = intval($_POST['xstart'] ?? 0);
55                 $selectionY = intval($_POST['ystart'] ?? 0);
56                 $selectionW = intval($_POST['width']  ?? 0);
57                 $selectionH = intval($_POST['height'] ?? 0);
58
59                 $path = 'profile/' . DI::app()->user['nickname'];
60
61                 $base_image = Photo::selectFirst([], ['resource-id' => $resource_id, 'uid' => local_user(), 'scale' => $scale]);
62                 if (DBA::isResult($base_image)) {
63                         $Image = Photo::getImageForPhoto($base_image);
64                         if ($Image->isValid()) {
65                                 // If setting for the default profile, unset the profile photo flag from any other photos I own
66                                 DBA::update('photo', ['profile' => 0], ['uid' => local_user()]);
67
68                                 // Normalizing expected square crop parameters
69                                 $selectionW = $selectionH = min($selectionW, $selectionH);
70
71                                 $imageIsSquare = $Image->getWidth() === $Image->getHeight();
72                                 $selectionIsFullImage = $selectionX === 0 && $selectionY === 0 && $selectionW === $Image->getWidth() && $selectionH === $Image->getHeight();
73
74                                 // Bypassed UI with a rectangle image, we force a square cropped image
75                                 if (!$imageIsSquare && $action == 'skip') {
76                                         $selectionX = $selectionY = 0;
77                                         $selectionW = $selectionH = min($Image->getWidth(), $Image->getHeight());
78                                         $action = 'crop';
79                                 }
80
81                                 // Selective crop if it was asked and the selection isn't the full image
82                                 if ($action == 'crop'
83                                         && !($imageIsSquare && !$selectionIsFullImage)
84                                 ) {
85                                         $Image->crop(300, $selectionX, $selectionY, $selectionW, $selectionH);
86                                         $resource_id = Photo::newResource();
87                                 } else {
88                                         $Image->scaleDown(300);
89                                 }
90
91                                 $r = Photo::store(
92                                         $Image,
93                                         local_user(),
94                                         0,
95                                         $resource_id,
96                                         $base_image['filename'],
97                                         DI::l10n()->t('Profile Photos'),
98                                         4,
99                                         1
100                                 );
101                                 if ($r === false) {
102                                         notice(DI::l10n()->t('Image size reduction [%s] failed.', '300'));
103                                 }
104
105                                 $Image->scaleDown(80);
106
107                                 $r = Photo::store(
108                                         $Image,
109                                         local_user(),
110                                         0,
111                                         $resource_id,
112                                         $base_image['filename'],
113                                         DI::l10n()->t('Profile Photos'),
114                                         5,
115                                         1
116                                 );
117                                 if ($r === false) {
118                                         notice(DI::l10n()->t('Image size reduction [%s] failed.', '80'));
119                                 }
120
121                                 $Image->scaleDown(48);
122
123                                 $r = Photo::store(
124                                         $Image,
125                                         local_user(),
126                                         0,
127                                         $resource_id,
128                                         $base_image['filename'],
129                                         DI::l10n()->t('Profile Photos'),
130                                         6,
131                                         1
132                                 );
133                                 if ($r === false) {
134                                         notice(DI::l10n()->t('Image size reduction [%s] failed.', '48'));
135                                 }
136
137                                 Contact::updateSelfFromUserID(local_user(), true);
138
139                                 info(DI::l10n()->t('Shift-reload the page or clear browser cache if the new photo does not display immediately.'));
140                                 // Update global directory in background
141                                 if ($path && strlen(DI::config()->get('system', 'directory'))) {
142                                         Worker::add(PRIORITY_LOW, 'Directory', DI::baseUrl()->get() . '/' . $path);
143                                 }
144
145                                 Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user());
146                         } else {
147                                 notice(DI::l10n()->t('Unable to process image'));
148                         }
149                 }
150
151                 DI::baseUrl()->redirect($path);
152         }
153
154         public static function content(array $parameters = [])
155         {
156                 if (!Session::isAuthenticated()) {
157                         throw new HTTPException\ForbiddenException(DI::l10n()->t('Permission denied.'));
158                 }
159
160                 parent::content();
161
162                 $resource_id = $parameters['guid'];
163
164                 $photos = Photo::selectToArray([], ['resource-id' => $resource_id, 'uid' => local_user()], ['order' => ['scale' => false]]);
165                 if (!DBA::isResult($photos)) {
166                         throw new HTTPException\NotFoundException(DI::l10n()->t('Photo not found.'));
167                 }
168
169                 $havescale = false;
170                 $smallest = 0;
171                 foreach ($photos as $photo) {
172                         $smallest = $photo['scale'] == 1 ? 1 : $smallest;
173                         $havescale = $havescale || $photo['scale'] == 5;
174                 }
175
176                 // set an already uloaded photo as profile photo
177                 // if photo is in 'Profile Photos', change it in db
178                 if ($photos[0]['album'] == DI::l10n()->t('Profile Photos') && $havescale) {
179                         Photo::update(['profile' => false], ['uid' => local_user()]);
180
181                         Photo::update(['profile' => true], ['resource-id' => $resource_id, 'uid' => local_user()]);
182
183                         Contact::updateSelfFromUserID(local_user(), true);
184
185                         // Update global directory in background
186                         if (Session::get('my_url') && strlen(DI::config()->get('system', 'directory'))) {
187                                 Worker::add(PRIORITY_LOW, 'Directory', Session::get('my_url'));
188                         }
189
190                         info(DI::l10n()->t('Profile picture successfully updated.'));
191
192                         DI::baseUrl()->redirect('profile/' . DI::app()->user['nickname']);
193                 }
194
195                 $Image = Photo::getImageForPhoto($photos[0]);
196
197                 $imagecrop = [
198                         'resource-id' => $resource_id,
199                         'scale'       => $smallest,
200                         'ext'         => $Image->getExt(),
201                 ];
202
203                 $isSquare = $Image->getWidth() === $Image->getHeight();
204
205                 DI::page()['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('settings/profile/photo/crop_head.tpl'), []);
206
207                 $filename = $imagecrop['resource-id'] . '-' . $imagecrop['scale'] . '.' . $imagecrop['ext'];
208                 $tpl = Renderer::getMarkupTemplate('settings/profile/photo/crop.tpl');
209                 $o = Renderer::replaceMacros($tpl, [
210                         '$filename'  => $filename,
211                         '$resource'  => $imagecrop['resource-id'] . '-' . $imagecrop['scale'],
212                         '$image_url' => DI::baseUrl() . '/photo/' . $filename,
213                         '$title'     => DI::l10n()->t('Crop Image'),
214                         '$desc'      => DI::l10n()->t('Please adjust the image cropping for optimum viewing.'),
215                         '$form_security_token' => self::getFormSecurityToken('settings_profile_photo_crop'),
216                         '$skip'      => $isSquare ? DI::l10n()->t('Use Image As Is') : '',
217                         '$crop'      => DI::l10n()->t('Crop Image'),
218                 ]);
219
220                 return $o;
221         }
222 }