Passed
Push — master ( 4209dc...b3eb0b )
by Julius
16:39 queued 12s
created

LinkReferenceProvider::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
dl 0
loc 9
rs 10
c 1
b 0
f 0
eloc 8
nc 1
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
/**
5
 * @copyright Copyright (c) 2022 Julius Härtl <[email protected]>
6
 *
7
 * @author Julius Härtl <[email protected]>
8
 *
9
 * @license GNU AGPL version 3 or any later version
10
 *
11
 * This program is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Affero General Public License as
13
 * published by the Free Software Foundation, either version 3 of the
14
 * License, or (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License
22
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
namespace OC\Collaboration\Reference;
26
27
use Fusonic\OpenGraph\Consumer;
28
use GuzzleHttp\Psr7\LimitStream;
29
use GuzzleHttp\Psr7\Utils;
30
use OC\Security\RateLimiting\Exception\RateLimitExceededException;
31
use OC\Security\RateLimiting\Limiter;
32
use OC\SystemConfig;
33
use OCP\Collaboration\Reference\IReference;
34
use OCP\Collaboration\Reference\IReferenceProvider;
35
use OCP\Files\AppData\IAppDataFactory;
36
use OCP\Files\NotFoundException;
37
use OCP\Http\Client\IClientService;
38
use OCP\IRequest;
39
use OCP\IURLGenerator;
40
use OCP\IUserSession;
41
use Psr\Log\LoggerInterface;
42
43
class LinkReferenceProvider implements IReferenceProvider {
44
	public const MAX_PREVIEW_SIZE = 1024 * 1024;
45
46
	public const ALLOWED_CONTENT_TYPES = [
47
		'image/png',
48
		'image/jpg',
49
		'image/jpeg',
50
		'image/gif',
51
		'image/svg+xml',
52
		'image/webp'
53
	];
54
55
	private IClientService $clientService;
56
	private LoggerInterface $logger;
57
	private SystemConfig $systemConfig;
58
	private IAppDataFactory $appDataFactory;
59
	private IURLGenerator $urlGenerator;
60
	private Limiter $limiter;
61
	private IUserSession $userSession;
62
	private IRequest $request;
63
64
	public function __construct(IClientService $clientService, LoggerInterface $logger, SystemConfig $systemConfig, IAppDataFactory $appDataFactory, IURLGenerator $urlGenerator, Limiter $limiter, IUserSession $userSession, IRequest $request) {
65
		$this->clientService = $clientService;
66
		$this->logger = $logger;
67
		$this->systemConfig = $systemConfig;
68
		$this->appDataFactory = $appDataFactory;
69
		$this->urlGenerator = $urlGenerator;
70
		$this->limiter = $limiter;
71
		$this->userSession = $userSession;
72
		$this->request = $request;
73
	}
74
75
	public function matchReference(string $referenceText): bool {
76
		if ($this->systemConfig->getValue('reference_opengraph', true) !== true) {
77
			return false;
78
		}
79
80
		return (bool)preg_match(IURLGenerator::URL_REGEX, $referenceText);
81
	}
82
83
	public function resolveReference(string $referenceText): ?IReference {
84
		if ($this->matchReference($referenceText)) {
85
			$reference = new Reference($referenceText);
86
			$this->fetchReference($reference);
87
			return $reference;
88
		}
89
90
		return null;
91
	}
92
93
	private function fetchReference(Reference $reference): void {
94
		try {
95
			$user = $this->userSession->getUser();
96
			if ($user) {
97
				$this->limiter->registerUserRequest('opengraph', 10, 120, $user);
98
			} else {
99
				$this->limiter->registerAnonRequest('opengraph', 10, 120, $this->request->getRemoteAddress());
100
			}
101
		} catch (RateLimitExceededException $e) {
102
			return;
103
		}
104
105
		$client = $this->clientService->newClient();
106
		try {
107
			$response = $client->get($reference->getId(), [ 'timeout' => 10 ]);
108
		} catch (\Exception $e) {
109
			$this->logger->debug('Failed to fetch link for obtaining open graph data', ['exception' => $e]);
110
			return;
111
		}
112
113
		$responseBody = (string)$response->getBody();
114
115
		// OpenGraph handling
116
		$consumer = new Consumer();
117
		$consumer->useFallbackMode = true;
118
		$object = $consumer->loadHtml($responseBody);
119
120
		$reference->setUrl($reference->getId());
121
122
		if ($object->title) {
123
			$reference->setTitle($object->title);
124
		}
125
126
		if ($object->description) {
127
			$reference->setDescription($object->description);
128
		}
129
130
		if ($object->images) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $object->images of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
131
			try {
132
				$appData = $this->appDataFactory->get('core');
133
				try {
134
					$folder = $appData->getFolder('opengraph');
135
				} catch (NotFoundException $e) {
136
					$folder = $appData->newFolder('opengraph');
137
				}
138
				$response = $client->get($object->images[0]->url, [ 'timeout' => 10 ]);
139
				$contentType = $response->getHeader('Content-Type');
140
				$contentLength = $response->getHeader('Content-Length');
141
142
				if (in_array($contentType, self::ALLOWED_CONTENT_TYPES, true) && $contentLength < self::MAX_PREVIEW_SIZE) {
143
					$stream = Utils::streamFor($response->getBody());
144
					$bodyStream = new LimitStream($stream, self::MAX_PREVIEW_SIZE, 0);
145
					$reference->setImageContentType($contentType);
146
					$folder->newFile(md5($reference->getId()), $bodyStream->getContents());
147
					$reference->setImageUrl($this->urlGenerator->linkToRouteAbsolute('core.Reference.preview', ['referenceId' => md5($reference->getId())]));
148
				}
149
			} catch (\Throwable $e) {
150
				$this->logger->error('Failed to fetch and store the open graph image for ' . $reference->getId(), ['exception' => $e]);
151
			}
152
		}
153
	}
154
155
	public function getCachePrefix(string $referenceId): string {
156
		return $referenceId;
157
	}
158
159
	public function getCacheKey(string $referenceId): ?string {
160
		return null;
161
	}
162
}
163