Passed
Push — development ( a254f4...4fadb5 )
by Spuds
01:18 queued 34s
created

Tenor::request()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 3
eloc 11
c 1
b 0
f 1
nc 3
nop 3
dl 0
loc 19
rs 9.9
1
<?php
2
3
/**
4
 * Functions to interact with the Tenor API and return JSON results to the tenor plugin
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * @version 2.0 Beta 1
11
 *
12
 */
13
14
namespace ElkArte\Controller;
15
16
use ElkArte\AbstractController;
17
use ElkArte\Action;
18
use ElkArte\Errors\Errors;
0 ignored issues
show
Bug introduced by
The type ElkArte\Errors\Errors was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
20
/**
21
 * Functions to interact with the Tenor API and return JSON results to the tenor plugin
22
 */
23
class Tenor extends AbstractController
24
{
25
	/** @var string $baseApiUrl The base API URL for Tenor v2. */
26
	protected $baseApiUrl = 'https://tenor.googleapis.com/v2/';
27
28
	/** @var string The API key used for authentication. */
29
	protected $apiKey;
30
31
	/** @var array default values to pass to the Tenor API */
32
	protected $config = [
33
		'client_key' => 'elkarte',
34
		'contentfilter' => 'medium',
35
		'locale' => 'en',
36
		'limit' => 20,
37
	];
38
39
	/**
40
	 * Pre-dispatch, called before all other methods.  Sets the Tenor API key for the Dispatch class.
41
	 *
42
	 * This method retrieves the Tenor API key from the global $modSettings variable
43
	 * @return void
44
	 */
45
	public function pre_dispatch(): void
46
	{
47
		global $modSettings;
48
49
		$this->apiKey = $modSettings['tenorApiKey'] ?? '';
50
	}
51
52
	/**
53
	 * Index action, based on the SA, sends control to the right method.
54
	 *
55
	 * @return void
56
	 */
57
	public function action_index(): void
58
	{
59
		global $context, $modSettings;
60
61
		if (empty($modSettings['enableTenor']) || empty($this->apiKey))
62
		{
63
			return;
64
		}
65
66
		$this->setConfig();
67
68
		$subActions = [
69
			'search' => [$this, 'action_getSearchResults'],
70
			'trending' => [$this, 'action_getFeatured'],
71
		];
72
73
		$action = new Action('tenor');
74
		$subAction = $action->initialize($subActions, 'trending');
75
		$context['sub_action'] = $subAction;
76
		$action->dispatch($subAction);
77
	}
78
79
	/**
80
	 * Sets the configuration settings for the object.
81
	 *
82
	 * @return array The updated configuration settings after merging with the existing configuration.
83
	 */
84
	public function setConfig(): array
85
	{
86
		global $modSettings;
87
88
		$config = [
89
			'contentfilter' => $modSettings['tenorRating'] ?? 'medium',
90
			'locale' => $modSettings['tenorLanguage'] ?? 'en',
91
		];
92
93
		$this->config = array_replace($this->config, $config);
94
95
		return $this->config;
96
	}
97
98
	/**
99
	 * Tracks the statistics for a given action.
100
	 *
101
	 * @return bool Returns false indicating that the statistics tracking is not needed
102
	 */
103
	public function trackStats($action = ''): bool
104
	{
105
		return false;
106
	}
107
108
	/**
109
	 * Retrieves featured GIFs (trending).
110
	 *
111
	 * @return bool The featured GIFs and pagination information.
112
	 */
113
	public function action_getFeatured(): bool
114
	{
115
		checkSession('get');
116
117
		is_not_guest();
118
119
		$result = $this->request('featured', [
120
			'contentfilter' => $this->config['contentfilter'],
121
			'limit' => $this->config['limit'],
122
			'pos' => $this->_req->getQuery('pos', 'trim', '')
123
		], $error);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $error seems to be never defined.
Loading history...
124
125
		if ($error)
126
		{
127
			return $this->sendResults([], []);
128
		}
129
130
		$images = $this->prepareImageResults($result);
131
132
		return $this->sendResults($images, $result);
133
	}
134
135
	/**
136
	 * Retrieves search results for GIFs based on the provided query.
137
	 *
138
	 * @return bool The search results and pagination information.
139
	 */
140
	public function action_getSearchResults(): bool
141
	{
142
		checkSession('get');
143
144
		is_not_guest();
145
146
		$result = $this->request('search', [
147
			'q' => $this->_req->getQuery('q', 'trim', ''),
148
			'contentfilter' => $this->config['contentfilter'],
149
			'limit' => $this->config['limit'],
150
			'pos' => $this->_req->getQuery('pos', 'trim', '')
151
		], $error);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $error seems to be never defined.
Loading history...
152
153
		if ($error)
154
		{
155
			return $this->sendResults([], []);
156
		}
157
158
		$images =  $this->prepareImageResults($result);
159
160
		return $this->sendResults($images, $result);
161
	}
162
163
	/**
164
	 * Sets the results in context so the JSON template can deliver them.
165
	 *
166
	 * @param array $images An array of GIFs.
167
	 * @param array $result The pagination and meta-information.
168
	 *
169
	 * @return bool Returns true after sending the results.
170
	 */
171
	public function sendResults($images, $result): bool
172
	{
173
		global $context;
174
175
		setJsonTemplate();
176
		$context['json_data'] = [
177
			'tenor' => $images,
178
			'data' => $result
179
		];
180
181
		return true;
182
	}
183
184
	/**
185
	 * Sends a request to the Tenor API.
186
	 *
187
	 * @param string $path The API endpoint path.
188
	 * @param array $params The additional parameters for the request (optional).
189
	 * @param bool|null $error A flag to indicate any error message (optional).
190
	 *
191
	 * @return array The response from the API as an associative array, or an empty array if there was an error.
192
	 */
193
	public function request(string $path, array $params = [], bool|null &$error = null): array
194
	{
195
		$result = [];
196
		$params = ['key' => $this->apiKey, 'client_key' => $this->config['client_key']] + $params;
197
		$path .= '?' . http_build_query($params, '', '&');
198
199
		require_once(SUBSDIR . '/Package.subs.php');
200
		$body = fetch_web_data($this->baseApiUrl . $path);
201
		if ($body !== false)
202
		{
203
			$contents = json_decode($body, true);
204
205
			return is_array($contents) ? $contents : [];
206
		}
207
208
		$error = true;
209
		Errors::instance()->log_error('Tenor API error');
210
211
		return $result;
212
	}
213
214
	/**
215
	 * Prepares the results from the API response.
216
	 *
217
	 * @param array $result The API response containing the image data.
218
	 * @return array The prepared image results.
219
	 */
220
	protected function prepareImageResults($result): array
221
	{
222
		$images = [];
223
224
		if (is_array($result) && isset($result['results']))
225
		{
226
			foreach ($result['results'] as $data)
227
			{
228
				// Tenor v2 response structure:
229
				// media_formats -> tinygif, gif, etc.
230
				$media = $data['media_formats'];
231
232
				$images[$data['id']] = [
233
					'title' => $data['title'],
234
					'insert' => $media['gif']['url'],
235
					'src' => $media['tinygif']['url'],
236
					'thumbnail' => $media['tinygif']['url'],
237
				];
238
			}
239
		}
240
241
		return $images;
242
	}
243
}
244