LoginAccounts::deleteLoginAccount()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 2
b 0
f 0
nc 1
nop 1
dl 0
loc 5
rs 10
1
<?php
2
/**
3
 * @link      https://dukt.net/social/
4
 * @copyright Copyright (c) Dukt
5
 * @license   https://github.com/dukt/social/blob/v2/LICENSE.md
6
 */
7
8
namespace dukt\social\services;
9
10
use Craft;
11
use craft\elements\User;
12
use craft\helpers\FileHelper;
13
use dukt\social\errors\ImageTypeException;
14
use dukt\social\errors\LoginAccountNotFoundException;
15
use dukt\social\helpers\SocialHelper;
16
use dukt\social\Plugin;
17
use yii\base\Component;
18
use craft\elements\User as UserModel;
19
use dukt\social\elements\LoginAccount;
20
use dukt\social\records\LoginAccount as LoginAccountRecord;
21
use craft\helpers\UrlHelper;
22
23
/**
24
 * The LoginAccounts service provides APIs for managing login accounts in Craft.
25
 *
26
 * An instance of the LoginAccounts service is globally accessible in Craft via [[Plugin::loginAccounts `Plugin::getInstance()->getLoginAccounts()`]].
27
 *
28
 * @author  Dukt <[email protected]>
29
 * @since   1.0
30
 */
31
class LoginAccounts extends Component
32
{
33
    // Public Methods
34
    // =========================================================================
35
36
    /**
37
     * Get all social accounts.
38
     *
39
     * @return array|null
40
     */
41
    public function getLoginAccounts()
42
    {
43
        return LoginAccount::find()->all();
44
    }
45
46
    /**
47
     * Get all of the social accounts for a given user id.
48
     *
49
     * @param $userId
50
     *
51
     * @return array|null
52
     */
53
    public function getLoginAccountsByUserId($userId)
54
    {
55
        return LoginAccount::find()->userId($userId)->all();
56
    }
57
58
    /**
59
     * Returns a social account from its ID.
60
     *
61
     *
62
     * @return LoginAccount|null
63
     */
64
    public function getLoginAccountById(int $id)
65
    {
66
        return Craft::$app->elements->getElementById($id);
0 ignored issues
show
Bug introduced by
The method getElementById() 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

66
        return Craft::$app->elements->/** @scrutinizer ignore-call */ getElementById($id);

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...
67
    }
68
69
    /**
70
     * Get a social account by provider handle for the currently logged in user.
71
     *
72
     *
73
     * @return LoginAccount|null
74
     */
75
    public function getLoginAccountByLoginProvider(string $providerHandle)
76
    {
77
        $currentUser = Craft::$app->getUser()->getIdentity();
78
79
        // Check if there is a current user or not
80
        if (!$currentUser) {
0 ignored issues
show
introduced by
$currentUser is of type yii\web\IdentityInterface, thus it always evaluated to true.
Loading history...
81
            return false;
82
        }
83
84
        return LoginAccount::find()->userId($currentUser->id)->providerHandle($providerHandle)->one();
0 ignored issues
show
Bug Best Practice introduced by
The expression return dukt\social\eleme...$providerHandle)->one() also could return the type array which is incompatible with the documented return type dukt\social\elements\LoginAccount|null.
Loading history...
Bug introduced by
Accessing id on the interface yii\web\IdentityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
85
    }
86
87
    /**
88
     * Get a social account by social UID.
89
     *
90
     *
91
     * @return LoginAccount|null
92
     */
93
    public function getLoginAccountByUid(string $providerHandle, string $socialUid)
94
    {
95
        return LoginAccount::find()->providerHandle($providerHandle)->socialUid($socialUid)->one();
0 ignored issues
show
Bug Best Practice introduced by
The expression return dukt\social\eleme...lUid($socialUid)->one() also could return the type array which is incompatible with the documented return type dukt\social\elements\LoginAccount|null.
Loading history...
96
    }
97
98
    /**
99
     * Deletes a social account by provider.
100
     *
101
     * @param $providerHandle
102
     *
103
     * @return bool
104
     * @throws \Throwable
105
     */
106
    public function deleteLoginAccountByProvider($providerHandle): bool
