Passed
Pull Request — master (#33)
by Anton
02:36
created

AuthChoice.php$0 ➔ __toString()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 3
ccs 0
cts 1
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\AuthClient\Widget;
6
7
use Yiisoft\Assets\AssetManager;
8
use Yiisoft\Html\Html;
9
use Yiisoft\Html\NoEncodeStringableInterface;
10
use Yiisoft\Html\Tag\A;
11
use Yiisoft\Json\Json;
12
use Yiisoft\Router\UrlGeneratorInterface;
13
use Yiisoft\View\WebView;
0 ignored issues
show
Bug introduced by
The type Yiisoft\View\WebView 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...
14
use Yiisoft\Widget\Widget;
15
use Yiisoft\Yii\AuthClient\Asset\AuthChoiceAsset;
16
use Yiisoft\Yii\AuthClient\Asset\AuthChoiceStyleAsset;
17
use Yiisoft\Yii\AuthClient\AuthClientInterface;
18
use Yiisoft\Yii\AuthClient\Collection;
19
use Yiisoft\Yii\AuthClient\Exception\InvalidConfigException;
20
21
/**
22
 * AuthChoice prints buttons for authentication via various auth clients.
23
 * It opens a popup window for the client authentication process.
24
 * By default this widget relies on presence of {@see \Yiisoft\Yii\AuthClient\Collection} among application components
25
 * to get auth clients information.
26
 *
27
 * Example:
28
 *
29
 * ```php
30
 * <?= AuthChoice::widget()->authRoute('site/auth'); ?>
31
 * ```
32
 *
33
 * You can customize the widget appearance by using {@see begin()} and {@see end()} syntax
34
 * along with using method {@see clientLink()} or {@see createClientUrl()}.
35
 * For example:
36
 *
37
 * ```php
38
 * <?php
39
 * use Yiisoft\Yii\AuthClient\Widget\AuthChoice;
40
 * ?>
41
 * <?php $authChoice = AuthChoice::begin([
42
 *     'baseAuthUrl' => ['site/auth']
43
 * ]); ?>
44
 * <ul>
45
 * <?php foreach ($authChoice->getClients() as $client): ?>
46
 *     <li><?= $authChoice->clientLink($client) ?></li>
47
 * <?php endforeach; ?>
48
 * </ul>
49
 * <?php AuthChoice::end(); ?>
50
 * ```
51
 *
52
 * This widget supports following keys for {@see AuthClientInterface::getViewOptions()} result:
53
 *
54
 *  - popupWidth: int, width of the popup window in pixels.
55
 *  - popupHeight: int, height of the popup window in pixels.
56
 *  - widget: array, configuration for the widget, which should be used to render a client link;
57
 *    such widget should be a subclass of {@see AuthChoiceItem}.
58
 *
59
 * @see \Yiisoft\Yii\AuthClient\AuthAction
60
 */
61
final class AuthChoice extends Widget
62
{
63
    /**
64
     * @var string name of the GET param , which should be used to passed auth client id to URL
65
     * defined by {@see baseAuthUrl}.
66
     */
67
    private string $clientIdGetParamName = 'authclient';
68
    /**
69
     * @var array the HTML attributes that should be rendered in the div HTML tag representing the container element.
70
     *
71
     * @see Html::renderTagAttributes() for details on how attributes are being rendered.
72
     */
73
    private array $options = [];
74
    /**
75
     * @var array additional options to be passed to the underlying JS plugin.
76
     */
77
    private array $clientOptions = [];
78
    /**
79
     * @var bool indicates if popup window should be used instead of direct links.
80
     */
81
    private bool $popupMode = true;
82
    /**
83
     * @var bool indicates if widget content, should be rendered automatically.
84
     * Note: this value automatically set to 'false' at the first call of {@see createClientUrl()}
85
     */
86
    private bool $autoRender = true;
87
88
    /**
89
     * @var string route name for the external clients authentication URL.
90
     */
91
    private string $authRoute;
92
    /**
93
     * @var AuthClientInterface[] auth providers list.
94
     */
95
    private array $clients;
96
    private UrlGeneratorInterface $urlGenerator;
97
    private WebView $webView;
98
    private AssetManager $assetManager;
99
100
    public function __construct(
101
        Collection $clientCollection,
102
        UrlGeneratorInterface $urlGenerator,
103
        WebView $webView,
104
        AssetManager $assetManager
105
    ) {
106
        $this->clients = $clientCollection->getClients();
107
        $this->urlGenerator = $urlGenerator;
108
        $this->webView = $webView;
109
        $this->assetManager = $assetManager;
110
        $this->init();
111
    }
112
113
    /**
114
     * Initializes the widget.
115
     */
116
    public function init(): void
117
    {
118
        if ($this->popupMode) {
119
            $this->assetManager->register(
120
                [
121
                    AuthChoiceAsset::class,
122
                ]
123
            );
124
            if (empty($this->clientOptions)) {
125
                $options = '';
126
            } else {
127
                $options = Json::htmlEncode($this->clientOptions);
128
            }
129
            $this->webView->registerJs("jQuery('#" . $this->getId() . "').authchoice({$options});");
130
        } else {
131
            $this->assetManager->register(
132
                [
133
                    AuthChoiceStyleAsset::class,
134
                ]
135
            );
136
        }
137
        $this->options['id'] = $this->getId();
138
        echo Html::openTag('div', $this->options);
139
    }
140
141
    public function getId(): string
142
    {
143
        return 'yii-auth-client';
144
    }
145
146
    /**
147
     * Runs the widget.
148
     *
149
     * @throws \Yiisoft\Factory\Exception\InvalidConfigException
150
     *
151
     * @return string rendered HTML.
152
     */
153
    public function run(): string
154
    {
155
        $content = '';
156
        if ($this->autoRender) {
157
            $content .= $this->renderMainContent();
158
        }
159
        $content .= Html::closeTag('div');
160
        return $content;
161
    }
162
163
    /**
164
     * Renders the main content, which includes all external services links.
165
     *
166
     * @throws InvalidConfigException
167
     * @throws \Yiisoft\Factory\Exception\InvalidConfigException
168
     *
169
     * @return string generated HTML.
170
     */
171
    protected function renderMainContent(): string
172
    {
173
        $ul = Html::tag('ul', '', ['class' => 'auth-clients']);
174
        foreach ($this->getClients() as $externalService) {
175
            $ul = $ul->addContent(Html::tag('li')->content($this->clientLink($externalService)));
176
        }
177
        return $ul->render();
178
    }
179
180
    /**
181
     * @return AuthClientInterface[] auth providers
182
     */
183
    public function getClients(): array
184
    {
185
        return $this->clients;
186
    }
187
188
    /**
189
     * @param AuthClientInterface[] $clients auth providers
190
     */
191
    public function setClients(array $clients): void
192
    {
193
        $this->clients = $clients;
194
    }
195
196
    /**
197
     * Outputs client auth link.
198
     *
199
     * @param AuthClientInterface $client external auth client instance.
200
     * @param string|null $text link text, if not set - default value will be generated.
201
     * @param array $htmlOptions link HTML options.
202
     *
203
     * @throws InvalidConfigException on wrong configuration.
204
     * @throws \Yiisoft\Factory\Exception\InvalidConfigException
205
     *
206
     * @return NoEncodeStringableInterface generated HTML.
207
     */
208
    public function clientLink(AuthClientInterface $client, string $text = null, array $htmlOptions = []): NoEncodeStringableInterface
209
    {
210
        $viewOptions = $client->getViewOptions();
211
212
        if (empty($viewOptions['widget'])) {
213
            if ($text === null) {
214
                $text = Html::tag('span', '', ['class' => 'auth-icon ' . $client->getName()]);
215
            }
216
            if (!isset($htmlOptions['class'])) {
217
                $htmlOptions['class'] = $client->getName();
218
            }
219
            if (!isset($htmlOptions['title'])) {
220
                $htmlOptions['title'] = $client->getTitle();
221
            }
222
            Html::addCssClass($htmlOptions, ['widget' => 'auth-link']);
223
224
            if ($this->popupMode) {
225
                if (isset($viewOptions['popupWidth'])) {
226
                    $htmlOptions['data-popup-width'] = $viewOptions['popupWidth'];
227
                }
228
                if (isset($viewOptions['popupHeight'])) {
229
                    $htmlOptions['data-popup-height'] = $viewOptions['popupHeight'];
230
                }
231
            }
232
            return Html::a('', $this->createClientUrl($client), $htmlOptions)->content($text);
233
        }
234
235
        $widgetConfig = $viewOptions['widget'];
236
        if (!isset($widgetConfig['class'])) {
237
            throw new InvalidConfigException('Widget config "class" parameter is missing');
238
        }
239
        /* @var $widgetClass Widget */
240
        $widgetClass = $widgetConfig['class'];
241
        if (!(is_subclass_of($widgetClass, AuthChoiceItem::class))) {
242
            throw new InvalidConfigException('Item widget class must be subclass of "' . AuthChoiceItem::class . '"');
243
        }
244
        unset($widgetConfig['class']);
245
        $widgetConfig['client'] = $client;
246
        $widgetConfig['authChoice'] = $this;
247
        return new class($widgetClass::widget($widgetConfig)->render()) implements NoEncodeStringableInterface {
248
            private string $string;
249
250
            public function __construct(string $string) {
251
                $this->string = $string;
252
            }
253
254
            public function __toString(): string
255
            {
256
                return $this->string;
257
            }
258
        };
259
    }
260
261
    /**
262
     * Composes client auth URL.
263
     *
264
     * @param AuthClientInterface $client external auth client instance.
265
     *
266
     * @return string auth URL.
267
     */
268
    public function createClientUrl($client): string
269
    {
270
        $this->autoRender = false;
271
        $params = [];
272
        $params[$this->clientIdGetParamName] = $client->getName();
273
274
        return $this->urlGenerator->generate($this->authRoute, $params);
275
    }
276
277
    /**
278
     * @param string $authRoute
279
     *
280
     * @return self
0 ignored issues
show
Documentation Bug introduced by
The doc comment self at position 0 could not be parsed: 'self' is only available from within classes.
Loading history...
281
     */
282
    public function authRoute(string $authRoute): self
283
    {
284
        $this->authRoute = $authRoute;
285
        return $this;
286
    }
287
}
288