Browse Source

Merge pull request 'Add Account section' (#3) from account into master

pull/4/head
L. Bradley LaBoon 2 years ago
parent
commit
f0da123f82
  1. 15
      README.md
  2. 91
      account/account.css
  3. 387
      account/account.js
  4. 30
      account/backups_enable_all/backups_enable_all.css
  5. 179
      account/backups_enable_all/backups_enable_all.js
  6. 39
      account/backups_enable_all/index.shtml
  7. 44
      account/billing_history/billing_history.css
  8. 210
      account/billing_history/billing_history.js
  9. 52
      account/billing_history/index.shtml
  10. 34
      account/cancel/cancel.css
  11. 74
      account/cancel/cancel.js
  12. 62
      account/cancel/index.shtml
  13. 27
      account/contact/contact.css
  14. 143
      account/contact/contact.js
  15. 1013
      account/contact/countries.js
  16. 107
      account/contact/index.shtml
  17. 27
      account/creditcard/creditcard.css
  18. 105
      account/creditcard/creditcard.js
  19. 91
      account/creditcard/index.shtml
  20. 165
      account/index.shtml
  21. 68
      account/invoice/index.shtml
  22. 59
      account/invoice/invoice.css
  23. 151
      account/invoice/invoice.js
  24. 96
      account/make_a_payment/index.shtml
  25. 43
      account/make_a_payment/make_a_payment.css
  26. 125
      account/make_a_payment/make_a_payment.js
  27. 54
      account/paymentreceipt/index.shtml
  28. 36
      account/paymentreceipt/paymentreceipt.css
  29. 87
      account/paymentreceipt/paymentreceipt.js
  30. 55
      account/paymentreceiptyear/index.shtml
  31. 40
      account/paymentreceiptyear/paymentreceiptyear.css
  32. 146
      account/paymentreceiptyear/paymentreceiptyear.js
  33. 83
      account/settings/index.shtml
  34. 35
      account/settings/settings.css
  35. 100
      account/settings/settings.js
  36. 4
      dns/domain_add/domain_add.css
  37. 4
      dns/domain_clone/domain_clone.css
  38. 4
      dns/domain_import/domain_import.css
  39. 4
      dns/domain_soa/domain_soa.css
  40. 4
      dns/resource/resource.css
  41. 9
      global.css
  42. 19
      global.js
  43. 4
      images/create/create.css
  44. 4
      images/edit/edit.css
  45. 11
      include/account_subnav.html
  46. 4
      linodes/backup_details/backup_details.css
  47. 4
      linodes/backups/backups.css
  48. 4
      linodes/clone/clone.css
  49. 4
      linodes/config/config.css
  50. 12
      linodes/dashboard/dashboard.css
  51. 4
      linodes/deploy/deploy.css
  52. 4
      linodes/disk/disk.css
  53. 4
      linodes/ip_failover/ip_failover.css
  54. 4
      linodes/ip_remove/ip_remove.css
  55. 4
      linodes/ip_swap/ip_swap.css
  56. 4
      linodes/mutate/mutate.css
  57. 4
      linodes/rdns/rdns.css
  58. 4
      linodes/rebuild/rebuild.css
  59. 4
      linodes/remote_access/remote_access.css
  60. 4
      linodes/rescue/rescue.css
  61. 4
      linodes/resize/resize.css
  62. 4
      linodes/settings/settings.css
  63. 5
      login.js
  64. 27
      user/add/add.css
  65. 80
      user/add/add.js
  66. 66
      user/add/index.shtml
  67. 27
      user/edit/edit.css
  68. 118
      user/edit/edit.js
  69. 67
      user/edit/index.shtml
  70. 56
      user/grants/grants.css
  71. 271
      user/grants/grants.js
  72. 132
      user/grants/index.shtml
  73. 53
      user/index.shtml
  74. 36
      user/remove/index.shtml
  75. 22
      user/remove/remove.css
  76. 84
      user/remove/remove.js
  77. 32
      user/user.css
  78. 148
      user/user.js
  79. 4
      volumes/add/add.css
  80. 4
      volumes/attach/attach.css
  81. 4
      volumes/clone/clone.css

15
README.md

@ -10,17 +10,18 @@ The canonical deployment of this app can be found at https://lmc.laboon.io, howe
## Development Progress
This project is currently a work in progress. The following list provides a high-level overview which features have been implemented and which features are planned for the future:
- [x] ~~OAuth Authentication/Login/Logout~~
- [x] ~~Gravatar~~
- [x] ~~Linodes (including the dashboard and all related subpages, but not including graphs)~~
- [x] ~~Block Storage (Volumes)~~
- [x] ~~Images~~
- [x] ~~DNS~~
- [x] OAuth Authentication/Login/Logout
- [x] Gravatar
- [x] Linodes (including the dashboard and all related subpages, but not including graphs)
- [x] Block Storage (Volumes)
- [x] Images
- [x] DNS
- [x] Account Details (exluding PayPal support)
- [ ] PayPal payments
- [ ] Graphs
- [ ] StackScripts
- [ ] NodeBalancers
- [ ] Longview
- [ ] Account Details
- [ ] User Profile Settings
- [ ] Support Tickets

91
account/account.css

@ -0,0 +1,91 @@
/*
* This file is part of Linode Manager Classic.
*
* Linode Manager Classic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Linode Manager Classic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
*/
@import url('/global.css');
#account {
padding: 15px 15px 15px;
}
#balance {
font-weight: bold;
}
.balance-negative {
color: #F00;
}
.balance-positive {
color: #008000;
}
#billing-activity tr td:first-of-type {
font-weight: normal;
text-align: left;
}
#billing-activity tr td:last-of-type {
text-align: right;
}
#billing-link {
text-align: right;
}
#billing-row {
border: none;
}
#cancel {
font-size: 12px;
}
.gdpr {
display: none;
}
#learn-more {
font-size: 11px;
}
#managed {
display: none;
margin-top: 50px;
}
p {
margin-top: 50px;
text-align: center;
}
#pay {
display: none;
}
.promotions {
display: none;
}
tbody:not(.lmc-tbody-head) tr td:first-of-type {
font-weight: bold;
text-align: right;
white-space: nowrap;
}
#uninvoiced {
display: none;
}