107
    {
108
        $loginAccount = $this->getLoginAccountByLoginProvider($providerHandle);
109
110
        return $this->deleteLoginAccount($loginAccount);
111
    }
112
113
    /**
114
     * Deletes a social login account by its ID.
115
     *
116
     *
117
     * @return bool
118
     * @throws \Throwable
119
     */
120
    public function deleteLoginAccountById(int $id): bool
121
    {
122
        $loginAccount = $this->getLoginAccountById($id);
123
124
        return $this->deleteLoginAccount($loginAccount);
0 ignored issues
show
Bug introduced by
It seems like $loginAccount can also be of type null; however, parameter $loginAccount of dukt\social\services\Log...s::deleteLoginAccount() does only seem to accept dukt\social\elements\LoginAccount, 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

124
        return $this->deleteLoginAccount(/** @scrutinizer ignore-type */ $loginAccount);
Loading history...
125
    }
126
127
    /**
128
     * Deletes a login account.
129
     *
130
     * @param LoginAccount $loginAccount
131
     *
132
     * @return bool
133
     * @throws \Throwable
134
     */
135
    public function deleteLoginAccount(LoginAccount $loginAccount): bool
136
    {
137
        Craft::$app->elements->deleteElement($loginAccount);
138
139
        return true;
140
    }
141
142
    /**
143
     * Get login URL.
144
     *
145
     * @param       $providerHandle
146
     * @param array $params
147
     *
148
     * @return string
149
     */
150
    public function getLoginUrl($providerHandle, array $params = []): string
151
    {
152
        $params['provider'] = $providerHandle;
153
154
        return UrlHelper::actionUrl('social/login-accounts/login', $params);
155
    }
156
157
    /**
158
     * Get link account URL.
159
     *
160
     * @param $handle
161
     *
162
     * @return string
163
     */
164
    public function getLoginAccountConnectUrl($handle): string
165
    {
166
        return UrlHelper::actionUrl('social/login-accounts/connect-login-account', [
167
            'provider' => $handle
168
        ]);
169
    }
170
171
    /**
172
     * Get unlink account URL.
173
     *
174
     * @param $handle
175
     *
176
     * @return string
177
     */
178
    public function getLoginAccountDisconnectUrl($handle): string
179
    {
180
        return UrlHelper::actionUrl('social/login-accounts/disconnect-login-account', [
181
            'provider' => $handle
182
        ]);
183
    }
184
185
    /**
186
     * Saves a remote photo.
187
     *
188
     * @param string $providerHandle
189
     * @param User $newUser
190
     * @param $profile
191
     * @throws \GuzzleHttp\Exception\GuzzleException
192
     * @throws \craft\errors\ImageException
193
     * @throws \craft\errors\VolumeException
194
     * @throws \yii\base\Exception
195
     */
196
    public function saveRemotePhoto(string $providerHandle, User $newUser, $profile)
