Skip to content

Commit

Permalink
Merge pull request #12918 from minkyngkm/oem-store
Browse files Browse the repository at this point in the history
Build activate page to redeem activation key for OEM store
  • Loading branch information
minkyngkm authored Aug 4, 2023
2 parents b5bc9b2 + e3c8285 commit 793b04b
Show file tree
Hide file tree
Showing 6 changed files with 382 additions and 17 deletions.
3 changes: 2 additions & 1 deletion build.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ let entries = {
utmInheritance: "./static/js/src/utm-inheritance.js",
"kernel-form": "./static/js/src/kernel-form.js",
"random-partner-logos": "./static/js/src/random-partner-logos.js",
"credEnterprisePurchasing": "./static/js/src/advantage/credentials/app.tsx"
"credEnterprisePurchasing": "./static/js/src/advantage/credentials/app.tsx",
activate: "./static/js/src/activate.js"
};

const isDev = process && process.env && process.env.NODE_ENV === "development";
Expand Down
1 change: 1 addition & 0 deletions navigation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ support:
path: /pricing/pro
- title: Discourse
path: https://discourse.ubuntu.com/c/ubuntu-pro

pro:
title: Pro
path: /pro
Expand Down
254 changes: 254 additions & 0 deletions static/js/src/activate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
// validate messages for keys
const formControl = document.querySelector(".p-form__control");
const activation_key = document.querySelector(".activate-input");
const validate_message = document.querySelector(".p-form-validation__message");
const notification = document.querySelector("#notification");

function addIsError() {
const formControl = document.querySelector(".p-form__control");
if (validate_message.textContent.length > 0) {
if (!formControl.classList.contains("is-error")) {
formControl.classList.add("is-error");
}
} else {
formControl.classList.remove("is-error");
}
}

function validateKey(e) {
const firstLetter = "The activation key starts with a K.";
const length = "The activation key is 23 characters long.";
if (e.target.value.length === 0) {
validate_message.textContent = "";
} else if (e.target.value.charAt(0) !== "K") {
validate_message.textContent = firstLetter;
} else if (e.target.value.length !== 23) {
validate_message.textContent = length;
} else if (e.target.value.charAt(0) === "K" && e.target.value.length === 23) {
validate_message.textContent = "";
}
addIsError();
}
activation_key.addEventListener("keyup", validateKey);

// JS for modal
(function () {
var currentDialog = null;
var lastFocus = null;
var ignoreFocusChanges = false;
var focusAfterClose = null;

// Traps the focus within the currently open modal dialog
function trapFocus(event) {
if (ignoreFocusChanges) return;

if (currentDialog.contains(event.target)) {
lastFocus = event.target;
} else {
focusFirstDescendant(currentDialog);
if (lastFocus == document.activeElement) {
focusLastDescendant(currentDialog);
}
lastFocus = document.activeElement;
}
}

// Attempts to focus given element
function attemptFocus(child) {
if (child.focus) {
ignoreFocusChanges = true;
child.focus();
ignoreFocusChanges = false;
return document.activeElement === child;
}

return false;
}

// Focuses first child element
function focusFirstDescendant(element) {
for (var i = 0; i < element.childNodes.length; i++) {
var child = element.childNodes[i];
if (attemptFocus(child) || focusFirstDescendant(child)) {
return true;
}
}
return false;
}

// Focuses last child element
function focusLastDescendant(element) {
for (var i = element.childNodes.length - 1; i >= 0; i--) {
var child = element.childNodes[i];
if (attemptFocus(child) || focusLastDescendant(child)) {
return true;
}
}
return false;
}

/**
Toggles visibility of modal dialog.
@param {HTMLElement} modal Modal dialog to show or hide.
@param {HTMLElement} sourceEl Element that triggered toggling modal
@param {Boolean} open If defined as `true` modal will be opened, if `false` modal will be closed, undefined toggles current visibility.
*/
function toggleModal(modal, sourceEl, open) {
if (modal && modal.classList.contains("p-modal")) {
if (typeof open === "undefined") {
open = modal.style.display === "none";
}
if (open) {
currentDialog = modal;
modal.style.display = "flex";
focusFirstDescendant(modal);
focusAfterClose = sourceEl;
document.addEventListener("focus", trapFocus, true);
} else {
modal.style.display = "none";
if (focusAfterClose && focusAfterClose.focus) {
focusAfterClose.focus();
}
document.removeEventListener("focus", trapFocus, true);
currentDialog = null;
}
}
}

toggleModal(
document.querySelector("#modal"),
document.querySelector("[aria-controls=modal]"),
true
);
})();