387
account/account.js

@ -0,0 +1,387 @@
/*
* This file is part of Linode Manager Classic.
*
* Linode Manager Classic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Linode Manager Classic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
*/
import { settings, elements, apiGet, apiPost, parseParams, setupHeader, timeString } from "/global.js";
(function()
{
// Element names specific to this page
elements.active = "active";
elements.address = "address";
elements.balance = "balance";
elements.balanceNegative = "balance-negative";
elements.balancePositive = "balance-positive";
elements.balanceStatus = "balance-status";
elements.billingActivity = "billing-activity";
elements.ccNumber = "cc-number";
elements.ccDuration = "cc-duration";
elements.ccExpire = "cc-expire";
elements.current = "current";
elements.email = "email";
elements.gdpr = "gdpr";
elements.gdprDate = "gdpr-date";
elements.info = "info";
elements.lmcRow = "lmc-tr1";
elements.lmcRowAlt = "lmc-tr2";
elements.lmcRowStandard = "lmc-tr3";
elements.managed = "managed";
elements.managedButton = "managed-button";
elements.pay = "pay";
elements.promotions = "promotions";
elements.promotionsTable = "promotions-table";
elements.uninvoiced = "uninvoiced";
elements.uninvoicedBalance = "uninvoiced-balance";
// Data received from API calls
var data = {};
data.account = {};
data.invoices = [];
data.linodes = [];
data.payments = [];
// Static references to UI elements
var ui = {};
ui.active = {};
ui.address = {};
ui.balance = {};
ui.balanceStatus = {};
ui.billingActivity = {};
ui.ccNumber = {};
ui.ccDuration = {};
ui.ccExpire = {};
ui.current = {};
ui.email = {};
ui.gdpr = [];
ui.gdprDate = {};
ui.managed = {};
ui.managedButton = {};
ui.pay = {};
ui.promotions = [];
ui.promotionsTable = {};
ui.uninvoiced = {};
ui.uninvoicedBalance = {};
// Temporary state
var state = {};
state.haveInvoices = false;
state.havePayments = false;
// Creates a row for the promotion table
var createPromoRow = function(promo)
{
var row = document.createElement("tr");
row.className = elements.lmcRowStandard;
var name = document.createElement("td");
name.innerHTML = promo.summary;
row.appendChild(name);
var details = document.createElement("td");
var line1 = document.createElement("span");
var expire = new Date(promo.expire_dt + "Z");
line1.innerHTML = "$" + promo.credit_remaining + " remaining. Exp: " + expire.toLocaleDateString();
var br = document.createElement("br");
var line2 = document.createElement("span");
line2.innerHTML = "($" + promo.this_month_credit_remaining + " remaining this month. Monthly cap: $" + promo.credit_monthly_cap + ")";
details.appendChild(line1);
details.appendChild(br);
details.appendChild(line2);
row.appendChild(details);
var description = document.createElement("td");
description.className = elements.info;
description.innerHTML = promo.description;
row.appendChild(description);
return row;
};
// Creates a row for the recent activity table
var createRecentRow = function(recent, alt)
{
var row = document.createElement("tr");
if (alt)
row.className = elements.lmcRowAlt;
else
row.className = elements.lmcRow;
var dateCell = document.createElement("td");
var date = document.createElement("span");
var recentDate = new Date(recent.date + "Z");
var now = new Date();
date.innerHTML = recentDate.toLocaleDateString();
var br = document.createElement("br");
var ago = document.createElement("span");
ago.className = elements.info;
ago.innerHTML = timeString(now - recentDate, true);
dateCell.appendChild(date);
dateCell.appendChild(br);
dateCell.appendChild(ago);
row.appendChild(dateCell);
if (recent.label) {
// Invoice stuff
var linkCell = document.createElement("td");
var link = document.createElement("a");
link.href = "/account/invoice?iid=" + recent.id;
link.innerHTML = recent.label;
linkCell.appendChild(link);
row.appendChild(linkCell);
var total = document.createElement("td");
if (recent.total < 0)
total.innerHTML = "($" + (-recent.total).toFixed(2) + ")";
else
total.innerHTML = "$" + recent.total.toFixed(2);
row.appendChild(total);
} else {
// Payment stuff
var thanks = document.createElement("td");
thanks.innerHTML = "Payment. Thank you";
row.appendChild(thanks);
var total = document.createElement("td");
if (total > 0)
total.innerHTML = "($" + recent.total.toFixed(2) + ")";
else
total.innerHTML = "$" + (-recent.total).toFixed(2);
row.appendChild(total);
}
return row;
};
// Callback for account details API call
var displayAccount = function(response)
{
data.account = response;
// Contact info
var br = document.createElement("br");
if (data.account.company.length) {
var company = document.createElement("span");
company.innerHTML = data.account.company;
ui.address.appendChild(company);
ui.address.appendChild(br);
}
var name = document.createElement("span");
name.innerHTML = data.account.first_name + " " + data.account.last_name;
ui.address.appendChild(name);
ui.address.appendChild(br.cloneNode(true));
var address1 = document.createElement("span");
address1.innerHTML = data.account.address_1;
ui.address.appendChild(address1);
ui.address.appendChild(br.cloneNode(true));
if (data.account.address_2.length) {
var address2 = document.createElement("span");
address2.innerHTML = data.account.address_2;
ui.address.appendChild(address2);
ui.address.appendChild(br.cloneNode(true));
}
var address3 = document.createElement("span");
address3.innerHTML = data.account.city + ", " + data.account.state + " " + data.account.zip;
ui.address.appendChild(address3);
// Email
ui.email.innerHTML = data.account.email;
// CC info
ui.ccNumber.innerHTML = data.account.credit_card.last_four;
var expired = document.createElement("span");
var strong = document.createElement("strong");
strong.innerHTML = "Expired!";
var dash = document.createElement("span");
dash.innerHTML = " - ";
var updateLink = document.createElement("a");
updateLink.href = "/account/creditcard";
updateLink.innerHTML = "update credit card";
expired.appendChild(strong);
expired.appendChild(dash);
expired.appendChild(updateLink);
if (data.account.credit_card.expiry) {
ui.ccExpire.innerHTML = data.account.credit_card.expiry;
var monthYear = data.account.credit_card.expiry.split("/");
var expireDate = new Date(parseInt(monthYear[1]), parseInt(monthYear[0]), 0);
var now = new Date();
if (expireDate - now > 0)
ui.ccDuration.innerHTML = "Expires " + timeString(now - expireDate, true);
else
ui.ccDuration.appendChild(expired);
}
// Account balance
if (data.account.balance == 0) {
ui.balance.className = elements.balancePositive;
ui.current.innerHTML = "Your account is current.";
} else if (data.account.balance < 0) {
ui.balance.className = elements.balancePositive;
ui.balanceStatus.innerHTML = " credit";
ui.current.innerHTML = "This will be applied towards future invoices.";
data.account.balance = -data.account.balance;
} else {
ui.balance.className = elements.balanceNegative;
ui.balanceStatus.innerHTML = " due ";
ui.pay.href += "?amount=" + (-data.account.balance);
ui.pay.style.display = "initial";
ui.current.innerHTML = "Please pay now to avoid any service interruptions.";
}
ui.balance.innerHTML = "$" + data.account.balance.toFixed(2);
ui.uninvoicedBalance.innerHTML = "$" + data.account.balance_uninvoiced.toFixed(2);
if (data.account.balance_uninvoiced > 0)
ui.uninvoiced.style.display = "table-row";
// Promotions
if (data.account.active_promotions.length) {
for (var i = 0; i < ui.promotions.length; i++)
ui.promotions[i].style.display = "table-row-group";
}
for (var i = 0; i < data.account.active_promotions.length; i++)
ui.promotionsTable.appendChild(createPromoRow(data.account.active_promotions[i]));
var active = new Date(data.account.active_since + "Z");
ui.active.innerHTML = active.toLocaleDateString();
};
// Callback for invoices API call
var displayInvoices = function(response)
{
data.invoices = data.invoices.concat(response.data);
// Request the next page if there are more
if (response.page != response.pages) {
apiGet("/account/invoices?page=" + (response.page + 1), displayInvoices, null);
return;
}
state.haveInvoices = true;
if (state.havePayments)
displayRecent();
};
// Callback for linodes API call
var displayLinodes = function(response)
{
data.linodes = data.linodes.concat(response.data);
// Request the next page if there are more
if (response.page != response.pages) {
apiGet("/linode/instances?page=" + (response.page + 1), displayLinodes, null);
return;
}
ui.managedButton.disabled = false;
};
// Callback for payments API call
var displayPayments = function(response)
{
data.payments = data.payments.concat(response.data);
// Request the next page if there are more
if (response.page != response.pages) {
apiGet("/account/payments?page=" + (response.page + 1), displayPayments, null);
return;
}
state.havePayments = true;
if (state.haveInvoices)
displayRecent();
};
// Populates table with recent invoices and payments
var displayRecent = function()
{
// Combine to single array and sort by date
var recent = data.invoices.concat(data.payments);
recent.sort(function(a, b)
{
var aDate = new Date(a.date + "Z");
var bDate = new Date(b.date + "Z");
return aDate - bDate;
});
// Insert items into table
for (var i = recent.length - 1, count = 0; i >= 0, count < 4; i--, count++)
ui.billingActivity.appendChild(createRecentRow(recent[i], ui.billingActivity.children.length % 2));
};
// Callback for account settings API call
var displaySettings = function(response)
{
// Show managed signup if not managed
if (!response.managed)
ui.managed.style.display = "table";
};
// Click handler for managed button
var handleManaged = function(event)
{
if (event.currentTarget.disabled)
return;
if (!confirm("Linode Managed costs an additional $100/mo per Linode. This will increase your projected monthly bill by $" + (data.linodes.length * 100) + ". Are you sure?"))
return;
apiPost("/account/settings/managed-enable", {}, function(response)
{
location.reload();
});
};
// Initial setup
var setup = function()
{
// Parse URL parameters
data.params = parseParams();
setupHeader();
// Get element references
ui.active = document.getElementById(elements.active);
ui.address = document.getElementById(elements.address);
ui.balance = document.getElementById(elements.balance);
ui.balanceStatus = document.getElementById(elements.balanceStatus);
ui.billingActivity = document.getElementById(elements.billingActivity);
ui.ccNumber = document.getElementById(elements.ccNumber);
ui.ccDuration = document.getElementById(elements.ccDuration);
ui.ccExpire = document.getElementById(elements.ccExpire);
ui.current = document.getElementById(elements.current);
ui.email = document.getElementById(elements.email);
ui.gdpr = document.getElementsByClassName(elements.gdpr);
ui.gdprDate = document.getElementById(elements.gdprDate);
ui.managed = document.getElementById(elements.managed);
ui.managedButton = document.getElementById(elements.managedButton);
ui.pay = document.getElementById(elements.pay);
ui.promotions = document.getElementsByClassName(elements.promotions);
ui.promotionsTable = document.getElementById(elements.promotionsTable);
ui.uninvoiced = document.getElementById(elements.uninvoiced);
ui.uninvoicedBalance = document.getElementById(elements.uninvoicedBalance);
// Register event handlers
ui.managedButton.addEventListener("click", handleManaged);
// Get data from API
apiGet("/account", displayAccount, null);
apiGet("/account/settings", displaySettings, null);
apiGet("/account/invoices", displayInvoices, null);
apiGet("/account/payments", displayPayments, null);
apiGet("/linode/instances", displayLinodes, null);
};
// Attach onload handler
window.addEventListener("load", setup);
})();

