Passed
Push — v2 ( 62fbe8...8bc98f )
by Benjamin
04:19
created

Gateway::getEmbedHtml()   B

Complexity

Conditions 6
Paths 20

Size

Total Lines 36
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 20
nc 20
nop 2
dl 0
loc 36
rs 8.439
c 0
b 0
f 0
1
<?php
2
/**
3
 * @link      https://dukt.net/videos/
4
 * @copyright Copyright (c) 2018, Dukt
5
 * @license   https://github.com/dukt/videos/blob/v2/LICENSE.md
6
 */
7
8
namespace dukt\videos\base;
9
10
use Craft;
11
use craft\helpers\Json;
12
use craft\helpers\UrlHelper;
13
use dukt\videos\errors\ApiResponseException;
14
use dukt\videos\errors\GatewayMethodNotFoundException;
15
use dukt\videos\errors\JsonParsingException;
16
use dukt\videos\errors\VideoNotFoundException;
17
use dukt\videos\Plugin as Videos;
18
use GuzzleHttp\Exception\BadResponseException;
19
use Exception;
20
use Psr\Http\Message\ResponseInterface;
21
use yii\web\Response;
22
23
24
/**
25
 * Gateway is the base class for classes representing video gateways.
26
 *
27
 * @author Dukt <[email protected]>
28
 * @since  1.0
29
 */