197
    {
198
        $photoUrl = false;
199
        $loginProvider = Plugin::getInstance()->getLoginProviders()->getLoginProvider($providerHandle);
200
        $userFieldMapping = $loginProvider->getUserFieldMapping();
0 ignored issues
show
Bug introduced by
The method getUserFieldMapping() does not exist on dukt\social\base\LoginProviderInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to dukt\social\base\LoginProviderInterface. ( Ignorable by Annotation )

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

200
        /** @scrutinizer ignore-call */ 
201
        $userFieldMapping = $loginProvider->getUserFieldMapping();
Loading history...
201
202
        if (isset($userFieldMapping['photo'])) {
203
            try {
204
                $photoUrl = html_entity_decode(Craft::$app->getView()->renderString($userFieldMapping['photo'], ['profile' => $profile]));
205
            } catch (\Exception $exception) {
206
                Craft::warning('Could not map:' . print_r(['photo', $userFieldMapping['photo'], $profile, $exception->getMessage()], true), __METHOD__);
0 ignored issues
show
Bug introduced by
Are you sure print_r(array('photo', $...n->getMessage()), true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

206
                Craft::warning('Could not map:' . /** @scrutinizer ignore-type */ print_r(['photo', $userFieldMapping['photo'], $profile, $exception->getMessage()], true), __METHOD__);
Loading history...
207
            }
208
        }
209
210
        if ($photoUrl) {
211
            $this->_saveRemotePhoto($photoUrl, $newUser);
212
        }
213
    }
214
215
    /**
216
     * Save a remote photo.
217
     *
218
     * @param           $photoUrl
219
     * @param UserModel $user
220
     *
221
     * @return bool|void
222
     * @throws \GuzzleHttp\Exception\GuzzleException
223
     * @throws \craft\errors\ImageException
224
     * @throws \craft\errors\VolumeException
225
     * @throws \yii\base\Exception
226
     */
227
    private function _saveRemotePhoto($photoUrl, UserModel $user)
228
    {
229
        $filename = 'photo';
230
231
        $tempPath = Craft::$app->path->getTempPath() . '/social/userphotos/' . $user->email . '/';
0 ignored issues
show
Bug introduced by
The method getTempPath() 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

231
        $tempPath = Craft::$app->path->/** @scrutinizer ignore-call */ getTempPath() . '/social/userphotos/' . $user->email . '/';

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...
232
233
        FileHelper::createDirectory($tempPath);
234
235
        $client = new \GuzzleHttp\Client();
236
237
        $response = $client->request('GET', $photoUrl, [
238
            'sink' => $tempPath . $filename
239
        ]);
240
241
        if ($response->getStatusCode() !== 200) {
242
            return;
243
        }
244
245
        $contentTypes = $response->getHeader('Content-Type');
246
247
        if (is_array($contentTypes) && isset($contentTypes[0])) {
248
            switch ($contentTypes[0]) {
249
                case 'image/gif':
250
                    $extension = 'gif';
251
                    break;
252
                case 'image/jpeg':
253
                    $extension = 'jpg';
254
                    break;
255
                case 'image/png':
256
                    $extension = 'png';
257
                    break;
258
                case 'image/svg+xml':
259
                    $extension = 'svg';
260
                    break;
261
262
                default:
263
                    throw new ImageTypeException('Image type “' . $contentTypes[0] . '” not supported');
264
            }
265
        } else {
266
            throw new ImageTypeException('Image type not supported');
267
        }
268
269
        rename($tempPath . $filename, $tempPath . $filename . '.' . $extension);
270
271
        $image = Craft::$app->images->loadImage($tempPath . $filename . '.' . $extension);
0 ignored issues
show
Bug introduced by
The method loadImage() 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

271
        /** @scrutinizer ignore-call */ 
272
        $image = Craft::$app->images->loadImage($tempPath . $filename . '.' . $extension);

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...
272
        $imageWidth = $image->getWidth();
273
        $imageHeight = $image->getHeight();
274
275
        $dimension = min($imageWidth, $imageHeight);
276
        $horizontalMargin = ($imageWidth - $dimension) / 2;
277
        $verticalMargin = ($imageHeight - $dimension) / 2;
278
        $image->crop($horizontalMargin, $imageWidth - $horizontalMargin, $verticalMargin, $imageHeight - $verticalMargin);
279
280
        Craft::$app->users->saveUserPhoto($tempPath . $filename . '.' . $extension, $user, $filename . '.' . $extension);
0 ignored issues
show
Bug introduced by
The method saveUserPhoto() 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

280
        Craft::$app->users->/** @scrutinizer ignore-call */ 
281
                            saveUserPhoto($tempPath . $filename . '.' . $extension, $user, $filename . '.' . $extension);

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...
281
282
        return true;
283
    }
284
285
    // Private Methods
286
    // =========================================================================
287
288
    /**
289
     * Gets a user record by its ID.
290
     *
291
     * @param int $loginAccountId
292
     *
293
     * @return LoginAccountRecord
294
     * @throws LoginAccountNotFoundException if $loginAccountId is invalid
295
     */
296
    private function _getLoginAccountRecordById(int $loginAccountId): LoginAccountRecord
0 ignored issues
show
Unused Code introduced by
The method _getLoginAccountRecordById() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
297
    {
298
        $loginAccountRecord = LoginAccountRecord::findOne($loginAccountId);
299
300
        if (!$loginAccountRecord instanceof \dukt\social\records\LoginAccount) {
301
            throw new LoginAccountNotFoundException(sprintf('No login account exists with the ID \'%d\'', $loginAccountId));
302
        }
303
304
        return $loginAccountRecord;
305
    }
306
}
307