30
account/backups_enable_all/backups_enable_all.css

@ -0,0 +1,30 @@
/*
* This file is part of Linode Manager Classic.
*
* Linode Manager Classic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Linode Manager Classic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
*/
@import url('/global.css');
#backups-enable-all {
padding: 15px 15px 15px;
}
h2 {
font-size: 18px;
}
.info {
font-size: 12px;
}

179
account/backups_enable_all/backups_enable_all.js

@ -0,0 +1,179 @@
/*
* This file is part of Linode Manager Classic.
*
* Linode Manager Classic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Linode Manager Classic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
*/
import { settings, elements, apiGet, apiPost, parseParams, setupHeader } from "/global.js";
(function()
{
// Element names specific to this page
elements.cost = "cost";
elements.doIt = "doit";
elements.linodeCount = "linode-count";
elements.subnav = "subnav-link";
elements.subnavActive = "subnav-link-active";
// Data received from API calls
var data = {};
data.linodes = [];
data.plans = {};
// Static references to UI elements
var ui = {};
ui.cost = {};
ui.dotIt = {};
ui.linodeCount = [];
// Temporary state
var state = {};
state.haveLinodes = false;
state.havePlans = false;
state.numResponses = 0;
state.numRequests = 0;
// Computes and displays the total monthly cost
var displayCost = function()
{
var cost = 0;
var haveAll = true;
for (var i = 0; i < data.linodes.length; i++) {
if (data.linodes[i].backups.enabled)
continue;
if (!data.plans[data.linodes[i].type]) {
haveAll = false;
apiGet("/linode/types/" + data.linodes[i].type, function(response)
{
data.plans[response.id] = response;
displayCost();
}, null);
continue;
}
cost += data.plans[data.linodes[i].type].addons.backups.price.monthly;
}
if (haveAll) {
ui.cost.innerHTML = cost.toFixed(2);
ui.doIt.disabled = false;
}
};
// Callback for linodes API call
var displayLinodes = function(response)
{
data.linodes = data.linodes.concat(response.data);
// Request the next page if there are more
if (response.page != response.pages) {
apiGet("/linode/instances?page=" + (response.page + 1), displayLinodes, null);
return;
}
state.haveLinodes = true;
// Display count of linodes without backups
var count = 0;
for (var i = 0; i < data.linodes.length; i++) {
if (!data.linodes[i].backups.enabled)
count++;
}
// If there are no linodes without backups, go back
if (!count)
location.href = "/account/settings";
for (var i = 0; i < ui.linodeCount.length; i++)
ui.linodeCount[i].innerHTML = count;
if (state.havePlans)
displayCost();
};
// Callback for plans API call
var displayPlans = function(response)
{
for (var i = 0; i < response.data.length; i++)
data.plans[response.data[i].id] = response.data[i];
// Request the next page if there are more
if (response.page != response.pages) {
apiGet("/linode/types?page=" + (response.page + 1), displayPlans, null);
return;
}
state.havePlans = true;
if (state.haveLinode)
displayCost();
};
// Click handler for enable button
var doIt = function(event)
{
if (event.currentTarget.disabled)
return;
// Enable backups for each linode without backups
for (var i = 0; i < data.linodes.length; i++) {
if (data.linodes[i].backups.enabled)
continue;
state.numRequests++;
apiPost("/linode/instances/" + data.linodes[i].id + "/backups/enable", {}, function(response)
{
state.numResponses++;
if (state.numResponses >= state.numRequests)
location.href = "/account/settings";
});
}
ui.doIt.disabled = true;
};
// Initial setup
var setup = function()
{
// Parse URL parameters
data.params = parseParams();
setupHeader();
// Highlight the account settings subnav link
var subnavLinks = document.getElementsByClassName(elements.subnav);
for (var i = 0; i < subnavLinks.length; i++) {
if (subnavLinks[i].pathname == "/account/settings")
subnavLinks[i].className = elements.subnav + " " + elements.subnavActive;
else
subnavLinks[i].className = elements.subnav;
}
// Get element references
ui.cost = document.getElementById(elements.cost);
ui.doIt = document.getElementById(elements.doIt);
ui.linodeCount = document.getElementsByClassName(elements.linodeCount);
// Attach event handlers
ui.doIt.addEventListener("click", doIt);
// Get data from the API
apiGet("/linode/instances", displayLinodes, null);
apiGet("/linode/types", displayPlans, null);
};
// Attach onload handler
window.addEventListener("load", setup);
})();

