DisqusService   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 235
Duplicated Lines 0 %

Importance

Changes 7
Bugs 1 Features 0
Metric Value
eloc 112
c 7
b 1
f 0
dl 0
loc 235
rs 10
wmc 19

5 Methods

Rating   Name   Duplication   Size   Complexity  
A renderPluginTemplate() 0 26 4
B getSSOVars() 0 64 6
A disqusHmacSha1() 0 23 2
A getCommentsCount() 0 34 5
A outputEmbedTag() 0 28 2
1
<?php
2
/**
3
 * Disqus plugin for Craft CMS 3.x
4
 *
5
 * Integrates the Disqus commenting system into Craft 3 websites, including
6
 * Single Sign On (SSO) and custom login/logout URLs
7
 *
8
 * @link      https://nystudio107.com
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @copyright tag
Loading history...
9
 * @copyright Copyright (c) 2017 nystudio107
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
10
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
Missing @package tag in file comment
Loading history...
Coding Style introduced by
Missing @author tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
11
12
namespace nystudio107\disqus\services;
13
14
use Craft;
15
use craft\base\Component;
16
use craft\helpers\Html;
17
use craft\helpers\Template;
18
use craft\web\User;
19
use craft\web\View;
20
use nystudio107\disqus\Disqus;
21
use nystudio107\disqus\models\Settings;
22
use Twig\Markup;
23
use yii\base\Exception;
24
use yii\base\InvalidConfigException;
25
26
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
27
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
28
 * @package   Disqus
0 ignored issues
show
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
29
 * @since     1.0.0
0 ignored issues
show
Coding Style introduced by
The tag in position 3 should be the @author tag
Loading history...
Coding Style introduced by
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
30
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
31
class DisqusService extends Component
32
{
33
    // Public Methods
34
    // =========================================================================
35
36
    /**
37
     * Output the Disqus Tag
38
     *
39
     * @param string $disqusIdentifier
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
40
     * @param string $disqusTitle
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
41
     * @param string $disqusUrl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
42
     * @param string $disqusCategoryId
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
43
     * @param string $disqusLanguage
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
44
     * @param array $scriptAttributes
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
45
     *
46
     * @return Markup
47
     */
48
    public function outputEmbedTag(
49
        string $disqusIdentifier = "",
50
        string $disqusTitle = "",
51
        string $disqusUrl = "",
52
        string $disqusCategoryId = "",
53
        string $disqusLanguage = "",
54
        array  $scriptAttributes = [],
55
    ): Markup {
56
        /** @var Settings $settings */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
57
        $settings = Disqus::$plugin->getSettings();
0 ignored issues
show
Bug introduced by
The method getSettings() does not exist on null. ( Ignorable by Annotation )

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

57
        /** @scrutinizer ignore-call */ 
58
        $settings = Disqus::$plugin->getSettings();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
58
        $disqusShortname = $settings->getDisqusShortName();
59
60
        $vars = [
61
            'disqusShortname' => $disqusShortname,
62
            'disqusIdentifier' => $disqusIdentifier,
63
            'disqusTitle' => $disqusTitle,
64
            'disqusUrl' => $disqusUrl,
65
            'disqusCategoryId' => $disqusCategoryId,
66
            'disqusLanguage' => $disqusLanguage,
67
            'scriptAttributes' => Html::renderTagAttributes($scriptAttributes),
68
        ];
69
        $vars = array_merge($vars, $this->getSSOVars());
70
        $templateName = 'disqusEmbedTag';
71
        if ($settings->lazyLoadDisqus) {
72
            $templateName = 'disqusEmbedTagLazy';
73
        }
74
75
        return $this->renderPluginTemplate($templateName, $vars);
76
    }
77
78
    /**
79
     * Return the number of comments for a particular thread
80
     *
81
     * @param string $disqusIdentifier
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
82
     *
83
     * @return int
0 ignored issues
show
Coding Style introduced by
Tag value for @return tag indented incorrectly; expected 7 spaces but found 1
Loading history...
84
     * @noinspection PhpComposerExtensionStubsInspection
85
     */
86
    public function getCommentsCount(
87
        string $disqusIdentifier = "",
88
    ): int {
89
        /** @var Settings $settings */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
90
        $settings = Disqus::$plugin->getSettings();
91
        if (!empty($settings->getDisqusPublicKey())) {
92
            $disqusShortname = $settings->getDisqusShortname();
93
            $apiKey = $settings->getDisqusPublicKey();
94
95
            $url = "https://disqus.com/api/3.0/threads/details.json?api_key="
96
                . $apiKey
97
                . "&forum=" . $disqusShortname
98
                . "&thread:ident="
99
                . $disqusIdentifier;
100
101
            $ch = curl_init();
102
            curl_setopt($ch, CURLOPT_URL, $url);
103
            curl_setopt($ch, CURLOPT_HEADER, 0);
104
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
105
            $return = curl_exec($ch);
106
            curl_close($ch);
107
108
            $json = json_decode($return, true);
0 ignored issues
show
Bug introduced by
It seems like $return can also be of type true; however, parameter $json of json_decode() 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

108
            $json = json_decode(/** @scrutinizer ignore-type */ $return, true);
Loading history...
109
            if ($json !== null && !empty($json["code"]) && $json["code"] == 0) {
110
                return $json["response"]["posts"];
111
            } else {
112
                Craft::error(Craft::t('disqus', print_r($json, true)), __METHOD__);
0 ignored issues
show
Bug introduced by
It seems like print_r($json, true) can also be of type true; however, parameter $message of yii\BaseYii::t() 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

112
                Craft::error(Craft::t('disqus', /** @scrutinizer ignore-type */ print_r($json, true)), __METHOD__);
Loading history...
113
114
                return 0;
115
            }
116
        } else {
117
            Craft::error(Craft::t('disqus', "Public API Key missing"), __METHOD__);
118
119
            return 0;
120
        }
121
    }
