| skipped 12 lines |
13 | 13 | | @param List<String> domains |
14 | 14 | | @param String query |
15 | 15 | | @param HttpServletRequest request |
| 16 | + | @param boolean isXTokenActive |
16 | 17 | | |
17 | 18 | | @template.layout(securityContext = securityContext, uriInfo = uriInfo, request = request, content = @` |
18 | 19 | | <div class="card m-3 bg-white"> |
| skipped 57 lines |
76 | 77 | | <i class="bi bi-safe2-fill me-2"></i>Change password |
77 | 78 | | </a> |
78 | 79 | | </li> |
| 80 | + | @if(isXTokenActive) |
| 81 | + | <li> |
| 82 | + | <a class="dropdown-item text-warning text-nowrap" href="#" data-bs-toggle="modal" |
| 83 | + | data-bs-target="#generateAuthToken-${user.jid().toString().replace('@','-').replace('.','-')}"> |
| 84 | + | <i class="bi bi-qr-code me-2"></i>New QR code |
| 85 | + | </a> |
| 86 | + | </li> |
| 87 | + | @endif |
79 | 88 | | <li> |
80 | 89 | | <a class="dropdown-item text-danger text-nowrap" href="#" data-bs-toggle="modal" |
81 | 90 | | data-bs-target="#deleteUser-${user.jid().toString().replace('@','-').replace('.','-')}"> |
| skipped 51 lines |
133 | 142 | | </div> |
134 | 143 | | </div> |
135 | 144 | | </div> |
| 145 | + | @if(isXTokenActive) |
| 146 | + | <div class="modal fade" id="generateAuthToken-${user.jid().toString().replace('@','-').replace('.','-')}" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="#generateAuthTokenModalLabel-${user.toString().replace('@','-').replace('.','-')}" aria-hidden="true"> |
| 147 | + | <div class="modal-dialog"> |
| 148 | + | <div class="modal-content"> |
| 149 | + | <form action="${uriInfo.getBaseUriBuilder().path(UsersHandler.class,"generateAuthQrCode").build(user.jid().toString()).toString()}" method="post"> |
| 150 | + | <div class="modal-header"> |
| 151 | + | <h1 class="modal-title fs-5" id="generateAuthTokenModalLabel-${user.jid().toString().replace('@','-').replace('.','-')}">Generate new token</h1> |
| 152 | + | <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
| 153 | + | </div> |
| 154 | + | <div class="modal-body"> |
| 155 | + | <p>Do you wish to generate a new authentication token for user ${user.jid().toString()}?</p> |
| 156 | + | </div> |
| 157 | + | <div class="modal-footer"> |
| 158 | + | <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button> |
| 159 | + | <a type="submit" class="btn btn-primary" onclick="generateQrCode('${user.jid().toString()}')">Generate</a> |
| 160 | + | </div> |
| 161 | + | </form> |
| 162 | + | </div> |
| 163 | + | </div> |
| 164 | + | </div> |
| 165 | + | @endif |
136 | 166 | | </td> |
137 | 167 | | </tr> |
138 | 168 | | @endfor |
139 | 169 | | </tbody> |
| 170 | + | @if(isXTokenActive) |
| 171 | + | <script> |
| 172 | + | let generateQrCode = function (jid) { |
| 173 | + | let escapedJid = jid.replace("@","-").replace(".","-"); |
| 174 | + | let modalBody = document.querySelector("#generateAuthToken-" + escapedJid + " div.modal-body"); |
| 175 | + | let generateButton = document.querySelector("#generateAuthToken-" + escapedJid + " div.modal-footer a[type='submit']"); |
| 176 | + | // remove existing error message |
| 177 | + | document.querySelector("#generateAuthToken-" + escapedJid + " div.modal-body span.error-message")?.remove(); |
| 178 | + | // update "generate" button label |
| 179 | + | let originalGenerateButtonLabel = generateButton.textContent; |
| 180 | + | generateButton.classList.add("disabled"); |
| 181 | + | generateButton.innerHTML = ""; |
| 182 | + | let spinner = document.createElement("div"); |
| 183 | + | spinner.setAttribute("role", "status"); |
| 184 | + | spinner.classList.add("spinner-border", "spinner-border-sm", "me-2"); |
| 185 | + | generateButton.append(spinner); |
| 186 | + | generateButton.append(document.createTextNode("Processing...")); |
| 187 | + | // send request |
| 188 | + | let xhr = new XMLHttpRequest(); |
| 189 | + | let url = "${uriInfo.getBaseUriBuilder().path(UsersHandler.class,"generateAuthQrCode").build("@").toString()}".replace("@", jid); |
| 190 | + | xhr.open("POST", url, true); |
| 191 | + | xhr.setRequestHeader('Content-type', "application/x-www-form-urlencoded"); |
| 192 | + | xhr.send(""); |
| 193 | + | xhr.responseType = "blob"; |
| 194 | + | let handleResult = function () { |
| 195 | + | // update generate button |
| 196 | + | spinner.remove(); |
| 197 | + | generateButton.classList.remove("disabled"); |
| 198 | + | generateButton.innerHTML = ""; |
| 199 | + | generateButton.append(document.createTextNode(originalGenerateButtonLabel)); |
| 200 | + | if (xhr.status === 200) { |
| 201 | + | // replace content of modal |
| 202 | + | modalBody.innerHTML = ""; |
| 203 | + | let image = new Image(); |
| 204 | + | image.classList.add("d-block", "mx-auto", "w-50"); |
| 205 | + | let qrCodeImageDataUrl = URL.createObjectURL(xhr.response); |
| 206 | + | image.src = qrCodeImageDataUrl; |
| 207 | + | modalBody.append(image); |
| 208 | + | let p = document.createElement("p"); |
| 209 | + | p.classList.add("text-secondary", "small", "w-100", "text-center", "m-0", "pt-1") |
| 210 | + | p.append(document.createTextNode("Scan QR code to authenticate account.")) |
| 211 | + | modalBody.append(p); |
| 212 | + | // replace generate button with "save" button |
| 213 | + | let modalFooter = generateButton.parentElement; |
| 214 | + | generateButton.remove(); |
| 215 | + | let saveLink = document.createElement("a"); |
| 216 | + | saveLink.classList.add("btn", "btn-secondary"); |
| 217 | + | saveLink.append(document.createTextNode("Save to file")) |
| 218 | + | saveLink.href = qrCodeImageDataUrl; |
| 219 | + | saveLink.setAttribute("download", "qrcode-" + jid + ".png"); |
| 220 | + | modalFooter.append(saveLink); |
| 221 | + | } else { |
| 222 | + | // show error |
| 223 | + | let errorSpan = document.createElement("span"); |
| 224 | + | errorSpan.append(document.createTextNode("An error occurred. Please try again later.")); |
| 225 | + | errorSpan.classList.add("text-danger", "error-message"); |
| 226 | + | modalBody.append(errorSpan); |
| 227 | + | } |
| 228 | + | } |
| 229 | + | xhr.onerror = handleResult; |
| 230 | + | xhr.onload = handleResult; |
| 231 | + | } |
| 232 | + | </script> |
| 233 | + | @endif |
140 | 234 | | </table> |
141 | 235 | | @template.pagination(uriInfo = uriInfo, page = users) |
142 | 236 | | </div> |
| skipped 2 lines |