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