39
account/backups_enable_all/index.shtml

@ -0,0 +1,39 @@
<!--
This file is part of Linode Manager Classic.
Linode Manager Classic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Linode Manager Classic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
-->
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>LMC - Account // Enable Backups for All Linodes</title>
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" type="text/css" href="backups_enable_all.css" />
<script src="backups_enable_all.js" type="module"></script>
</head>
<body>
<!--#include virtual="/include/header.html"-->
<!--#include virtual="/include/account_subnav.html"-->
<div id="main-content" class="wrapper">
<div id="backups-enable-all">
<h2>Backup Enrollment</h2>
<p>This will enable the Linode Backup Service for <strong><span class="linode-count"></span></strong> Linodes, for a total additional cost of <strong>$<span id="cost"></span></strong>/month.</p>
<p>Are you sure you want to do this?</p>
<p class="info">The cost of the Linode Backup Service for each Linode plan is listed on the <a href="https://www.linode.com/products/backups" target="_blank">Backups info page</a>.</p>
<button disabled id="doit" type="button">Yes, I want to enable Backups for <span class="linode-count"></span> Linodes</button>
</div>
</div>
</body>
</html>

44
account/billing_history/billing_history.css

@ -0,0 +1,44 @@
/*
* This file is part of Linode Manager Classic.
*
* Linode Manager Classic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Linode Manager Classic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
*/
@import url('/global.css');
.balance-negative {
color: #F00;
}
.balance-positive {
color: #008000;
}
#billing-history {
padding: 15px 15px 15px;
}
#current {
font-size: 13px;
font-weight: bold;
text-align: right;
}
td:nth-of-type(3) {
text-align: right;
}
tr {
border-bottom: 1px solid #E8E8E8;
}