// buying for input
const shouldCreateAccount = document.querySelector(
"#create-purchase-account-form"
);
if (shouldCreateAccount) {
const organisationContainer = document.querySelector(
".js-organisation-container"
);
const radioMyself = organisationContainer.querySelector(".radio-myself");
const radioOrganisation = organisationContainer.querySelector(
".radio-organisation"
);
const organisationNameContainer = organisationContainer.querySelector(
".js-organisation-name-container"
);
const organisationNameInput = organisationNameContainer.querySelector(
'input[name="activate-organisation-name"]'
);
radioOrganisation.addEventListener("change", function (e) {
if (e.target.checked) {
organisationNameContainer.style.display = "block";
organisationNameContainer.focus();
organisationNameInput.setAttribute("required", true);
}
});
radioMyself.addEventListener("change", function (e) {
if (e.target.checked) {
organisationNameContainer.style.display = "none";
organisationNameInput.removeAttribute("required");
}
});

const createPurchaseAccountForm = document.getElementById(
"create-purchase-account-form"
);

createPurchaseAccountForm.addEventListener("submit", function (event) {
event.preventDefault();
const selectedOption = document.querySelector(
'input[name="activate-buy-for"]:checked'
);
let accountName = document.querySelector('input[name="activate-name"]')
.value;
if (selectedOption.value == "organisation") {
accountName = document.querySelector(
'input[name="activate-organisation-name"]'
).value;
}

fetch("/account/purchase-account", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
marketplace: "canonical-ua",
account_name: accountName,
}),
})
.then(function (response) {
if (response.status != 200) {
throw new Error("Error: " + response.status);
}
})
.then(function (data) {
location.reload();
})
.catch(function (error) {
console.error(error);
});
});
}

//submit activate key
const activateKeyForm = document.getElementById("activate-key-form");

activateKeyForm.addEventListener("submit", function (event) {
event.preventDefault();
if (validate_message.textContent.length > 0) {
return;
} else {
const key = document.querySelector('input[name="key"]').value;
fetch("/pro/activate", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
key: key,
}),
})
.then((response) => {
if (response.ok) {
window.location.href = "/pro/dashboard";
} else {
return response.text().then((text) => {
throw new Error(text);
});
}
return response.json();
})
.catch(function (err) {
const meta = document.querySelector(".p-notification__meta");
const message = document.querySelector("#notification-message");
const errorMessage = JSON.parse(err?.message)?.errors;
notification.style.display = "block";
message.innerHTML = errorMessage;

if (errorMessage === "activation key already used") {
if (!meta) {
const div1 = document.createElement("div");
const div2 = document.createElement("div");
const a = document.createElement("a");

div1.classList.add("p-notification__meta");
div2.classList.add("p-notification__actions");
a.setAttribute("href", "/pro/dashboard");
a.textContent = "Go to dashboard";
div2.appendChild(a);
div1.appendChild(div2);
notification.appendChild(div1);
}
} else {
if (meta) {
meta.remove();
}
}
});
}
});
72 changes: 72 additions & 0 deletions templates/pro/activate.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{% extends "templates/base.html" %}

{% block title %}Activate Ubuntu Pro subscription{% endblock %}

