mirror of
https://github.com/nextcloud/all-in-one.git
synced 2026-07-21 21:52:53 +00:00
feat(desec): run registration flow in a modal iframe
Move the deSEC "register a free domain" flow out of the inline containers page form and into a modal backed by a dedicated /desec view loaded in an iframe. The multi-step register -> verify -> domain process now re-renders inside the modal, so the user can adjust the details and complete email verification without reloading the whole page each step. Only once the domain is fully registered does the view reload the parent containers page. - add /desec route + desec.twig standalone view - add desec-modal.js (open/close, backdrop + Escape) and desec-done.js (parent reload on completion) - redirect register POST to the /desec view so steps stay in the modal - drop the redundant <details> wrapper in desec-register.twig, add heading - style the modal and let <button class="button"> pick up button styles - drive the modal iframe in the Playwright specs and update the QA checklist Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Simon L. <szaimen@e.mail.de>
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
|
||||
// Rendered into the deSEC modal view (desec.twig) once a deSEC domain has been fully
|
||||
// registered. The view lives inside an iframe opened by the containers page; the whole
|
||||
// process is now done, so reload the parent window to show the updated containers page.
|
||||
// When opened directly (not embedded), window.top === window, so this just reloads here.
|
||||
(function () {
|
||||
// Give the success message a brief moment so the user sees that it worked.
|
||||
setTimeout(function () {
|
||||
window.top.location.reload();
|
||||
}, 1500);
|
||||
})();
|
||||
@@ -0,0 +1,50 @@
|
||||
"use strict";
|
||||
|
||||
// Opens the deSEC registration flow (the /desec view) inside a modal iframe so the user can
|
||||
// run the multi-step register -> verify -> domain process without leaving the containers
|
||||
// page. The iframe re-navigates itself between steps; once a domain is registered, the
|
||||
// /desec view reloads the parent window via desec-done.js, so this script only has to deal
|
||||
// with opening and closing the modal.
|
||||
(function () {
|
||||
const modal = document.getElementById('desec-modal');
|
||||
const frame = document.getElementById('desec-frame');
|
||||
if (!modal || !frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
function openModal() {
|
||||
// Load (or reload) the flow each time the modal is opened so it always reflects the
|
||||
// current registration state on the server.
|
||||
frame.src = 'desec';
|
||||
modal.hidden = false;
|
||||
document.body.classList.add('modal-open');
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
modal.hidden = true;
|
||||
document.body.classList.remove('modal-open');
|
||||
// Drop the iframe content so credentials are not left rendered in the background.
|
||||
frame.src = 'about:blank';
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-desec-open]').forEach((el) => {
|
||||
el.addEventListener('click', openModal);
|
||||
});
|
||||
document.querySelectorAll('[data-desec-close]').forEach((el) => {
|
||||
el.addEventListener('click', closeModal);
|
||||
});
|
||||
|
||||
// Close when clicking the dimmed backdrop (but not when clicking inside the dialog).
|
||||
modal.addEventListener('click', (event) => {
|
||||
if (event.target === modal) {
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Close on Escape for keyboard users.
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Escape' && !modal.hidden) {
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -193,6 +193,25 @@ $app->get('/containers', function (Request $request, Response $response, array $
|
||||
])->withHeader('Cache-Control', 'no-store');
|
||||
})->setName('profile');
|
||||
|
||||
// Renders only the deSEC registration flow. The containers page opens this in a modal
|
||||
// iframe so the user can run the multi-step register -> verify -> domain process (adjusting
|
||||
// the inputs and re-submitting as needed) without reloading the whole page each time. Once a
|
||||
// deSEC domain is configured the view tells the parent window to reload (see desec-done.js).
|
||||
$app->get('/desec', function (Request $request, Response $response, array $args) use ($container) {
|
||||
$view = Twig::fromRequest($request);
|
||||
/** @var \AIO\Data\ConfigurationManager $configurationManager */
|
||||
$configurationManager = $container->get(\AIO\Data\ConfigurationManager::class);
|
||||
return $view->render($response, 'desec.twig', [
|
||||
'domain' => $configurationManager->domain,
|
||||
'desec_email' => $configurationManager->desecEmail,
|
||||
'desec_password' => $configurationManager->desecPassword,
|
||||
'is_desec_domain' => $configurationManager->isDesecDomain(),
|
||||
'desec_account_registered' => $configurationManager->isDesecAccountRegistered(),
|
||||
'desec_awaiting_verification' => $configurationManager->isDesecAwaitingVerification(),
|
||||
// Do not cache the page as it shows credentials
|
||||
])->withHeader('Cache-Control', 'no-store');
|
||||
})->setName('desec');
|
||||
|
||||
$app->get('/login', function (Request $request, Response $response, array $args) use ($container) {
|
||||
$view = Twig::fromRequest($request);
|
||||
/** @var \AIO\Docker\DockerActionManager $dockerActionManager */
|
||||
|
||||
@@ -84,6 +84,7 @@ a:hover {
|
||||
}
|
||||
|
||||
a.button,
|
||||
button.button,
|
||||
input[type="submit"] {
|
||||
padding: 8px 16px;
|
||||
width: auto;
|
||||
@@ -100,11 +101,13 @@ input[type="submit"] {
|
||||
}
|
||||
|
||||
a.button:focus,
|
||||
button.button:focus,
|
||||
input[type="submit"]:focus {
|
||||
outline: 2px solid var(--color-main-border);
|
||||
}
|
||||
|
||||
a.button:hover,
|
||||
button.button:hover,
|
||||
input[type="submit"]:hover {
|
||||
background-color: var(--color-primary-element-hover);
|
||||
}
|
||||
@@ -490,6 +493,63 @@ input[type="checkbox"]:disabled:not(:checked) + label {
|
||||
background-color: var(--color-main-background);
|
||||
}
|
||||
|
||||
/* deSEC registration modal (containers page) */
|
||||
.modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 3;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.modal[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modal .modal-content {
|
||||
position: relative;
|
||||
width: min(640px, 100%);
|
||||
max-height: calc(100vh - 4rem);
|
||||
background-color: var(--color-main-background);
|
||||
border-radius: var(--border-radius-large);
|
||||
border: solid thin rgb(192, 192, 192);
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal .modal-content iframe {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: min(640px, calc(100vh - 4rem));
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.modal .modal-close {
|
||||
position: absolute;
|
||||
top: 0.25rem;
|
||||
right: 0.5rem;
|
||||
z-index: 1;
|
||||
background: transparent;
|
||||
border: none;
|
||||
font-size: 1.8rem;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
body.modal-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* deSEC modal view body (desec.twig) */
|
||||
.desec-modal-body,
|
||||
.desec-modal-done {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.overlay-iframe {
|
||||
padding: 1rem;
|
||||
font-family: monospace, system-ui, -apple-system, 'Segoe UI', Roboto, Oxygen-Sans, Cantarell, Ubuntu, 'Helvetica Neue', 'Noto Sans', 'Liberation Sans', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
|
||||
Reference in New Issue
Block a user