xref: /plugin/combo/action/login.php (revision 0bb301e4810e02240c0e53e1eea739c8f546dd14)
1<?php
2/**
3 * Action Component
4 * Add a button in the edit toolbar
5 *
6 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
7 * @author     Nicolas GERARD
8 */
9
10use ComboStrap\BrandColors;
11use ComboStrap\Identity;
12use ComboStrap\LogUtility;
13use ComboStrap\PluginUtility;
14use dokuwiki\Form\Form;
15use dokuwiki\Form\InputElement;
16use dokuwiki\Menu\Item\Login;
17
18if (!defined('DOKU_INC')) die();
19require_once(__DIR__ . '/../ComboStrap/PluginUtility.php');
20
21/**
22 * Class action_plugin_combo_login
23 *
24 * $conf['rememberme']
25 */
26class action_plugin_combo_login extends DokuWiki_Action_Plugin
27{
28
29
30    const CANONICAL = Identity::CANONICAL;
31    const TAG = "login";
32    const FORM_LOGIN_CLASS = "form-" . self::TAG;
33
34    const CONF_ENABLE_LOGIN_FORM = "enableLoginForm";
35
36
37    /**
38     * Update the old form
39     * @param Doku_Form $form
40     * @return void
41     */
42    private static function updateDokuFormLogin(Doku_Form &$form)
43    {
44        /**
45         * The Login page is an admin page created via buffer
46         * We print before the forms
47         * to avoid a FOUC
48         */
49        print Identity::getHtmlStyleTag(self::TAG);
50
51
52        $form->params["class"] = Identity::FORM_IDENTITY_CLASS . " " . self::FORM_LOGIN_CLASS;
53
54
55        /**
56         * Heading
57         */
58        $newFormContent[] = Identity::getHeaderHTML($form, self::FORM_LOGIN_CLASS);
59
60        /**
61         * Field
62         */
63        foreach ($form->_content as $field) {
64            if (!is_array($field)) {
65                continue;
66            }
67            $fieldName = $field["name"];
68            if ($fieldName == null) {
69                // this is not an input field
70                if ($field["type"] == "submit") {
71                    /**
72                     * This is important to keep the submit element intact
73                     * for forms integration such as captcha
74                     * They search the submit button to insert before it
75                     */
76                    $classes = "btn btn-primary btn-block";
77                    if (isset($field["class"])) {
78                        $field["class"] = $field["class"] . " " . $classes;
79                    } else {
80                        $field["class"] = $classes;
81                    }
82                    $newFormContent[] = $field;
83                }
84                continue;
85            }
86            switch ($fieldName) {
87                case "u":
88                    $loginText = $field["_text"];
89                    $loginValue = $field["value"];
90                    $loginHTMLField = <<<EOF
91<div class="form-floating">
92    <input type="text" id="inputUserName" class="form-control" placeholder="$loginText" required="required" autofocus="" name="u" value="$loginValue">
93    <label for="inputUserName">$loginText</label>
94</div>
95EOF;
96                    $newFormContent[] = $loginHTMLField;
97                    break;
98                case "p":
99                    $passwordText = $field["_text"];
100                    $passwordFieldHTML = <<<EOF
101<div class="form-floating">
102    <input type="password" id="inputPassword" class="form-control" placeholder="$passwordText" required="required" name="p">
103    <label for="inputPassword">$passwordText</label>
104</div>
105EOF;
106                    $newFormContent[] = $passwordFieldHTML;
107                    break;
108                case "r":
109                    $rememberText = $field["_text"];
110                    $rememberValue = $field["value"];
111                    $rememberMeHtml = <<<EOF
112<div class="checkbox rememberMe">
113    <label><input type="checkbox" id="remember__me" name="r" value="$rememberValue"> $rememberText</label>
114</div>
115EOF;
116                    $newFormContent[] = $rememberMeHtml;
117                    break;
118                default:
119                    $tag = self::TAG;
120                    LogUtility::msg("The $tag field name ($fieldName) is unknown", LogUtility::LVL_MSG_ERROR, self::CANONICAL);
121
122
123            }
124        }
125
126
127        $registerHtml = action_plugin_combo_registration::getRegisterLinkAndParagraph();
128        if (!empty($registerHtml)) {
129            $newFormContent[] = $registerHtml;
130        }
131        $resendPwdHtml = action_plugin_combo_resend::getResendPasswordParagraphWithLinkToFormPage();
132        if (!empty($resendPwdHtml)) {
133            $newFormContent[] = $resendPwdHtml;
134        }
135
136        /**
137         * Set the new in place of the old one
138         */
139        $form->_content = $newFormContent;
140    }
141
142
143    function register(Doku_Event_Handler $controller)
144    {
145        /**
146         * To modify the form and add class
147         *
148         * The event HTML_LOGINFORM_OUTPUT is deprecated
149         * for FORM_LOGIN_OUTPUT
150         *
151         * The difference is on the type of object that we got in the event
152         */
153        if (PluginUtility::getConfValue(self::CONF_ENABLE_LOGIN_FORM, 1)) {
154
155            /**
156             * Old event: Deprecated object passed by the event but still in use
157             * https://www.dokuwiki.org/devel:event:html_loginform_output
158             */
159            $controller->register_hook('HTML_LOGINFORM_OUTPUT', 'BEFORE', $this, 'handle_login_html', array());
160
161            /**
162             * New Event: using the new object but only in use in
163             * the {@link https://codesearch.dokuwiki.org/xref/dokuwiki/lib/plugins/authad/action.php authad plugin}
164             * (ie login against active directory)
165             *
166             * https://www.dokuwiki.org/devel:event:form_login_output
167             */
168            $controller->register_hook('FORM_LOGIN_OUTPUT', 'BEFORE', $this, 'handle_login_html', array());
169        }
170
171
172    }
173
174    function handle_login_html(&$event, $param): void
175    {
176
177        $form = &$event->data;
178        $class = get_class($form);
179        switch ($class) {
180            case Doku_Form::class:
181                /**
182                 * Old one
183                 * @var Doku_Form $form
184                 */
185                self::updateDokuFormLogin($form);
186                return;
187            case dokuwiki\Form\Form::class;
188                /**
189                 * New One
190                 * @var Form $form
191                 */
192                self::updateNewFormLogin($form);
193                return;
194        }
195
196
197    }
198
199
200    /**
201     * Login
202     * @return string
203     */
204    public static function getLoginParagraphWithLinkToFormPage(): string
205    {
206
207        $loginPwLink = (new Login())->asHtmlLink('', false);
208        global $lang;
209        $loginText = $lang['btn_login'];
210        return <<<EOF
211<p class="login">$loginText ? : $loginPwLink</p>
212EOF;
213
214    }
215
216    /**
217     * https://www.dokuwiki.org/devel:form - documentation
218     * @param Form $form
219     * @return void
220     */
221    private static function updateNewFormLogin(Form &$form)
222    {
223        /**
224         * The Login page is an admin page created via buffer
225         * We print before the forms
226         * to avoid a FOUC
227         */
228        print Identity::getHtmlStyleTag(self::TAG);
229
230
231        $form->addClass(Identity::FORM_IDENTITY_CLASS . " " . self::FORM_LOGIN_CLASS);
232
233        /**
234         * Heading
235         */
236        $headerHTML = Identity::getHeaderHTML($form, self::FORM_LOGIN_CLASS);
237        if ($headerHTML != "") {
238            $form->addHTML($headerHTML, 1);
239        }
240
241        $brPositionElement = [4, 5]; // 4 and 6 but when you delete 4, it's on 5
242        foreach ($brPositionElement as $brPosition) {
243            $fieldBr = $form->getElementAt($brPosition);
244            if ($fieldBr->val() === "<br>\n") {
245                $form->removeElement($brPosition);
246            } else {
247                LogUtility::msg("Internal: the login br $brPosition element was not found and not deleted");
248            }
249        }
250
251        /**
252         * Fieldset delete
253         */
254        $elementsTypeToDelete = ["fieldsetopen", "fieldsetclose"];
255        foreach ($elementsTypeToDelete as $type) {
256            $field = $form->findPositionByType($type);
257            if ($field != false) {
258                $form->removeElement($field);
259            }
260        }
261
262        /**
263         * Field
264         */
265        $submitButtonPosition = $form->findPositionByAttribute("type", "submit");
266        if ($submitButtonPosition == false) {
267            LogUtility::msg("Internal error: No submit button found");
268            return;
269        }
270        /**
271         * This is important to keep the submit element intact
272         * for forms integration such as captcha
273         * They search the submit button to insert before it
274         */
275        $form->getElementAt($submitButtonPosition)
276            ->addClass("btn")
277            ->addClass("btn-primary")
278            ->addClass("btn-block")
279            ->addClass("mb-2");
280
281        $userPosition = $form->findPositionByAttribute("name", "u");
282        if ($userPosition == false) {
283            LogUtility::msg("Internal error: No user field found");
284            return;
285        }
286        /**
287         * @var InputElement $userField
288         */
289        $userField = $form->getElementAt($userPosition);
290        $newUserField = new InputElement($userField->getType(), "u");
291        $loginText = $userField->getLabel()->val();
292        foreach ($userField->attrs() as $keyAttr => $valueAttr) {
293            $newUserField->attr($keyAttr, $valueAttr);
294        }
295        $newUserField->addClass("form-control");
296        $newUserField->attr("placeholder", $loginText);
297        $newUserField->attr("required", "required");
298        $newUserField->attr("autofocus", "");
299        $userFieldId = $userField->attr("id");
300
301        $form->replaceElement($newUserField, $userPosition);
302
303        $form->addHTML("<div class=\"form-floating\">", $userPosition);
304        $form->addHTML("<label for=\"$userFieldId\">$loginText</label>", $userPosition + 2);
305        $form->addHTML("</div>", $userPosition + 3);
306
307
308        $pwdPosition = $form->findPositionByAttribute("name", "p");
309        if ($pwdPosition == false) {
310            LogUtility::msg("Internal error: No password field found");
311            return;
312        }
313        $pwdField = $form->getElementAt($pwdPosition);
314        $newPwdField = new InputElement($pwdField->getType(), "p");
315        foreach ($pwdField->attrs() as $keyAttr => $valueAttr) {
316            $newPwdField->attr($keyAttr, $valueAttr);
317        }
318        $newPwdField->addClass("form-control");
319        $passwordText = $pwdField->getLabel()->val();
320        $newPwdField->attr("placeholder", $passwordText);
321        $newPwdField->attr("required", "required");
322        $pwdFieldId = $newPwdField->attr("id");
323        if (empty($pwdFieldId)) {
324            $pwdFieldId = "input__password";
325            $newPwdField->id($pwdFieldId);
326        }
327        $form->replaceElement($newPwdField, $pwdPosition);
328
329
330        $form->addHTML("<div class=\"form-floating\">", $pwdPosition);
331        $form->addHTML("<label for=\"$pwdFieldId\">$passwordText</label>", $pwdPosition + 2);
332        $form->addHTML("</div>", $pwdPosition + 3);
333
334
335        $rememberPosition = $form->findPositionByAttribute("name", "r");
336        if ($rememberPosition == false) {
337            LogUtility::msg("Internal error: No remember field found");
338            return;
339        }
340        $rememberField = $form->getElementAt($rememberPosition);
341        $newRememberField = new InputElement($rememberField->getType(), "r");
342        foreach ($rememberField->attrs() as $keyAttr => $valueAttr) {
343            $newRememberField->attr($keyAttr, $valueAttr);
344        }
345        $newRememberField->addClass("form-check-input");
346        $form->replaceElement($newRememberField, $rememberPosition);
347
348        $remberText = $rememberField->getLabel()->val();
349        $remFieldId = $newRememberField->attr("id");
350
351        $form->addHTML("<div class=\"form-check py-2\">", $rememberPosition);
352        $form->addHTML("<label for=\"$remFieldId\" class=\"form-check-label\">$remberText</label>", $rememberPosition + 2);
353        $form->addHTML("</div>", $rememberPosition + 3);
354
355
356//        $registerHtml = action_plugin_combo_registration::getRegisterLinkAndParagraph();
357//        if (!empty($registerHtml)) {
358//            $newFormContent[] = $registerHtml;
359//        }
360//
361//        $resendPwdHtml = action_plugin_combo_resend::getResendPasswordParagraphWithLinkToFormPage();
362//        if (!empty($resendPwdHtml)) {
363//            $newFormContent[] = $resendPwdHtml;
364//        }
365
366
367    }
368
369}
370