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