210
account/billing_history/billing_history.js

@ -0,0 +1,210 @@
/*
* This file is part of Linode Manager Classic.
*
* Linode Manager Classic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Linode Manager Classic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
*/
import { settings, elements, apiGet, parseParams, setupHeader } from "/global.js";
(function()
{
// Element names specific to this page
elements.balance = "balance";
elements.balanceNegative = "balance-negative";
elements.billingTable = "billing-table";
elements.lmcRow = "lmc-tr1";
elements.lmcRowAlt = "lmc-tr2";
elements.loading = "loading";
// Data received from API calls
var data = {};
data.invoices = [];
data.payments = [];
// Static references to UI elements
var ui = {};
ui.balance = {};
ui.billingTable = {};
ui.loading = {};
// Temporary state
var state = {};
state.haveInvoices = false;
state.havePayments = false;
// Creates a row for the billing table
var createBillingRow = function(item, alt)
{
var row = document.createElement("tr");
if (alt)
row.className = elements.lmcRowAlt;
else
row.className = elements.lmcRow;
var dateCell = document.createElement("td");
var date = new Date(item.date + "Z");
dateCell.innerHTML = date.toLocaleDateString();
row.appendChild(dateCell);
var description = document.createElement("td");
var amount = document.createElement("td");
if (item.year) {
// Yearly payment report
var span1 = document.createElement("span");
span1.innerHTML = "Yearly Payments Report (";
var link = document.createElement("a");
link.href = "/account/paymentreceiptyear?year=" + item.year;
link.innerHTML = item.year;
var span2 = document.createElement("span");
span2.innerHTML = ")";
description.appendChild(span1);
description.appendChild(link);
description.appendChild(span2);
} else if (item.label) {
// Invoice
var link = document.createElement("a");
link.href = "/account/invoice?iid=" + item.id;
link.innerHTML = item.label;
description.appendChild(link);
if (item.total < 0)
amount.innerHTML = "($" + (-item.total).toFixed(2) + ")";
else
amount.innerHTML = "$" + item.total.toFixed(2);
} else {
// Payment
var link = document.createElement("a");
link.href = "/account/paymentreceipt?pid=" + item.id;
link.innerHTML = "Payment";
var thanks = document.createElement("span");
thanks.innerHTML = " - Thank you";
description.appendChild(link);
description.appendChild(thanks);
if (item.usd > 0)
amount.innerHTML = "($" + item.usd.toFixed(2) + ")";
else
amount.innerHTML = "$" + (-item.usd).toFixed(2);
}
row.appendChild(description);
row.appendChild(amount);
return row;
};
// Populate billing table with invoices and payments
var displayAll = function()
{
// Remove loading row
ui.loading.remove();
// Combine to single array
var combined = data.invoices.concat(data.payments);
// Insert yearly payment reports
var now = new Date();
var lowestYear = now.getFullYear();
for (var i = 0; i < combined.length; i++) {
var date = new Date(combined[i].date + "Z");
if (date.getFullYear() < lowestYear)
lowestYear = date.getFullYear();
}
for (var i = now.getFullYear(); i >= lowestYear; i--) {
combined.push({
"date": i + "-01-01T12:00:00",
"year": i
});
}
// Sort
combined.sort(function(a, b)
{
var aDate = new Date(a.date + "Z");
var bDate = new Date(b.date + "Z");
return bDate - aDate;
});
// Insert
for (var i = 0; i < combined.length; i++)
ui.billingTable.appendChild(createBillingRow(combined[i], ui.billingTable.children.length % 2));
};
// Callback for account details API call
var displayBalance = function(response)
{
ui.balance.innerHTML = "$" + response.balance.toFixed(2);
if (response.balance < 0) {
ui.balance.innerHTML += " credit";
} else if (response.balance > 0) {
ui.balance.innerHTML += " outstanding";
ui.balance.className = elements.balanceNegative;
}
};
// Callback for invoices API call
var displayInvoices = function(response)
{
data.invoices = data.invoices.concat(response.data);
// Request the next page if there are more
if (response.page != response.pages) {
apiGet("/account/invoices?page=" + (response.page + 1), displayInvoices, null);
return;
}
state.haveInvoices = true;
if (state.havePayments)
displayAll();
};
// Callback for payments API call
var displayPayments = function(response)
{
data.payments = data.payments.concat(response.data);
// Request the next page if there are more
if (response.page != response.pages) {
apiGet("/account/payments?page=" + (response.page + 1), displayPayments, null);
return;
}
state.havePayments = true;
if (state.haveInvoices)
displayAll();
};
// Initial setup
var setup = function()
{
// Parse URL parameters
data.params = parseParams();
setupHeader();
// Get element references
ui.balance = document.getElementById(elements.balance);
ui.billingTable = document.getElementById(elements.billingTable);
ui.loading = document.getElementById(elements.loading);
// Get data from API
apiGet("/account", displayBalance, null);
apiGet("/account/invoices", displayInvoices, null);
apiGet("/account/payments", displayPayments, null);
};
// Attach onload handler
window.addEventListener("load", setup);
})();

