use Friendica\Core\Config\IConfig;
use Friendica\Database\Database;
use Friendica\Model\Storage;
+use Friendica\Network\IHTTPRequest;
use Psr\Log\LoggerInterface;
private $logger;
/** @var L10n */
private $l10n;
+ /** @var IHTTPRequest */
+ private $httpRequest;
/** @var Storage\IStorage */
private $currentBackend;
* @param LoggerInterface $logger
* @param L10n $l10n
*/
- public function __construct(Database $dba, IConfig $config, LoggerInterface $logger, L10n $l10n)
+ public function __construct(Database $dba, IConfig $config, LoggerInterface $logger, L10n $l10n, IHTTPRequest $httpRequest)
{
- $this->dba = $dba;
- $this->config = $config;
- $this->logger = $logger;
- $this->l10n = $l10n;
+ $this->dba = $dba;
+ $this->config = $config;
+ $this->logger = $logger;
+ $this->l10n = $l10n;
+ $this->httpRequest = $httpRequest;
$this->backends = $config->get('storage', 'backends', self::DEFAULT_BACKENDS);
$currentName = $this->config->get('storage', 'name', '');
$this->backendInstances[$name] = new Storage\SystemResource();
break;
case Storage\ExternalResource::getName():
- $this->backendInstances[$name] = new Storage\ExternalResource();
+ $this->backendInstances[$name] = new Storage\ExternalResource($this->httpRequest);
break;
default:
$data = [
if (!empty($contact)) {
$contact = self::checkAvatarCacheByArray($contact, $no_update);
if (!empty($contact[$field])) {
- $avatar = $contact[$field];
+ return $contact[$field];
+ } elseif (!empty($contact['id'])) {
+ return self::getAvatarUrlForId($contact['id'], $size, $contact['updated'] ?? '');
+ } elseif (!empty($contact['avatar'])) {
+ $avatar = $contact['avatar'];
}
}
- if ($no_update && empty($avatar) && !empty($contact['avatar'])) {
- $avatar = $contact['avatar'];
+ if (empty($avatar)) {
+ $avatar = self::getDefaultAvatar([], $size);
}
- if (!empty($avatar) && Proxy::isLocalImage($avatar)) {
+ if (Proxy::isLocalImage($avatar)) {
return $avatar;
} else {
- return self::getAvatarUrlForId($contact['id'], $size);
+ return Proxy::proxifyUrl($avatar, false, $size);
}
}
/**
* Get avatar link for given contact id
*
- * @param integer $cid contact id
- * @param string $size One of the ProxyUtils::SIZE_* constants
+ * @param integer $cid contact id
+ * @param string $size One of the ProxyUtils::SIZE_* constants
+ * @param string $updated Contact update date
* @return string avatar link
*/
- public static function getAvatarUrlForId(int $cid, string $size = ''):string
+ public static function getAvatarUrlForId(int $cid, string $size = '', string $updated = ''):string
{
+ // We have to fetch the "updated" variable when it wasn't provided
+ // The parameter can be provided to improve performance
+ if (empty($updated)) {
+ $contact = self::getById($cid, ['updated']);
+ $updated = $contact['updated'] ?? '';
+ }
+
$url = DI::baseUrl() . '/photo/contact/';
switch ($size) {
case Proxy::SIZE_MICRO:
$url .= Proxy::PIXEL_LARGE . '/';
break;
}
- return $url . $cid;
+ return $url . $cid . ($updated ? '?ts=' . strtotime($updated) : '');
}
/**
{
$condition = ["`nurl` = ? AND ((`uid` = ? AND `network` IN (?, ?)) OR `uid` = ?)",
Strings::normaliseLink($url), $uid, Protocol::FEED, Protocol::MAIL, 0];
- $contact = self::selectFirst(['id'], $condition);
- return self::getAvatarUrlForId($contact['id'] ?? 0, $size);
+ $contact = self::selectFirst(['id', 'updated'], $condition);
+ return self::getAvatarUrlForId($contact['id'] ?? 0, $size, $contact['updated']);
}
/**
* Get header link for given contact id
*
- * @param integer $cid contact id
+ * @param integer $cid contact id
+ * @param string $size One of the ProxyUtils::SIZE_* constants
+ * @param string $updated Contact update date
* @return string header link
*/
- public static function getHeaderUrlForId(int $cid):string
+ public static function getHeaderUrlForId(int $cid, string $size = '', string $updated = ''):string
{
- return DI::baseUrl() . '/photo/header/' . $cid;
+ // We have to fetch the "updated" variable when it wasn't provided
+ // The parameter can be provided to improve performance
+ if (empty($updated)) {
+ $contact = self::getById($cid, ['updated']);
+ $updated = $contact['updated'] ?? '';
+ }
+
+ $url = DI::baseUrl() . '/photo/header/';
+ switch ($size) {
+ case Proxy::SIZE_MICRO:
+ $url .= Proxy::PIXEL_MICRO . '/';
+ break;
+ case Proxy::SIZE_THUMB:
+ $url .= Proxy::PIXEL_THUMB . '/';
+ break;
+ case Proxy::SIZE_SMALL:
+ $url .= Proxy::PIXEL_SMALL . '/';
+ break;
+ case Proxy::SIZE_MEDIUM:
+ $url .= Proxy::PIXEL_MEDIUM . '/';
+ break;
+ case Proxy::SIZE_LARGE:
+ $url .= Proxy::PIXEL_LARGE . '/';
+ break;
+ }
+
+ return $url . $cid . ($updated ? '?ts=' . strtotime($updated) : '');
}
/**
}
$body = $item['body'] ?? '';
+ $shared = BBCode::fetchShareAttributes($body);
+ if (!empty($shared['guid'])) {
+ $shared_item = Post::selectFirst(['uri-id', 'plink'], ['guid' => $shared['guid']]);
+ $shared_uri_id = $shared_item['uri-id'] ?? 0;
+ $shared_links = [strtolower($shared_item['plink'] ?? '')];
+ $shared_attachments = Post\Media::splitAttachments($shared_uri_id, $shared['guid']);
+ $shared_links = array_merge($shared_links, array_column($shared_attachments['visual'], 'url'));
+ $shared_links = array_merge($shared_links, array_column($shared_attachments['link'], 'url'));
+ $shared_links = array_merge($shared_links, array_column($shared_attachments['additional'], 'url'));
+ $item['body'] = self::replaceVisualAttachments($shared_attachments, $item['body']);
+ } else {
+ $shared_uri_id = 0;
+ $shared_links = [];
+ }
+ $attachments = Post\Media::splitAttachments($item['uri-id'], $item['guid'] ?? '', $shared_links);
+ $item['body'] = self::replaceVisualAttachments($attachments, $item['body'] ?? '');
+
$item['body'] = preg_replace("/\s*\[attachment .*?\].*?\[\/attachment\]\s*/ism", "\n", $item['body']);
self::putInCache($item);
$item['body'] = $body;
return $s;
}
- $shared = BBCode::fetchShareAttributes($item['body']);
- if (!empty($shared['guid'])) {
- $shared_item = Post::selectFirst(['uri-id', 'plink'], ['guid' => $shared['guid']]);
- $shared_uri_id = $shared_item['uri-id'] ?? 0;
- $shared_links = [strtolower($shared_item['plink'] ?? '')];
- $attachments = Post\Media::splitAttachments($shared_uri_id, $shared['guid']);
- $s = self::addVisualAttachments($attachments, $item, $s, true);
- $s = self::addLinkAttachment($attachments, $body, $s, true, []);
- $s = self::addNonVisualAttachments($attachments, $item, $s, true);
- $shared_links = array_merge($shared_links, array_column($attachments['visual'], 'url'));
- $shared_links = array_merge($shared_links, array_column($attachments['link'], 'url'));
- $shared_links = array_merge($shared_links, array_column($attachments['additional'], 'url'));
+ if (!empty($shared_attachments)) {
+ $s = self::addVisualAttachments($shared_attachments, $item, $s, true);
+ $s = self::addLinkAttachment($shared_attachments, $body, $s, true, []);
+ $s = self::addNonVisualAttachments($shared_attachments, $item, $s, true);
$body = preg_replace("/\s*\[share .*?\].*?\[\/share\]\s*/ism", '', $body);
- } else {
- $shared_uri_id = 0;
- $shared_links = [];
}
- $attachments = Post\Media::splitAttachments($item['uri-id'], $item['guid'] ?? '', $shared_links);
$s = self::addVisualAttachments($attachments, $item, $s, false);
$s = self::addLinkAttachment($attachments, $body, $s, false, $shared_links);
$s = self::addNonVisualAttachments($attachments, $item, $s, false);
return false;
}
+ /**
+ * Replace visual attachments in the body
+ *
+ * @param array $attachments
+ * @param string $body
+ * @return string modified body
+ */
+ private static function replaceVisualAttachments(array $attachments, string $body)
+ {
+ $stamp1 = microtime(true);
+
+ foreach ($attachments['visual'] as $attachment) {
+ if (!empty($attachment['preview'])) {
+ $body = str_replace($attachment['preview'], Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_LARGE), $body);
+ } elseif ($attachment['filetype'] == 'image') {
+ $body = str_replace($attachment['url'], Post\Media::getUrlForId($attachment['id']), $body);
+ }
+ }
+ DI::profiler()->saveTimestamp($stamp1, 'rendering');
+ return $body;
+ }
+
/**
* Add visual attachments to the content
*
}
if (!empty($attachment['preview'])) {
- $preview_url = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_LARGE);
+ $preview_url = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_LARGE);
} else {
$preview_url = '';
}
}
} elseif ($attachment['filetype'] == 'image') {
$media = Renderer::replaceMacros(Renderer::getMarkupTemplate('content/image.tpl'), [
- '$image' => [
+ '$image' => [
'src' => Post\Media::getUrlForId($attachment['id']),
'preview' => Post\Media::getPreviewUrlForId($attachment['id'], ($attachment['width'] > $attachment['height']) ? Proxy::SIZE_MEDIUM : Proxy::SIZE_LARGE),
'attachment' => $attachment,
if ($preview && !empty($attachment['preview'])) {
if ($attachment['preview-width'] >= 500) {
- $data['image'] = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_MEDIUM);
+ $data['image'] = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_MEDIUM);
} else {
$data['preview'] = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_MEDIUM);
}
* Construct a photo array for an external resource image
*
* @param string $url Image URL
+ * @param int $uid User ID of the requesting person
* @param string $mimetype Image mime type. Defaults to "image/jpeg"
*
* @return array
* @throws \Exception
*/
- public static function createPhotoForExternalResource($url, $mimetype = "image/jpeg")
+ public static function createPhotoForExternalResource($url, $uid = 0, $mimetype = "image/jpeg")
{
$fields = self::getFields();
$values = array_fill(0, count($fields), "");
$photo = array_combine($fields, $values);
$photo['backend-class'] = ExternalResource::NAME;
- $photo['backend-ref'] = $url;
+ $photo['backend-ref'] = json_encode(['url' => $url, 'uid' => $uid]);
$photo['type'] = $mimetype;
- $photo['cacheable'] = false;
+ $photo['cacheable'] = true;
return $photo;
}
$p['address'] = BBCode::convert($p['address']);
}
- $p['photo'] = Contact::getAvatarUrlForId($cid);
+ $p['photo'] = Contact::getAvatarUrlForId($cid, ProxyUtils::SIZE_SMALL);
- $p['url'] = Contact::magicLink($profile_url);
+ $p['url'] = Contact::magicLinkById($cid);
$tpl = Renderer::getMarkupTemplate('profile/vcard.tpl');
$o .= Renderer::replaceMacros($tpl, [
namespace Friendica\Model\Storage;
use BadMethodCallException;
+use Friendica\Util\HTTPSignature;
use Friendica\Network\IHTTPRequest;
/**
/**
* @inheritDoc
*/
- public function get(string $filename)
+ public function get(string $reference)
{
- $parts = parse_url($filename);
+ $data = json_decode($reference);
+ if (empty($data->url)) {
+ return "";
+ }
+
+ $parts = parse_url($data->url);
if (empty($parts['scheme']) || empty($parts['host'])) {
return "";
}
- $curlResult = $this->httpRequest->get($filename);
- if ($curlResult->isSuccess()) {
- return $curlResult->getBody();
+ $fetchResult = HTTPSignature::fetchRaw($data->url, $data->uid);
+ if ($fetchResult->isSuccess()) {
+ return $fetchResult->getBody();
} else {
return "";
}
/**
* @inheritDoc
*/
- public function put(string $data, string $filename = '')
+ public function put(string $data, string $reference = '')
{
throw new BadMethodCallException();
}
- public function delete(string $filename)
+ public function delete(string $reference)
{
throw new BadMethodCallException();
}
}
$customsize = 0;
+ $square_resize = true;
$photo = false;
$scale = null;
$stamp = microtime(true);
$customsize = intval($parameters['customsize']);
$uid = MPhoto::stripExtension($parameters['name']);
$photo = self::getAvatar($uid, $parameters['type'], $customsize);
+ $square_resize = !in_array($parameters['type'], ['media', 'preview']);
} elseif (!empty($parameters['type'])) {
$uid = MPhoto::stripExtension($parameters['name']);
$photo = self::getAvatar($uid, $parameters['type'], Proxy::PIXEL_SMALL);
}
// if customsize is set and image is not a gif, resize it
- if ($photo['type'] !== "image/gif" && $customsize > 0 && $customsize < 501) {
+ if ($photo['type'] !== "image/gif" && $customsize > 0 && $customsize <= Proxy::PIXEL_THUMB && $square_resize) {
$img = new Image($imgdata, $photo['type']);
$img->scaleToSquare($customsize);
$imgdata = $img->asString();
+ } elseif ($photo['type'] !== "image/gif" && $customsize > 0) {
+ $img = new Image($imgdata, $photo['type']);
+ $img->scaleDown($customsize);
+ $imgdata = $img->asString();
}
if (function_exists("header_remove")) {
return false;
}
- $author = Contact::selectFirst([], ["`id` IN (SELECT `author-id` FROM `post` WHERE `uri-id` = ?)", $media['uri-id']]);
- $url = Contact::magicLinkByContact($author, $url);
-
- return MPhoto::createPhotoForExternalResource($url);
+ return MPhoto::createPhotoForExternalResource($url, (int)local_user());
case "media":
- $media = DBA::selectFirst('post-media', ['url'], ['id' => $uid, 'type' => Post\Media::IMAGE]);
- if (empty($media['url'])) {
+ $media = DBA::selectFirst('post-media', ['url', 'uri-id'], ['id' => $uid, 'type' => Post\Media::IMAGE]);
+ if (empty($media)) {
return false;
}
- $author = Contact::selectFirst([], ["`id` IN (SELECT `author-id` FROM `post` WHERE `uri-id` = ?)", $media['uri-id']]);
- $url = Contact::magicLinkByContact($author, $media['url']);
-
- return MPhoto::createPhotoForExternalResource($url);
+ return MPhoto::createPhotoForExternalResource($media['url'], (int)local_user());
case "contact":
$contact = Contact::getById($uid, ['uid', 'url', 'avatar', 'photo', 'xmpp', 'addr']);
if (empty($contact)) {
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Proxy;
/**
* Class Account
$this->note = BBCode::convert($publicContact['about'], false);
$this->url = $publicContact['url'];
- $this->avatar = $userContact['photo'] ?? '' ?: $publicContact['photo'] ?: Contact::getAvatarUrlForId($userContact['id'] ?? 0 ?: $publicContact['id']);
+ $this->avatar = Contact::getAvatarUrlForId($userContact['id'] ?? 0 ?: $publicContact['id'], Proxy::SIZE_SMALL, $userContact['updated'] ?? '' ?: $publicContact['updated']);
$this->avatar_static = $this->avatar;
- $this->header = Contact::getHeaderUrlForId($userContact['id'] ?? 0 ?: $publicContact['id']);
+ $this->header = Contact::getHeaderUrlForId($userContact['id'] ?? 0 ?: $publicContact['id'], '', $userContact['updated'] ?? '' ?: $publicContact['updated']);
$this->header_static = $this->header;
$this->followers_count = $apcontact['followers_count'] ?? 0;
$this->following_count = $apcontact['following_count'] ?? 0;
use Friendica\Model\Storage;
use Friendica\Core\Session;
use Friendica\Model\Storage\StorageException;
+use Friendica\Network\HTTPRequest;
use Friendica\Test\DatabaseTest;
use Friendica\Test\Util\Database\StaticDatabase;
use Friendica\Test\Util\VFSTrait;
private $logger;
/** @var L10n */
private $l10n;
+ /** @var HTTPRequest */
+ private $httpRequest;
use VFSTrait;
$this->config = new PreloadConfig($configCache, $configModel);
$this->l10n = \Mockery::mock(L10n::class);
+
+ $this->httpRequest = \Mockery::mock(HTTPRequest::class);
}
/**
*/
public function testInstance()
{
- $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
+ $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n, $this->httpRequest);
self::assertInstanceOf(StorageManager::class, $storageManager);
}
*/
public function testGetByName($name, $assert, $assertName, $userBackend)
{
- $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
+ $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n, $this->httpRequest);
$storage = $storageManager->getByName($name, $userBackend);
*/
public function testIsValidBackend($name, $assert, $assertName, $userBackend)
{
- $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
+ $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n, $this->httpRequest);
// true in every of the backends
self::assertEquals(!empty($assertName), $storageManager->isValidBackend($name));
*/
public function testListBackends()
{
- $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
+ $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n, $this->httpRequest);
self::assertEquals(StorageManager::DEFAULT_BACKENDS, $storageManager->listBackends());
}
*/
public function testGetBackend($name, $assert, $assertName, $userBackend)
{
- $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
+ $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n, $this->httpRequest);
self::assertNull($storageManager->getBackend());
{
$this->config->set('storage', 'name', $name);
- $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
+ $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n, $this->httpRequest);
if ($userBackend) {
self::assertInstanceOf($assert, $storageManager->getBackend());
->addRule(ISession::class, ['instanceOf' => Session\Memory::class, 'shared' => true, 'call' => null]);
DI::init($dice);
- $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
+ $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n, $this->httpRequest);
self::assertTrue($storageManager->register(SampleStorageBackend::class));
$this->loadFixture(__DIR__ . '/../../datasets/storage/database.fixture.php', $this->dba);
- $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
+ $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n, $this->httpRequest);
$storage = $storageManager->getByName($name);
$storageManager->move($storage);
$this->expectExceptionMessage("Can't move to storage backend 'SystemResource'");
$this->expectException(StorageException::class);
- $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n);
+ $storageManager = new StorageManager($this->dba, $this->config, $this->logger, $this->l10n, $this->httpRequest);
$storage = $storageManager->getByName(Storage\SystemResource::getName());
$storageManager->move($storage);
}