{% block outer_content %}
{% block content %}
{% if needs_paid_account_created %}
<div class="p-modal" id="modal">
<section class="p-modal__dialog" role="dialog" aria-modal="true" aria-labelledby="modal-title" aria-describedby="modal-description">
<header class="p-modal__header">
<h2 class="p-modal__title" id="modal-title">Activating for a company or yourself?</h2>
</header>
<form id="create-purchase-account-form">
<div class="js-organisation-container u-sv3">
<div class="col-4">
<label class="p-radio">
<input class="p-radio__input radio-myself" type="radio" aria-labelledby="activate-myself-input" name="activate-buy-for" value="myself" required>
<span class="p-radio__label" id="activate-myself">Myself</span>
<input type="hidden" name="activate-name" value="{{ user_info.fullname }}">
</label>
</div>
<div class="col-4">
<label class="p-radio">
<input class="p-radio__input radio-organisation" type="radio" aria-labelledby="activate-organisation-input" name="activate-buy-for" value="organisation">
<span class="p-radio__label" id="activate-organisation">Organisation</span>
</label>
<div class="p-form__group js-organisation-name-container" style="display:none">
<label for="activate-organisation-name" class="p-form__label">Organisation name: </label>
<input class="u-no-margin--bottom" type="text" id="activate-organisation-name" name="activate-organisation-name" placeholder="Ex: Canonical" style="margin-top: .25rem;" aria-label="What?">
</div>
</div>
</div>
<button class="p-button--positive" type="submit" name="submit">Submit</button>
</form>
</section>
</div>
{% endif %}

<section class="p-strip">
<div class="u-fixed-width">
<h2>Activate your Ubuntu Pro subscription</h2>
<p>
You are currently signed in as {{ user_info.fullname }}. Make sure it is the account you will be using to manage your subscription. If not, <a href="https://login.ubuntu.com/+login">log in to a different account</a>.
</p>
<p>
Your product key should be in the confirmation email you received after buying your Ubuntu Pro Desktop subscription.
</p>
<p>The activation key is 23 characters long and starts with a K.</p>
<form id="activate-key-form">
<div class="p-form__group">
<label for="pro-activation-key" class="p-form__label">Enter your code</label>
<div class="p-form__control" style="min-width: 15rem">
<input type="text" id="pro-activation-key" class="activate-input" name="key" autocomplete="off" placeholder="Ex: Kabcedfghij12345678910-" required="" aria-describedby="pro-activate-input">
<p class="p-form-validation__message" id="pro-activatio-validation__message"></p>
</div>
</div>
<button class="p-button--positive" type="submit" name="submit">Activate</button>
</form>
</div>
<div class="u-fixed-width">
<div class="p-notification--negative" id="notification" style="display: none">
<div class="p-notification__content">
<h5 class="p-notification__title">Something went wrong</h5>
<p class="p-notification__message" id="notification-message"></p>
</div>
</div>
</div>
</section>
<script src="{{ versioned_static('js/dist/activate.js') }}" defer></script>

{% endblock %}
{% endblock %}
10 changes: 9 additions & 1 deletion webapp/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
delete_account_user_role,
get_account_offers,
get_account_users,
get_activate_view,
get_advantage_offers,
get_annotated_subscriptions,
get_contract_token,
Expand All @@ -59,13 +60,15 @@
post_advantage_subscriptions,
post_auto_renewal_settings,
post_offer,
pro_activate_activation_key,
pro_page_view,
put_account_user_role,
put_contract_entitlements,
)
from webapp.shop.cred.views import (
activate_activation_key,
cred_assessments,
cred_beta_activation,
cred_cancel_exam,
cred_exam,
cred_home,
Expand All @@ -77,7 +80,6 @@
cred_submit_form,
cred_syllabus_data,
cred_your_exams,
cred_beta_activation,
get_activation_keys,
get_filtered_webhook_responses,
get_issued_badges,
Expand Down Expand Up @@ -196,6 +198,12 @@
app.add_url_rule("/mirrors.json", view_func=mirrors_query)
app.add_url_rule("/marketo/submit", view_func=marketo_submit, methods=["POST"])
app.add_url_rule("/thank-you", view_func=thank_you)
app.add_url_rule("/pro/activate", view_func=get_activate_view)
app.add_url_rule(
"/pro/activate",
view_func=pro_activate_activation_key,
methods=["POST"],
)
app.add_url_rule("/pro/dashboard", view_func=advantage_view)
app.add_url_rule("/pro/user-subscriptions", view_func=get_user_subscriptions)
app.add_url_rule(
Expand Down
Loading

0 comments on commit 793b04b

Please sign in to comment.