52
account/billing_history/index.shtml

@ -0,0 +1,52 @@
<!--
This file is part of Linode Manager Classic.
Linode Manager Classic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Linode Manager Classic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
-->
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>LMC - Account // Billing History</title>
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" type="text/css" href="billing_history.css" />
<script src="billing_history.js" type="module"></script>
</head>
<body>
<!--#include virtual="/include/header.html"-->
<!--#include virtual="/include/account_subnav.html"-->
<div id="main-content" class="wrapper">
<div id="billing-history">
<table class="lmc-table">
<thead>
<tr>
<td colspan="3">Billing History</td>
</tr>
<tr>
<td>Date</td>
<td>Description</td>
<td>Amount</td>
</tr>
</thead>
<tbody id="billing-table">
<tr id="loading" class="lmc-tr1">
<td colspan="3">Loading...</td>
</tr>
</tbody>
</table>
<p id="current">Current Balance <span id="balance" class="balance-positive"></span></p>
</div>
</div>
</body>
</html>

34
account/cancel/cancel.css

@ -0,0 +1,34 @@
/*
* This file is part of Linode Manager Classic.
*
* Linode Manager Classic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Linode Manager Classic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
*/
@import url('/global.css');
#cancel {
padding: 15px 15px 15px;
}
#cancel-note {
background-color: #8BCBEA;
margin: 15px auto 0;
padding: 10px;
width: 51%;
}
tbody tr td:first-of-type {
font-weight: bold;
text-align: right;
}

