--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Media\Attachment;
+
+use Friendica\App;
+use Friendica\BaseModule;
+use Friendica\Core\L10n;
+use Friendica\Core\Renderer;
+use Friendica\Core\Session\Capability\IHandleUserSessions;
+use Friendica\Core\System;
+use Friendica\Model\Attach;
+use Friendica\Module\Response;
+use Friendica\Network\HTTPException\UnauthorizedException;
+use Friendica\Util\Profiler;
+use Friendica\Util\Strings;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Browser for Attachments
+ */
+class Browser extends BaseModule
+{
+ /** @var IHandleUserSessions */
+ protected $session;
+ /** @var App */
+ protected $app;
+
+ public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, App $app, array $server, array $parameters = [])
+ {
+ parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
+
+ $this->session = $session;
+ $this->app = $app;
+ }
+
+ protected function content(array $request = []): string
+ {
+ if (!$this->session->getLocalUserId()) {
+ throw new UnauthorizedException($this->t('Permission denied.'));
+ }
+
+ // Needed to match the correct template in a module that uses a different theme than the user/site/default
+ $theme = Strings::sanitizeFilePathItem($request['theme'] ?? '');
+ if ($theme && is_file("view/theme/$theme/config.php")) {
+ $this->app->setCurrentTheme($theme);
+ }
+
+ $files = Attach::selectToArray(['id', 'filename', 'filetype'], ['uid' => $this->session->getLocalUserId()]);
+
+
+ $fileArray = array_map([$this, 'map_files'], $files);
+
+ $tpl = Renderer::getMarkupTemplate('media/filebrowser.tpl');
+ $output = Renderer::replaceMacros($tpl, [
+ '$type' => 'attachment',
+ '$path' => ['' => $this->t('Files')],
+ '$folders' => false,
+ '$files' => $fileArray,
+ '$cancel' => $this->t('Cancel'),
+ '$nickname' => $this->app->getLoggedInUserNickname(),
+ '$upload' => $this->t('Upload'),
+ ]);
+
+ if (empty($request['mode'])) {
+ System::httpExit($output);
+ }
+
+ return $output;
+ }
+
+ protected function map_files(array $record): array
+ {
+ [$m1, $m2] = explode('/', $record['filetype']);
+ $filetype = file_exists(sprintf('images/icons/%s.png', $m1) ? $m1 : 'text');
+
+ return [
+ sprintf('%s/attach/%s', $this->baseUrl, $record['id']),
+ $record['filename'],
+ sprintf('%s/images/icon/16/%s.png', $this->baseUrl, $filetype),
+ ];
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Media\Attachment;
+
+use Friendica\App;
+use Friendica\Core\Config\Capability\IManageConfigValues;
+use Friendica\Core\L10n;
+use Friendica\Core\Session\Capability\IHandleUserSessions;
+use Friendica\Database\Database;
+use Friendica\Model\Attach;
+use Friendica\Model\User;
+use Friendica\Module\Response;
+use Friendica\Navigation\SystemMessages;
+use Friendica\Network\HTTPException\InternalServerErrorException;
+use Friendica\Util\Profiler;
+use Friendica\Util\Strings;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Asynchronous attachment upload module
+ *
+ * Only used as the target action of the AjaxUpload Javascript library
+ */
+class Upload extends \Friendica\BaseModule
+{
+ /** @var Database */
+ private $database;
+
+ /** @var IHandleUserSessions */
+ private $userSession;
+
+ /** @var IManageConfigValues */
+ private $config;
+
+ /** @var SystemMessages */
+ private $systemMessages;
+
+ /** @var bool */
+ private $isJson;
+
+ public function __construct(SystemMessages $systemMessages, IManageConfigValues $config, IHandleUserSessions $userSession, Database $database, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
+ {
+ parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
+
+ $this->database = $database;
+ $this->userSession = $userSession;
+ $this->config = $config;
+ $this->systemMessages = $systemMessages;
+ }
+
+ protected function post(array $request = [])
+ {
+ if ($this->isJson = !empty($request['response']) && $request['response'] == 'json') {
+ $this->response->setType(Response::TYPE_JSON, 'application/json');
+ }
+
+ $owner = User::getOwnerDataById($this->userSession->getLocalUserId());
+
+ if (!$owner) {
+ $this->logger->warning('Owner not found.', ['uid' => $this->userSession->getLocalUserId()]);
+ return $this->return(401, $this->t('Invalid request.'));
+ }
+
+ if (empty($_FILES['userfile'])) {
+ $this->logger->warning('No file uploaded (empty userfile)');
+ return $this->return(401, $this->t('Invalid request.'), true);
+ }
+
+ $tempFileName = $_FILES['userfile']['tmp_name'];
+ $fileName = basename($_FILES['userfile']['name']);
+ $fileSize = intval($_FILES['userfile']['size']);
+ $maxFileSize = $this->config->get('system', 'maxfilesize');
+
+ /*
+ * Found html code written in text field of form, when trying to upload a
+ * file with filesize greater than upload_max_filesize. Cause is unknown.
+ * Then Filesize gets <= 0.
+ */
+ if ($fileSize <= 0) {
+ @unlink($tempFileName);
+ $msg = $this->t('Sorry, maybe your upload is bigger than the PHP configuration allows') . '<br />' . $this->t('Or - did you try to upload an empty file?');
+ $this->logger->warning($msg, ['fileSize' => $fileSize]);
+ return $this->return(401, $msg, true);
+ }
+
+ if ($maxFileSize && $fileSize > $maxFileSize) {
+ @unlink($tempFileName);
+ $msg = $this->t('File exceeds size limit of %s', Strings::formatBytes($maxFileSize));
+ $this->logger->warning($msg, ['fileSize' => $fileSize]);
+ return $this->return(401, $msg);
+ }
+
+ $newid = Attach::storeFile($tempFileName, $owner['uid'], $fileName, '<' . $owner['id'] . '>');
+
+ @unlink($tempFileName);
+
+ if ($newid === false) {
+ $msg = $this->t('File upload failed.');
+ $this->logger->warning($msg);
+ return $this->return(500, $msg);
+ }
+
+ if ($this->isJson) {
+ $content = json_encode(['ok' => true, 'id' => $newid], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+ } else {
+ $content = "\n\n" . '[attachment]' . $newid . '[/attachment]' . "\n";
+ }
+
+ return $this->response->addContent($content);
+ }
+
+ /**
+ * @param int $httpCode
+ * @param string $message
+ * @param bool $systemMessage
+ * @return void
+ * @throws InternalServerErrorException
+ */
+ private function return(int $httpCode, string $message, bool $systemMessage = false): void
+ {
+ $this->response->setStatus($httpCode, $message);
+
+ if ($this->isJson) {
+ $this->response->addContent(json_encode(['error' => $message], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
+ } else {
+ if ($systemMessage) {
+ $this->systemMessages->addNotice($message);
+ }
+
+ $this->response->addContent($message);
+ }
+ }
+}
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2010-2022, the Friendica project
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Module\Media;
-
-use Friendica\App;
-use Friendica\BaseModule;
-use Friendica\Core\L10n;
-use Friendica\Core\Renderer;
-use Friendica\Core\Session\Capability\IHandleUserSessions;
-use Friendica\Core\System;
-use Friendica\Model\Attach;
-use Friendica\Module\Response;
-use Friendica\Network\HTTPException\UnauthorizedException;
-use Friendica\Util\Profiler;
-use Friendica\Util\Strings;
-use Psr\Log\LoggerInterface;
-
-/**
- * Browser for Attachments
- */
-class AttachmentBrowser extends BaseModule
-{
- /** @var IHandleUserSessions */
- protected $session;
- /** @var App */
- protected $app;
-
- public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, App $app, array $server, array $parameters = [])
- {
- parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
-
- $this->session = $session;
- $this->app = $app;
- }
-
- protected function content(array $request = []): string
- {
- if (!$this->session->getLocalUserId()) {
- throw new UnauthorizedException($this->t('Permission denied.'));
- }
-
- // Needed to match the correct template in a module that uses a different theme than the user/site/default
- $theme = Strings::sanitizeFilePathItem($request['theme'] ?? '');
- if ($theme && is_file("view/theme/$theme/config.php")) {
- $this->app->setCurrentTheme($theme);
- }
-
- $files = Attach::selectToArray(['id', 'filename', 'filetype'], ['uid' => $this->session->getLocalUserId()]);
-
-
- $fileArray = array_map([$this, 'map_files'], $files);
-
- $tpl = Renderer::getMarkupTemplate('media/filebrowser.tpl');
- $output = Renderer::replaceMacros($tpl, [
- '$type' => 'attachment',
- '$path' => ['' => $this->t('Files')],
- '$folders' => false,
- '$files' => $fileArray,
- '$cancel' => $this->t('Cancel'),
- '$nickname' => $this->app->getLoggedInUserNickname(),
- '$upload' => $this->t('Upload'),
- ]);
-
- if (empty($request['mode'])) {
- System::httpExit($output);
- }
-
- return $output;
- }
-
- protected function map_files(array $record): array
- {
- [$m1, $m2] = explode('/', $record['filetype']);
- $filetype = file_exists(sprintf('images/icons/%s.png', $m1) ? $m1 : 'text');
-
- return [
- sprintf('%s/attach/%s', $this->baseUrl, $record['id']),
- $record['filename'],
- sprintf('%s/images/icon/16/%s.png', $this->baseUrl, $filetype),
- ];
- }
-}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Media\Photo;
+
+use Friendica\App;
+use Friendica\BaseModule;
+use Friendica\Core\L10n;
+use Friendica\Core\Renderer;
+use Friendica\Core\Session\Capability\IHandleUserSessions;
+use Friendica\Core\System;
+use Friendica\Model\Photo;
+use Friendica\Module\Response;
+use Friendica\Network\HTTPException\UnauthorizedException;
+use Friendica\Util\Images;
+use Friendica\Util\Profiler;
+use Friendica\Util\Strings;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Browser for Photos
+ */
+class Browser extends BaseModule
+{
+ /** @var IHandleUserSessions */
+ protected $session;
+ /** @var App */
+ protected $app;
+
+ public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, App $app, array $server, array $parameters = [])
+ {
+ parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
+
+ $this->session = $session;
+ $this->app = $app;
+ }
+
+ protected function content(array $request = []): string
+ {
+ if (!$this->session->getLocalUserId()) {
+ throw new UnauthorizedException($this->t('Permission denied.'));
+ }
+
+ // Needed to match the correct template in a module that uses a different theme than the user/site/default
+ $theme = Strings::sanitizeFilePathItem($request['theme'] ?? '');
+ if ($theme && is_file("view/theme/$theme/config.php")) {
+ $this->app->setCurrentTheme($theme);
+ }
+
+ $album = $this->parameters['album'] ?? null;
+
+ $photos = Photo::getBrowsablePhotosForUser($this->session->getLocalUserId(), $album);
+ $albums = $album ? false : Photo::getBrowsableAlbumsForUser($this->session->getLocalUserId());
+
+ $path = [
+ '' => $this->t('Photos'),
+ ];
+ if (!empty($album)) {
+ $path[$album] = $album;
+ }
+
+ $photosArray = array_map([$this, 'map_files'], $photos);
+
+ $tpl = Renderer::getMarkupTemplate('media/filebrowser.tpl');
+ $output = Renderer::replaceMacros($tpl, [
+ '$type' => 'photo',
+ '$path' => $path,
+ '$folders' => $albums,
+ '$files' => $photosArray,
+ '$cancel' => $this->t('Cancel'),
+ '$nickname' => $this->app->getLoggedInUserNickname(),
+ '$upload' => $this->t('Upload'),
+ ]);
+
+ if (empty($request['mode'])) {
+ System::httpExit($output);
+ }
+
+ return $output;
+ }
+
+ protected function map_files(array $record): array
+ {
+ $types = Images::supportedTypes();
+ $ext = $types[$record['type']];
+ $filename_e = $record['filename'];
+
+ // Take the largest picture that is smaller or equal 640 pixels
+ $photo = Photo::selectFirst(
+ ['scale'],
+ [
+ "`resource-id` = ? AND `height` <= ? AND `width` <= ?",
+ $record['resource-id'],
+ 640,
+ 640
+ ],
+ ['order' => ['scale']]);
+ $scale = $photo['scale'] ?? $record['loq'];
+
+ return [
+ sprintf('%s/photos/%s/image/%s', $this->baseUrl, $this->app->getLoggedInUserNickname(), $record['resource-id']),
+ $filename_e,
+ sprintf('%s/photo/%s-%s.%s', $this->baseUrl, $record['resource-id'], $scale, $ext),
+ $record['desc'],
+ ];
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Media\Photo;
+
+use Friendica\App;
+use Friendica\Core\Config\Capability\IManageConfigValues;
+use Friendica\Core\L10n;
+use Friendica\Core\Session\Capability\IHandleUserSessions;
+use Friendica\Database\Database;
+use Friendica\Model\Photo;
+use Friendica\Model\User;
+use Friendica\Module\BaseApi;
+use Friendica\Module\Response;
+use Friendica\Navigation\SystemMessages;
+use Friendica\Network\HTTPException\InternalServerErrorException;
+use Friendica\Object\Image;
+use Friendica\Util\Images;
+use Friendica\Util\Profiler;
+use Friendica\Util\Strings;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Asynchronous photo upload module
+ *
+ * Only used as the target action of the AjaxUpload Javascript library
+ */
+class Upload extends \Friendica\BaseModule
+{
+ /** @var Database */
+ private $database;
+
+ /** @var IHandleUserSessions */
+ private $userSession;
+
+ /** @var SystemMessages */
+ private $systemMessages;
+
+ /** @var IManageConfigValues */
+ private $config;
+
+ /** @var bool */
+ private $isJson = false;
+
+ public function __construct(IManageConfigValues $config, SystemMessages $systemMessages, IHandleUserSessions $userSession, Database $database, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
+ {
+ parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
+
+ $this->database = $database;
+ $this->userSession = $userSession;
+ $this->systemMessages = $systemMessages;
+ $this->config = $config;
+ }
+
+ protected function post(array $request = [])
+ {
+ $this->isJson = !empty($request['response']) && $request['response'] == 'json';
+
+ $album = trim($request['album'] ?? '');
+
+ $owner = User::getOwnerDataById($this->userSession->getLocalUserId());
+
+ if (!$owner) {
+ $this->logger->warning('Owner not found.', ['uid' => $this->userSession->getLocalUserId()]);
+ return $this->return(401, $this->t('Invalid request.'));
+ }
+
+ if (empty($_FILES['userfile']) && empty($_FILES['media'])) {
+ $this->logger->warning('Empty "userfile" and "media" field');
+ return $this->return(401, $this->t('Invalid request.'));
+ }
+
+ $src = '';
+ $filename = '';
+ $filesize = 0;
+ $filetype = '';
+
+ if (!empty($_FILES['userfile'])) {
+ $src = $_FILES['userfile']['tmp_name'];
+ $filename = basename($_FILES['userfile']['name']);
+ $filesize = intval($_FILES['userfile']['size']);
+ $filetype = $_FILES['userfile']['type'];
+ } elseif (!empty($_FILES['media'])) {
+ if (!empty($_FILES['media']['tmp_name'])) {
+ if (is_array($_FILES['media']['tmp_name'])) {
+ $src = $_FILES['media']['tmp_name'][0];
+ } else {
+ $src = $_FILES['media']['tmp_name'];
+ }
+ }
+
+ if (!empty($_FILES['media']['name'])) {
+ if (is_array($_FILES['media']['name'])) {
+ $filename = basename($_FILES['media']['name'][0]);
+ } else {
+ $filename = basename($_FILES['media']['name']);
+ }
+ }
+
+ if (!empty($_FILES['media']['size'])) {
+ if (is_array($_FILES['media']['size'])) {
+ $filesize = intval($_FILES['media']['size'][0]);
+ } else {
+ $filesize = intval($_FILES['media']['size']);
+ }
+ }
+
+ if (!empty($_FILES['media']['type'])) {
+ if (is_array($_FILES['media']['type'])) {
+ $filetype = $_FILES['media']['type'][0];
+ } else {
+ $filetype = $_FILES['media']['type'];
+ }
+ }
+ }
+
+ if ($src == '') {
+ $this->logger->warning('File source (temporary file) cannot be determined', ['$_FILES' => $_FILES]);
+ return $this->return(401, $this->t('Invalid request.'), true);
+ }
+
+ $filetype = Images::getMimeTypeBySource($src, $filename, $filetype);
+
+ $this->logger->info('File upload:', [
+ 'src' => $src,
+ 'filename' => $filename,
+ 'filesize' => $filesize,
+ 'filetype' => $filetype,
+ ]);
+
+ $imagedata = @file_get_contents($src);
+ $image = new Image($imagedata, $filetype);
+
+ if (!$image->isValid()) {
+ @unlink($src);
+ $this->logger->warning($this->t('Unable to process image.'), ['imagedata[]' => gettype($imagedata), 'filetype' => $filetype]);
+ return $this->return(401, $this->t('Unable to process image.'));
+ }
+
+ $image->orient($src);
+ @unlink($src);
+
+ $max_length = $this->config->get('system', 'max_image_length');
+ if ($max_length > 0) {
+ $image->scaleDown($max_length);
+ $filesize = strlen($image->asString());
+ $this->logger->info('File upload: Scaling picture to new size', ['max_length' => $max_length]);
+ }
+
+ $width = $image->getWidth();
+ $height = $image->getHeight();
+
+ $maximagesize = $this->config->get('system', 'maximagesize');
+
+ if (!empty($maximagesize) && $filesize > $maximagesize) {
+ // Scale down to multiples of 640 until the maximum size isn't exceeded anymore
+ foreach ([5120, 2560, 1280, 640] as $pixels) {
+ if ($filesize > $maximagesize && max($width, $height) > $pixels) {
+ $this->logger->info('Resize', ['size' => $filesize, 'width' => $width, 'height' => $height, 'max' => $maximagesize, 'pixels' => $pixels]);
+ $image->scaleDown($pixels);
+ $filesize = strlen($image->asString());
+ $width = $image->getWidth();
+ $height = $image->getHeight();
+ }
+ }
+
+ if ($filesize > $maximagesize) {
+ @unlink($src);
+ $this->logger->notice('Image size is too big', ['size' => $filesize, 'max' => $maximagesize]);
+ return $this->return(401, $this->t('Image exceeds size limit of %s', Strings::formatBytes($maximagesize)));
+ }
+ }
+
+ $resource_id = Photo::newResource();
+
+ $smallest = 0;
+
+ // If we don't have an album name use the Wall Photos album
+ if (!strlen($album)) {
+ $album = $this->t('Wall Photos');
+ }
+
+ $allow_cid = '<' . $owner['id'] . '>';
+
+ $result = Photo::store($image, $owner['uid'], 0, $resource_id, $filename, $album, 0, Photo::DEFAULT, $allow_cid);
+ if (!$result) {
+ $this->logger->warning('Photo::store() failed', ['result' => $result]);
+ return $this->return(401, $this->t('Image upload failed.'));
+ }
+
+ if ($width > 640 || $height > 640) {
+ $image->scaleDown(640);
+ $result = Photo::store($image, $owner['uid'], 0, $resource_id, $filename, $album, 1, Photo::DEFAULT, $allow_cid);
+ if ($result) {
+ $smallest = 1;
+ }
+ }
+
+ if ($width > 320 || $height > 320) {
+ $image->scaleDown(320);
+ $result = Photo::store($image, $owner['uid'], 0, $resource_id, $filename, $album, 2, Photo::DEFAULT, $allow_cid);
+ if ($result && ($smallest == 0)) {
+ $smallest = 2;
+ }
+ }
+
+ $this->logger->info('upload done');
+ return $this->return(200, "\n\n" . '[url=' . $this->baseUrl . '/photos/' . $owner['nickname'] . '/image/' . $resource_id . '][img]' . $this->baseUrl . "/photo/$resource_id-$smallest." . $image->getExt() . "[/img][/url]\n\n");
+ }
+
+ /**
+ * @param int $httpCode
+ * @param string $message
+ * @param bool $systemMessage
+ * @return void
+ * @throws InternalServerErrorException
+ */
+ private function return(int $httpCode, string $message, bool $systemMessage = false): void
+ {
+ if ($this->isJson) {
+ $message = $httpCode >= 400 ? ['error' => $message] : ['ok' => true];
+ $this->response->setType(Response::TYPE_JSON, 'application/json');
+ $this->response->addContent(json_encode($message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
+ } else {
+ if ($systemMessage) {
+ $this->systemMessages->addNotice($message);
+ }
+
+ if ($httpCode >= 400) {
+ $this->response->setStatus($httpCode, $message);
+ }
+
+ $this->response->addContent($message);
+ }
+ }
+}
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2010-2022, the Friendica project
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Module\Media;
-
-use Friendica\App;
-use Friendica\BaseModule;
-use Friendica\Core\L10n;
-use Friendica\Core\Renderer;
-use Friendica\Core\Session\Capability\IHandleUserSessions;
-use Friendica\Core\System;
-use Friendica\Model\Photo;
-use Friendica\Module\Response;
-use Friendica\Network\HTTPException\UnauthorizedException;
-use Friendica\Util\Images;
-use Friendica\Util\Profiler;
-use Friendica\Util\Strings;
-use Psr\Log\LoggerInterface;
-
-/**
- * Browser for Photos
- */
-class PhotosBrowser extends BaseModule
-{
- /** @var IHandleUserSessions */
- protected $session;
- /** @var App */
- protected $app;
-
- public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, App $app, array $server, array $parameters = [])
- {
- parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
-
- $this->session = $session;
- $this->app = $app;
- }
-
- protected function content(array $request = []): string
- {
- if (!$this->session->getLocalUserId()) {
- throw new UnauthorizedException($this->t('Permission denied.'));
- }
-
- // Needed to match the correct template in a module that uses a different theme than the user/site/default
- $theme = Strings::sanitizeFilePathItem($request['theme'] ?? '');
- if ($theme && is_file("view/theme/$theme/config.php")) {
- $this->app->setCurrentTheme($theme);
- }
-
- $album = $this->parameters['album'] ?? null;
-
- $photos = Photo::getBrowsablePhotosForUser($this->session->getLocalUserId(), $album);
- $albums = $album ? false : Photo::getBrowsableAlbumsForUser($this->session->getLocalUserId());
-
- $path = [
- '' => $this->t('Photos'),
- ];
- if (!empty($album)) {
- $path[$album] = $album;
- }
-
- $photosArray = array_map([$this, 'map_files'], $photos);
-
- $tpl = Renderer::getMarkupTemplate('media/filebrowser.tpl');
- $output = Renderer::replaceMacros($tpl, [
- '$type' => 'photos',
- '$path' => $path,
- '$folders' => $albums,
- '$files' => $photosArray,
- '$cancel' => $this->t('Cancel'),
- '$nickname' => $this->app->getLoggedInUserNickname(),
- '$upload' => $this->t('Upload'),
- ]);
-
- if (empty($request['mode'])) {
- System::httpExit($output);
- }
-
- return $output;
- }
-
- protected function map_files(array $record): array
- {
- $types = Images::supportedTypes();
- $ext = $types[$record['type']];
- $filename_e = $record['filename'];
-
- // Take the largest picture that is smaller or equal 640 pixels
- $photo = Photo::selectFirst(
- ['scale'],
- [
- "`resource-id` = ? AND `height` <= ? AND `width` <= ?",
- $record['resource-id'],
- 640,
- 640
- ],
- ['order' => ['scale']]);
- $scale = $photo['scale'] ?? $record['loq'];
-
- return [
- sprintf('%s/photos/%s/image/%s', $this->baseUrl, $this->app->getLoggedInUserNickname(), $record['resource-id']),
- $filename_e,
- sprintf('%s/photo/%s-%s.%s', $this->baseUrl, $record['resource-id'], $scale, $ext),
- $record['desc'],
- ];
- }
-}
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2010-2022, the Friendica project
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Module\Profile\Attachment;
-
-use Friendica\App;
-use Friendica\Core\Config\Capability\IManageConfigValues;
-use Friendica\Core\L10n;
-use Friendica\Core\Session\Capability\IHandleUserSessions;
-use Friendica\Database\Database;
-use Friendica\Model\Attach;
-use Friendica\Model\User;
-use Friendica\Module\Response;
-use Friendica\Navigation\SystemMessages;
-use Friendica\Network\HTTPException\InternalServerErrorException;
-use Friendica\Util\Profiler;
-use Friendica\Util\Strings;
-use Psr\Log\LoggerInterface;
-
-/**
- * Asynchronous attachment upload module
- *
- * Only used as the target action of the AjaxUpload Javascript library
- */
-class Upload extends \Friendica\BaseModule
-{
- /** @var Database */
- private $database;
-
- /** @var IHandleUserSessions */
- private $userSession;
-
- /** @var IManageConfigValues */
- private $config;
-
- /** @var SystemMessages */
- private $systemMessages;
-
- /** @var bool */
- private $isJson;
-
- public function __construct(SystemMessages $systemMessages, IManageConfigValues $config, IHandleUserSessions $userSession, Database $database, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
- {
- parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
-
- $this->database = $database;
- $this->userSession = $userSession;
- $this->config = $config;
- $this->systemMessages = $systemMessages;
- }
-
- protected function post(array $request = [])
- {
- if ($this->isJson = !empty($request['response']) && $request['response'] == 'json') {
- $this->response->setType(Response::TYPE_JSON, 'application/json');
- }
-
- $nick = $this->parameters['nickname'];
- $owner = User::getOwnerDataByNick($nick);
- if (!$owner) {
- $this->logger->warning('owner is not a valid record:', ['owner' => $owner, 'nick' => $nick]);
- return $this->return(401, $this->t('Invalid request.'));
- }
-
- $can_post = false;
- $contact_id = 0;
- $page_owner_uid = $owner['uid'];
- $community_page = $owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY;
-
- if ($this->userSession->getLocalUserId() && $this->userSession->getLocalUserId() == $page_owner_uid) {
- $can_post = true;
- } elseif ($community_page && !empty($this->userSession->getRemoteContactID($page_owner_uid))) {
- $contact_id = $this->userSession->getRemoteContactID($page_owner_uid);
- $can_post = $this->database->exists('contact', ['blocked' => false, 'pending' => false, 'id' => $contact_id, 'uid' => $page_owner_uid]);
- }
-
- if (!$can_post) {
- $this->logger->warning('User does not have required permissions', ['contact_id' => $contact_id, 'page_owner_uid' => $page_owner_uid]);
- return $this->return(403, $this->t('Permission denied.'), true);
- }
-
- if (empty($_FILES['userfile'])) {
- $this->logger->warning('No file uploaded (empty userfile)');
- return $this->return(401, $this->t('Invalid request.'), true);
- }
-
- $tempFileName = $_FILES['userfile']['tmp_name'];
- $fileName = basename($_FILES['userfile']['name']);
- $fileSize = intval($_FILES['userfile']['size']);
- $maxFileSize = $this->config->get('system', 'maxfilesize');
-
- /*
- * Found html code written in text field of form, when trying to upload a
- * file with filesize greater than upload_max_filesize. Cause is unknown.
- * Then Filesize gets <= 0.
- */
- if ($fileSize <= 0) {
- @unlink($tempFileName);
- $msg = $this->t('Sorry, maybe your upload is bigger than the PHP configuration allows') . '<br />' . $this->t('Or - did you try to upload an empty file?');
- $this->logger->warning($msg, ['fileSize' => $fileSize]);
- return $this->return(401, $msg, true);
- }
-
- if ($maxFileSize && $fileSize > $maxFileSize) {
- @unlink($tempFileName);
- $msg = $this->t('File exceeds size limit of %s', Strings::formatBytes($maxFileSize));
- $this->logger->warning($msg, ['fileSize' => $fileSize]);
- return $this->return(401, $msg);
- }
-
- $newid = Attach::storeFile($tempFileName, $page_owner_uid, $fileName, '<' . $owner['id'] . '>');
-
- @unlink($tempFileName);
-
- if ($newid === false) {
- $msg = $this->t('File upload failed.');
- $this->logger->warning($msg);
- return $this->return(500, $msg);
- }
-
- if ($this->isJson) {
- $content = json_encode(['ok' => true, 'id' => $newid], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
- } else {
- $content = "\n\n" . '[attachment]' . $newid . '[/attachment]' . "\n";
- }
-
- return $this->response->addContent($content);
- }
-
- /**
- * @param int $httpCode
- * @param string $message
- * @param bool $systemMessage
- * @return void
- * @throws InternalServerErrorException
- */
- private function return(int $httpCode, string $message, bool $systemMessage = false): void
- {
- $this->response->setStatus($httpCode, $message);
-
- if ($this->isJson) {
- $this->response->addContent(json_encode(['error' => $message], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
- } else {
- if ($systemMessage) {
- $this->systemMessages->addNotice($message);
- }
-
- $this->response->addContent($message);
- }
- }
-}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module\Profile;
+
+use Friendica\App;
+use Friendica\Content\Pager;
+use Friendica\Content\Widget;
+use Friendica\Core\Config\Capability\IManageConfigValues;
+use Friendica\Core\L10n;
+use Friendica\Core\Renderer;
+use Friendica\Core\Session\Capability\IHandleUserSessions;
+use Friendica\Database\Database;
+use Friendica\Model\Contact;
+use Friendica\Model\Photo;
+use Friendica\Model\Profile;
+use Friendica\Model\User;
+use Friendica\Module\Response;
+use Friendica\Network\HTTPException;
+use Friendica\Security\Security;
+use Friendica\Util\Images;
+use Friendica\Util\Profiler;
+use Psr\Log\LoggerInterface;
+
+class Photos extends \Friendica\Module\BaseProfile
+{
+ /** @var IHandleUserSessions */
+ private $session;
+ /** @var App\Page */
+ private $page;
+ /** @var IManageConfigValues */
+ private $config;
+ /** @var App */
+ private $app;
+ /** @var Database */
+ private $database;
+
+ public function __construct(Database $database, App $app, IManageConfigValues $config, App\Page $page, IHandleUserSessions $session, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
+ {
+ parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
+
+ $this->session = $session;
+ $this->page = $page;
+ $this->config = $config;
+ $this->app = $app;
+ $this->database = $database;
+ }
+
+ protected function content(array $request = []): string
+ {
+ parent::content($request);
+
+ if ($this->config->get('system', 'block_public') && !$this->session->isAuthenticated()) {
+ throw new HttpException\ForbiddenException($this->t('Public access denied.'));
+ }
+
+ $owner = User::getOwnerDataByNick($this->parameters['nickname']);
+ if (!isset($owner['account_removed']) || $owner['account_removed']) {
+ throw new HTTPException\NotFoundException($this->t('User not found.'));
+ }
+
+ $owner_uid = $owner['uid'];
+ $is_owner = $this->session->getLocalUserId() && ($this->session->getLocalUserId() == $owner_uid);
+
+ $remote_contact = false;
+ if ($this->session->getRemoteContactID($owner_uid)) {
+ $contact_id = $this->session->getRemoteContactID($owner_uid);
+
+ $contact = Contact::getContactForUser($contact_id, $owner_uid, ['blocked', 'pending']);
+ $remote_contact = $contact && !$contact['blocked'] && !$contact['pending'];
+ }
+
+ if ($owner['hidewall'] && !$is_owner && !$remote_contact) {
+ throw new HttpException\ForbiddenException($this->t('Access to this item is restricted.'));
+ }
+
+ $this->session->set('photo_return', $this->args->getCommand());
+
+ $sql_extra = Security::getPermissionsSQLByUserId($owner_uid);
+
+ $photo = $this->database->toArray($this->database->p(
+ "SELECT COUNT(DISTINCT `resource-id`) AS `count`
+ FROM `photo`
+ WHERE `uid` = ?
+ AND `photo-type` = ?
+ $sql_extra",
+ $owner['uid'],
+ Photo::DEFAULT,
+ ));
+ $total = $photo[0]['count'];
+
+ $pager = new Pager($this->l10n, $this->args->getQueryString(), 20);
+
+ $photos = $this->database->toArray($this->database->p(
+ "SELECT
+ `resource-id`,
+ ANY_VALUE(`id`) AS `id`,
+ ANY_VALUE(`filename`) AS `filename`,
+ ANY_VALUE(`type`) AS `type`,
+ ANY_VALUE(`album`) AS `album`,
+ max(`scale`) AS `scale`,
+ ANY_VALUE(`created`) AS `created`
+ FROM `photo`
+ WHERE `uid` = ?
+ AND `photo-type` = ?
+ $sql_extra
+ GROUP BY `resource-id`
+ ORDER BY `created` DESC
+ LIMIT ? , ?",
+ $owner['uid'],
+ Photo::DEFAULT,
+ $pager->getStart(),
+ $pager->getItemsPerPage()
+ ));
+
+ $phototypes = Images::supportedTypes();
+
+ $photos = array_map(function ($photo) use ($owner, $phototypes) {
+ return [
+ 'id' => $photo['id'],
+ 'link' => 'photos/' . $owner['nickname'] . '/image/' . $photo['resource-id'],
+ 'title' => $this->t('View Photo'),
+ 'src' => 'photo/' . $photo['resource-id'] . '-' . ((($photo['scale']) == 6) ? 4 : $photo['scale']) . '.' . $phototypes[$photo['type']],
+ 'alt' => $photo['filename'],
+ 'album' => [
+ 'link' => 'photos/' . $owner['nickname'] . '/album/' . bin2hex($photo['album']),
+ 'name' => $photo['album'],
+ 'alt' => $this->t('View Album'),
+ ],
+ ];
+ }, $photos);
+
+ $tpl = Renderer::getMarkupTemplate('photos_head.tpl');
+ $this->page['htmlhead'] .= Renderer::replaceMacros($tpl, [
+ '$ispublic' => $this->t('everybody')
+ ]);
+
+ if ($albums = Photo::getAlbums($owner['uid'])) {
+ $albums = array_map(function ($album) use ($owner) {
+ return [
+ 'text' => $album['album'],
+ 'total' => $album['total'],
+ 'url' => 'photos/' . $owner['nickname'] . '/album/' . bin2hex($album['album']),
+ 'urlencode' => urlencode($album['album']),
+ 'bin2hex' => bin2hex($album['album'])
+ ];
+ }, $albums);
+
+ $photo_albums_widget = Renderer::replaceMacros(Renderer::getMarkupTemplate('photo_albums.tpl'), [
+ '$nick' => $owner['nickname'],
+ '$title' => $this->t('Photo Albums'),
+ '$recent' => $this->t('Recent Photos'),
+ '$albums' => $albums,
+ '$upload' => [$this->t('Upload New Photos'), 'photos/' . $owner['nickname'] . '/upload'],
+ '$can_post' => $this->session->getLocalUserId() && $owner['uid'] == $this->session->getLocalUserId(),
+ ]);
+ }
+
+ $this->page['aside'] .= Widget\VCard::getHTML($owner);
+
+ if (!empty($photo_albums_widget)) {
+ $this->page['aside'] .= $photo_albums_widget;
+ }
+
+ $o = self::getTabsHTML($this->app, 'photos', $is_owner, $owner['nickname'], Profile::getByUID($owner['uid'])['hide-friends'] ?? false);
+
+ $tpl = Renderer::getMarkupTemplate('photos_recent.tpl');
+ $o .= Renderer::replaceMacros($tpl, [
+ '$title' => $this->t('Recent Photos'),
+ '$can_post' => $is_owner || $remote_contact && $owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY,
+ '$upload' => [$this->t('Upload New Photos'), 'photos/' . $owner['nickname'] . '/upload'],
+ '$photos' => $photos,
+ '$paginate' => $pager->renderFull($total),
+ ]);
+
+ return $o;
+ }
+}
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2010-2022, the Friendica project
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Module\Profile\Photos;
-
-use Friendica\App;
-use Friendica\Content\Pager;
-use Friendica\Content\Widget;
-use Friendica\Core\Config\Capability\IManageConfigValues;
-use Friendica\Core\L10n;
-use Friendica\Core\Renderer;
-use Friendica\Core\Session\Capability\IHandleUserSessions;
-use Friendica\Database\Database;
-use Friendica\Model\Contact;
-use Friendica\Model\Photo;
-use Friendica\Model\Profile;
-use Friendica\Model\User;
-use Friendica\Module\Response;
-use Friendica\Network\HTTPException;
-use Friendica\Security\Security;
-use Friendica\Util\Images;
-use Friendica\Util\Profiler;
-use Psr\Log\LoggerInterface;
-
-class Index extends \Friendica\Module\BaseProfile
-{
- /** @var IHandleUserSessions */
- private $session;
- /** @var App\Page */
- private $page;
- /** @var IManageConfigValues */
- private $config;
- /** @var App */
- private $app;
- /** @var Database */
- private $database;
-
- public function __construct(Database $database, App $app, IManageConfigValues $config, App\Page $page, IHandleUserSessions $session, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
- {
- parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
-
- $this->session = $session;
- $this->page = $page;
- $this->config = $config;
- $this->app = $app;
- $this->database = $database;
- }
-
- protected function content(array $request = []): string
- {
- parent::content($request);
-
- if ($this->config->get('system', 'block_public') && !$this->session->isAuthenticated()) {
- throw new HttpException\ForbiddenException($this->t('Public access denied.'));
- }
-
- $owner = User::getOwnerDataByNick($this->parameters['nickname']);
- if (!isset($owner['account_removed']) || $owner['account_removed']) {
- throw new HTTPException\NotFoundException($this->t('User not found.'));
- }
-
- $owner_uid = $owner['uid'];
- $is_owner = $this->session->getLocalUserId() && ($this->session->getLocalUserId() == $owner_uid);
-
- $remote_contact = false;
- if ($this->session->getRemoteContactID($owner_uid)) {
- $contact_id = $this->session->getRemoteContactID($owner_uid);
-
- $contact = Contact::getContactForUser($contact_id, $owner_uid, ['blocked', 'pending']);
- $remote_contact = $contact && !$contact['blocked'] && !$contact['pending'];
- }
-
- if ($owner['hidewall'] && !$is_owner && !$remote_contact) {
- throw new HttpException\ForbiddenException($this->t('Access to this item is restricted.'));
- }
-
- $this->session->set('photo_return', $this->args->getCommand());
-
- $sql_extra = Security::getPermissionsSQLByUserId($owner_uid);
-
- $photo = $this->database->toArray($this->database->p(
- "SELECT COUNT(DISTINCT `resource-id`) AS `count`
- FROM `photo`
- WHERE `uid` = ?
- AND `photo-type` = ?
- $sql_extra",
- $owner['uid'],
- Photo::DEFAULT,
- ));
- $total = $photo[0]['count'];
-
- $pager = new Pager($this->l10n, $this->args->getQueryString(), 20);
-
- $photos = $this->database->toArray($this->database->p(
- "SELECT
- `resource-id`,
- ANY_VALUE(`id`) AS `id`,
- ANY_VALUE(`filename`) AS `filename`,
- ANY_VALUE(`type`) AS `type`,
- ANY_VALUE(`album`) AS `album`,
- max(`scale`) AS `scale`,
- ANY_VALUE(`created`) AS `created`
- FROM `photo`
- WHERE `uid` = ?
- AND `photo-type` = ?
- $sql_extra
- GROUP BY `resource-id`
- ORDER BY `created` DESC
- LIMIT ? , ?",
- $owner['uid'],
- Photo::DEFAULT,
- $pager->getStart(),
- $pager->getItemsPerPage()
- ));
-
- $phototypes = Images::supportedTypes();
-
- $photos = array_map(function ($photo) use ($owner, $phototypes) {
- return [
- 'id' => $photo['id'],
- 'link' => 'photos/' . $owner['nickname'] . '/image/' . $photo['resource-id'],
- 'title' => $this->t('View Photo'),
- 'src' => 'photo/' . $photo['resource-id'] . '-' . ((($photo['scale']) == 6) ? 4 : $photo['scale']) . '.' . $phototypes[$photo['type']],
- 'alt' => $photo['filename'],
- 'album' => [
- 'link' => 'photos/' . $owner['nickname'] . '/album/' . bin2hex($photo['album']),
- 'name' => $photo['album'],
- 'alt' => $this->t('View Album'),
- ],
- ];
- }, $photos);
-
- $tpl = Renderer::getMarkupTemplate('photos_head.tpl');
- $this->page['htmlhead'] .= Renderer::replaceMacros($tpl, [
- '$ispublic' => $this->t('everybody')
- ]);
-
- if ($albums = Photo::getAlbums($owner['uid'])) {
- $albums = array_map(function ($album) use ($owner) {
- return [
- 'text' => $album['album'],
- 'total' => $album['total'],
- 'url' => 'photos/' . $owner['nickname'] . '/album/' . bin2hex($album['album']),
- 'urlencode' => urlencode($album['album']),
- 'bin2hex' => bin2hex($album['album'])
- ];
- }, $albums);
-
- $photo_albums_widget = Renderer::replaceMacros(Renderer::getMarkupTemplate('photo_albums.tpl'), [
- '$nick' => $owner['nickname'],
- '$title' => $this->t('Photo Albums'),
- '$recent' => $this->t('Recent Photos'),
- '$albums' => $albums,
- '$upload' => [$this->t('Upload New Photos'), 'photos/' . $owner['nickname'] . '/upload'],
- '$can_post' => $this->session->getLocalUserId() && $owner['uid'] == $this->session->getLocalUserId(),
- ]);
- }
-
- $this->page['aside'] .= Widget\VCard::getHTML($owner);
-
- if (!empty($photo_albums_widget)) {
- $this->page['aside'] .= $photo_albums_widget;
- }
-
- $o = self::getTabsHTML($this->app, 'photos', $is_owner, $owner['nickname'], Profile::getByUID($owner['uid'])['hide-friends'] ?? false);
-
- $tpl = Renderer::getMarkupTemplate('photos_recent.tpl');
- $o .= Renderer::replaceMacros($tpl, [
- '$title' => $this->t('Recent Photos'),
- '$can_post' => $is_owner || $remote_contact && $owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY,
- '$upload' => [$this->t('Upload New Photos'), 'photos/' . $owner['nickname'] . '/upload'],
- '$photos' => $photos,
- '$paginate' => $pager->renderFull($total),
- ]);
-
- return $o;
- }
-}
+++ /dev/null
-<?php
-/**
- * @copyright Copyright (C) 2010-2022, the Friendica project
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Module\Profile\Photos;
-
-use Friendica\App;
-use Friendica\Core\Config\Capability\IManageConfigValues;
-use Friendica\Core\L10n;
-use Friendica\Core\Session\Capability\IHandleUserSessions;
-use Friendica\Database\Database;
-use Friendica\Model\Photo;
-use Friendica\Model\User;
-use Friendica\Module\BaseApi;
-use Friendica\Module\Response;
-use Friendica\Navigation\SystemMessages;
-use Friendica\Network\HTTPException\InternalServerErrorException;
-use Friendica\Object\Image;
-use Friendica\Util\Images;
-use Friendica\Util\Profiler;
-use Friendica\Util\Strings;
-use Psr\Log\LoggerInterface;
-
-/**
- * Asynchronous photo upload module
- *
- * Only used as the target action of the AjaxUpload Javascript library
- */
-class Upload extends \Friendica\BaseModule
-{
- /** @var Database */
- private $database;
-
- /** @var IHandleUserSessions */
- private $userSession;
-
- /** @var SystemMessages */
- private $systemMessages;
-
- /** @var IManageConfigValues */
- private $config;
-
- /** @var bool */
- private $isJson = false;
-
- public function __construct(IManageConfigValues $config, SystemMessages $systemMessages, IHandleUserSessions $userSession, Database $database, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
- {
- parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
-
- $this->database = $database;
- $this->userSession = $userSession;
- $this->systemMessages = $systemMessages;
- $this->config = $config;
- }
-
- protected function post(array $request = [])
- {
- $this->isJson = !empty($request['response']) && $request['response'] == 'json';
-
- $album = trim($request['album'] ?? '');
-
- if (empty($_FILES['media'])) {
- $user = $this->database->selectFirst('owner-view', ['id', 'uid', 'nickname', 'page-flags'], ['nickname' => $this->parameters['nickname'], 'blocked' => false]);
- } else {
- $user = $this->database->selectFirst('owner-view', ['id', 'uid', 'nickname', 'page-flags'], ['uid' => BaseApi::getCurrentUserID() ?: null, 'blocked' => false]);
- }
-
- if (!$this->database->isResult($user)) {
- $this->logger->warning('User is not valid', ['nickname' => $this->parameters['nickname'], 'user' => $user]);
- return $this->return(404, $this->t('User not found.'));
- }
-
- /*
- * Setup permissions structures
- */
- $can_post = false;
- $visitor = 0;
- $contact_id = 0;
- $page_owner_uid = $user['uid'];
-
- if ($this->userSession->getLocalUserId() && $this->userSession->getLocalUserId() == $page_owner_uid) {
- $can_post = true;
- } elseif ($user['page-flags'] == User::PAGE_FLAGS_COMMUNITY && !$this->userSession->getRemoteContactID($page_owner_uid)) {
- $contact_id = $this->userSession->getRemoteContactID($page_owner_uid);
- $can_post = $this->database->exists('contact', ['blocked' => false, 'pending' => false, 'id' => $contact_id, 'uid' => $page_owner_uid]);
- $visitor = $contact_id;
- }
-
- if (!$can_post) {
- $this->logger->warning('No permission to upload files', ['contact_id' => $contact_id, 'page_owner_uid' => $page_owner_uid]);
- return $this->return(403, $this->t('Permission denied.'), true);
- }
-
- if (empty($_FILES['userfile']) && empty($_FILES['media'])) {
- $this->logger->warning('Empty "userfile" and "media" field');
- return $this->return(401, $this->t('Invalid request.'));
- }
-
- $src = '';
- $filename = '';
- $filesize = 0;
- $filetype = '';
-
- if (!empty($_FILES['userfile'])) {
- $src = $_FILES['userfile']['tmp_name'];
- $filename = basename($_FILES['userfile']['name']);
- $filesize = intval($_FILES['userfile']['size']);
- $filetype = $_FILES['userfile']['type'];
- } elseif (!empty($_FILES['media'])) {
- if (!empty($_FILES['media']['tmp_name'])) {
- if (is_array($_FILES['media']['tmp_name'])) {
- $src = $_FILES['media']['tmp_name'][0];
- } else {
- $src = $_FILES['media']['tmp_name'];
- }
- }
-
- if (!empty($_FILES['media']['name'])) {
- if (is_array($_FILES['media']['name'])) {
- $filename = basename($_FILES['media']['name'][0]);
- } else {
- $filename = basename($_FILES['media']['name']);
- }
- }
-
- if (!empty($_FILES['media']['size'])) {
- if (is_array($_FILES['media']['size'])) {
- $filesize = intval($_FILES['media']['size'][0]);
- } else {
- $filesize = intval($_FILES['media']['size']);
- }
- }
-
- if (!empty($_FILES['media']['type'])) {
- if (is_array($_FILES['media']['type'])) {
- $filetype = $_FILES['media']['type'][0];
- } else {
- $filetype = $_FILES['media']['type'];
- }
- }
- }
-
- if ($src == '') {
- $this->logger->warning('File source (temporary file) cannot be determined', ['$_FILES' => $_FILES]);
- return $this->return(401, $this->t('Invalid request.'), true);
- }
-
- $filetype = Images::getMimeTypeBySource($src, $filename, $filetype);
-
- $this->logger->info('File upload:', [
- 'src' => $src,
- 'filename' => $filename,
- 'filesize' => $filesize,
- 'filetype' => $filetype,
- ]);
-
- $imagedata = @file_get_contents($src);
- $image = new Image($imagedata, $filetype);
-
- if (!$image->isValid()) {
- @unlink($src);
- $this->logger->warning($this->t('Unable to process image.'), ['imagedata[]' => gettype($imagedata), 'filetype' => $filetype]);
- return $this->return(401, $this->t('Unable to process image.'));
- }
-
- $image->orient($src);
- @unlink($src);
-
- $max_length = $this->config->get('system', 'max_image_length');
- if ($max_length > 0) {
- $image->scaleDown($max_length);
- $filesize = strlen($image->asString());
- $this->logger->info('File upload: Scaling picture to new size', ['max_length' => $max_length]);
- }
-
- $width = $image->getWidth();
- $height = $image->getHeight();
-
- $maximagesize = $this->config->get('system', 'maximagesize');
-
- if (!empty($maximagesize) && $filesize > $maximagesize) {
- // Scale down to multiples of 640 until the maximum size isn't exceeded anymore
- foreach ([5120, 2560, 1280, 640] as $pixels) {
- if ($filesize > $maximagesize && max($width, $height) > $pixels) {
- $this->logger->info('Resize', ['size' => $filesize, 'width' => $width, 'height' => $height, 'max' => $maximagesize, 'pixels' => $pixels]);
- $image->scaleDown($pixels);
- $filesize = strlen($image->asString());
- $width = $image->getWidth();
- $height = $image->getHeight();
- }
- }
-
- if ($filesize > $maximagesize) {
- @unlink($src);
- $this->logger->notice('Image size is too big', ['size' => $filesize, 'max' => $maximagesize]);
- return $this->return(401, $this->t('Image exceeds size limit of %s', Strings::formatBytes($maximagesize)));
- }
- }
-
- $resource_id = Photo::newResource();
-
- $smallest = 0;
-
- // If we don't have an album name use the Wall Photos album
- if (!strlen($album)) {
- $album = $this->t('Wall Photos');
- }
-
- $allow_cid = '<' . $user['id'] . '>';
-
- $result = Photo::store($image, $page_owner_uid, $visitor, $resource_id, $filename, $album, 0, Photo::DEFAULT, $allow_cid);
- if (!$result) {
- $this->logger->warning('Photo::store() failed', ['result' => $result]);
- return $this->return(401, $this->t('Image upload failed.'));
- }
-
- if ($width > 640 || $height > 640) {
- $image->scaleDown(640);
- $result = Photo::store($image, $page_owner_uid, $visitor, $resource_id, $filename, $album, 1, Photo::DEFAULT, $allow_cid);
- if ($result) {
- $smallest = 1;
- }
- }
-
- if ($width > 320 || $height > 320) {
- $image->scaleDown(320);
- $result = Photo::store($image, $page_owner_uid, $visitor, $resource_id, $filename, $album, 2, Photo::DEFAULT, $allow_cid);
- if ($result && ($smallest == 0)) {
- $smallest = 2;
- }
- }
-
- $this->logger->info('upload done');
- return $this->return(200, "\n\n" . '[url=' . $this->baseUrl . '/photos/' . $user['nickname'] . '/image/' . $resource_id . '][img]' . $this->baseUrl . "/photo/$resource_id-$smallest." . $image->getExt() . "[/img][/url]\n\n");
- }
-
- /**
- * @param int $httpCode
- * @param string $message
- * @param bool $systemMessage
- * @return void
- * @throws InternalServerErrorException
- */
- private function return(int $httpCode, string $message, bool $systemMessage = false): void
- {
- if ($this->isJson) {
- $message = $httpCode >= 400 ? ['error' => $message] : ['ok' => true];
- $this->response->setType(Response::TYPE_JSON, 'application/json');
- $this->response->addContent(json_encode($message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
- } else {
- if ($systemMessage) {
- $this->systemMessages->addNotice($message);
- }
-
- if ($httpCode >= 400) {
- $this->response->setStatus($httpCode, $message);
- }
-
- $this->response->addContent($message);
- }
- }
-}
use Friendica\Module;
$profileRoutes = [
- '' => [Module\Profile\Index::class, [R::GET]],
- '/attachment/upload' => [Module\Profile\Attachment\Upload::class, [ R::POST]],
- '/contacts/common' => [Module\Profile\Common::class, [R::GET]],
- '/contacts[/{type}]' => [Module\Profile\Contacts::class, [R::GET]],
- '/media' => [Module\Profile\Media::class, [R::GET]],
- '/photos' => [Module\Profile\Photos\Index::class, [R::GET ]],
- '/photos/upload' => [Module\Profile\Photos\Upload::class, [ R::POST]],
- '/profile' => [Module\Profile\Profile::class, [R::GET]],
- '/remote_follow' => [Module\Profile\RemoteFollow::class, [R::GET, R::POST]],
- '/schedule' => [Module\Profile\Schedule::class, [R::GET, R::POST]],
- '/status[/{category}[/{date1}[/{date2}]]]' => [Module\Profile\Status::class, [R::GET]],
- '/unkmail' => [Module\Profile\UnkMail::class, [R::GET, R::POST]],
+ '' => [Module\Profile\Index::class, [R::GET]],
+ '/contacts/common' => [Module\Profile\Common::class, [R::GET]],
+ '/contacts[/{type}]' => [Module\Profile\Contacts::class, [R::GET]],
+ '/media' => [Module\Profile\Media::class, [R::GET]],
+ '/photos' => [Module\Profile\Photos::class, [R::GET ]],
+ '/profile' => [Module\Profile\Profile::class, [R::GET]],
+ '/remote_follow' => [Module\Profile\RemoteFollow::class, [R::GET, R::POST]],
+ '/schedule' => [Module\Profile\Schedule::class, [R::GET, R::POST]],
+ '/status[/{category}[/{date1}[/{date2}]]]' => [Module\Profile\Status::class, [R::GET]],
+ '/unkmail' => [Module\Profile\UnkMail::class, [R::GET, R::POST]],
];
$apiRoutes = [
'/friendica.webmanifest' => [Module\Manifest::class, [R::GET]],
'/media' => [
- '/attachment/browser' => [Module\Media\AttachmentBrowser::class, [R::GET]],
- '/photos/browser[/{album}]' => [Module\Media\PhotosBrowser::class, [R::GET]],
+ '/attachment/browser' => [Module\Media\Attachment\Browser::class, [R::GET]],
+ '/attachment/upload' => [Module\Media\Attachment\Upload::class, [ R::POST]],
+ '/photo/browser[/{album}]' => [Module\Media\Photo\Browser::class, [R::GET]],
+ '/photo/upload' => [Module\Media\Photo\Upload::class, [ R::POST]],
],
'/moderation' => [
// Kept for backwards-compatibility
// @TODO remove by version 2023.12
- '/photos/{nickname}' => [Module\Profile\Photos\Index::class, [R::GET]],
+ '/photos/{nickname}' => [Module\Profile\Photos::class, [R::GET]],
'/ping' => [Module\Notifications\Ping::class, [R::GET]],
.fbrowser .path a:before, .fbrowser .path .btn-link:before { content: "/"; padding-right: 5px;}
.fbrowser .folders ul { list-style-type: none; padding-left: 10px;}
.fbrowser .list { height: auto; overflow-y: hidden; margin: 10px 0px; }
-.fbrowser.photos .photo-album-image-wrapper { float: left; }
-.fbrowser.photos a img, .fbrowser.photos .btn-link img { height: 48px; }
-.fbrowser.photos a p, .fbrowser.photos .btn-link p { display: none;}
+.fbrowser.photo .photo-album-image-wrapper { float: left; }
+.fbrowser.photo a img, .fbrowser.photo .btn-link img { height: 48px; }
+.fbrowser.photo a p, .fbrowser.photo .btn-link p { display: none;}
.fbrowser.attachment .photo-album-image-wrapper { float:none; white-space: nowrap; }
.fbrowser.attachment img { display: inline; }
.fbrowser.attachment p { display: inline; white-space: nowrap; }
/* event from comment textarea button popups */
/* insert returned bbcode at cursor position or replace selected text */
- $('body').on('fbrowser.photos.comment', function(e, filename, bbcode, id) {
+ $('body').on('fbrowser.photo.comment', function(e, filename, bbcode, id) {
$.colorbox.close();
var textarea = document.getElementById("comment-edit-text-" +id);
var start = textarea.selectionStart;
* to the event handler
*/
doImageBrowser : function (name, id) {
- var url = Dialog._get_url('photos', name, id);
+ var url = Dialog._get_url('photo', name, id);
return Dialog.show(url);
},
e.preventDefault();
let embed = '';
- if (FileBrowser.type === "photos") {
+ if (FileBrowser.type === "photo") {
embed = '[url=' + this.dataset.link + '][img=' + this.dataset.img + ']' + this.dataset.alt + '[/img][/url]';
}
if (FileBrowser.type === "attachment") {
});
- if ($('#upload-photos').length)
- {
+ if ($('#upload-photo').length) {
new window.AjaxUpload(
- 'upload-photos',
+ 'upload-photo',
{
- action: 'profile/' + FileBrowser.nickname + '/photos/upload?response=json',
+ action: 'media/photo/upload?response=json',
name: 'userfile',
responseType: 'json',
onSubmit: function (file, ext) {
);
}
- if ($('#upload-attachment').length)
- {
+ if ($('#upload-attachment').length) {
new window.AjaxUpload(
'upload-attachment',
{
- action: 'profile/' + FileBrowser.nickname + '/attachment/upload?response=json',
+ action: 'media/attachment/upload?response=json',
name: 'userfile',
responseType: 'json',
onSubmit: function (file, ext) {
location.reload();
}
}
- );
- }
+ );
+ }
},
_getUrl: function (mode, hash, folder) {
**/
/* callback */
- $('body').on('fbrowser.photos.main', function(e, filename, embedcode, id) {
+ $('body').on('fbrowser.photo.main', function(e, filename, embedcode, id) {
$.colorbox.close();
addeditortext(embedcode);
});
- $('body').on('fbrowser.photos.main', function(e, filename, embedcode, id) {
+ $('body').on('fbrowser.attachment.main', function(e, filename, embedcode, id) {
$.colorbox.close();
addeditortext(embedcode);
});
max-height: calc(100vh - 220px);
}
}
-.fbrowser.photos .photo-album-image-wrapper {
+.fbrowser.photo .photo-album-image-wrapper {
box-shadow: 2px 2px 5px 0px rgba(0, 0, 0, 0.2);
}
-.fbrowser.photos .photo-album-image-wrapper .caption {
+.fbrowser.photo .photo-album-image-wrapper .caption {
pointer-events: none;
}
.fbrowser .profile-rotator-wrapper {
});
// Insert filebrowser images into the input field (field_fileinput.tpl).
- $("body").on("fbrowser.photos.input", function (e, filename, embedcode, id, img) {
+ $("body").on("fbrowser.photo.input", function (e, filename, embedcode, id, img) {
// Select the clicked button by it's attribute.
var elm = $("[image-input='select']");
// Select the input field which belongs to this button.
// Does load the filebrowser into the jot modal.
Dialog.showJot = function () {
- var type = "photos";
+ var type = "photo";
var name = "main";
var url = Dialog._get_url(type, name);
e.preventDefault();
let embed = '';
- if (FileBrowser.type === 'photos') {
+ if (FileBrowser.type === 'photo') {
embed = '[url=' + this.dataset.link + '][img=' + this.dataset.img + ']' + this.dataset.alt + '[/img][/url]';
}
if (FileBrowser.type === 'attachment') {
// Initialize the AjaxUpload for the upload buttons
uploadButtons: function () {
- if ($('#upload-photos').length) {
+ if ($('#upload-photo').length) {
//AjaxUpload for photos
new window.AjaxUpload(
- 'upload-photos',
+ 'upload-photo',
{
- action: 'profile/' + FileBrowser.nickname + '/photos/upload?response=json&album=' + encodeURIComponent(FileBrowser.folder),
+ action: 'media/photo/upload?response=json&album=' + encodeURIComponent(FileBrowser.folder),
name: 'userfile',
responseType: 'json',
onSubmit: function (file, ext) {
new window.AjaxUpload(
'upload-attachment',
{
- action: 'profile/' + FileBrowser.nickname + '/attachment/upload?response=json',
+ action: 'media/attachment/upload?response=json',
name: 'userfile',
responseType: 'json',
onSubmit: function (file, ext) {
// Initialize justified Gallery
initGallery: function () {
- $('.fbrowser.photos .fbrowser-content-container').justifiedGallery({
+ $('.fbrowser.photo .fbrowser-content-container').justifiedGallery({
rowHeight: 80,
margins: 4,
border: 0,
**/
/* callback */
- $('body').on('fbrowser.photos.main', function(e, filename, embedcode, id) {
+ $('body').on('fbrowser.photo.main', function(e, filename, embedcode, id) {
///@todo this part isn't ideal and need to be done in a better way
jotTextOpenUI(document.getElementById("profile-jot-text"));
jotActive();
{{* Switch between image and file mode *}}
<div class="fbswitcher btn-group btn-group-xs pull-right" aria-label="Switch between photo and attachment mode">
- <button type="button" class="btn btn-default" data-mode="photos" aria-label="Photo Mode"><i class="fa fa-picture-o" aria-hidden="true"></i></button>
+ <button type="button" class="btn btn-default" data-mode="photo" aria-label="Photo Mode"><i class="fa fa-picture-o" aria-hidden="true"></i></button>
<button type="button" class="btn btn-default" data-mode="attachment" aria-label="Attachment Mode"><i class="fa fa-file-o" aria-hidden="true"></i></button>
</div>
</ol>
.fbrowser .list {
padding: 10px;
}
-.fbrowser.photos .photo-album-image-wrapper {
+.fbrowser.photo .photo-album-image-wrapper {
width: 48px;
height: 48px;
}
-.fbrowser.photos a img {
+.fbrowser.photo a img {
width: auto;
height: 48px;
}
-.fbrowser.photos a p {
+.fbrowser.photo a p {
display: none;
}
.fbrowser.attachment .photo-album-image-wrapper {
.fbrowser .list {
padding: 10px;
}
-.fbrowser.photos .photo-album-image-wrapper {
+.fbrowser.photo .photo-album-image-wrapper {
width: 48px;
height: 48px;
}
-.fbrowser.photos a img {
+.fbrowser.photo a img {
width: auto;
height: 48px;
}
-.fbrowser.photos a p {
+.fbrowser.photo a p {
display: none;
}
.fbrowser.attachment .photo-album-image-wrapper {
.fbrowser .list {
padding: 10px;
}
-.fbrowser.photos .photo-album-image-wrapper {
+.fbrowser.photo .photo-album-image-wrapper {
width: 48px;
height: 48px;
}
-.fbrowser.photos a img {
+.fbrowser.photo a img {
width: auto;
height: 48px;
}
-.fbrowser.photos a p {
+.fbrowser.photo a p {
display: none;
}
.fbrowser.attachment .photo-album-image-wrapper {
}
.fbrowser .folders ul { list-style: url("icons/folder.png"); padding-left: 22px;}
.fbrowser .list { padding: 10px; }
-.fbrowser.photos .photo-album-image-wrapper { width: 48px; height: 48px; }
-.fbrowser.photos a img { width: auto; height: 48px; }
-.fbrowser.photos a p { display: none;}
+.fbrowser.photo .photo-album-image-wrapper { width: 48px; height: 48px; }
+.fbrowser.photo a img { width: auto; height: 48px; }
+.fbrowser.photo a p { display: none;}
.fbrowser.attachment .photo-album-image-wrapper { float:none; white-space: nowrap; width: 100%; height: auto; }
.fbrowser.attachment img { display: inline; width: 16px; height: 16px}
.fbrowser.attachment p { display: inline; white-space: nowrap; }
}
/* upload/select popup */
-fbrowser.photos .photo-album-image-wrapper { margin-left: 10px; }
+fbrowser.photo .photo-album-image-wrapper { margin-left: 10px; }
#message-preview { margin-top: 15px; }
#message-preview span { width: 100%; }
#message-preview .mail-count, #message-preview .mail-delete { display:none; }