diff --git a/internal/cloudcp/portal/assets/portal_services.js b/internal/cloudcp/portal/assets/portal_services.js
index 3e909b7763..5a1eeca81c 100644
--- a/internal/cloudcp/portal/assets/portal_services.js
+++ b/internal/cloudcp/portal/assets/portal_services.js
@@ -3,8 +3,8 @@
var serviceState = {
openPanelID: '',
flows: {
- manage: newVerificationFlowState(),
- retrieve: newVerificationFlowState(),
+ manage: newVerificationFlowState(),
+ retrieve: newVerificationFlowState(),
export: newVerificationFlowState(),
delete: newVerificationFlowState(),
},
@@ -22,6 +22,8 @@
step2Visible: false,
status: emptyStatus(),
result: null,
+ emailValue: '',
+ codeValue: '',
};
}
@@ -41,6 +43,19 @@
return document.getElementById(id);
}
+ function escapeText(value) {
+ return String(value || '')
+ .replace(/&/g, '&')
+ .replace(//g, '>');
+ }
+
+ function escapeAttribute(value) {
+ return escapeText(value)
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ }
+
function readValue(id) {
var el = getElement(id);
return el ? el.value.trim() : '';
@@ -137,6 +152,9 @@
var flow = verificationFlows[flowID];
if (!flow) return;
var flowState = serviceState.flows[flowID];
+ if (flow.renderPanel) {
+ flow.renderPanel(flowState);
+ }
renderButton(flow.requestButtonID, flowState.requesting, flowState.requesting ? flow.requestPendingLabel : flow.requestLabel);
renderButton(flow.confirmButtonID, flowState.confirming, flowState.confirming ? flow.confirmPendingLabel : flow.confirmLabel);
renderStatus(flow.statusID, flowState.status);
@@ -164,7 +182,9 @@
function resetVerificationFlow(flowID) {
var flow = verificationFlows[flowID];
if (!flow) return;
+ var previous = serviceState.flows[flowID];
serviceState.flows[flowID] = newVerificationFlowState();
+ serviceState.flows[flowID].emailValue = previous.emailValue;
if (flow.codeInputID) {
setValue(flow.codeInputID, '');
}
@@ -212,51 +232,66 @@
resendSuccessMessage: 'New verification code sent.',
requestErrorMessage: 'Failed to send verification code',
confirmErrorMessage: 'Failed to retrieve license',
+ readEmailValue: function() {
+ return serviceState.flows.retrieve.emailValue;
+ },
+ readCodeValue: function() {
+ return serviceState.flows.retrieve.codeValue;
+ },
onRequestStart: function() {
serviceState.flows.retrieve.result = null;
},
onConfirmSuccess: function(data) {
serviceState.flows.retrieve.result = data.license;
+ serviceState.flows.retrieve.codeValue = '';
setFlowStatus('retrieve', 'License retrieved successfully.', false);
},
+ renderPanel: function(flowState) {
+ var root = getElement('retrieve-service-root');
+ if (!root) return;
+ var result = flowState.result;
+ var invoiceURL = result && result.invoice_url ? result.invoice_url : '#';
+ var invoiceDisplay = result && result.invoice_url ? 'inline-block' : 'none';
+ var copyDisplay = result ? 'inline-block' : 'none';
+ var resultDisplay = result ? 'block' : 'none';
+ root.innerHTML = '' +
+ '
Retrieve licenses
' +
+ 'Request a verification code for the commercial email, then reveal the current active self-hosted license without leaving Pulse Account.
' +
+ '' +
+ '
' +
+ '' +
+ '' +
+ '
' +
+ '
' +
+ '' +
+ '
' +
+ '
' +
+ '' +
+ '
' +
+ '' +
+ '' +
+ '
' +
+ '
' +
+ '
Use the latest active self-hosted license for this commercial email.
' +
+ '
' +
+ '' +
+ '' +
+ '
' +
+ '
' +
+ '
' +
+ '
Plan
' + escapeText(result ? result.tier : '') + '
' +
+ '
Issued
' + escapeText(result ? new Date(result.issued_at).toLocaleString() : '') + '
' +
+ '
Expires
' + escapeText(result ? (result.expires_at ? new Date(result.expires_at).toLocaleString() : 'Does not expire') : '') + '
' +
+ '
Purchase Email
' + escapeText(result ? result.email : '') + '
' +
+ '
' +
+ '
';
+ },
renderResult: function(result) {
- setVisible('retrieve-inline-result', !!result);
- if (!result) {
- setValue('retrieve-inline-token', '');
- setText('retrieve-inline-tier', '');
- setText('retrieve-inline-issued', '');
- setText('retrieve-inline-expires', '');
- setText('retrieve-inline-email-value', '');
- var emptyInvoice = getElement('retrieve-inline-invoice');
- if (emptyInvoice) {
- emptyInvoice.href = '#';
- emptyInvoice.style.display = 'none';
- }
- var emptyCopy = getElement('retrieve-inline-copy');
- if (emptyCopy) {
- emptyCopy.style.display = 'none';
- }
- return;
- }
- setValue('retrieve-inline-token', result.token);
- setText('retrieve-inline-tier', result.tier);
- setText('retrieve-inline-issued', new Date(result.issued_at).toLocaleString());
- setText('retrieve-inline-expires', result.expires_at ? new Date(result.expires_at).toLocaleString() : 'Does not expire');
- setText('retrieve-inline-email-value', result.email);
- var invoice = getElement('retrieve-inline-invoice');
- if (invoice) {
- if (result.invoice_url) {
- invoice.href = result.invoice_url;
- invoice.style.display = 'inline-block';
- } else {
- invoice.href = '#';
- invoice.style.display = 'none';
- }
- }
- var copy = getElement('retrieve-inline-copy');
- if (copy) {
- copy.style.display = 'inline-block';
- }
+ void result;
}
},
export: {
@@ -328,7 +363,7 @@
async function requestVerificationCode(flowID) {
var flow = verificationFlows[flowID];
if (!flow) return;
- var email = readValue(flow.emailInputID);
+ var email = flow.readEmailValue ? flow.readEmailValue() : readValue(flow.emailInputID);
if (!email) {
focusElement(flow.emailInputID);
return;
@@ -373,7 +408,7 @@
var flow = verificationFlows[flowID];
if (!flow) return;
var email = serviceState.flows[flowID].pendingEmail;
- var code = readValue(flow.codeInputID);
+ var code = flow.readCodeValue ? flow.readCodeValue() : readValue(flow.codeInputID);
if (!email || !code) return;
if (flow.beforeConfirm && flow.beforeConfirm() === false) {
return;
@@ -394,7 +429,7 @@
}
async function copyRetrievedLicense() {
- var token = getElement('retrieve-inline-token').value;
+ var token = serviceState.flows.retrieve.result ? serviceState.flows.retrieve.result.token : '';
if (!token) return;
try {
await navigator.clipboard.writeText(token);
@@ -428,6 +463,7 @@
}
renderOpenPanels();
+ serviceState.flows.retrieve.emailValue = (runtime.getBootstrap ? ((runtime.getBootstrap() || {}).email || '') : '');
renderAllFlows();
document.addEventListener('click', function(event) {
@@ -496,4 +532,20 @@
return;
}
});
+
+ document.addEventListener('input', function(event) {
+ var target = event.target;
+ if (!target) return;
+ var inputKind = target.getAttribute('data-account-service-input') || '';
+ switch (inputKind) {
+ case 'retrieve-email':
+ serviceState.flows.retrieve.emailValue = target.value;
+ return;
+ case 'retrieve-code':
+ serviceState.flows.retrieve.codeValue = target.value;
+ return;
+ default:
+ return;
+ }
+ });
})();
diff --git a/internal/cloudcp/portal/handlers_test.go b/internal/cloudcp/portal/handlers_test.go
index 6df28ea9f8..ebde40e675 100644
--- a/internal/cloudcp/portal/handlers_test.go
+++ b/internal/cloudcp/portal/handlers_test.go
@@ -806,12 +806,16 @@ func TestPortalPageTemplate_AccountServicesRendered(t *testing.T) {
`data-account-service-action="data-delete-confirm"`,
`id="manage-service-panel"`,
`id="retrieve-service-panel"`,
+ `id="retrieve-service-root"`,
`id="refund-service-panel"`,
`id="data-service-panel"`,
`var verificationFlows = {`,
`var serviceState = {`,
`renderOpenPanels();`,
`renderAllFlows();`,
+ `data-account-service-input="retrieve-email"`,
+ `data-account-service-input="retrieve-code"`,
+ `renderPanel: function(flowState) {`,
`requestVerificationCode('manage')`,
`confirmVerificationCode('retrieve')`,
`resendVerificationCode('export', event)`,
diff --git a/internal/cloudcp/portal/templates/portal.html b/internal/cloudcp/portal/templates/portal.html
index af32a44ddb..f07fc56285 100644
--- a/internal/cloudcp/portal/templates/portal.html
+++ b/internal/cloudcp/portal/templates/portal.html
@@ -166,49 +166,51 @@
-
Retrieve licenses
-
Request a verification code for the commercial email, then reveal the current active self-hosted license without leaving Pulse Account.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Use the latest active self-hosted license for this commercial email.
-
-
-
-
-
-
-
-
Plan
-
+
+
Retrieve licenses
+
Request a verification code for the commercial email, then reveal the current active self-hosted license without leaving Pulse Account.
+
+
+
+
-
-
Issued
-
+
+
-
+
+
+
+
-
-
Purchase Email
-
+
+
Use the latest active self-hosted license for this commercial email.
+
+
+