74
account/cancel/cancel.js

@ -0,0 +1,74 @@
/*
* This file is part of Linode Manager Classic.
*
* Linode Manager Classic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Linode Manager Classic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
*/
import { settings, elements, apiPost, parseParams, setupHeader } from "/global.js";
(function()
{
// Element names specific to this page
elements.cancelButton = "cancel-button";
elements.confirm = "confirm";
elements.reason = "reason";
// Data received from API calls
var data = {};
// Static references to UI elements
var ui = {};
ui.cancelButton = {};
ui.confirm = {};
ui.reason = {};
// Click handler for cancel button
var handleCancel = function(event)
{
if (!ui.confirm.checked)
return;
var req = {
"comments": ui.reason.value
};
apiPost("/account/cancel", req, function(response)
{
if (response.survey_link)
location.href = response.survey_link;
else
location.href = "/logout";
});
};
// Initial setup
var setup = function()
{
// Parse URL parameters
data.params = parseParams();
setupHeader();
// Get element references
ui.cancelButton = document.getElementById(elements.cancelButton);
ui.confirm = document.getElementById(elements.confirm);
ui.reason = document.getElementById(elements.reason);
// Attach event handlers
ui.cancelButton.addEventListener("click", handleCancel);
};
// Attach onload handler
window.addEventListener("load", setup);
})();

62
account/cancel/index.shtml

@ -0,0 +1,62 @@
<!--
This file is part of Linode Manager Classic.
Linode Manager Classic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Linode Manager Classic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
-->
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>LMC - Account // Cancel</title>
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" type="text/css" href="cancel.css" />
<script src="cancel.js" type="module"></script>
</head>
<body>
<!--#include virtual="/include/header.html"-->
<!--#include virtual="/include/account_subnav.html"-->
<div id="main-content" class="wrapper">
<div id="cancel">
<table class="lmc-table">
<thead>
<tr>
<td colspan="2">Cancel Account</td>
</tr>
<tr>
<td colspan="2">Sorry to see you go!</td>
</tr>
</thead>
<tbody>
<tr class="lmc-tr3">
<td>Reason</td>
<td>
Please share why you're cancelling your account:<br />
<textarea id="reason" rows="6" cols="65"></textarea>
</td>
</tr>
<tr class="lmc-tr3">
<td></td>
<td>
<input id="confirm" type="checkbox" /> <label for="confirm">I confirm that I want to cancel this entire account</label><br />
<br />
<button id="cancel-button" type="button">Cancel this Account Immediately</button>
</td>
</tr>
</tbody>
</table>
<p id="cancel-note">NOTE: This will <strong>immediately</strong> shut down and cancel all of your Linodes, DNS hosting, and inactivate all Users. You will not be able to log into your account after cancelling.</p>
</div>
</div>
</body>
</html>

27
account/contact/contact.css

@ -0,0 +1,27 @@
/*
* This file is part of Linode Manager Classic.
*
* Linode Manager Classic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Linode Manager Classic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
*/
@import url('/global.css');
#contact {
padding: 15px 15px 15px;
}
tbody tr td:first-of-type {
font-weight: bold;
text-align: right;
}

143
account/contact/contact.js

