mirror of
https://github.com/friendica/friendica
synced 2024-11-09 17:02:54 +00:00
Image handling reworked, new image formats added (#13900)
* Image handling reworked, new image formats added * Updated messages.po * The dot is now part of the file extension * Added WebP in install documentation * Handle unhandled mime types * Fixed animated picture detected
This commit is contained in:
parent
1ea8a4042d
commit
14e5b06029
31 changed files with 541 additions and 386 deletions
|
@ -44,7 +44,7 @@ For alternative server configurations (such as Nginx server and MariaDB database
|
|||
|
||||
### Optional
|
||||
|
||||
* PHP ImageMagick extension (php-imagick) for animated GIF support.
|
||||
* PHP ImageMagick extension (php-imagick) for animated GIF and animated WebP support.
|
||||
|
||||
## Installation procedure
|
||||
|
||||
|
|
|
@ -132,8 +132,6 @@ function photos_post(App $a)
|
|||
throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.'));
|
||||
}
|
||||
|
||||
$phototypes = Images::supportedTypes();
|
||||
|
||||
$can_post = false;
|
||||
$visitor = 0;
|
||||
|
||||
|
@ -337,7 +335,7 @@ function photos_post(App $a)
|
|||
|
||||
if (DBA::isResult($photos)) {
|
||||
$photo = $photos[0];
|
||||
$ext = $phototypes[$photo['type']];
|
||||
$ext = Images::getExtensionByMimeType($photo['type']);
|
||||
Photo::update(
|
||||
['desc' => $desc, 'album' => $albname, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_circle_allow, 'deny_cid' => $str_contact_deny, 'deny_gid' => $str_circle_deny],
|
||||
['resource-id' => $resource_id, 'uid' => $page_owner_uid]
|
||||
|
@ -590,8 +588,6 @@ function photos_content(App $a)
|
|||
|
||||
$profile = Profile::getByUID($user['uid']);
|
||||
|
||||
$phototypes = Images::supportedTypes();
|
||||
|
||||
$_SESSION['photo_return'] = DI::args()->getCommand();
|
||||
|
||||
// Parse arguments
|
||||
|
@ -844,7 +840,7 @@ function photos_content(App $a)
|
|||
foreach ($r as $rr) {
|
||||
$twist = !$twist;
|
||||
|
||||
$ext = $phototypes[$rr['type']];
|
||||
$ext = Images::getExtensionByMimeType($rr['type']);
|
||||
|
||||
$imgalt_e = $rr['filename'];
|
||||
$desc_e = $rr['desc'];
|
||||
|
@ -855,7 +851,7 @@ function photos_content(App $a)
|
|||
'link' => 'photos/' . $user['nickname'] . '/image/' . $rr['resource-id']
|
||||
. ($order_field === 'created' ? '?order=created' : ''),
|
||||
'title' => DI::l10n()->t('View Photo'),
|
||||
'src' => 'photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' . $ext,
|
||||
'src' => 'photo/' . $rr['resource-id'] . '-' . $rr['scale'] . $ext,
|
||||
'alt' => $imgalt_e,
|
||||
'desc' => $desc_e,
|
||||
'ext' => $ext,
|
||||
|
@ -1013,9 +1009,9 @@ function photos_content(App $a)
|
|||
}
|
||||
|
||||
$photo = [
|
||||
'href' => 'photo/' . $hires['resource-id'] . '-' . $hires['scale'] . '.' . $phototypes[$hires['type']],
|
||||
'href' => 'photo/' . $hires['resource-id'] . '-' . $hires['scale'] . Images::getExtensionByMimeType($hires['type']),
|
||||
'title' => DI::l10n()->t('View Full Size'),
|
||||
'src' => 'photo/' . $lores['resource-id'] . '-' . $lores['scale'] . '.' . $phototypes[$lores['type']] . '?_u=' . DateTimeFormat::utcNow('ymdhis'),
|
||||
'src' => 'photo/' . $lores['resource-id'] . '-' . $lores['scale'] . Images::getExtensionByMimeType($lores['type']) . '?_u=' . DateTimeFormat::utcNow('ymdhis'),
|
||||
'height' => $hires['height'],
|
||||
'width' => $hires['width'],
|
||||
'album' => $hires['album'],
|
||||
|
|
|
@ -150,7 +150,7 @@ HELP;
|
|||
|
||||
if ($valid) {
|
||||
$this->out('3', false);
|
||||
$image = new Image($imgdata, Images::getMimeTypeByData($imgdata));
|
||||
$image = new Image($imgdata);
|
||||
if (!$image->isValid()) {
|
||||
$this->out(' ' . $this->l10n->t('invalid image for id %s', $resourceid) . ' ', false);
|
||||
$valid = false;
|
||||
|
|
|
@ -80,13 +80,18 @@ class Avatar
|
|||
return $fields;
|
||||
}
|
||||
|
||||
if (!$fetchResult->isSuccess()) {
|
||||
Logger::debug('Fetching was unsuccessful', ['avatar' => $avatar]);
|
||||
return $fields;
|
||||
}
|
||||
|
||||
$img_str = $fetchResult->getBodyString();
|
||||
if (empty($img_str)) {
|
||||
Logger::debug('Avatar is invalid', ['avatar' => $avatar]);
|
||||
return $fields;
|
||||
}
|
||||
|
||||
$image = new Image($img_str, Images::getMimeTypeByData($img_str));
|
||||
$image = new Image($img_str, $fetchResult->getContentType(), $avatar);
|
||||
if (!$image->isValid()) {
|
||||
Logger::debug('Avatar picture is invalid', ['avatar' => $avatar]);
|
||||
return $fields;
|
||||
|
@ -145,7 +150,7 @@ class Avatar
|
|||
return '';
|
||||
}
|
||||
|
||||
$path = $filename . $size . '.' . $image->getExt();
|
||||
$path = $filename . $size . $image->getExt();
|
||||
|
||||
$basepath = self::basePath();
|
||||
if (empty($basepath)) {
|
||||
|
|
|
@ -40,6 +40,7 @@ use Friendica\Model\Post;
|
|||
use Friendica\Model\Tag;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
|
||||
use Friendica\Util\Images;
|
||||
use Friendica\Util\Map;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\ParseUrl;
|
||||
|
@ -1027,12 +1028,12 @@ class BBCode
|
|||
if (is_null($text)) {
|
||||
$curlResult = DI::httpClient()->head($match[1], [HttpClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout')]);
|
||||
if ($curlResult->isSuccess()) {
|
||||
$mimetype = $curlResult->getHeader('Content-Type')[0] ?? '';
|
||||
$mimetype = $curlResult->getContentType() ?? '';
|
||||
} else {
|
||||
$mimetype = '';
|
||||
}
|
||||
|
||||
if (substr($mimetype, 0, 6) == 'image/') {
|
||||
if (Images::isSupportedMimeType($mimetype)) {
|
||||
$text = '[url=' . $match[1] . ']' . $match[1] . '[/url]';
|
||||
} else {
|
||||
$text = '[url=' . $match[2] . ']' . $match[2] . '[/url]';
|
||||
|
@ -1125,13 +1126,13 @@ class BBCode
|
|||
|
||||
$curlResult = DI::httpClient()->head($match[1], [HttpClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout')]);
|
||||
if ($curlResult->isSuccess()) {
|
||||
$mimetype = $curlResult->getHeader('Content-Type')[0] ?? '';
|
||||
$mimetype = $curlResult->getContentType() ?? '';
|
||||
} else {
|
||||
$mimetype = '';
|
||||
}
|
||||
|
||||
// if its a link to a picture then embed this picture
|
||||
if (substr($mimetype, 0, 6) == 'image/') {
|
||||
if (Images::isSupportedMimeType($mimetype)) {
|
||||
$text = '[img]' . $match[1] . '[/img]';
|
||||
} else {
|
||||
if (!empty($match[3])) {
|
||||
|
|
|
@ -632,23 +632,10 @@ class Installer
|
|||
*/
|
||||
public function checkImagick()
|
||||
{
|
||||
$imagick = false;
|
||||
$gif = false;
|
||||
|
||||
if (class_exists('Imagick')) {
|
||||
$imagick = true;
|
||||
$supported = Images::supportedTypes();
|
||||
if (array_key_exists('image/gif', $supported)) {
|
||||
$gif = true;
|
||||
}
|
||||
}
|
||||
if (!$imagick) {
|
||||
$this->addCheck(DI::l10n()->t('ImageMagick PHP extension is not installed'), $imagick, false, "");
|
||||
if (!class_exists('Imagick')) {
|
||||
$this->addCheck(DI::l10n()->t('ImageMagick PHP extension is not installed'), false, false, "");
|
||||
} else {
|
||||
$this->addCheck(DI::l10n()->t('ImageMagick PHP extension is installed'), $imagick, false, "");
|
||||
if ($imagick) {
|
||||
$this->addCheck(DI::l10n()->t('ImageMagick supports GIF'), $gif, false, "");
|
||||
}
|
||||
$this->addCheck(DI::l10n()->t('ImageMagick PHP extension is installed'), true, false, "");
|
||||
}
|
||||
|
||||
// Imagick is not required
|
||||
|
|
|
@ -84,7 +84,7 @@ class Attachment extends BaseFactory
|
|||
$type = 'audio';
|
||||
} elseif (($filetype == 'video') || ($attachment['type'] == Post\Media::VIDEO)) {
|
||||
$type = 'video';
|
||||
} elseif ($attachment['mimetype'] == 'image/gif') {
|
||||
} elseif ($attachment['mimetype'] == image_type_to_mime_type(IMAGETYPE_GIF)) {
|
||||
$type = 'gifv';
|
||||
} elseif (($filetype == 'image') || ($attachment['type'] == Post\Media::IMAGE)) {
|
||||
$type = 'image';
|
||||
|
@ -130,14 +130,13 @@ class Attachment extends BaseFactory
|
|||
'blurhash' => $photo['blurhash'],
|
||||
];
|
||||
|
||||
$photoTypes = Images::supportedTypes();
|
||||
$ext = $photoTypes[$photo['type']];
|
||||
$ext = Images::getExtensionByMimeType($photo['type']);
|
||||
|
||||
$url = $this->baseUrl . '/photo/' . $photo['resource-id'] . '-0.' . $ext;
|
||||
$url = $this->baseUrl . '/photo/' . $photo['resource-id'] . '-0' . $ext;
|
||||
|
||||
$preview = Photo::selectFirst(['scale'], ["`resource-id` = ? AND `uid` = ? AND `scale` > ?", $photo['resource-id'], $photo['uid'], 0], ['order' => ['scale']]);
|
||||
if (!empty($preview)) {
|
||||
$preview_url = $this->baseUrl . '/photo/' . $photo['resource-id'] . '-' . $preview['scale'] . '.' . $ext;
|
||||
$preview_url = $this->baseUrl . '/photo/' . $photo['resource-id'] . '-' . $preview['scale'] . $ext;
|
||||
} else {
|
||||
$preview_url = '';
|
||||
}
|
||||
|
|
|
@ -842,7 +842,6 @@ class Contact
|
|||
return false;
|
||||
}
|
||||
|
||||
$file_suffix = 'jpg';
|
||||
$url = DI::baseUrl() . '/profile/' . $user['nickname'];
|
||||
|
||||
$fields = [
|
||||
|
@ -875,17 +874,11 @@ class Contact
|
|||
$fields['avatar-date'] = DateTimeFormat::utcNow();
|
||||
}
|
||||
|
||||
// Creating the path to the avatar, beginning with the file suffix
|
||||
$types = Images::supportedTypes();
|
||||
if (isset($types[$avatar['type']])) {
|
||||
$file_suffix = $types[$avatar['type']];
|
||||
}
|
||||
|
||||
// We are adding a timestamp value so that other systems won't use cached content
|
||||
$timestamp = strtotime($fields['avatar-date']);
|
||||
|
||||
$prefix = DI::baseUrl() . '/photo/' . $avatar['resource-id'] . '-';
|
||||
$suffix = '.' . $file_suffix . '?ts=' . $timestamp;
|
||||
$suffix = Images::getExtensionByMimeType($avatar['type']) . '?ts=' . $timestamp;
|
||||
|
||||
$fields['photo'] = $prefix . '4' . $suffix;
|
||||
$fields['thumb'] = $prefix . '5' . $suffix;
|
||||
|
@ -2313,8 +2306,8 @@ class Contact
|
|||
$fetchResult = HTTPSignature::fetchRaw($avatar, 0, [HttpClientOptions::ACCEPT_CONTENT => [HttpClientAccept::IMAGE]]);
|
||||
|
||||
$img_str = $fetchResult->getBodyString();
|
||||
if (!empty($img_str)) {
|
||||
$image = new Image($img_str, Images::getMimeTypeByData($img_str));
|
||||
if ($fetchResult->isSuccess() && !empty($img_str)) {
|
||||
$image = new Image($img_str, $fetchResult->getContentType(), $avatar);
|
||||
if ($image->isValid()) {
|
||||
$update_fields['blurhash'] = $image->getBlurHash();
|
||||
} else {
|
||||
|
|
|
@ -363,6 +363,7 @@ class Photo
|
|||
$photo['backend-class'] = SystemResource::NAME;
|
||||
$photo['backend-ref'] = $filename;
|
||||
$photo['type'] = $mimetype;
|
||||
$photo['filename'] = basename($filename);
|
||||
$photo['cacheable'] = false;
|
||||
|
||||
return $photo;
|
||||
|
@ -394,6 +395,7 @@ class Photo
|
|||
$photo['backend-class'] = ExternalResource::NAME;
|
||||
$photo['backend-ref'] = json_encode(['url' => $url, 'uid' => $uid]);
|
||||
$photo['type'] = $mimetype;
|
||||
$photo['filename'] = basename(parse_url($url, PHP_URL_PATH));
|
||||
$photo['cacheable'] = true;
|
||||
$photo['blurhash'] = $blurhash;
|
||||
$photo['width'] = $width;
|
||||
|
@ -608,9 +610,7 @@ class Photo
|
|||
return false;
|
||||
}
|
||||
|
||||
$type = Images::getMimeTypeByData($img_str, $image_url, $type);
|
||||
|
||||
$image = new Image($img_str, $type);
|
||||
$image = new Image($img_str, $type, $image_url);
|
||||
if ($image->isValid()) {
|
||||
$image->scaleToSquare(300);
|
||||
|
||||
|
@ -619,9 +619,9 @@ class Photo
|
|||
|
||||
if ($maximagesize && ($filesize > $maximagesize)) {
|
||||
Logger::info('Avatar exceeds image limit', ['uid' => $uid, 'cid' => $cid, 'maximagesize' => $maximagesize, 'size' => $filesize, 'type' => $image->getType()]);
|
||||
if ($image->getType() == 'image/gif') {
|
||||
if ($image->getImageType() == IMAGETYPE_GIF) {
|
||||
$image->toStatic();
|
||||
$image = new Image($image->asString(), 'image/png');
|
||||
$image = new Image($image->asString(), image_type_to_mime_type(IMAGETYPE_PNG));
|
||||
|
||||
$filesize = strlen($image->asString());
|
||||
Logger::info('Converted gif to a static png', ['uid' => $uid, 'cid' => $cid, 'size' => $filesize, 'type' => $image->getType()]);
|
||||
|
@ -662,9 +662,9 @@ class Photo
|
|||
|
||||
$suffix = '?ts=' . time();
|
||||
|
||||
$image_url = DI::baseUrl() . '/photo/' . $resource_id . '-4.' . $image->getExt() . $suffix;
|
||||
$thumb = DI::baseUrl() . '/photo/' . $resource_id . '-5.' . $image->getExt() . $suffix;
|
||||
$micro = DI::baseUrl() . '/photo/' . $resource_id . '-6.' . $image->getExt() . $suffix;
|
||||
$image_url = DI::baseUrl() . '/photo/' . $resource_id . '-4' . $image->getExt() . $suffix;
|
||||
$thumb = DI::baseUrl() . '/photo/' . $resource_id . '-5' . $image->getExt() . $suffix;
|
||||
$micro = DI::baseUrl() . '/photo/' . $resource_id . '-6' . $image->getExt() . $suffix;
|
||||
} else {
|
||||
$photo_failure = true;
|
||||
}
|
||||
|
@ -1060,9 +1060,7 @@ class Photo
|
|||
return [];
|
||||
}
|
||||
|
||||
$type = Images::getMimeTypeByData($img_str, $image_url, $type);
|
||||
|
||||
$image = new Image($img_str, $type);
|
||||
$image = new Image($img_str, $type, $image_url);
|
||||
|
||||
$image = self::fitImageSize($image);
|
||||
if (empty($image)) {
|
||||
|
@ -1132,12 +1130,10 @@ class Photo
|
|||
return [];
|
||||
}
|
||||
|
||||
$filetype = Images::getMimeTypeBySource($src, $filename, $filetype);
|
||||
|
||||
Logger::info('File upload', ['src' => $src, 'filename' => $filename, 'size' => $filesize, 'type' => $filetype]);
|
||||
|
||||
$imagedata = @file_get_contents($src);
|
||||
$image = new Image($imagedata, $filetype);
|
||||
$image = new Image($imagedata, $filetype, $filename);
|
||||
if (!$image->isValid()) {
|
||||
Logger::notice('Image is unvalid', ['files' => $files]);
|
||||
return [];
|
||||
|
|
|
@ -134,15 +134,23 @@ class Link
|
|||
Logger::notice('Error fetching url', ['url' => $url, 'exception' => $exception]);
|
||||
return [];
|
||||
}
|
||||
$fields = ['mimetype' => $curlResult->getHeader('Content-Type')[0]];
|
||||
|
||||
$img_str = $curlResult->getBodyString();
|
||||
$image = new Image($img_str, Images::getMimeTypeByData($img_str));
|
||||
if ($image->isValid()) {
|
||||
$fields['mimetype'] = $image->getType();
|
||||
$fields['width'] = $image->getWidth();
|
||||
$fields['height'] = $image->getHeight();
|
||||
$fields['blurhash'] = $image->getBlurHash();
|
||||
if (!$curlResult->isSuccess()) {
|
||||
Logger::notice('Fetching unsuccessful', ['url' => $url]);
|
||||
return [];
|
||||
}
|
||||
|
||||
$fields = ['mimetype' => $curlResult->getContentType()];
|
||||
|
||||
if (Images::isSupportedMimeType($fields['mimetype'])) {
|
||||
$img_str = $curlResult->getBodyString();
|
||||
$image = new Image($img_str, $fields['mimetype'], $url);
|
||||
if ($image->isValid()) {
|
||||
$fields['mimetype'] = $image->getType();
|
||||
$fields['width'] = $image->getWidth();
|
||||
$fields['height'] = $image->getHeight();
|
||||
$fields['blurhash'] = $image->getBlurHash();
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
|
|
|
@ -196,7 +196,7 @@ class Media
|
|||
|
||||
if ($curlResult->isSuccess()) {
|
||||
if (empty($media['mimetype'])) {
|
||||
$media['mimetype'] = $curlResult->getHeader('Content-Type')[0] ?? '';
|
||||
$media['mimetype'] = $curlResult->getContentType() ?? '';
|
||||
}
|
||||
if (empty($media['size'])) {
|
||||
$media['size'] = (int)($curlResult->getHeader('Content-Length')[0] ?? 0);
|
||||
|
|
|
@ -1403,9 +1403,7 @@ class User
|
|||
$type = '';
|
||||
}
|
||||
|
||||
$type = Images::getMimeTypeByData($img_str, $photo, $type);
|
||||
|
||||
$image = new Image($img_str, $type);
|
||||
$image = new Image($img_str, $type, $photo);
|
||||
if ($image->isValid()) {
|
||||
$image->scaleToSquare(300);
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ class Instance extends BaseApi
|
|||
|
||||
return new InstanceV2Entity\Configuration(
|
||||
$statuses_config,
|
||||
new InstanceV2Entity\MediaAttachmentsConfig(array_keys(Images::supportedTypes()), $image_size_limit, $image_matrix_limit),
|
||||
new InstanceV2Entity\MediaAttachmentsConfig(Images::supportedMimeTypes(), $image_size_limit, $image_matrix_limit),
|
||||
new InstanceV2Entity\Polls(),
|
||||
new InstanceV2Entity\Accounts(),
|
||||
);
|
||||
|
|
|
@ -131,7 +131,7 @@ class InstanceV2 extends BaseApi
|
|||
|
||||
return new InstanceEntity\Configuration(
|
||||
$statuses_config,
|
||||
new InstanceEntity\MediaAttachmentsConfig(array_keys(Images::supportedTypes()), $image_size_limit, $image_matrix_limit),
|
||||
new InstanceEntity\MediaAttachmentsConfig(Images::supportedMimeTypes(), $image_size_limit, $image_matrix_limit),
|
||||
new InstanceEntity\Polls(),
|
||||
new InstanceEntity\Accounts(),
|
||||
);
|
||||
|
|
|
@ -403,11 +403,10 @@ class Statuses extends BaseApi
|
|||
|
||||
Photo::setPermissionForResource($media[0]['resource-id'], $item['uid'], $item['allow_cid'], $item['allow_gid'], $item['deny_cid'], $item['deny_gid']);
|
||||
|
||||
$phototypes = Images::supportedTypes();
|
||||
$ext = $phototypes[$media[0]['type']];
|
||||
$ext = Images::getExtensionByMimeType($media[0]['type']);
|
||||
|
||||
$attachment = ['type' => Post\Media::IMAGE, 'mimetype' => $media[0]['type'],
|
||||
'url' => DI::baseUrl() . '/photo/' . $media[0]['resource-id'] . '-' . $media[0]['scale'] . '.' . $ext,
|
||||
'url' => DI::baseUrl() . '/photo/' . $media[0]['resource-id'] . '-' . $media[0]['scale'] . $ext,
|
||||
'size' => $media[0]['datasize'],
|
||||
'name' => $media[0]['filename'] ?: $media[0]['resource-id'],
|
||||
'description' => $media[0]['desc'] ?? '',
|
||||
|
@ -415,7 +414,7 @@ class Statuses extends BaseApi
|
|||
'height' => $media[0]['height']];
|
||||
|
||||
if (count($media) > 1) {
|
||||
$attachment['preview'] = DI::baseUrl() . '/photo/' . $media[1]['resource-id'] . '-' . $media[1]['scale'] . '.' . $ext;
|
||||
$attachment['preview'] = DI::baseUrl() . '/photo/' . $media[1]['resource-id'] . '-' . $media[1]['scale'] . $ext;
|
||||
$attachment['preview-width'] = $media[1]['width'];
|
||||
$attachment['preview-height'] = $media[1]['height'];
|
||||
}
|
||||
|
|
|
@ -155,13 +155,12 @@ class Update extends BaseApi
|
|||
|
||||
Photo::setPermissionForResource($media[0]['resource-id'], $uid, $item['allow_cid'], $item['allow_gid'], $item['deny_cid'], $item['deny_gid']);
|
||||
|
||||
$phototypes = Images::supportedTypes();
|
||||
$ext = $phototypes[$media[0]['type']];
|
||||
$ext = Images::getExtensionByMimeType($media[0]['type']);
|
||||
|
||||
$attachment = [
|
||||
'type' => Post\Media::IMAGE,
|
||||
'mimetype' => $media[0]['type'],
|
||||
'url' => DI::baseUrl() . '/photo/' . $media[0]['resource-id'] . '-' . $media[0]['scale'] . '.' . $ext,
|
||||
'url' => DI::baseUrl() . '/photo/' . $media[0]['resource-id'] . '-' . $media[0]['scale'] . $ext,
|
||||
'size' => $media[0]['datasize'],
|
||||
'name' => $media[0]['filename'] ?: $media[0]['resource-id'],
|
||||
'description' => $media[0]['desc'] ?? '',
|
||||
|
@ -170,7 +169,7 @@ class Update extends BaseApi
|
|||
];
|
||||
|
||||
if (count($media) > 1) {
|
||||
$attachment['preview'] = DI::baseUrl() . '/photo/' . $media[1]['resource-id'] . '-' . $media[1]['scale'] . '.' . $ext;
|
||||
$attachment['preview'] = DI::baseUrl() . '/photo/' . $media[1]['resource-id'] . '-' . $media[1]['scale'] . $ext;
|
||||
$attachment['preview-width'] = $media[1]['width'];
|
||||
$attachment['preview-height'] = $media[1]['height'];
|
||||
}
|
||||
|
|
|
@ -99,8 +99,7 @@ class Browser extends BaseModule
|
|||
|
||||
protected function map_files(array $record): array
|
||||
{
|
||||
$types = Images::supportedTypes();
|
||||
$ext = $types[$record['type']];
|
||||
$ext = Images::getExtensionByMimeType($record['type']);
|
||||
$filename_e = $record['filename'];
|
||||
|
||||
// Take the largest picture that is smaller or equal 640 pixels
|
||||
|
@ -118,7 +117,7 @@ class Browser extends BaseModule
|
|||
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),
|
||||
sprintf('%s/photo/%s-%s%s', $this->baseUrl, $record['resource-id'], $scale, $ext),
|
||||
$record['desc'],
|
||||
];
|
||||
}
|
||||
|
|
|
@ -135,8 +135,6 @@ class Upload extends \Friendica\BaseModule
|
|||
$this->return(401, $this->t('Invalid request.'), true);
|
||||
}
|
||||
|
||||
$filetype = Images::getMimeTypeBySource($src, $filename, $filetype);
|
||||
|
||||
$this->logger->info('File upload:', [
|
||||
'src' => $src,
|
||||
'filename' => $filename,
|
||||
|
@ -145,7 +143,7 @@ class Upload extends \Friendica\BaseModule
|
|||
]);
|
||||
|
||||
$imagedata = @file_get_contents($src);
|
||||
$image = new Image($imagedata, $filetype);
|
||||
$image = new Image($imagedata, $filetype, $filename);
|
||||
|
||||
if (!$image->isValid()) {
|
||||
@unlink($src);
|
||||
|
|
|
@ -167,14 +167,16 @@ class Photo extends BaseApi
|
|||
$stamp = microtime(true);
|
||||
|
||||
if (empty($request['blur']) || empty($photo['blurhash'])) {
|
||||
$imgdata = MPhoto::getImageDataForPhoto($photo);
|
||||
$imgdata = MPhoto::getImageDataForPhoto($photo);
|
||||
$mimetype = $photo['type'];
|
||||
}
|
||||
if (empty($imgdata) && empty($photo['blurhash'])) {
|
||||
throw new HTTPException\NotFoundException();
|
||||
} elseif (empty($imgdata) && !empty($photo['blurhash'])) {
|
||||
$image = New Image('', 'image/png');
|
||||
$image = New Image('', image_type_to_mime_type(IMAGETYPE_WEBP));
|
||||
$image->getFromBlurHash($photo['blurhash'], $photo['width'], $photo['height']);
|
||||
$imgdata = $image->asString();
|
||||
$imgdata = $image->asString();
|
||||
$mimetype = $image->getType();
|
||||
}
|
||||
|
||||
// The mimetype for an external or system resource can only be known reliably after it had been fetched
|
||||
|
@ -199,20 +201,23 @@ class Photo extends BaseApi
|
|||
}
|
||||
|
||||
if (!empty($request['static'])) {
|
||||
$img = new Image($imgdata, $photo['type']);
|
||||
$img = new Image($imgdata, $photo['type'], $photo['filename']);
|
||||
$img->toStatic();
|
||||
$imgdata = $img->asString();
|
||||
$imgdata = $img->asString();
|
||||
$mimetype = $img->getType();
|
||||
}
|
||||
|
||||
// if customsize is set and image is not a gif, resize it
|
||||
if ($photo['type'] !== 'image/gif' && $customsize > 0 && $customsize <= Proxy::PIXEL_THUMB && $square_resize) {
|
||||
$img = new Image($imgdata, $photo['type']);
|
||||
if ($photo['type'] !== image_type_to_mime_type(IMAGETYPE_GIF) && $customsize > 0 && $customsize <= Proxy::PIXEL_THUMB && $square_resize) {
|
||||
$img = new Image($imgdata, $photo['type'], $photo['filename']);
|
||||
$img->scaleToSquare($customsize);
|
||||
$imgdata = $img->asString();
|
||||
} elseif ($photo['type'] !== 'image/gif' && $customsize > 0) {
|
||||
$img = new Image($imgdata, $photo['type']);
|
||||
$imgdata = $img->asString();
|
||||
$mimetype = $img->getType();
|
||||
} elseif ($photo['type'] !== image_type_to_mime_type(IMAGETYPE_GIF) && $customsize > 0) {
|
||||
$img = new Image($imgdata, $photo['type'], $photo['filename']);
|
||||
$img->scaleDown($customsize);
|
||||
$imgdata = $img->asString();
|
||||
$imgdata = $img->asString();
|
||||
$mimetype = $img->getType();
|
||||
}
|
||||
|
||||
if (function_exists('header_remove')) {
|
||||
|
@ -220,7 +225,7 @@ class Photo extends BaseApi
|
|||
header_remove('pragma');
|
||||
}
|
||||
|
||||
header('Content-type: ' . $photo['type']);
|
||||
header('Content-type: ' . $mimetype);
|
||||
|
||||
$stamp = microtime(true);
|
||||
if (!$cacheable) {
|
||||
|
@ -391,7 +396,7 @@ class Photo extends BaseApi
|
|||
}
|
||||
}
|
||||
if (empty($mimetext) && !empty($contact['blurhash'])) {
|
||||
$image = New Image('', 'image/png');
|
||||
$image = New Image('', image_type_to_mime_type(IMAGETYPE_WEBP));
|
||||
$image->getFromBlurHash($contact['blurhash'], $customsize, $customsize);
|
||||
return MPhoto::createPhotoForImageData($image->asString());
|
||||
} elseif (empty($mimetext)) {
|
||||
|
|
|
@ -184,8 +184,6 @@ class Photos extends \Friendica\Module\BaseProfile
|
|||
return;
|
||||
}
|
||||
|
||||
$type = Images::getMimeTypeBySource($src, $filename, $type);
|
||||
|
||||
$this->logger->info('photos: upload: received file: ' . $filename . ' as ' . $src . ' ('. $type . ') ' . $filesize . ' bytes');
|
||||
|
||||
$maximagesize = Strings::getBytesFromShorthand($this->config->get('system', 'maximagesize'));
|
||||
|
@ -210,7 +208,7 @@ class Photos extends \Friendica\Module\BaseProfile
|
|||
|
||||
$imagedata = @file_get_contents($src);
|
||||
|
||||
$image = new Image($imagedata, $type);
|
||||
$image = new Image($imagedata, $type, $filename);
|
||||
|
||||
if (!$image->isValid()) {
|
||||
$this->logger->notice('unable to process image');
|
||||
|
@ -341,14 +339,12 @@ class Photos extends \Friendica\Module\BaseProfile
|
|||
$pager->getItemsPerPage()
|
||||
));
|
||||
|
||||
$phototypes = Images::supportedTypes();
|
||||
|
||||
$photos = array_map(function ($photo) use ($phototypes) {
|
||||
$photos = array_map(function ($photo){
|
||||
return [
|
||||
'id' => $photo['id'],
|
||||
'link' => 'photos/' . $this->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']],
|
||||
'src' => 'photo/' . $photo['resource-id'] . '-' . ((($photo['scale']) == 6) ? 4 : $photo['scale']) . Images::getExtensionByMimeType($photo['type']),
|
||||
'alt' => $photo['filename'],
|
||||
'album' => [
|
||||
'link' => 'photos/' . $this->owner['nickname'] . '/album/' . bin2hex($photo['album']),
|
||||
|
|
|
@ -99,17 +99,15 @@ class Proxy extends BaseModule
|
|||
|
||||
Logger::debug('Got picture', ['Content-Type' => $fetchResult->getHeader('Content-Type'), 'uid' => DI::userSession()->getLocalUserId(), 'image' => $request['url']]);
|
||||
|
||||
$mime = Images::getMimeTypeByData($img_str);
|
||||
|
||||
$image = new Image($img_str, $mime);
|
||||
$image = new Image($img_str, $fetchResult->getContentType(), $request['url']);
|
||||
if (!$image->isValid()) {
|
||||
Logger::notice('The image is invalid', ['image' => $request['url'], 'mime' => $mime]);
|
||||
Logger::notice('The image is invalid', ['image' => $request['url'], 'mime' => $fetchResult->getContentType()]);
|
||||
self::responseError();
|
||||
// stop.
|
||||
}
|
||||
|
||||
// reduce quality - if it isn't a GIF
|
||||
if ($image->getType() != 'image/gif') {
|
||||
if ($image->getImageType() != IMAGETYPE_GIF) {
|
||||
$image->scaleDown($request['size']);
|
||||
}
|
||||
|
||||
|
|
|
@ -213,7 +213,7 @@ class Crop extends BaseSettings
|
|||
|
||||
DI::page()['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('settings/profile/photo/crop_head.tpl'), []);
|
||||
|
||||
$filename = $imagecrop['resource-id'] . '-' . $imagecrop['scale'] . '.' . $imagecrop['ext'];
|
||||
$filename = $imagecrop['resource-id'] . '-' . $imagecrop['scale'] . $imagecrop['ext'];
|
||||
$tpl = Renderer::getMarkupTemplate('settings/profile/photo/crop.tpl');
|
||||
$o = Renderer::replaceMacros($tpl, [
|
||||
'$filename' => $filename,
|
||||
|
|
|
@ -52,8 +52,6 @@ class Index extends BaseSettings
|
|||
$filesize = intval($_FILES['userfile']['size']);
|
||||
$filetype = $_FILES['userfile']['type'];
|
||||
|
||||
$filetype = Images::getMimeTypeBySource($src, $filename, $filetype);
|
||||
|
||||
$maximagesize = Strings::getBytesFromShorthand(DI::config()->get('system', 'maximagesize', 0));
|
||||
|
||||
if ($maximagesize && $filesize > $maximagesize) {
|
||||
|
@ -63,7 +61,7 @@ class Index extends BaseSettings
|
|||
}
|
||||
|
||||
$imagedata = @file_get_contents($src);
|
||||
$Image = new Image($imagedata, $filetype);
|
||||
$Image = new Image($imagedata, $filetype, $filename);
|
||||
|
||||
if (!$Image->isValid()) {
|
||||
DI::sysmsg()->addNotice(DI::l10n()->t('Unable to process image.'));
|
||||
|
|
|
@ -392,7 +392,7 @@ class Import extends \Friendica\BaseModule
|
|||
$photo['data'] = hex2bin($photo['data']);
|
||||
|
||||
$r = Photo::store(
|
||||
new Image($photo['data'], $photo['type']),
|
||||
new Image($photo['data'], $photo['type'], $photo['filename']),
|
||||
$photo['uid'], $photo['contact-id'], //0
|
||||
$photo['resource-id'], $photo['filename'], $photo['album'], $photo['scale'], $photo['profile'], //1
|
||||
$photo['allow_cid'], $photo['allow_gid'], $photo['deny_cid'], $photo['deny_gid']
|
||||
|
|
|
@ -32,7 +32,7 @@ class HttpClientAccept
|
|||
public const ATOM_XML = 'application/atom+xml,text/xml;q=0.9,*/*;q=0.8';
|
||||
public const FEED_XML = 'application/atom+xml,application/rss+xml;q=0.9,application/rdf+xml;q=0.8,text/xml;q=0.7,*/*;q=0.6';
|
||||
public const HTML = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
|
||||
public const IMAGE = 'image/png,image/jpeg,image/gif,image/*;q=0.9,*/*;q=0.8';
|
||||
public const IMAGE = 'image/webp,image/png,image/jpeg,image/gif,image/*;q=0.9,*/*;q=0.8'; // @todo add image/avif once our minimal supported PHP version is 8.1.0
|
||||
public const JRD_JSON = 'application/jrd+json,application/json;q=0.9';
|
||||
public const JSON = 'application/json,*/*;q=0.9';
|
||||
public const JSON_AS = 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
||||
|
|
|
@ -45,25 +45,37 @@ class Image
|
|||
private $width;
|
||||
private $height;
|
||||
private $valid;
|
||||
private $type;
|
||||
private $types;
|
||||
private $imageType;
|
||||
private $filename;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $data Image data
|
||||
* @param string $type optional, default null
|
||||
* @param string $data Image data
|
||||
* @param string $type optional, default ''
|
||||
* @param string $filename optional, default ''
|
||||
* @param string $imagick optional, default 'true'
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public function __construct(string $data, string $type = null)
|
||||
public function __construct(string $data, string $type = '', string $filename = '', bool $imagick = true)
|
||||
{
|
||||
$this->imagick = class_exists('Imagick');
|
||||
$this->types = Images::supportedTypes();
|
||||
if (!array_key_exists($type, $this->types)) {
|
||||
$type = 'image/jpeg';
|
||||
$this->filename = $filename;
|
||||
$type = Images::addMimeTypeByDataIfInvalid($type, $data);
|
||||
$type = Images::addMimeTypeByExtensionIfInvalid($type, $filename);
|
||||
|
||||
if (Images::isSupportedMimeType($type)) {
|
||||
$this->imageType = Images::getImageTypeByMimeType($type);
|
||||
} elseif (($type == '') || substr($type, 0, 6) != 'image/' || substr($type, 0, 12) != ' application/') {
|
||||
$this->imageType = IMAGETYPE_WEBP;
|
||||
DI::logger()->debug('Unhandled image mime type, use WebP instead', ['type' => $type, 'filename' => $filename, 'size' => strlen($data)]);
|
||||
} else {
|
||||
DI::logger()->debug('Unhandled mime type', ['type' => $type, 'filename' => $filename, 'size' => strlen($data)]);
|
||||
$this->valid = false;
|
||||
return;
|
||||
}
|
||||
$this->type = $type;
|
||||
|
||||
$this->imagick = $imagick && $this->useImagick($data);
|
||||
|
||||
if ($this->isImagick() && (empty($data) || $this->loadData($data))) {
|
||||
$this->valid = !empty($data);
|
||||
|
@ -75,6 +87,49 @@ class Image
|
|||
$this->loadData($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Imagick will be used
|
||||
*
|
||||
* @param string $data
|
||||
* @return boolean
|
||||
*/
|
||||
private function useImagick(string $data): bool
|
||||
{
|
||||
if (!class_exists('Imagick')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->imageType == IMAGETYPE_GIF) {
|
||||
$count = preg_match_all("#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s", $data);
|
||||
return ($count > 0);
|
||||
}
|
||||
|
||||
return (($this->imageType == IMAGETYPE_WEBP) && $this->isAnimatedWebP(substr($data, 0, 90)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if a WebP image is animated.
|
||||
* @see https://www.php.net/manual/en/function.imagecreatefromwebp.php#126269
|
||||
* @param string $data
|
||||
* @return boolean
|
||||
*/
|
||||
private function isAnimatedWebP(string $data) {
|
||||
$header_format = 'A4Riff/I1Filesize/A4Webp/A4Vp/A74Chunk';
|
||||
$header = unpack($header_format, $data);
|
||||
|
||||
if (!isset($header['Riff']) || strtoupper($header['Riff']) !== 'RIFF') {
|
||||
return false;
|
||||
}
|
||||
if (!isset($header['Webp']) || strtoupper($header['Webp']) !== 'WEBP') {
|
||||
return false;
|
||||
}
|
||||
if (!isset($header['Vp']) || strpos(strtoupper($header['Vp']), 'VP8') === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strpos(strtoupper($header['Chunk']), 'ANIM') !== false || strpos(strtoupper($header['Chunk']), 'ANMF') !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
|
@ -118,28 +173,28 @@ class Image
|
|||
$this->image->readImageBlob($data);
|
||||
} catch (Exception $e) {
|
||||
// Imagick couldn't use the data
|
||||
DI::logger()->debug('Error during readImageBlob', ['message' => $e->getMessage(), 'code' => $e->getCode(), 'trace' => $e->getTraceAsString(), 'previous' => $e->getPrevious(), 'file' => $this->filename]);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the image to the format it will be saved to
|
||||
*/
|
||||
$map = Images::getFormatsMap();
|
||||
$format = $map[$this->type];
|
||||
$this->image->setFormat($format);
|
||||
$this->image->setFormat(Images::getImagickFormatByImageType($this->imageType));
|
||||
|
||||
// Always coalesce, if it is not a multi-frame image it won't hurt anyway
|
||||
try {
|
||||
$this->image = $this->image->coalesceImages();
|
||||
} catch (Exception $e) {
|
||||
DI::logger()->debug('Error during coalesceImages', ['message' => $e->getMessage(), 'code' => $e->getCode(), 'trace' => $e->getTraceAsString(), 'previous' => $e->getPrevious(), 'file' => $this->filename]);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* setup the compression here, so we'll do it only once
|
||||
*/
|
||||
switch ($this->getType()) {
|
||||
case 'image/png':
|
||||
switch ($this->getImageType()) {
|
||||
case IMAGETYPE_PNG:
|
||||
$quality = DI::config()->get('system', 'png_quality');
|
||||
/*
|
||||
* From http://www.imagemagick.org/script/command-line-options.php#quality:
|
||||
|
@ -150,13 +205,12 @@ class Image
|
|||
* unless the image has a color map, in which case it means compression level 7 with no PNG filtering'
|
||||
*/
|
||||
$quality = $quality * 10;
|
||||
$this->image->setCompressionQuality($quality);
|
||||
$this->image->setImageCompressionQuality($quality);
|
||||
break;
|
||||
|
||||
case 'image/jpg':
|
||||
case 'image/jpeg':
|
||||
case IMAGETYPE_JPEG:
|
||||
$quality = DI::config()->get('system', 'jpeg_quality');
|
||||
$this->image->setCompressionQuality($quality);
|
||||
$this->image->setImageCompressionQuality($quality);
|
||||
}
|
||||
|
||||
$this->width = $this->image->getImageWidth();
|
||||
|
@ -182,9 +236,9 @@ class Image
|
|||
} catch (\Throwable $error) {
|
||||
/** @see https://github.com/php/doc-en/commit/d09a881a8e9059d11e756ee59d75bf404d6941ed */
|
||||
if (strstr($error->getMessage(), "gd-webp cannot allocate temporary buffer")) {
|
||||
DI::logger()->notice('Image is probably animated and therefore unsupported', ['error' => $error]);
|
||||
DI::logger()->notice('Image is probably animated and therefore unsupported', ['message' => $error->getMessage(), 'code' => $error->getCode(), 'trace' => $error->getTraceAsString(), 'file' => $this->filename]);
|
||||
} else {
|
||||
DI::logger()->warning('Unexpected throwable.', ['error' => $error]);
|
||||
DI::logger()->warning('Unexpected throwable.', ['message' => $error->getMessage(), 'code' => $error->getCode(), 'trace' => $error->getTraceAsString(), 'file' => $this->filename]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,7 +310,19 @@ class Image
|
|||
return false;
|
||||
}
|
||||
|
||||
return $this->type;
|
||||
return image_type_to_mime_type($this->imageType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getImageType()
|
||||
{
|
||||
if (!$this->isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->imageType;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -268,7 +334,7 @@ class Image
|
|||
return false;
|
||||
}
|
||||
|
||||
return $this->types[$this->getType()];
|
||||
return Images::getExtensionByImageType($this->imageType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -398,7 +464,7 @@ class Image
|
|||
return false;
|
||||
}
|
||||
|
||||
if ((!function_exists('exif_read_data')) || ($this->getType() !== 'image/jpeg')) {
|
||||
if ((!function_exists('exif_read_data')) || ($this->getImageType() !== IMAGETYPE_JPEG)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -545,7 +611,7 @@ class Image
|
|||
imagealphablending($dest, false);
|
||||
imagesavealpha($dest, true);
|
||||
|
||||
if ($this->type=='image/png') {
|
||||
if ($this->imageType == IMAGETYPE_PNG) {
|
||||
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
}
|
||||
|
||||
|
@ -570,13 +636,13 @@ class Image
|
|||
*/
|
||||
public function toStatic()
|
||||
{
|
||||
if ($this->type != 'image/gif') {
|
||||
if ($this->imageType != IMAGETYPE_GIF) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isImagick()) {
|
||||
$this->type == 'image/png';
|
||||
$this->image->setFormat('png');
|
||||
$this->imageType = IMAGETYPE_PNG;
|
||||
$this->image->setFormat(Images::getImagickFormatByImageType($this->imageType));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -614,7 +680,7 @@ class Image
|
|||
imagealphablending($dest, false);
|
||||
imagesavealpha($dest, true);
|
||||
|
||||
if ($this->type=='image/png') {
|
||||
if ($this->imageType == IMAGETYPE_PNG) {
|
||||
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
}
|
||||
imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $max, $max, $w, $h);
|
||||
|
@ -668,17 +734,28 @@ class Image
|
|||
|
||||
$stream = fopen('php://memory','r+');
|
||||
|
||||
switch ($this->getType()) {
|
||||
case 'image/png':
|
||||
switch ($this->getImageType()) {
|
||||
case IMAGETYPE_PNG:
|
||||
$quality = DI::config()->get('system', 'png_quality');
|
||||
imagepng($this->image, $stream, $quality);
|
||||
break;
|
||||
|
||||
case 'image/jpeg':
|
||||
case 'image/jpg':
|
||||
case IMAGETYPE_JPEG:
|
||||
$quality = DI::config()->get('system', 'jpeg_quality');
|
||||
imagejpeg($this->image, $stream, $quality);
|
||||
break;
|
||||
|
||||
case IMAGETYPE_GIF:
|
||||
imagegif($this->image, $stream);
|
||||
break;
|
||||
|
||||
case IMAGETYPE_WEBP:
|
||||
imagewebp($this->image, $stream, DI::config()->get('system', 'jpeg_quality'));
|
||||
break;
|
||||
|
||||
case IMAGETYPE_BMP:
|
||||
imagebmp($this->image, $stream);
|
||||
break;
|
||||
}
|
||||
rewind($stream);
|
||||
return stream_get_contents($stream);
|
||||
|
@ -692,7 +769,7 @@ class Image
|
|||
*/
|
||||
public function getBlurHash(): string
|
||||
{
|
||||
$image = New Image($this->asString());
|
||||
$image = New Image($this->asString(), $this->getType(), $this->filename, false);
|
||||
if (empty($image) || !$this->isValid()) {
|
||||
return '';
|
||||
}
|
||||
|
|
|
@ -304,10 +304,8 @@ class DFRN
|
|||
$profilephotos = Photo::selectToArray(['resource-id', 'scale', 'type'], ['profile' => true, 'uid' => $uid], ['order' => ['scale']]);
|
||||
|
||||
$photos = [];
|
||||
$ext = Images::supportedTypes();
|
||||
|
||||
foreach ($profilephotos as $p) {
|
||||
$photos[$p['scale']] = DI::baseUrl() . '/photo/' . $p['resource-id'] . '-' . $p['scale'] . '.' . $ext[$p['type']];
|
||||
$photos[$p['scale']] = DI::baseUrl() . '/photo/' . $p['resource-id'] . '-' . $p['scale'] . Images::getExtensionByMimeType($p['type']);
|
||||
}
|
||||
|
||||
$doc = new DOMDocument('1.0', 'utf-8');
|
||||
|
|
|
@ -43,6 +43,7 @@ use Friendica\Model\Tag;
|
|||
use Friendica\Model\User;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Images;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\ParseUrl;
|
||||
use Friendica\Util\Proxy;
|
||||
|
@ -573,7 +574,7 @@ class Feed
|
|||
if (in_array($fetch_further_information, [LocalRelationship::FFI_INFORMATION, LocalRelationship::FFI_BOTH])) {
|
||||
// Handle enclosures and treat them as preview picture
|
||||
foreach ($attachments as $attachment) {
|
||||
if ($attachment['mimetype'] == 'image/jpeg') {
|
||||
if (Images::isSupportedMimeType($attachment['mimetype'])) {
|
||||
$preview = $attachment['url'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,19 +33,107 @@ use Friendica\Object\Image;
|
|||
*/
|
||||
class Images
|
||||
{
|
||||
// @todo add IMAGETYPE_AVIF once our minimal supported PHP version is 8.1.0
|
||||
const IMAGETYPES = [IMAGETYPE_WEBP, IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF, IMAGETYPE_BMP];
|
||||
|
||||
/**
|
||||
* Maps Mime types to Imagick formats
|
||||
* Get the Imagick format for the given image type
|
||||
*
|
||||
* @return array Format map
|
||||
* @param int $imagetype
|
||||
* @return string
|
||||
*/
|
||||
public static function getFormatsMap()
|
||||
public static function getImagickFormatByImageType(int $imagetype): string
|
||||
{
|
||||
return [
|
||||
'image/jpeg' => 'JPG',
|
||||
'image/jpg' => 'JPG',
|
||||
'image/png' => 'PNG',
|
||||
'image/gif' => 'GIF',
|
||||
$formats = [
|
||||
// @todo add "IMAGETYPE_AVIF => 'AVIF'" once our minimal supported PHP version is 8.1.0
|
||||
IMAGETYPE_WEBP => 'WEBP',
|
||||
IMAGETYPE_PNG => 'PNG',
|
||||
IMAGETYPE_JPEG => 'JPEG',
|
||||
IMAGETYPE_GIF => 'GIF',
|
||||
IMAGETYPE_BMP => 'BMP',
|
||||
];
|
||||
|
||||
if (empty($formats[$imagetype])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $formats[$imagetype];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the provided mime type, replace invalid mime types with valid ones.
|
||||
*
|
||||
* @param string $mimetype
|
||||
* @return string
|
||||
*/
|
||||
private static function sanitizeMimeType(string $mimetype): string
|
||||
{
|
||||
$mimetype = current(explode(';', $mimetype));
|
||||
|
||||
if ($mimetype == 'image/jpg') {
|
||||
$mimetype = image_type_to_mime_type(IMAGETYPE_JPEG);
|
||||
} elseif (in_array($mimetype, ['image/vnd.mozilla.apng', 'image/apng'])) {
|
||||
$mimetype = image_type_to_mime_type(IMAGETYPE_PNG);
|
||||
} elseif (in_array($mimetype, ['image/x-ms-bmp', 'image/x-bmp'])) {
|
||||
$mimetype = image_type_to_mime_type(IMAGETYPE_BMP);
|
||||
}
|
||||
|
||||
return $mimetype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace invalid extensions with valid ones.
|
||||
*
|
||||
* @param string $extension
|
||||
* @return string
|
||||
*/
|
||||
private static function sanitizeExtensions(string $extension): string
|
||||
{
|
||||
if (in_array($extension, ['jpg', 'jpe', 'jfif'])) {
|
||||
$extension = image_type_to_extension(IMAGETYPE_JPEG, false);
|
||||
} elseif ($extension == 'apng') {
|
||||
$extension = image_type_to_extension(IMAGETYPE_PNG, false);
|
||||
} elseif ($extension == 'dib') {
|
||||
$extension = image_type_to_extension(IMAGETYPE_BMP, false);
|
||||
}
|
||||
|
||||
return $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image type for the given mime type
|
||||
*
|
||||
* @param string $mimetype
|
||||
* @return integer
|
||||
*/
|
||||
public static function getImageTypeByMimeType(string $mimetype): int
|
||||
{
|
||||
$mimetype = self::sanitizeMimeType($mimetype);
|
||||
|
||||
foreach (self::IMAGETYPES as $type) {
|
||||
if ($mimetype == image_type_to_mime_type($type)) {
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
|
||||
Logger::debug('Undetected mimetype', ['mimetype' => $mimetype]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the extension for the given image type
|
||||
*
|
||||
* @param integer $type
|
||||
* @return string
|
||||
*/
|
||||
public static function getExtensionByImageType(int $type): string
|
||||
{
|
||||
if (empty($type)) {
|
||||
Logger::debug('Invalid image type', ['type' => $type]);
|
||||
return '';
|
||||
}
|
||||
|
||||
return image_type_to_extension($type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,51 +144,40 @@ class Images
|
|||
*/
|
||||
public static function getExtensionByMimeType(string $mimetype): string
|
||||
{
|
||||
switch ($mimetype) {
|
||||
case 'image/png':
|
||||
$imagetype = IMAGETYPE_PNG;
|
||||
break;
|
||||
|
||||
case 'image/gif':
|
||||
$imagetype = IMAGETYPE_GIF;
|
||||
break;
|
||||
|
||||
case 'image/jpeg':
|
||||
case 'image/jpg':
|
||||
$imagetype = IMAGETYPE_JPEG;
|
||||
break;
|
||||
|
||||
default: // Unknown type must be a blob then
|
||||
return 'blob';
|
||||
break;
|
||||
if (empty($mimetype)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return image_type_to_extension($imagetype);
|
||||
return self::getExtensionByImageType(self::getImageTypeByMimeType($mimetype));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns supported image mimetypes and corresponding file extensions
|
||||
* Returns supported image mimetypes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function supportedTypes(): array
|
||||
public static function supportedMimeTypes(): array
|
||||
{
|
||||
$types = [
|
||||
'image/jpeg' => 'jpg',
|
||||
'image/jpg' => 'jpg',
|
||||
];
|
||||
$types = [];
|
||||
|
||||
if (class_exists('Imagick')) {
|
||||
// Imagick::queryFormats won't help us a lot there...
|
||||
// At least, not yet, other parts of friendica uses this array
|
||||
$types += [
|
||||
'image/png' => 'png',
|
||||
'image/gif' => 'gif'
|
||||
];
|
||||
} elseif (imagetypes() & IMG_PNG) {
|
||||
$types += [
|
||||
'image/png' => 'png'
|
||||
];
|
||||
// @todo enable, once our lowest supported PHP version is 8.1.0
|
||||
//if (imagetypes() & IMG_AVIF) {
|
||||
// $types[] = image_type_to_mime_type(IMAGETYPE_AVIF);
|
||||
//}
|
||||
if (imagetypes() & IMG_WEBP) {
|
||||
$types[] = image_type_to_mime_type(IMAGETYPE_WEBP);
|
||||
}
|
||||
if (imagetypes() & IMG_PNG) {
|
||||
$types[] = image_type_to_mime_type(IMAGETYPE_PNG);
|
||||
}
|
||||
if (imagetypes() & IMG_JPG) {
|
||||
$types[] = image_type_to_mime_type(IMAGETYPE_JPEG);
|
||||
}
|
||||
if (imagetypes() & IMG_GIF) {
|
||||
$types[] = image_type_to_mime_type(IMAGETYPE_GIF);
|
||||
}
|
||||
if (imagetypes() & IMG_BMP) {
|
||||
$types[] = image_type_to_mime_type(IMAGETYPE_BMP);
|
||||
}
|
||||
|
||||
return $types;
|
||||
|
@ -115,45 +192,69 @@ class Images
|
|||
* @return string MIME type
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getMimeTypeByData(string $image_data, string $filename = '', string $default = ''): string
|
||||
public static function getMimeTypeByData(string $image_data): string
|
||||
{
|
||||
if (substr($default, 0, 6) == 'image/') {
|
||||
Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $default]);
|
||||
return $default;
|
||||
}
|
||||
|
||||
$image = @getimagesizefromstring($image_data);
|
||||
if (!empty($image['mime'])) {
|
||||
Logger::info('Mime type detected via data', ['filename' => $filename, 'default' => $default, 'mime' => $image['mime']]);
|
||||
return $image['mime'];
|
||||
}
|
||||
|
||||
return self::guessTypeByExtension($filename);
|
||||
Logger::debug('Undetected mime type', ['image' => $image, 'size' => strlen($image_data)]);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch image mimetype from the image data or guessing from the file name
|
||||
* Checks if the provided mime type is supported by the system
|
||||
*
|
||||
* @param string $sourcefile Source file of the image
|
||||
* @param string $filename File name (for guessing the type via the extension)
|
||||
* @param string $default default MIME type
|
||||
* @return string MIME type
|
||||
* @throws \Exception
|
||||
* @param string $mimetype
|
||||
* @return boolean
|
||||
*/
|
||||
public static function getMimeTypeBySource(string $sourcefile, string $filename = '', string $default = ''): string
|
||||
public static function isSupportedMimeType(string $mimetype): bool
|
||||
{
|
||||
if (substr($default, 0, 6) == 'image/') {
|
||||
Logger::info('Using default mime type', ['filename' => $filename, 'mime' => $default]);
|
||||
return $default;
|
||||
if (substr($mimetype, 0, 6) != 'image/') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$image = @getimagesize($sourcefile);
|
||||
if (!empty($image['mime'])) {
|
||||
Logger::info('Mime type detected via file', ['filename' => $filename, 'default' => $default, 'image' => $image]);
|
||||
return $image['mime'];
|
||||
return in_array(self::sanitizeMimeType($mimetype), self::supportedMimeTypes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the provided mime type is supported. If not, it is fetched from the provided image data.
|
||||
*
|
||||
* @param string $mimetype
|
||||
* @param string $image_data
|
||||
* @return string
|
||||
*/
|
||||
public static function addMimeTypeByDataIfInvalid(string $mimetype, string $image_data): string
|
||||
{
|
||||
$mimetype = self::sanitizeMimeType($mimetype);
|
||||
|
||||
if (($image_data == '') || self::isSupportedMimeType($mimetype)) {
|
||||
return $mimetype;
|
||||
}
|
||||
|
||||
return self::guessTypeByExtension($filename);
|
||||
$alternative = self::getMimeTypeByData($image_data);
|
||||
return $alternative ?: $mimetype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the provided mime type is supported. If not, it is fetched from the provided file name.
|
||||
*
|
||||
* @param string $mimetype
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
public static function addMimeTypeByExtensionIfInvalid(string $mimetype, string $filename): string
|
||||
{
|
||||
$mimetype = self::sanitizeMimeType($mimetype);
|
||||
|
||||
if (($filename == '') || self::isSupportedMimeType($mimetype)) {
|
||||
return $mimetype;
|
||||
}
|
||||
|
||||
$alternative = self::guessTypeByExtension($filename);
|
||||
return $alternative ?: $mimetype;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -165,17 +266,24 @@ class Images
|
|||
*/
|
||||
public static function guessTypeByExtension(string $filename): string
|
||||
{
|
||||
$ext = pathinfo(parse_url($filename, PHP_URL_PATH), PATHINFO_EXTENSION);
|
||||
$types = self::supportedTypes();
|
||||
$type = 'image/jpeg';
|
||||
foreach ($types as $m => $e) {
|
||||
if ($ext == $e) {
|
||||
$type = $m;
|
||||
if (empty($filename)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$ext = strtolower(pathinfo(parse_url($filename, PHP_URL_PATH), PATHINFO_EXTENSION));
|
||||
$ext = self::sanitizeExtensions($ext);
|
||||
if ($ext == '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
foreach (self::IMAGETYPES as $type) {
|
||||
if ($ext == image_type_to_extension($type, false)) {
|
||||
return image_type_to_mime_type($type);
|
||||
}
|
||||
}
|
||||
|
||||
Logger::info('Mime type guessed via extension', ['filename' => $filename, 'type' => $type]);
|
||||
return $type;
|
||||
Logger::debug('Unhandled extension', ['filename' => $filename, 'extension' => $ext]);
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -256,7 +364,7 @@ class Images
|
|||
return [];
|
||||
}
|
||||
|
||||
$image = new Image($img_str);
|
||||
$image = new Image($img_str, '', $url);
|
||||
|
||||
if ($image->isValid()) {
|
||||
$data['blurhash'] = $image->getBlurHash();
|
||||
|
@ -344,7 +452,7 @@ class Images
|
|||
{
|
||||
return self::getBBCodeByUrl(
|
||||
DI::baseUrl() . '/photos/' . $nickname . '/image/' . $resource_id,
|
||||
DI::baseUrl() . '/photo/' . $resource_id . '-' . $preview. '.' . $ext,
|
||||
DI::baseUrl() . '/photo/' . $resource_id . '-' . $preview. $ext,
|
||||
$description
|
||||
);
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ class ParseUrl
|
|||
return [];
|
||||
}
|
||||
|
||||
$contenttype = $curlResult->getHeader('Content-Type')[0] ?? '';
|
||||
$contenttype = $curlResult->getContentType();
|
||||
if (empty($contenttype)) {
|
||||
return ['application', 'octet-stream'];
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: 2024.03-rc\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-13 05:20+0000\n"
|
||||
"POT-Creation-Date: 2024-02-16 02:33+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -45,7 +45,7 @@ msgid "Item not found."
|
|||
msgstr ""
|
||||
|
||||
#: mod/item.php:457 mod/message.php:67 mod/message.php:113 mod/notes.php:45
|
||||
#: mod/photos.php:152 mod/photos.php:670 src/Model/Event.php:520
|
||||
#: mod/photos.php:150 mod/photos.php:666 src/Model/Event.php:520
|
||||
#: src/Module/Attach.php:55 src/Module/BaseApi.php:103
|
||||
#: src/Module/BaseNotifications.php:98 src/Module/BaseSettings.php:50
|
||||
#: src/Module/Calendar/Event/API.php:88 src/Module/Calendar/Event/Form.php:84
|
||||
|
@ -70,7 +70,7 @@ msgstr ""
|
|||
#: src/Module/Settings/Channels.php:135 src/Module/Settings/Delegation.php:90
|
||||
#: src/Module/Settings/Display.php:90 src/Module/Settings/Display.php:199
|
||||
#: src/Module/Settings/Profile/Photo/Crop.php:165
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:112
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:110
|
||||
#: src/Module/Settings/RemoveMe.php:119 src/Module/Settings/UserExport.php:80
|
||||
#: src/Module/Settings/UserExport.php:114
|
||||
#: src/Module/Settings/UserExport.php:215
|
||||
|
@ -289,16 +289,16 @@ msgstr ""
|
|||
msgid "Insert web link"
|
||||
msgstr ""
|
||||
|
||||
#: mod/message.php:201 mod/message.php:357 mod/photos.php:1301
|
||||
#: mod/message.php:201 mod/message.php:357 mod/photos.php:1297
|
||||
#: src/Content/Conversation.php:401 src/Content/Conversation.php:1586
|
||||
#: src/Module/Item/Compose.php:206 src/Module/Post/Edit.php:145
|
||||
#: src/Object/Post.php:609
|
||||
msgid "Please wait"
|
||||
msgstr ""
|
||||
|
||||
#: mod/message.php:202 mod/message.php:356 mod/photos.php:705
|
||||
#: mod/photos.php:824 mod/photos.php:1101 mod/photos.php:1142
|
||||
#: mod/photos.php:1198 mod/photos.php:1278
|
||||
#: mod/message.php:202 mod/message.php:356 mod/photos.php:701
|
||||
#: mod/photos.php:820 mod/photos.php:1097 mod/photos.php:1138
|
||||
#: mod/photos.php:1194 mod/photos.php:1274
|
||||
#: src/Module/Calendar/Event/Form.php:250 src/Module/Contact/Advanced.php:132
|
||||
#: src/Module/Contact/Profile.php:364
|
||||
#: src/Module/Debug/ActivityPubConversion.php:140
|
||||
|
@ -386,7 +386,7 @@ msgstr ""
|
|||
msgid "Save"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:67 mod/photos.php:132 mod/photos.php:578
|
||||
#: mod/photos.php:67 mod/photos.php:132 mod/photos.php:576
|
||||
#: src/Model/Event.php:512 src/Model/Profile.php:233
|
||||
#: src/Module/Calendar/Export.php:74 src/Module/Calendar/Show.php:74
|
||||
#: src/Module/DFRN/Poll.php:43 src/Module/Feed.php:65 src/Module/HCard.php:51
|
||||
|
@ -399,99 +399,99 @@ msgid "User not found."
|
|||
msgstr ""
|
||||
|
||||
#: mod/photos.php:106 src/Module/BaseProfile.php:68
|
||||
#: src/Module/Profile/Photos.php:379
|
||||
#: src/Module/Profile/Photos.php:375
|
||||
msgid "Photo Albums"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:107 src/Module/Profile/Photos.php:380
|
||||
#: src/Module/Profile/Photos.php:400
|
||||
#: mod/photos.php:107 src/Module/Profile/Photos.php:376
|
||||
#: src/Module/Profile/Photos.php:396
|
||||
msgid "Recent Photos"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:109 mod/photos.php:872 src/Module/Profile/Photos.php:382
|
||||
#: src/Module/Profile/Photos.php:402
|
||||
#: mod/photos.php:109 mod/photos.php:868 src/Module/Profile/Photos.php:378
|
||||
#: src/Module/Profile/Photos.php:398
|
||||
msgid "Upload New Photos"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:121 src/Module/BaseSettings.php:72
|
||||
#: src/Module/Profile/Photos.php:363
|
||||
#: src/Module/Profile/Photos.php:359
|
||||
msgid "everybody"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:159
|
||||
#: mod/photos.php:157
|
||||
msgid "Contact information unavailable"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:188
|
||||
#: mod/photos.php:186
|
||||
msgid "Album not found."
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:244
|
||||
#: mod/photos.php:242
|
||||
msgid "Album successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:246
|
||||
#: mod/photos.php:244
|
||||
msgid "Album was empty."
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:277
|
||||
#: mod/photos.php:275
|
||||
msgid "Failed to delete the photo."
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:545
|
||||
#: mod/photos.php:543
|
||||
msgid "a photo"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:545
|
||||
#: mod/photos.php:543
|
||||
#, php-format
|
||||
msgid "%1$s was tagged in %2$s by %3$s"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:582 src/Module/Conversation/Community.php:160
|
||||
#: src/Module/Directory.php:48 src/Module/Profile/Photos.php:295
|
||||
#: mod/photos.php:580 src/Module/Conversation/Community.php:160
|
||||
#: src/Module/Directory.php:48 src/Module/Profile/Photos.php:293
|
||||
#: src/Module/Search/Index.php:65
|
||||
msgid "Public access denied."
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:587
|
||||
#: mod/photos.php:585
|
||||
msgid "No photos selected"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:721
|
||||
#: mod/photos.php:717
|
||||
#, php-format
|
||||
msgid "The maximum accepted image size is %s"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:728
|
||||
#: mod/photos.php:724
|
||||
msgid "Upload Photos"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:732 mod/photos.php:820
|
||||
#: mod/photos.php:728 mod/photos.php:816
|
||||
msgid "New album name: "
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:733
|
||||
#: mod/photos.php:729
|
||||
msgid "or select existing album:"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:734
|
||||
#: mod/photos.php:730
|
||||
msgid "Do not show a status post for this upload"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:736 mod/photos.php:1097 src/Content/Conversation.php:403
|
||||
#: mod/photos.php:732 mod/photos.php:1093 src/Content/Conversation.php:403
|
||||
#: src/Module/Calendar/Event/Form.php:253 src/Module/Post/Edit.php:183
|
||||
msgid "Permissions"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:801
|
||||
#: mod/photos.php:797
|
||||
msgid "Do you really want to delete this photo album and all its photos?"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:802 mod/photos.php:825
|
||||
#: mod/photos.php:798 mod/photos.php:821
|
||||
msgid "Delete Album"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:803 mod/photos.php:903 src/Content/Conversation.php:419
|
||||
#: mod/photos.php:799 mod/photos.php:899 src/Content/Conversation.php:419
|
||||
#: src/Module/Contact/Follow.php:173 src/Module/Contact/Revoke.php:109
|
||||
#: src/Module/Contact/Unfollow.php:126
|
||||
#: src/Module/Media/Attachment/Browser.php:77
|
||||
|
@ -501,132 +501,132 @@ msgstr ""
|
|||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:829
|
||||
#: mod/photos.php:825
|
||||
msgid "Edit Album"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:830
|
||||
#: mod/photos.php:826
|
||||
msgid "Drop Album"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:834
|
||||
#: mod/photos.php:830
|
||||
msgid "Show Newest First"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:836
|
||||
#: mod/photos.php:832
|
||||
msgid "Show Oldest First"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:857 src/Module/Profile/Photos.php:350
|
||||
#: mod/photos.php:853 src/Module/Profile/Photos.php:346
|
||||
msgid "View Photo"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:889
|
||||
#: mod/photos.php:885
|
||||
msgid "Permission denied. Access to this item may be restricted."
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:891
|
||||
#: mod/photos.php:887
|
||||
msgid "Photo not available"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:901
|
||||
#: mod/photos.php:897
|
||||
msgid "Do you really want to delete this photo?"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:902 mod/photos.php:1102
|
||||
#: mod/photos.php:898 mod/photos.php:1098
|
||||
msgid "Delete Photo"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1000
|
||||
#: mod/photos.php:996
|
||||
msgid "View photo"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1002
|
||||
#: mod/photos.php:998
|
||||
msgid "Edit photo"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1003
|
||||
#: mod/photos.php:999
|
||||
msgid "Delete photo"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1004
|
||||
#: mod/photos.php:1000
|
||||
msgid "Use as profile photo"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1011
|
||||
#: mod/photos.php:1007
|
||||
msgid "Private Photo"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1017
|
||||
#: mod/photos.php:1013
|
||||
msgid "View Full Size"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1070
|
||||
#: mod/photos.php:1066
|
||||
msgid "Tags: "
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1073
|
||||
#: mod/photos.php:1069
|
||||
msgid "[Select tags to remove]"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1088
|
||||
#: mod/photos.php:1084
|
||||
msgid "New album name"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1089
|
||||
#: mod/photos.php:1085
|
||||
msgid "Caption"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1090
|
||||
#: mod/photos.php:1086
|
||||
msgid "Add a Tag"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1090
|
||||
#: mod/photos.php:1086
|
||||
msgid "Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1091
|
||||
#: mod/photos.php:1087
|
||||
msgid "Do not rotate"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1092
|
||||
#: mod/photos.php:1088
|
||||
msgid "Rotate CW (right)"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1093
|
||||
#: mod/photos.php:1089
|
||||
msgid "Rotate CCW (left)"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1139 mod/photos.php:1195 mod/photos.php:1275
|
||||
#: mod/photos.php:1135 mod/photos.php:1191 mod/photos.php:1271
|
||||
#: src/Module/Contact.php:618 src/Module/Item/Compose.php:188
|
||||
#: src/Object/Post.php:1151
|
||||
msgid "This is you"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1141 mod/photos.php:1197 mod/photos.php:1277
|
||||
#: mod/photos.php:1137 mod/photos.php:1193 mod/photos.php:1273
|
||||
#: src/Module/Moderation/Reports.php:95 src/Object/Post.php:603
|
||||
#: src/Object/Post.php:1153
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1143 mod/photos.php:1199 mod/photos.php:1279
|
||||
#: mod/photos.php:1139 mod/photos.php:1195 mod/photos.php:1275
|
||||
#: src/Content/Conversation.php:416 src/Module/Calendar/Event/Form.php:248
|
||||
#: src/Module/Item/Compose.php:201 src/Module/Post/Edit.php:165
|
||||
#: src/Object/Post.php:1167
|
||||
msgid "Preview"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1144 src/Content/Conversation.php:369
|
||||
#: mod/photos.php:1140 src/Content/Conversation.php:369
|
||||
#: src/Module/Post/Edit.php:130 src/Object/Post.php:1155
|
||||
msgid "Loading..."
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1236 src/Content/Conversation.php:1501
|
||||
#: mod/photos.php:1232 src/Content/Conversation.php:1501
|
||||
#: src/Object/Post.php:261
|
||||
msgid "Select"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1237 src/Content/Conversation.php:1502
|
||||
#: mod/photos.php:1233 src/Content/Conversation.php:1502
|
||||
#: src/Module/Moderation/Users/Active.php:136
|
||||
#: src/Module/Moderation/Users/Blocked.php:136
|
||||
#: src/Module/Moderation/Users/Index.php:151
|
||||
|
@ -635,23 +635,23 @@ msgstr ""
|
|||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1298 src/Object/Post.php:426
|
||||
#: mod/photos.php:1294 src/Object/Post.php:426
|
||||
msgid "Like"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1299 src/Object/Post.php:426
|
||||
#: mod/photos.php:1295 src/Object/Post.php:426
|
||||
msgid "I like this (toggle)"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1300 src/Object/Post.php:427
|
||||
#: mod/photos.php:1296 src/Object/Post.php:427
|
||||
msgid "Dislike"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1302 src/Object/Post.php:427
|
||||
#: mod/photos.php:1298 src/Object/Post.php:427
|
||||
msgid "I don't like this (toggle)"
|
||||
msgstr ""
|
||||
|
||||
#: mod/photos.php:1324
|
||||
#: mod/photos.php:1320
|
||||
msgid "Map"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1803,31 +1803,31 @@ msgstr ""
|
|||
msgid "Follow Thread"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Item.php:430 src/Model/Contact.php:1250
|
||||
#: src/Content/Item.php:430 src/Model/Contact.php:1243
|
||||
msgid "View Status"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Item.php:431 src/Content/Item.php:452 src/Model/Contact.php:1184
|
||||
#: src/Model/Contact.php:1241 src/Model/Contact.php:1251
|
||||
#: src/Content/Item.php:431 src/Content/Item.php:452 src/Model/Contact.php:1177
|
||||
#: src/Model/Contact.php:1234 src/Model/Contact.php:1244
|
||||
#: src/Module/Directory.php:157 src/Module/Settings/Profile/Index.php:259
|
||||
msgid "View Profile"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Item.php:432 src/Model/Contact.php:1252
|
||||
#: src/Content/Item.php:432 src/Model/Contact.php:1245
|
||||
msgid "View Photos"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Item.php:433 src/Model/Contact.php:1219
|
||||
#: src/Content/Item.php:433 src/Model/Contact.php:1212
|
||||
#: src/Model/Profile.php:468
|
||||
msgid "Network Posts"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Item.php:434 src/Model/Contact.php:1243
|
||||
#: src/Model/Contact.php:1254
|
||||
#: src/Content/Item.php:434 src/Model/Contact.php:1236
|
||||
#: src/Model/Contact.php:1247
|
||||
msgid "View Contact"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Item.php:435 src/Model/Contact.php:1255
|
||||
#: src/Content/Item.php:435 src/Model/Contact.php:1248
|
||||
msgid "Send PM"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1863,7 +1863,7 @@ msgid "Languages"
|
|||
msgstr ""
|
||||
|
||||
#: src/Content/Item.php:449 src/Content/Widget.php:80
|
||||
#: src/Model/Contact.php:1244 src/Model/Contact.php:1256
|
||||
#: src/Model/Contact.php:1237 src/Model/Contact.php:1249
|
||||
#: src/Module/Contact/Follow.php:167 view/theme/vier/theme.php:195
|
||||
msgid "Connect/Follow"
|
||||
msgstr ""
|
||||
|
@ -2190,39 +2190,39 @@ msgstr ""
|
|||
msgid "last"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Text/BBCode.php:766 src/Content/Text/BBCode.php:1727
|
||||
#: src/Content/Text/BBCode.php:1728
|
||||
#: src/Content/Text/BBCode.php:767 src/Content/Text/BBCode.php:1728
|
||||
#: src/Content/Text/BBCode.php:1729
|
||||
msgid "Image/photo"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Text/BBCode.php:984
|
||||
#: src/Content/Text/BBCode.php:985
|
||||
#, php-format
|
||||
msgid ""
|
||||
"<a href=\"%1$s\" target=\"_blank\" rel=\"noopener noreferrer\">%2$s</a> %3$s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Text/BBCode.php:1009 src/Model/Item.php:3999
|
||||
#: src/Content/Text/BBCode.php:1010 src/Model/Item.php:3999
|
||||
#: src/Model/Item.php:4005 src/Model/Item.php:4006
|
||||
msgid "Link to source"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Text/BBCode.php:1634 src/Content/Text/HTML.php:905
|
||||
#: src/Content/Text/BBCode.php:1635 src/Content/Text/HTML.php:905
|
||||
msgid "Click to open/close"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Text/BBCode.php:1667
|
||||
#: src/Content/Text/BBCode.php:1668
|
||||
msgid "$1 wrote:"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Text/BBCode.php:1732 src/Content/Text/BBCode.php:1733
|
||||
#: src/Content/Text/BBCode.php:1733 src/Content/Text/BBCode.php:1734
|
||||
msgid "Encrypted content"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Text/BBCode.php:1996
|
||||
#: src/Content/Text/BBCode.php:1997
|
||||
msgid "Invalid source protocol"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Text/BBCode.php:2015
|
||||
#: src/Content/Text/BBCode.php:2016
|
||||
msgid "Invalid link protocol"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2370,7 +2370,7 @@ msgstr ""
|
|||
msgid "Organisations"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Widget.php:536 src/Model/Contact.php:1746
|
||||
#: src/Content/Widget.php:536 src/Model/Contact.php:1739
|
||||
msgid "News"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2438,12 +2438,12 @@ msgstr[1] ""
|
|||
msgid "More Trending Tags"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Widget/VCard.php:104 src/Model/Contact.php:1212
|
||||
#: src/Content/Widget/VCard.php:104 src/Model/Contact.php:1205
|
||||
#: src/Model/Profile.php:461
|
||||
msgid "Post to group"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Widget/VCard.php:109 src/Model/Contact.php:1217
|
||||
#: src/Content/Widget/VCard.php:109 src/Model/Contact.php:1210
|
||||
#: src/Model/Profile.php:466 src/Module/Moderation/Item/Source.php:85
|
||||
msgid "Mention"
|
||||
msgstr ""
|
||||
|
@ -2471,13 +2471,13 @@ msgstr ""
|
|||
msgid "Network:"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Widget/VCard.php:128 src/Model/Contact.php:1245
|
||||
#: src/Model/Contact.php:1257 src/Model/Profile.php:479
|
||||
#: src/Content/Widget/VCard.php:128 src/Model/Contact.php:1238
|
||||
#: src/Model/Contact.php:1250 src/Model/Profile.php:479
|
||||
#: src/Module/Contact/Profile.php:463
|
||||
msgid "Unfollow"
|
||||
msgstr ""
|
||||
|
||||
#: src/Content/Widget/VCard.php:134 src/Model/Contact.php:1214
|
||||
#: src/Content/Widget/VCard.php:134 src/Model/Contact.php:1207
|
||||
#: src/Model/Profile.php:463
|
||||
msgid "View group"
|
||||
msgstr ""
|
||||
|
@ -2849,23 +2849,19 @@ msgstr ""
|
|||
msgid "TLS detected"
|
||||
msgstr ""
|
||||
|
||||
#: src/Core/Installer.php:646
|
||||
#: src/Core/Installer.php:636
|
||||
msgid "ImageMagick PHP extension is not installed"
|
||||
msgstr ""
|
||||
|
||||
#: src/Core/Installer.php:648
|
||||
#: src/Core/Installer.php:638
|
||||
msgid "ImageMagick PHP extension is installed"
|
||||
msgstr ""
|
||||
|
||||
#: src/Core/Installer.php:650
|
||||
msgid "ImageMagick supports GIF"
|
||||
msgstr ""
|
||||
|
||||
#: src/Core/Installer.php:672
|
||||
#: src/Core/Installer.php:659
|
||||
msgid "Database already in use."
|
||||
msgstr ""
|
||||
|
||||
#: src/Core/Installer.php:677
|
||||
#: src/Core/Installer.php:664
|
||||
msgid "Could not connect to database."
|
||||
msgstr ""
|
||||
|
||||
|
@ -3247,90 +3243,90 @@ msgstr ""
|
|||
msgid "Edit circles"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:1264 src/Module/Moderation/Users/Pending.php:102
|
||||
#: src/Model/Contact.php:1257 src/Module/Moderation/Users/Pending.php:102
|
||||
#: src/Module/Notifications/Introductions.php:132
|
||||
#: src/Module/Notifications/Introductions.php:204
|
||||
msgid "Approve"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:1742
|
||||
#: src/Model/Contact.php:1735
|
||||
msgid "Organisation"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:1750
|
||||
#: src/Model/Contact.php:1743
|
||||
msgid "Group"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:1754 src/Module/Moderation/BaseUsers.php:130
|
||||
#: src/Model/Contact.php:1747 src/Module/Moderation/BaseUsers.php:130
|
||||
msgid "Relay"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3057
|
||||
#: src/Model/Contact.php:3050
|
||||
msgid "Disallowed profile URL."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3062 src/Module/Friendica.php:101
|
||||
#: src/Model/Contact.php:3055 src/Module/Friendica.php:101
|
||||
msgid "Blocked domain"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3067
|
||||
#: src/Model/Contact.php:3060
|
||||
msgid "Connect URL missing."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3076
|
||||
#: src/Model/Contact.php:3069
|
||||
msgid ""
|
||||
"The contact could not be added. Please check the relevant network "
|
||||
"credentials in your Settings -> Social Networks page."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3094
|
||||
#: src/Model/Contact.php:3087
|
||||
#, php-format
|
||||
msgid "Expected network %s does not match actual network %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3111
|
||||
#: src/Model/Contact.php:3104
|
||||
msgid "This seems to be a relay account. They can't be followed by users."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3118
|
||||
#: src/Model/Contact.php:3111
|
||||
msgid "The profile address specified does not provide adequate information."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3120
|
||||
#: src/Model/Contact.php:3113
|
||||
msgid "No compatible communication protocols or feeds were discovered."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3123
|
||||
#: src/Model/Contact.php:3116
|
||||
msgid "An author or name was not found."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3126
|
||||
#: src/Model/Contact.php:3119
|
||||
msgid "No browser URL could be matched to this address."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3129
|
||||
#: src/Model/Contact.php:3122
|
||||
msgid ""
|
||||
"Unable to match @-style Identity Address with a known protocol or email "
|
||||
"contact."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3130
|
||||
#: src/Model/Contact.php:3123
|
||||
msgid "Use mailto: in front of address to force email check."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3136
|
||||
#: src/Model/Contact.php:3129
|
||||
msgid ""
|
||||
"The profile address specified belongs to a network which has been disabled "
|
||||
"on this site."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3141
|
||||
#: src/Model/Contact.php:3134
|
||||
msgid ""
|
||||
"Limited profile. This person will be unable to receive direct/personal "
|
||||
"notifications from you."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Contact.php:3207
|
||||
#: src/Model/Contact.php:3200
|
||||
msgid "Unable to retrieve contact information."
|
||||
msgstr ""
|
||||
|
||||
|
@ -3527,7 +3523,7 @@ msgstr ""
|
|||
msgid "[no subject]"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/Photo.php:1191 src/Module/Media/Photo/Upload.php:170
|
||||
#: src/Model/Photo.php:1187 src/Module/Media/Photo/Upload.php:168
|
||||
msgid "Wall Photos"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3811,11 +3807,11 @@ msgid ""
|
|||
"An error occurred creating your default contact circle. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1415
|
||||
#: src/Model/User.php:1413
|
||||
msgid "Profile Photos"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1597
|
||||
#: src/Model/User.php:1595
|
||||
#, php-format
|
||||
msgid ""
|
||||
"\n"
|
||||
|
@ -3823,7 +3819,7 @@ msgid ""
|
|||
"\t\t\tthe administrator of %2$s has set up an account for you."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1600
|
||||
#: src/Model/User.php:1598
|
||||
#, php-format
|
||||
msgid ""
|
||||
"\n"
|
||||
|
@ -3859,12 +3855,12 @@ msgid ""
|
|||
"\t\tThank you and welcome to %4$s."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1632 src/Model/User.php:1738
|
||||
#: src/Model/User.php:1630 src/Model/User.php:1736
|
||||
#, php-format
|
||||
msgid "Registration details for %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1652
|
||||
#: src/Model/User.php:1650
|
||||
#, php-format
|
||||
msgid ""
|
||||
"\n"
|
||||
|
@ -3880,12 +3876,12 @@ msgid ""
|
|||
"\t\t"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1671
|
||||
#: src/Model/User.php:1669
|
||||
#, php-format
|
||||
msgid "Registration at %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1695
|
||||
#: src/Model/User.php:1693
|
||||
#, php-format
|
||||
msgid ""
|
||||
"\n"
|
||||
|
@ -3894,7 +3890,7 @@ msgid ""
|
|||
"\t\t\t"
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1703
|
||||
#: src/Model/User.php:1701
|
||||
#, php-format
|
||||
msgid ""
|
||||
"\n"
|
||||
|
@ -3932,7 +3928,7 @@ msgid ""
|
|||
"\t\t\tThank you and welcome to %2$s."
|
||||
msgstr ""
|
||||
|
||||
#: src/Model/User.php:1765
|
||||
#: src/Model/User.php:1763
|
||||
msgid ""
|
||||
"User with delegates can't be removed, please remove delegate users first"
|
||||
msgstr ""
|
||||
|
@ -7808,7 +7804,7 @@ msgstr ""
|
|||
|
||||
#: src/Module/Media/Attachment/Browser.php:79
|
||||
#: src/Module/Media/Photo/Browser.php:90
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:129
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:127
|
||||
msgid "Upload"
|
||||
msgstr ""
|
||||
|
||||
|
@ -7829,14 +7825,14 @@ msgstr ""
|
|||
msgid "File upload failed."
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Media/Photo/Upload.php:152 src/Module/Media/Photo/Upload.php:153
|
||||
#: src/Module/Profile/Photos.php:217
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:69
|
||||
#: src/Module/Media/Photo/Upload.php:150 src/Module/Media/Photo/Upload.php:151
|
||||
#: src/Module/Profile/Photos.php:215
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:67
|
||||
msgid "Unable to process image."
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Media/Photo/Upload.php:178 src/Module/Profile/Photos.php:237
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:96
|
||||
#: src/Module/Media/Photo/Upload.php:176 src/Module/Profile/Photos.php:235
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:94
|
||||
msgid "Image upload failed."
|
||||
msgstr ""
|
||||
|
||||
|
@ -9075,27 +9071,27 @@ msgstr ""
|
|||
|
||||
#: src/Module/Profile/Conversations.php:106
|
||||
#: src/Module/Profile/Conversations.php:109 src/Module/Profile/Profile.php:351
|
||||
#: src/Module/Profile/Profile.php:354 src/Protocol/Feed.php:1098
|
||||
#: src/Module/Profile/Profile.php:354 src/Protocol/Feed.php:1099
|
||||
#: src/Protocol/OStatus.php:1009
|
||||
#, php-format
|
||||
msgid "%s's timeline"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Profile/Conversations.php:107 src/Module/Profile/Profile.php:352
|
||||
#: src/Protocol/Feed.php:1102 src/Protocol/OStatus.php:1014
|
||||
#: src/Protocol/Feed.php:1103 src/Protocol/OStatus.php:1014
|
||||
#, php-format
|
||||
msgid "%s's posts"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Profile/Conversations.php:108 src/Module/Profile/Profile.php:353
|
||||
#: src/Protocol/Feed.php:1105 src/Protocol/OStatus.php:1018
|
||||
#: src/Protocol/Feed.php:1106 src/Protocol/OStatus.php:1018
|
||||
#, php-format
|
||||
msgid "%s's comments"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Profile/Photos.php:164 src/Module/Profile/Photos.php:167
|
||||
#: src/Module/Profile/Photos.php:194
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:60
|
||||
#: src/Module/Profile/Photos.php:192
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:58
|
||||
#, php-format
|
||||
msgid "Image exceeds size limit of %s"
|
||||
msgstr ""
|
||||
|
@ -9114,11 +9110,11 @@ msgid ""
|
|||
"administrator"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Profile/Photos.php:202
|
||||
#: src/Module/Profile/Photos.php:200
|
||||
msgid "Image file is empty."
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Profile/Photos.php:356
|
||||
#: src/Module/Profile/Photos.php:352
|
||||
msgid "View Album"
|
||||
msgstr ""
|
||||
|
||||
|
@ -10929,7 +10925,7 @@ msgstr ""
|
|||
#: src/Module/Settings/Profile/Photo/Crop.php:107
|
||||
#: src/Module/Settings/Profile/Photo/Crop.php:125
|
||||
#: src/Module/Settings/Profile/Photo/Crop.php:143
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:102
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:100
|
||||
#, php-format
|
||||
msgid "Image size reduction [%s] failed."
|
||||
msgstr ""
|
||||
|
@ -10969,31 +10965,31 @@ msgstr ""
|
|||
msgid "Missing uploaded image."
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:125
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:123
|
||||
msgid "Profile Picture Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:126
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:124
|
||||
msgid "Current Profile Picture"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:127
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:125
|
||||
msgid "Upload Profile Picture"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:128
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:126
|
||||
msgid "Upload Picture:"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:133
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:131
|
||||
msgid "or"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:135
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:133
|
||||
msgid "skip this step"
|
||||
msgstr ""
|
||||
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:137
|
||||
#: src/Module/Settings/Profile/Photo/Index.php:135
|
||||
msgid "select a photo from your photo albums"
|
||||
msgstr ""
|
||||
|
||||
|
|
Loading…
Reference in a new issue