30
abstract class Gateway implements GatewayInterface
31
{
32
    // Public Methods
33
    // =========================================================================
34
35
    /**
36
     * Return the handle of the gateway based on its class name
37
     *
38
     * @return string
39
     */
40
    public function getHandle(): string
41
    {
42
        $handle = \get_class($this);
43
        $handle = strtolower(substr($handle, strrpos($handle, "\\") + 1));
44
45
        return $handle;
46
    }
47
48
    /**
49
     * Returns the icon URL.
50
     *
51
     * @return string|false|null
52
     */
53
    public function getIconUrl()
54
    {
55
        $iconAlias = $this->getIconAlias();
56
57
        if (file_exists(Craft::getAlias($iconAlias))) {
58
            return Craft::$app->assetManager->getPublishedUrl($iconAlias, true);
59
        }
60
61
        return null;
62
    }
63
64
    /**
65
     * OAuth Connect.
66
     *
67
     * @return Response
68
     */
69
    public function oauthConnect(): Response
70
    {
71
        $provider = $this->getOauthProvider();
72
73
        Craft::$app->getSession()->set('videos.oauthState', $provider->getState());
74
75
        $scope = $this->getOauthScope();
76
        $options = $this->getOauthAuthorizationOptions();
77
78
        if (!\is_array($options)) {
0 ignored issues
show
introduced by
The condition is_array($options) is always false.
Loading history...
79
            $options = [];
80
        }
81
82
        $options['scope'] = $scope;
83
84
        $authorizationUrl = $provider->getAuthorizationUrl($options);
85
86
        return Craft::$app->getResponse()->redirect($authorizationUrl);
0 ignored issues
show
Bug Best Practice introduced by
The expression return Craft::app->getRe...rect($authorizationUrl) could return the type yii\console\Response which is incompatible with the type-hinted return yii\web\Response. Consider adding an additional type-check to rule them out.
Loading history...
87
    }
88
89
    /**
90
     * Returns the gateway's OAuth provider.
91
     *
92
     * @return mixed
93
     */
94
    public function getOauthProvider()
95
    {
96
        $oauthProviderOptions = Videos::$plugin->getSettings()->oauthProviderOptions;
97
98
        $options = [];
99
100
        if (isset($oauthProviderOptions[$this->getHandle()])) {
101
            $options = $oauthProviderOptions[$this->getHandle()];
102
        }
103
104
        if (!isset($options['redirectUri'])) {
105
            $options['redirectUri'] = $this->getRedirectUri();
106
        }
107
108
        return $this->createOauthProvider($options);
109
    }
110
111
    /**
112
     * Returns the OAuth provider’s name.
113
     *
114
     * @return string
115
     */
116
    public function getOauthProviderName(): string
117
    {
118
        return $this->getName();
119
    }
120
121
    /**
122
     * Returns the redirect URI.
123
     *
124
     * @return string
125
     */
126
    public function getRedirectUri(): string
127
    {
128
        return UrlHelper::actionUrl('videos/oauth/callback');
129
    }
130
131
    /**
132
     * OAuth Scope
133
     *
134
     * @return array|null
135
     */
136
    public function getOauthScope()
137
    {
138
        return null;
139
    }
140
141
    /**
142
     * OAuth Authorization Options
143
     *
144
     * @return array|null
145
     */
146
    public function getOauthAuthorizationOptions()
147
    {
148
        return null;
149
    }
150
151
    /**
152
     * OAuth Callback
153
     *
154
     * @return Response
155
     */
156
    public function oauthCallback(): Response
157
    {
158
        $provider = $this->getOauthProvider();
159
160
        $code = Craft::$app->getRequest()->getParam('code');
161
162
        try {
163
            // Try to get an access token (using the authorization code grant)
164
            $token = $provider->getAccessToken('authorization_code', [
165
                'code' => $code
166
            ]);
167
168
            // Save token
169
            Videos::$plugin->getOauth()->saveToken($this->getHandle(), $token);
170
171
            // Reset session variables
172
173
            // Redirect
174
            Craft::$app->getSession()->setNotice(Craft::t('videos', 'Connected to {gateway}.', ['gateway' => $this->getName()]));
175
        } catch (Exception $e) {
176
            Craft::error('Couldn’t connect to video gateway:'."\r\n"
177
                .'Message: '."\r\n".$e->getMessage()."\r\n"
178
                .'Trace: '."\r\n".$e->getTraceAsString(), __METHOD__);
179
180
            // Failed to get the token credentials or user details.
181
            Craft::$app->getSession()->setError($e->getMessage());
182
        }
183
184
        $redirectUrl = UrlHelper::cpUrl('videos/settings');
185
186
        return Craft::$app->getResponse()->redirect($redirectUrl);
0 ignored issues
show
Bug Best Practice introduced by
The expression return Craft::app->getRe...>redirect($redirectUrl) could return the type yii\console\Response which is incompatible with the type-hinted return yii\web\Response. Consider adding an additional type-check to rule them out.
Loading history...
187
    }
188
189
    /**
190
     * Has Token
191
     *
192
     * @return bool
193
     * @throws \yii\base\InvalidConfigException
194
     */
195
    public function hasToken(): bool
196
    {
197
        $tokenData = Videos::$plugin->getOauth()->getTokenData($this->getHandle());
198
199
        if ($tokenData) {
200
            return true;
201
        }
202
203
        return false;
204
    }
205
206
    /**
207
     * Returns the gateway's OAuth token.
208
     *
209
     * @return mixed
210
     * @throws \yii\base\InvalidConfigException
211
     */
212
    public function getOauthToken()
213
    {
214
        return Videos::$plugin->getOauth()->getToken($this->getHandle());
215
    }
216
217
    /**
218
     * Whether the OAuth flow should be enable or not for this gateway.
219
     *
220
     * @return bool
221
     */
222
    public function enableOauthFlow(): bool
223
    {
224
        return true;
225
    }
226
227
    /**
228
     * Returns the HTML of the embed from a video ID.
229
     *
230
     * @param       $videoId
231
     * @param array $options
232
     *
233
     * @return string
234
     */
235
    public function getEmbedHtml($videoId, array $options = []): string
236
    {
237
        $embedAttributes = [
238
            'frameborder' => '0',
239
            'allowfullscreen' => 'true',
240
            'allowscriptaccess' => 'true'
241
        ];
242
243
        $disableSize = $options['disable_size'] ?? false;
244
245
        if (!$disableSize) {
246
            if (isset($options['width'])) {
247
                $embedAttributes['width'] = $options['width'];
248
                unset($options['width']);
249
            }
250
251
            if (isset($options['height'])) {
252
                $embedAttributes['height'] = $options['height'];
253
                unset($options['height']);
254
            }
255
        }
256
257
        if (!empty($options['iframeClass'])) {
258
            $embedAttributes['class'] = $options['iframeClass'];
259
            unset($options['iframeClass']);
260
        }
261
262
        $embedUrl = $this->getEmbedUrl($videoId, $options);
263
264
        $embedAttributesString = '';
265
266
        foreach ($embedAttributes as $key => $value) {
267
            $embedAttributesString .= ' '.$key.'="'.$value.'"';
268
        }
269
270
        return '<iframe src="'.$embedUrl.'"'.$embedAttributesString.'></iframe>';
271
    }
272
273
    /**
274
     * Returns the URL of the embed from a video ID.
275
     *
276
     * @param       $videoId
277
     * @param array $options
278
     *
279
     * @return string
280
     */
281
    public function getEmbedUrl($videoId, array $options = []): string
282
    {
283
        $format = $this->getEmbedFormat();
284
285
        if (\count($options) > 0) {
286
            $queryMark = '?';
287
288
            if (strpos($this->getEmbedFormat(), '?') !== false) {
289
                $queryMark = '&';
290
            }
291
292
            $options = http_build_query($options);
293
294
            $format .= $queryMark.$options;
295
        }
296
297
        return sprintf($format, $videoId);
298
    }
299
300
    /**
301
     * Returns the javascript origin URL.
302
     *
303
     * @return string
304
     * @throws \craft\errors\SiteNotFoundException
305
     */
306
    public function getJavascriptOrigin(): string
307
    {
308
        return UrlHelper::baseUrl();
309
    }
310
311
    /**
312
     * Returns the account.
313
     *
314
     * @return mixed
315
     * @throws Exception
316
     */
317
    public function getAccount()
318
    {
319
        $token = $this->getOauthToken();
320
321
        if ($token) {
322
            $account = Videos::$plugin->getCache()->get(['getAccount', $token]);
323
324
            if (!$account) {
325
                $oauthProvider = $this->getOauthProvider();
326
                $account = $oauthProvider->getResourceOwner($token);
327
328
                Videos::$plugin->getCache()->set(['getAccount', $token], $account);
329
            }
330
331
            if ($account) {
332
                return $account;
333
            }
334
        }
335
336
        return null;
337
    }
338
339
    /**
340
     * Returns a video from its public URL.
341
     *
342
     * @param $url
343
     *
344
     * @return mixed
345
     * @throws VideoNotFoundException
346
     */
347
    public function getVideoByUrl($url)
348
    {
349
        $url = $url['url'];
350
351
        $videoId = $this->extractVideoIdFromUrl($url);
352
353
        if (!$videoId) {
354
            throw new VideoNotFoundException('Video not found with url given.');
355
        }
356
357
        return $this->getVideoById($videoId);
0 ignored issues
show
Bug introduced by
It seems like $videoId can also be of type true; however, parameter $id of dukt\videos\base\GatewayInterface::getVideoById() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

357
        return $this->getVideoById(/** @scrutinizer ignore-type */ $videoId);
Loading history...
358
    }
359
360
    /**
361
     * Returns a list of videos.
362
     *
363
     * @param $method
364
     * @param $options
365
     *
366
     * @return mixed
367
     * @throws GatewayMethodNotFoundException
368
     */
369
    public function getVideos($method, $options)
370
    {
371
        $realMethod = 'getVideos'.ucwords($method);
372
373
        if (method_exists($this, $realMethod)) {
374
            return $this->{$realMethod}($options);
375
        }
376
377
        throw new GatewayMethodNotFoundException('Gateway method “'.$realMethod.'” not found.');
378
    }
379
380
    /**
381
     * Number of videos per page.
382
     *
383
     * @return mixed
384
     */
385
    public function getVideosPerPage()
386
    {
387
        return Videos::$plugin->getSettings()->videosPerPage;
388
    }
389
390
    /**
391
     * Returns the OAuth provider options.
392
     *
393
     * @return array|null
394
     */
395
    public function getOauthProviderOptions()
396
    {
397
        $allOauthProviderOptions = Videos::$plugin->getSettings()->oauthProviderOptions;
398
399
        if (isset($allOauthProviderOptions[$this->getHandle()])) {
400
            return $allOauthProviderOptions[$this->getHandle()];
401
        }
402
403
        return null;
404
    }
405
406
    /**
407
     * Whether the gateway supports search or not.
408
     *
409
     * @return bool
410
     */
411
    public function supportsSearch(): bool
412
    {
413
        return false;
414
    }
415
416
    // Protected Methods
417
    // =========================================================================
418
419
    /**
420
     * Performs a GET request on the API.
421
     *
422
     * @param       $uri
423
     * @param array $options
424
     *
425
     * @return array
426
     * @throws ApiResponseException
427
     */
428
    protected function get($uri, array $options = []): array
429
    {
430
        $client = $this->createClient();
0 ignored issues
show
Bug introduced by
The method createClient() does not exist on dukt\videos\base\Gateway. Since it exists in all sub-types, consider adding an abstract or default implementation to dukt\videos\base\Gateway. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

430
        /** @scrutinizer ignore-call */ 
431
        $client = $this->createClient();
Loading history...
431
432
        try {
433
            $response = $client->request('GET', $uri, $options);
434
            $body = (string)$response->getBody();
435
            $data = Json::decode($body);
436
        } catch (BadResponseException $badResponseException) {
437
            $response = $badResponseException->getResponse();
438
            $body = (string)$response->getBody();
439
440
            try {
441
                $data = Json::decode($body);
442
            } catch (JsonParsingException $e) {
443
                throw $badResponseException;
444
            }
445
        }
446
447
        $this->checkResponse($response, $data);
448
449
        return $data;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $data returns the type null which is incompatible with the type-hinted return array.
Loading history...
450
    }
451
452
    /**
453
     * Checks a provider response for errors.
454
     *
455
     * @param ResponseInterface $response
456
     * @param                   $data
457
     *
458
     * @throws ApiResponseException
459
     */
460
    protected function checkResponse(ResponseInterface $response, $data)
461
    {
462
        if (!empty($data['error'])) {
463
            $code = 0;
464
            $error = $data['error'];
465
466
            if (\is_array($error)) {
467
                $code = $error['code'];
468
                $error = $error['message'];
469
            }
470
471
            throw new ApiResponseException($error, $code);
472
        }
473
    }
474
}
475