@ -0,0 +1,143 @@
/*
* This file is part of Linode Manager Classic.
*
* Linode Manager Classic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Linode Manager Classic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
*/
import { settings, elements, apiGet, apiPut, parseParams, setupHeader } from "/global.js";
import { countries } from "/account/contact/countries.js";
(function()
{
// Element names specific to this page
elements.address1 = "address1";
elements.address2 = "address2";
elements.city = "city";
elements.company = "company";
elements.country = "country";
elements.email = "email";
elements.firstName = "first-name";
elements.lastName = "last-name";
elements.phone = "phone";
elements.saveButton = "save-button";
elements.state = "state";
elements.tax = "tax";
elements.zip = "zip";
// Data received from API calls
var data = {};
// Static references to UI elements
var ui = {};
ui.address1 = {};
ui.address2 = {};
ui.city = {};
ui.company = {};
ui.country = {};
ui.email = {};
ui.firstName = {};
ui.lastName = {};
ui.phone = {};
ui.saveButton = {};
ui.state = {};
ui.tax = {};
ui.zip = {};
// Callback for account details API call
var displayAccount = function(response)
{
ui.company.value = response.company;
ui.email.value = response.email;
ui.firstName.value = response.first_name;
ui.lastName.value = response.last_name;
ui.address1.value = response.address_1;
ui.address2.value = response.address_2;
ui.city.value = response.city;
ui.state.value = response.state;
ui.zip.value = response.zip;
ui.country.value = response.country;
ui.tax.value = response.tax_id;
ui.phone.value = response.phone;
ui.saveButton.disabled = false;
};
// Click handler for save button
var handleSave = function(event)
{
if (event.currentTarget.disabled)
return;
var req = {
"company": ui.company.value,
"email": ui.email.value,
"first_name": ui.firstName.value,
"last_name": ui.lastName.value,
"address_1": ui.address1.value,
"address_2": ui.address2.value,
"city": ui.city.value,
"state": ui.state.value,
"zip": ui.zip.value,
"country": ui.country.value,
"tax": ui.tax.value,
"phone": ui.phone.value
};
apiPut("/account", req, function(response)
{
location.href = "/account";
});
};
// Initial setup
var setup = function()
{
// Parse URL parameters
data.params = parseParams();
setupHeader();
// Get element references
ui.address1 = document.getElementById(elements.address1);
ui.address2 = document.getElementById(elements.address2);
ui.city = document.getElementById(elements.city);
ui.company = document.getElementById(elements.company);
ui.country = document.getElementById(elements.country);
ui.email = document.getElementById(elements.email);
ui.firstName = document.getElementById(elements.firstName);
ui.lastName = document.getElementById(elements.lastName);
ui.phone = document.getElementById(elements.phone);
ui.saveButton = document.getElementById(elements.saveButton);
ui.state = document.getElementById(elements.state);
ui.tax = document.getElementById(elements.tax);
ui.zip = document.getElementById(elements.zip);
// Populate country selector
for (var i = 0; i < countries.length; i++) {
var option = document.createElement("option");
option.value = countries[i].code;
option.innerHTML = countries[i]["name"];
ui.country.appendChild(option);
}
// Register event handlers
ui.saveButton.addEventListener("click", handleSave);
// Get data from API
apiGet("/account", displayAccount, null);
};
// Attach onload handler
window.addEventListener("load", setup);
})();

1013
account/contact/countries.js

File diff suppressed because it is too large

107
account/contact/index.shtml

@ -0,0 +1,107 @@
<!--
This file is part of Linode Manager Classic.
Linode Manager Classic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Linode Manager Classic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
-->
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>LMC - Account // Contact Info</title>
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" type="text/css" href="contact.css" />
<script src="contact.js" type="module"></script>
</head>
<body>
<!--#include virtual="/include/header.html"-->
<!--#include virtual="/include/account_subnav.html"-->
<div id="main-content" class="wrapper">
<div id="contact">
<table class="lmc-table">
<thead>
<tr>
<td colspan="3">Contact Information</td>
</tr>
</thead>
<tbody>
<tr class="lmc-tr3">
<td>Company Name</td>
<td><input id="company" type="text" size="30" maxLength="128" /></td>
<td></td>
</tr>
<tr class="lmc-tr3">
<td>Email</td>
<td><input id="email" type="email" size="30" maxLength="128" /></td>
<td class="info">Main point of contact and receives all emails.</td>
</tr>
<tr class="lmc-tr3">
<td>First Name</td>
<td><input id="first-name" type="text" size="30" maxLength="50" /></td>
<td></td>
</tr>
<tr class="lmc-tr3">
<td>Last Name</td>
<td><input id="last-name" type="text" size="30" maxLength="50" /></td>
<td></td>
</tr>
<tr class="lmc-tr3">
<td>Address1</td>
<td><input id="address1" type="text" size="30" maxLength="64" /></td>
<td></td>
</tr>
<tr class="lmc-tr3">
<td>Address2</td>
<td><input id="address2" type="text" size="30" maxLength="64" /></td>
<td></td>
</tr>
<tr class="lmc-tr3">
<td>City</td>
<td><input id="city" type="text" size="30" maxLength="24" /></td>
<td></td>
</tr>
<tr class="lmc-tr3">
<td>State</td>
<td><input id="state" type="text" size="30" maxLength="24" /></td>
<td></td>
</tr>
<tr class="lmc-tr3">
<td>Zip</td>
<td><input id="zip" type="text" size="30" maxLength="16" /></td>
<td></td>
</tr>
<tr class="lmc-tr3">
<td>Country</td>
<td><select id="country"></select></td>
<td></td>
</tr>
<tr class="lmc-tr3">
<td>Tax ID</td>
<td><input id="tax" type="text" size="30" maxLength="100" /></td>
<td class="info">VAT, GST, etc. identification number</td>
</tr>
<tr class="lmc-tr3">
<td>Phone1</td>
<td><input id="phone" type="tel" size="30" maxLength="32" /></td>
<td></td>
</tr>
<tr class="lmc-tr3">
<td></td>
<td colspan="2"><button disabled id="save-button" type="button">Save Changes</button></td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>

27
account/creditcard/creditcard.css

@ -0,0 +1,27 @@
/*
* This file is part of Linode Manager Classic.
*
* Linode Manager Classic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Linode Manager Classic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the