122
123
    // Protected Methods
124
    // =========================================================================
125
126
    /**
127
     * Return the SSO vars
128
     *
129
     * @return array
130
     */
131
    protected function getSSOVars(): array
132
    {
133
        /** @var Settings $settings */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
134
        $settings = Disqus::$plugin->getSettings();
135
        $vars = [
136
            'useSSO' => false,
137
            'useCustomLogin' => false,
138
        ];
139
        if ($settings->getUseSSO()) {
140
            $data = [];
141
142
            // Set the data array
143
            /** @var User $user */
0 ignored issues
show
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
144
            $user = Craft::$app->getUser();
145
            $currentUser = $user->getIdentity();
146
            if ($currentUser) {
147
                $data['id'] = $currentUser->id;
148
                if (Craft::$app->getConfig()->getGeneral()->useEmailAsUsername) {
149
                    $data['username'] = $currentUser->getFullName();
0 ignored issues
show
Deprecated Code introduced by
The function craft\elements\User::getFullName() has been deprecated: in 4.0.0. [[fullName]] should be used instead. ( Ignorable by Annotation )

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

149
                    $data['username'] = /** @scrutinizer ignore-deprecated */ $currentUser->getFullName();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
150
                } else {
151
                    $data['username'] = $currentUser->username;
152
                }
153
                $data['email'] = $currentUser->email;
154
                try {
155
                    $data['avatar'] = $currentUser->getPhoto()->getUrl();
156
                } catch (InvalidConfigException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
157
                }
158
            }
159
160
            // Encode the data array and generate the hMac
161
            $message = base64_encode(json_encode($data));
162
            $timestamp = time();
163
            $hMac = $this->disqusHmacSha1(
164
                $message
165
                . ' '
166
                . $timestamp,
167
                $settings->getDisqusSecretKey()
168
            );
169
170
            // Set the vars for the template
171
            $vars = array_merge($vars, [
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
172
                'useSSO' => true,
173
                'message' => $message,
174
                'hmac' => $hMac,
175
                'timestamp' => $timestamp,
176
                'disqusPublicKey' => $settings->getDisqusPublicKey(),
177
            ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
178
179
            // Set the vars for the custom login
180
            if ($settings->getCustomLogin()) {
181
                $vars = array_merge($vars, [
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
182
                    'useCustomLogin' => true,
183
                    'loginName' => $settings->getLoginName(),
184
                    'loginButton' => $settings->getLoginButton(),
185
                    'loginIcon' => $settings->getLoginIcon(),
186
                    'loginUrl' => $settings->getLoginUrl(),
187
                    'loginLogoutUrl' => $settings->getLoginLogoutUrl(),
188
                    'loginWidth' => $settings->getLoginWidth(),
189
                    'loginHeight' => $settings->getLoginHeight(),
190
                ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
191
            }
192
        }
193
194
        return $vars;
195
    }
196
197
    /**
198
     * Render a plugin template
199
     *
200
     * @param $templatePath
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
201
     * @param $vars
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
202
     *
203
     * @return Markup
204
     */
205
    protected function renderPluginTemplate($templatePath, $vars): Markup
206
    {
207
        // Stash the old template mode, and set it Control Panel template mode
208
        $oldMode = Craft::$app->view->getTemplateMode();
209
        try {
210
            Craft::$app->view->setTemplateMode(View::TEMPLATE_MODE_CP);
211
        } catch (Exception $e) {
212
            Craft::error($e->getMessage(), __METHOD__);
213
        }
214
215
        // Render the template with our vars passed in
216
        try {
217
            $htmlText = Craft::$app->view->renderTemplate('disqus/' . $templatePath, $vars);
218
        } catch (\Exception $e) {
219
            $htmlText = 'Error rendering template ' . $templatePath . ' -> ' . $e->getMessage();
220
            Craft::error(Craft::t('disqus', $htmlText), __METHOD__);
221
        }
222
223
        // Restore the old template mode
224
        try {
225
            Craft::$app->view->setTemplateMode($oldMode);
226
        } catch (Exception $e) {
227
            Craft::error($e->getMessage(), __METHOD__);
228
        }
229
230
        return Template::raw($htmlText);
231
    }
232
233
    /**
234
     * HMAC->SHA1
235
     * From:
236
     * https://github.com/disqus/DISQUS-API-Recipes/blob/master/sso/php/sso.php
237
     *
238
     * @param $data
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
239
     * @param $key
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
240
     *
241
     * @return string
242
     */
243
    protected function disqusHmacSha1($data, $key): string
244
    {
245
        $blockSize = 64;
246
        $hashFunc = 'sha1';
247
        if (strlen($key) > $blockSize) {
248
            $key = pack('H*', $hashFunc($key));
249
        }
250
        $key = str_pad($key, $blockSize, chr(0x00));
251
        $iPad = str_repeat(chr(0x36), $blockSize);
252
        $oPad = str_repeat(chr(0x5c), $blockSize);
253
        $hMac = pack(
254
            'H*',
255
            $hashFunc(
256
                ($key ^ $oPad) . pack(
257
                    'H*',
258
                    $hashFunc(
259
                        ($key ^ $iPad) . $data
260
                    )
261
                )
262
            )
263
        );
264
265
        return bin2hex($hMac);
266
    }
267
}
268