]> git.mxchange.org Git - friendica.git/blob - src/Module/Settings/Profile/Photo/Crop.php
Merge remote-tracking branch 'upstream/develop' into audience
[friendica.git] / src / Module / Settings / Profile / Photo / Crop.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2023, 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\Database\DBA;
26 use Friendica\DI;
27 use Friendica\Model\Contact;
28 use Friendica\Model\Photo;
29 use Friendica\Model\Profile;
30 use Friendica\Module\BaseSettings;
31 use Friendica\Network\HTTPException;
32
33 class Crop extends BaseSettings
34 {
35         protected function post(array $request = [])
36         {
37                 if (!DI::userSession()->isAuthenticated()) {
38                         return;
39                 }
40
41                 $photo_prefix = $this->parameters['guid'];
42                 $resource_id = $photo_prefix;
43                 $scale = 0;
44                 if (substr($photo_prefix, -2, 1) == '-') {
45                         list($resource_id, $scale) = explode('-', $photo_prefix);
46                 }
47
48                 self::checkFormSecurityTokenRedirectOnError('settings/profile/photo/crop/' . $photo_prefix, 'settings_profile_photo_crop');
49
50                 $action = $_POST['action'] ?? 'crop';
51
52                 // Image selection origin is top left
53                 $selectionX = intval($_POST['xstart'] ?? 0);
54                 $selectionY = intval($_POST['ystart'] ?? 0);
55                 $selectionW = intval($_POST['width']  ?? 0);
56                 $selectionH = intval($_POST['height'] ?? 0);
57
58                 $path = 'profile/' . DI::app()->getLoggedInUserNickname();
59
60                 $base_image = Photo::selectFirst([], ['resource-id' => $resource_id, 'uid' => DI::userSession()->getLocalUserId(), 'scale' => $scale]);
61                 if (DBA::isResult($base_image)) {
62                         $Image = Photo::getImageForPhoto($base_image);
63                         if (empty($Image)) {
64                                 throw new HTTPException\InternalServerErrorException();
65                         }
66
67                         if ($Image->isValid()) {
68                                 // If setting for the default profile, unset the profile photo flag from any other photos I own
69                                 DBA::update('photo', ['profile' => 0], ['uid' => DI::userSession()->getLocalUserId()]);
70
71                                 // Normalizing expected square crop parameters
72                                 $selectionW = $selectionH = min($selectionW, $selectionH);
73
74                                 $imageIsSquare = $Image->getWidth() === $Image->getHeight();
75                                 $selectionIsFullImage = $selectionX === 0 && $selectionY === 0 && $selectionW === $Image->getWidth() && $selectionH === $Image->getHeight();
76
77                                 // Bypassed UI with a rectangle image, we force a square cropped image
78                                 if (!$imageIsSquare && $action == 'skip') {
79                                         $selectionX = $selectionY = 0;
80                                         $selectionW = $selectionH = min($Image->getWidth(), $Image->getHeight());
81                                         $action = 'crop';
82                                 }
83
84                                 // Selective crop if it was asked and the selection isn't the full image
85                                 if ($action == 'crop'
86                                         && !($imageIsSquare && !$selectionIsFullImage)
87                                 ) {
88                                         $Image->crop(300, $selectionX, $selectionY, $selectionW, $selectionH);
89                                         $resource_id = Photo::newResource();
90                                 } else {
91                                         $Image->scaleDown(300);
92                                 }
93
94                                 $condition = ['resource-id' => $resource_id, 'uid' => DI::userSession()->getLocalUserId(), 'contact-id' => 0];
95
96                                 $r = Photo::store(
97                                         $Image,
98                                         DI::userSession()->getLocalUserId(),
99                                         0,
100                                         $resource_id,
101                                         $base_image['filename'],
102                                         DI::l10n()->t(Photo::PROFILE_PHOTOS),
103                                         4,
104                                         Photo::USER_AVATAR
105                                 );
106                                 if ($r === false) {
107                                         DI::sysmsg()->addNotice(DI::l10n()->t('Image size reduction [%s] failed.', '300'));
108                                 } else {
109                                         Photo::update(['profile' => true], array_merge($condition, ['scale' => 4]));
110                                 }
111
112                                 $Image->scaleDown(80);
113
114                                 $r = Photo::store(
115                                         $Image,
116                                         DI::userSession()->getLocalUserId(),
117                                         0,
118                                         $resource_id,
119                                         $base_image['filename'],
120                                         DI::l10n()->t(Photo::PROFILE_PHOTOS),
121                                         5,
122                                         Photo::USER_AVATAR
123                                 );
124                                 if ($r === false) {
125                                         DI::sysmsg()->addNotice(DI::l10n()->t('Image size reduction [%s] failed.', '80'));
126                                 } else {
127                                         Photo::update(['profile' => true], array_merge($condition, ['scale' => 5]));
128                                 }
129
130                                 $Image->scaleDown(48);
131
132                                 $r = Photo::store(
133                                         $Image,
134                                         DI::userSession()->getLocalUserId(),
135                                         0,
136                                         $resource_id,
137                                         $base_image['filename'],
138                                         DI::l10n()->t(Photo::PROFILE_PHOTOS),
139                                         6,
140                                         Photo::USER_AVATAR
141                                 );
142                                 if ($r === false) {
143                                         DI::sysmsg()->addNotice(DI::l10n()->t('Image size reduction [%s] failed.', '48'));
144                                 } else {
145                                         Photo::update(['profile' => true], array_merge($condition, ['scale' => 6]));
146                                 }
147
148                                 Contact::updateSelfFromUserID(DI::userSession()->getLocalUserId(), true);
149
150                                 DI::sysmsg()->addInfo(DI::l10n()->t('Shift-reload the page or clear browser cache if the new photo does not display immediately.'));
151
152                                 // Update global directory in background
153                                 Profile::publishUpdate(DI::userSession()->getLocalUserId());
154                         } else {
155                                 DI::sysmsg()->addNotice(DI::l10n()->t('Unable to process image'));
156                         }
157                 }
158
159                 DI::baseUrl()->redirect($path);
160         }
161
162         protected function content(array $request = []): string
163         {
164                 if (!DI::userSession()->isAuthenticated()) {
165                         throw new HTTPException\ForbiddenException(DI::l10n()->t('Permission denied.'));
166                 }
167
168                 parent::content();
169
170                 $resource_id = $this->parameters['guid'];
171
172                 $photos = Photo::selectToArray([], ['resource-id' => $resource_id, 'uid' => DI::userSession()->getLocalUserId()], ['order' => ['scale' => false]]);
173                 if (!DBA::isResult($photos)) {
174                         throw new HTTPException\NotFoundException(DI::l10n()->t('Photo not found.'));
175                 }
176
177                 $havescale = false;
178                 $smallest = 0;
179                 foreach ($photos as $photo) {
180                         $smallest = $photo['scale'] == 1 ? 1 : $smallest;
181                         $havescale = $havescale || $photo['scale'] == 5;
182                 }
183
184                 // set an already uploaded photo as profile photo
185                 // if photo is in 'Profile Photos', change it in db
186                 if ($photos[0]['photo-type'] == Photo::USER_AVATAR && $havescale) {
187                         Photo::update(['profile' => false], ['uid' => DI::userSession()->getLocalUserId()]);
188
189                         Photo::update(['profile' => true], ['resource-id' => $resource_id, 'uid' => DI::userSession()->getLocalUserId()]);
190
191                         Contact::updateSelfFromUserID(DI::userSession()->getLocalUserId(), true);
192
193                         // Update global directory in background
194                         Profile::publishUpdate(DI::userSession()->getLocalUserId());
195
196                         DI::sysmsg()->addInfo(DI::l10n()->t('Profile picture successfully updated.'));
197
198                         DI::baseUrl()->redirect('profile/' . DI::app()->getLoggedInUserNickname());
199                 }
200
201                 $Image = Photo::getImageForPhoto($photos[0]);
202                 if (empty($Image)) {
203                         throw new HTTPException\InternalServerErrorException();
204                 }
205
206                 $imagecrop = [
207                         'resource-id' => $resource_id,
208                         'scale'       => $smallest,
209                         'ext'         => $Image->getExt(),
210                 ];
211
212                 $isSquare = $Image->getWidth() === $Image->getHeight();
213
214                 DI::page()['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('settings/profile/photo/crop_head.tpl'), []);
215
216                 $filename = $imagecrop['resource-id'] . '-' . $imagecrop['scale'] . '.' . $imagecrop['ext'];
217                 $tpl = Renderer::getMarkupTemplate('settings/profile/photo/crop.tpl');
218                 $o = Renderer::replaceMacros($tpl, [
219                         '$filename'  => $filename,
220                         '$resource'  => $imagecrop['resource-id'] . '-' . $imagecrop['scale'],
221                         '$image_url' => DI::baseUrl() . '/photo/' . $filename,
222                         '$title'     => DI::l10n()->t('Crop Image'),
223                         '$desc'      => DI::l10n()->t('Please adjust the image cropping for optimum viewing.'),
224                         '$form_security_token' => self::getFormSecurityToken('settings_profile_photo_crop'),
225                         '$skip'      => $isSquare ? DI::l10n()->t('Use Image As Is') : '',
226                         '$crop'      => DI::l10n()->t('Crop Image'),
227                 ]);
228
229                 return $o;
230         }
231 }