Merge branch 'master' into task/3785/my_bills_page
This commit is contained in:
commit
7bd61f708c
39 changed files with 1141 additions and 698 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -36,3 +36,4 @@ secret-key
|
||||||
.env
|
.env
|
||||||
*.mo
|
*.mo
|
||||||
*.log
|
*.log
|
||||||
|
*.sql
|
||||||
|
|
11
Changelog
11
Changelog
|
@ -1,4 +1,11 @@
|
||||||
Pre-changelog: 1.2.3 2017-09-20
|
Next:
|
||||||
|
* #3764: [hosting] Show cancelled VMs' invoices
|
||||||
|
* #3736: [dcl] Refactor the place where we compute the VM price
|
||||||
|
* #3730: [dcl] Refactor price parameter passed in the DCL flow
|
||||||
|
* #3807: [dcl] Remove PricingView as it is no more used
|
||||||
|
* #3813: [hosting] JS error in create ssh key page
|
||||||
|
* #3756: [dcl] Update landing calculator and billing info page
|
||||||
|
1.2.3: 2017-09-25
|
||||||
* #3484: [dcl, hosting] Refactored account activation, password reset, VM order and cancellation email
|
* #3484: [dcl, hosting] Refactored account activation, password reset, VM order and cancellation email
|
||||||
* #3731: [dcl, hosting] Added cdist ssh key handler
|
* #3731: [dcl, hosting] Added cdist ssh key handler
|
||||||
* #3628: [dcl] on hosting, VM is created at credit card info submit
|
* #3628: [dcl] on hosting, VM is created at credit card info submit
|
||||||
|
@ -6,7 +13,9 @@ Pre-changelog: 1.2.3 2017-09-20
|
||||||
* #3786: [hosting] Redesigned the hosting invoice and order-confirmation page
|
* #3786: [hosting] Redesigned the hosting invoice and order-confirmation page
|
||||||
* #3728: [hosting] VM Termination animation added
|
* #3728: [hosting] VM Termination animation added
|
||||||
* #3777: [hosting] Create new VM calculator added like dcl landing
|
* #3777: [hosting] Create new VM calculator added like dcl landing
|
||||||
|
* #3781: [hosting] Resend activation mail
|
||||||
* #3806: [hosting] Fix can not create VMs after password reset
|
* #3806: [hosting] Fix can not create VMs after password reset
|
||||||
|
* #3812: [hosting] Modal check icon made thin and font-size fixed
|
||||||
* Feature: [cms, blog] Added /cms prefix for all the django-cms generated urls
|
* Feature: [cms, blog] Added /cms prefix for all the django-cms generated urls
|
||||||
* Bugfix: [dcl, hosting] added host to celery error mails
|
* Bugfix: [dcl, hosting] added host to celery error mails
|
||||||
* Bugfix: [ungleich] Fixed wrong subdomain digitalglarus.ungleich.ch
|
* Bugfix: [ungleich] Fixed wrong subdomain digitalglarus.ungleich.ch
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2017-09-16 14:09+0000\n"
|
"POT-Creation-Date: 2017-09-23 21:22+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -65,24 +65,6 @@ msgstr "Bitte gib einen Wert von 10 bis 200 ein."
|
||||||
msgid "GB Storage (SSD)"
|
msgid "GB Storage (SSD)"
|
||||||
msgstr "GB Storage (SSD)"
|
msgstr "GB Storage (SSD)"
|
||||||
|
|
||||||
msgid "Name"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Your Name"
|
|
||||||
msgstr "Dein Name"
|
|
||||||
|
|
||||||
msgid "Please enter your name."
|
|
||||||
msgstr "Bitte gib Deinen Namen ein."
|
|
||||||
|
|
||||||
msgid "Email"
|
|
||||||
msgstr "E-Mail-Adresse"
|
|
||||||
|
|
||||||
msgid "Your Email"
|
|
||||||
msgstr "Deine E-Mail"
|
|
||||||
|
|
||||||
msgid "Please enter a valid email address."
|
|
||||||
msgstr "Bitte gib eine gültige E-Mailadresse ein."
|
|
||||||
|
|
||||||
msgid "Continue"
|
msgid "Continue"
|
||||||
msgstr "Weiter"
|
msgstr "Weiter"
|
||||||
|
|
||||||
|
@ -95,6 +77,18 @@ msgstr "Vielen Dank für Deine Nachricht."
|
||||||
msgid "Get in touch with us!"
|
msgid "Get in touch with us!"
|
||||||
msgstr "Sende uns eine Nachricht."
|
msgstr "Sende uns eine Nachricht."
|
||||||
|
|
||||||
|
msgid "Name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Please enter your name."
|
||||||
|
msgstr "Bitte gib Deinen Namen ein."
|
||||||
|
|
||||||
|
msgid "Email"
|
||||||
|
msgstr "E-Mail-Adresse"
|
||||||
|
|
||||||
|
msgid "Please enter a valid email address."
|
||||||
|
msgstr "Bitte gib eine gültige E-Mailadresse ein."
|
||||||
|
|
||||||
msgid "Message"
|
msgid "Message"
|
||||||
msgstr "Nachricht"
|
msgstr "Nachricht"
|
||||||
|
|
||||||
|
@ -151,6 +145,15 @@ msgstr ""
|
||||||
"Adressleiste deines Browsers.<br/>\n"
|
"Adressleiste deines Browsers.<br/>\n"
|
||||||
"%(base_url)s%(activation_link)s\n"
|
"%(base_url)s%(activation_link)s\n"
|
||||||
|
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Your account details are as follows:<br/><br/>\n"
|
||||||
|
"Username : Your email address<br/>\n"
|
||||||
|
"Password : %(account_details)s<br/><br/>\n"
|
||||||
|
"You can reset your password here:\n"
|
||||||
|
"%(base_url)s%(reset_password_url)s\n"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
"You can activate your Data Center Light account by clicking here.\n"
|
"You can activate your Data Center Light account by clicking here.\n"
|
||||||
|
@ -163,6 +166,17 @@ msgstr ""
|
||||||
"den folgenden Link in die Adressleiste deines Browsers.\n"
|
"den folgenden Link in die Adressleiste deines Browsers.\n"
|
||||||
"%(base_url)s%(activation_link)s\n"
|
"%(base_url)s%(activation_link)s\n"
|
||||||
|
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Your account details are as follows:\n"
|
||||||
|
"\n"
|
||||||
|
"Username : Your email address\n"
|
||||||
|
"Password : %(account_details)s\n"
|
||||||
|
"\n"
|
||||||
|
"You can reset your password here:\n"
|
||||||
|
"%(base_url)s%(reset_password_url)s\n"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Home"
|
msgid "Home"
|
||||||
msgstr "Home"
|
msgstr "Home"
|
||||||
|
|
||||||
|
@ -264,6 +278,45 @@ msgstr "Kontaktiere uns"
|
||||||
msgid "Switzerland "
|
msgid "Switzerland "
|
||||||
msgstr "Schweiz "
|
msgstr "Schweiz "
|
||||||
|
|
||||||
|
msgid "Welcome back"
|
||||||
|
msgstr "Willkommen zurück"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Review your billing address and card details and proceed to make payment."
|
||||||
|
msgstr ""
|
||||||
|
"Überprüfe die Rechnungsadresse und Kreditkartendaten und fahre mit der "
|
||||||
|
"Zahlung fort."
|
||||||
|
|
||||||
|
msgid "Log in"
|
||||||
|
msgstr "Anmelden"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Already signed up?<br>By logging in you can retrieve saved billing "
|
||||||
|
"information."
|
||||||
|
msgstr ""
|
||||||
|
"Bereits eingeloggt? Nach der Anmeldung kannst Du gespeicherte "
|
||||||
|
"Rechnungsinformationen abrufen."
|
||||||
|
|
||||||
|
msgid "LOGIN"
|
||||||
|
msgstr "ANMELDEN"
|
||||||
|
|
||||||
|
msgid "Don't have an account yet?"
|
||||||
|
msgstr "Besitzt du kein Benutzerkonto?"
|
||||||
|
|
||||||
|
msgid "You can sign up by filling in the information below."
|
||||||
|
msgstr ""
|
||||||
|
"Du kannst Dich anmelden, indem Du die die untenstehenden Informationen "
|
||||||
|
"ausfüllst."
|
||||||
|
|
||||||
|
msgid "Forgot password?"
|
||||||
|
msgstr "Passwort vergessen?"
|
||||||
|
|
||||||
|
msgid "Sign up"
|
||||||
|
msgstr "Registrieren"
|
||||||
|
|
||||||
|
msgid "Billing Address"
|
||||||
|
msgstr "Rechnungsadresse"
|
||||||
|
|
||||||
msgid "Your Order"
|
msgid "Your Order"
|
||||||
msgstr "Deine Bestellung"
|
msgstr "Deine Bestellung"
|
||||||
|
|
||||||
|
@ -288,23 +341,15 @@ msgstr "inkl. Mehrwertsteuer"
|
||||||
msgid "Month"
|
msgid "Month"
|
||||||
msgstr "Monat"
|
msgstr "Monat"
|
||||||
|
|
||||||
msgid "Billing Address"
|
|
||||||
msgstr "Rechnungsadresse"
|
|
||||||
|
|
||||||
msgid "Credit Card"
|
msgid "Credit Card"
|
||||||
msgstr "Kreditkarte"
|
msgstr "Kreditkarte"
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"Please fill in your credit card information below. We are using <a href="
|
||||||
" Please fill in your credit card information "
|
"\"https://stripe.com\" target=\"_blank\">Stripe</a> for payment and do not "
|
||||||
"below. We are using <a\n"
|
"store your information in our database."
|
||||||
" href=\"https://stripe.com\" target="
|
|
||||||
"\"_blank\">Stripe</a> for payment and do not store\n"
|
|
||||||
" your information in our database.\n"
|
|
||||||
" "
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"Bitte fülle Deine Kreditkarteninformationen unten aus. Wir nutzen <a href="
|
||||||
"Bitte füll Deine Kreditkarteninformationen unten aus. Wir nutzen <a href="
|
|
||||||
"\"https://stripe.com\" target=\"_blank\">Stripe</a> für die Bezahlung und "
|
"\"https://stripe.com\" target=\"_blank\">Stripe</a> für die Bezahlung und "
|
||||||
"speichern keine Informationen in unserer Datenbank."
|
"speichern keine Informationen in unserer Datenbank."
|
||||||
|
|
||||||
|
@ -330,6 +375,13 @@ msgstr ""
|
||||||
msgid "Card Type"
|
msgid "Card Type"
|
||||||
msgstr "Kartentyp"
|
msgstr "Kartentyp"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"You are not making any payment yet. After placing your order, you will be "
|
||||||
|
"taken to the Submit Payment Page."
|
||||||
|
msgstr ""
|
||||||
|
"Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst, "
|
||||||
|
"nachdem Du die Bestellung auf der nächsten Seite bestätigt hast."
|
||||||
|
|
||||||
msgid "Processing"
|
msgid "Processing"
|
||||||
msgstr "Weiter"
|
msgstr "Weiter"
|
||||||
|
|
||||||
|
@ -365,6 +417,15 @@ msgstr ""
|
||||||
msgid "Place order"
|
msgid "Place order"
|
||||||
msgstr "Bestellen"
|
msgstr "Bestellen"
|
||||||
|
|
||||||
|
msgid "Processing..."
|
||||||
|
msgstr "Abarbeitung..."
|
||||||
|
|
||||||
|
msgid "Hold tight, we are processing your request"
|
||||||
|
msgstr "Bitte warten - wir verbeiten Deine Anfrage gerade"
|
||||||
|
|
||||||
|
msgid "Some problem encountered. Please try again later."
|
||||||
|
msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
|
||||||
|
|
||||||
msgid "We are cutting down the costs significantly!"
|
msgid "We are cutting down the costs significantly!"
|
||||||
msgstr "Wir sorgen dafür, dass die Kosten für Dich signifikant abnehmen"
|
msgstr "Wir sorgen dafür, dass die Kosten für Dich signifikant abnehmen"
|
||||||
|
|
||||||
|
@ -479,11 +540,38 @@ msgstr "Ungültige RAM-Grösse"
|
||||||
msgid "Invalid storage size"
|
msgid "Invalid storage size"
|
||||||
msgstr "Ungültige Speicher-Grösse"
|
msgstr "Ungültige Speicher-Grösse"
|
||||||
|
|
||||||
msgid "is not a proper name"
|
msgid "Error."
|
||||||
msgstr "ist kein gültiger Name"
|
msgstr ""
|
||||||
|
|
||||||
msgid "is not a proper email"
|
msgid ""
|
||||||
msgstr "ist keine gültige E-Mailadresse"
|
"There was a payment related error. On close of this popup, you will be "
|
||||||
|
"redirected back to the payment page."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Thank you for the order."
|
||||||
|
msgstr "Danke für Deine Bestellung."
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Your VM will be up and running in a few moments. We will send you a "
|
||||||
|
"confirmation email as soon as it is ready."
|
||||||
|
msgstr ""
|
||||||
|
"Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du "
|
||||||
|
"auf sie zugreifen kannst."
|
||||||
|
|
||||||
|
#~ msgid "Email Address"
|
||||||
|
#~ msgstr "E-Mail-Adresse"
|
||||||
|
|
||||||
|
#~ msgid "is not a proper name"
|
||||||
|
#~ msgstr "ist kein gültiger Name"
|
||||||
|
|
||||||
|
#~ msgid "is not a proper email"
|
||||||
|
#~ msgstr "ist keine gültige E-Mailadresse"
|
||||||
|
|
||||||
|
#~ msgid "Your Name"
|
||||||
|
#~ msgstr "Dein Name"
|
||||||
|
|
||||||
|
#~ msgid "Your Email"
|
||||||
|
#~ msgstr "Deine E-Mail"
|
||||||
|
|
||||||
#~ msgid ""
|
#~ msgid ""
|
||||||
#~ "\n"
|
#~ "\n"
|
||||||
|
|
|
@ -1653,3 +1653,20 @@ a.list-group-item-danger.active:focus {
|
||||||
.panel-danger > .panel-heading .badge {
|
.panel-danger > .panel-heading .badge {
|
||||||
background-color: #eb4d5c;
|
background-color: #eb4d5c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkmark {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.checkmark:after {
|
||||||
|
/*Add another block-level blank space*/
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
/*Make it a small rectangle so the border will create an L-shape*/
|
||||||
|
width: 25px;
|
||||||
|
height: 60px;
|
||||||
|
/*Add a white border on the bottom and left, creating that 'L' */
|
||||||
|
border: solid #777;
|
||||||
|
border-width: 0 3px 3px 0;
|
||||||
|
/*Rotate the L 45 degrees to turn it into a checkmark*/
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
|
@ -155,9 +155,7 @@
|
||||||
function _calcPricing() {
|
function _calcPricing() {
|
||||||
var total = (cardPricing['cpu'].value * 5) + (2 * cardPricing['ram'].value) + (0.6 * cardPricing['storage'].value);
|
var total = (cardPricing['cpu'].value * 5) + (2 * cardPricing['ram'].value) + (0.6 * cardPricing['storage'].value);
|
||||||
total = parseFloat(total.toFixed(2));
|
total = parseFloat(total.toFixed(2));
|
||||||
|
|
||||||
$("#total").text(total);
|
$("#total").text(total);
|
||||||
$('input[name=total]').val(total);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function form_success() {
|
function form_success() {
|
||||||
|
|
|
@ -13,7 +13,7 @@ from hosting.models import HostingOrder, HostingBill
|
||||||
from membership.models import StripeCustomer, CustomUser
|
from membership.models import StripeCustomer, CustomUser
|
||||||
from opennebula_api.models import OpenNebulaManager
|
from opennebula_api.models import OpenNebulaManager
|
||||||
from opennebula_api.serializers import VirtualMachineSerializer
|
from opennebula_api.serializers import VirtualMachineSerializer
|
||||||
from utils.hosting_utils import get_all_public_keys
|
from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail
|
||||||
from utils.forms import UserBillingAddressForm
|
from utils.forms import UserBillingAddressForm
|
||||||
from utils.mailer import BaseEmail
|
from utils.mailer import BaseEmail
|
||||||
from utils.models import BillingAddress
|
from utils.models import BillingAddress
|
||||||
|
@ -52,7 +52,8 @@ def create_vm_task(self, vm_template_id, user, specs, template,
|
||||||
stripe_customer_id, billing_address_data,
|
stripe_customer_id, billing_address_data,
|
||||||
billing_address_id,
|
billing_address_id,
|
||||||
charge, cc_details):
|
charge, cc_details):
|
||||||
logger.debug("Running create_vm_task on {}".format(current_task.request.hostname))
|
logger.debug(
|
||||||
|
"Running create_vm_task on {}".format(current_task.request.hostname))
|
||||||
vm_id = None
|
vm_id = None
|
||||||
try:
|
try:
|
||||||
final_price = specs.get('price')
|
final_price = specs.get('price')
|
||||||
|
@ -144,7 +145,8 @@ def create_vm_task(self, vm_template_id, user, specs, template,
|
||||||
if 'pass' in user:
|
if 'pass' in user:
|
||||||
lang = 'en-us'
|
lang = 'en-us'
|
||||||
if user.get('language') is not None:
|
if user.get('language') is not None:
|
||||||
logger.debug("Language is set to {}".format(user.get('language')))
|
logger.debug(
|
||||||
|
"Language is set to {}".format(user.get('language')))
|
||||||
lang = user.get('language')
|
lang = user.get('language')
|
||||||
translation.activate(lang)
|
translation.activate(lang)
|
||||||
# Send notification to the user as soon as VM has been booked
|
# Send notification to the user as soon as VM has been booked
|
||||||
|
@ -174,6 +176,7 @@ def create_vm_task(self, vm_template_id, user, specs, template,
|
||||||
logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id))
|
logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id))
|
||||||
if new_host is not None:
|
if new_host is not None:
|
||||||
custom_user = CustomUser.objects.get(email=user.get('email'))
|
custom_user = CustomUser.objects.get(email=user.get('email'))
|
||||||
|
get_or_create_vm_detail(custom_user, manager, vm_id)
|
||||||
if custom_user is not None:
|
if custom_user is not None:
|
||||||
public_keys = get_all_public_keys(custom_user)
|
public_keys = get_all_public_keys(custom_user)
|
||||||
keys = [{'value': key, 'state': True} for key in
|
keys = [{'value': key, 'state': True} for key in
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="modal-icon"><i class="fa fa-check" aria-hidden="true"></i></div>
|
<div class="modal-icon"><i class="checkmark" aria-hidden="true"></i></div>
|
||||||
<h4 class="modal-title">{% trans "Request Sent" %}</h4>
|
<h4 class="modal-title">{% trans "Request Sent" %}</h4>
|
||||||
<p class="modal-text">{% trans "Thank you for your subscription! You will receive a confirmation mail from our team" %}</p>
|
<p class="modal-text">{% trans "Thank you for your subscription! You will receive a confirmation mail from our team" %}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -77,47 +77,9 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="total">
|
|
||||||
<!--<div class="description check-ip">
|
<!--<div class="description check-ip">
|
||||||
<input type="checkbox" name="ipv6"> Ipv6 Only<br>
|
<input type="checkbox" name="ipv6"> Ipv6 Only<br>
|
||||||
</div>-->
|
</div>-->
|
||||||
<div class="form-group">
|
|
||||||
<div class="description input justify-center">
|
|
||||||
<label for="name" class="control-label">{% trans "Name"%}</label>
|
|
||||||
<input type="text" name="name" class="form-control" placeholder="{% trans 'Your Name'%}"
|
|
||||||
data-minlength="3" data-error="{% trans 'Please enter your name.' %}" required>
|
|
||||||
</div>
|
|
||||||
<div class="help-block with-errors">
|
|
||||||
{% for message in messages %}
|
|
||||||
{% if 'name' in message.tags %}
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li>
|
|
||||||
{{ message|safe }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="description input justify-center">
|
|
||||||
<label for="email" class="control-label">{% trans "Email"%}</label>
|
|
||||||
<input name="email" type="email" pattern="^[^@\s]+@([^@\s]+\.)+[^@\s]+$" class="form-control"
|
|
||||||
placeholder="{% trans 'Your Email' %}"
|
|
||||||
data-error="{% trans 'Please enter a valid email address.' %}" required>
|
|
||||||
</div>
|
|
||||||
<div class="help-block with-errors">
|
|
||||||
{% for message in messages %}
|
|
||||||
{% if 'email' in message.tags %}
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li>
|
|
||||||
{{ message|safe }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<input type="submit" class="btn btn-primary disabled" value="{% trans 'Continue' %}"></input>
|
<input type="submit" class="btn btn-primary disabled" value="{% trans 'Continue' %}"></input>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -11,4 +11,14 @@ You can also copy and paste the following link into the address bar of your brow
|
||||||
to activate your Data Center Light account.<br/>
|
to activate your Data Center Light account.<br/>
|
||||||
{{base_url}}{{activation_link}}
|
{{base_url}}{{activation_link}}
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
|
{% if account_details %}
|
||||||
|
{% url 'hosting:reset_password' as reset_password_url %}
|
||||||
|
<br/><br/>
|
||||||
|
{% blocktrans %}Your account details are as follows:<br/><br/>
|
||||||
|
Username : Your email address<br/>
|
||||||
|
Password : {{account_details}}<br/><br/>
|
||||||
|
You can reset your password here:
|
||||||
|
{{base_url}}{{reset_password_url}}
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -7,4 +7,15 @@ You can also copy and paste the following link into the address bar of your brow
|
||||||
to activate your Data Center Light account.
|
to activate your Data Center Light account.
|
||||||
{{base_url}}{{activation_link}}
|
{{base_url}}{{activation_link}}
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
|
{% if account_details %}
|
||||||
|
{% url 'hosting:reset_password' as reset_password_url %}
|
||||||
|
{% blocktrans %}Your account details are as follows:
|
||||||
|
|
||||||
|
Username : Your email address
|
||||||
|
Password : {{account_details}}
|
||||||
|
|
||||||
|
You can reset your password here:
|
||||||
|
{{base_url}}{{reset_password_url}}
|
||||||
|
{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,85 +1,93 @@
|
||||||
{% extends "hosting/base_short.html" %}
|
{% extends "hosting/base_short.html" %}
|
||||||
{% load staticfiles bootstrap3 i18n %}
|
{% load staticfiles bootstrap3 i18n %}
|
||||||
|
|
||||||
|
{% block css_extra %}
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/paymentfont/1.1.2/css/paymentfont.min.css"/>
|
||||||
|
{% endblock css_extra %}
|
||||||
|
|
||||||
{% block navbar %}
|
{% block navbar %}
|
||||||
{% include "datacenterlight/includes/_navbar.html" %}
|
{% include "datacenterlight/includes/_navbar.html" %}
|
||||||
{% endblock navbar %}
|
{% endblock navbar %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- Credit card form -->
|
<!-- Credit card form -->
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/paymentfont/1.1.2/css/paymentfont.min.css"/>
|
|
||||||
<div class="dcl-order-container">
|
<div class="dcl-order-container">
|
||||||
<div class="payment-container">
|
<div class="payment-container">
|
||||||
<div class="row">
|
<div class="dcl-payment-grid">
|
||||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 dcl-order-sec">
|
<div class="dcl-payment-box">
|
||||||
<h3><strong>{%trans "Your Order" %}</strong></h3>
|
<div class="dcl-payment-section">
|
||||||
<div class="col-xs-6 col-sm-12 col-md-12 col-lg-12 dcl-order-table-header">
|
{% if request.user.is_authenticated %}
|
||||||
<div class="col-xs-12 col-sm-2 col-md-1 col-lg-1 tbl-header">
|
<div class="dcl-payment-user">
|
||||||
{%trans "Cores" %}
|
<h4>{% trans "Welcome back" %} {{request.user.name}}!</h4>
|
||||||
|
<p>{% trans "Review your billing address and card details and proceed to make payment." %}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-sm-3 col-md-4 col-lg-4 tbl-header">
|
{% else %}
|
||||||
{%trans "Memory" %}
|
<h3>{%trans "Log in" %}</h3>
|
||||||
</div>
|
<hr class="top-hr">
|
||||||
<div class="col-xs-12 col-sm-3 col-md-3 col-lg-3 tbl-header">
|
<p style="margin-bottom: 20px;">{% blocktrans %}Already signed up?<br>By logging in you can retrieve saved billing information.{% endblocktrans %}</p>
|
||||||
{%trans "Disk space" %}
|
<form role="form" id="login-form" method="post" action="{% url 'hosting:login' %}" novalidate>
|
||||||
</div>
|
{% for field in login_form %}
|
||||||
<div class="col-xs-12 col-sm-4 col-md-4 col-lg-4 tbl-header">
|
|
||||||
{%trans "Configuration" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-6 col-sm-12 col-md-12 col-lg-12 dcl-order-table-content">
|
|
||||||
<div class="col-xs-12 col-sm-2 col-md-1 col-lg-1 tbl-content">
|
|
||||||
{{request.session.specs.cpu|floatformat}}
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-12 col-sm-3 col-md-4 col-lg-4 tbl-content">
|
|
||||||
{{request.session.specs.memory|floatformat}} GB
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-12 col-sm-3 col-md-3 col-lg-3 tbl-content">
|
|
||||||
{{request.session.specs.disk_size|floatformat}} GB
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-12 col-sm-4 col-md-4 col-lg-4 tbl-content">
|
|
||||||
{{request.session.template.name}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 dcl-order-table-total">
|
|
||||||
<div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 tbl-tot tbl-no-padding">
|
|
||||||
{%trans "Total" %} <span>{%trans "including VAT" %}</span>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 tbl-no-padding">
|
|
||||||
<div class="col-xs-12 col-sm-4 col-md-4 col-lg-4"></div>
|
|
||||||
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6 tbl-total">{{request.session.specs.price}}
|
|
||||||
CHF<span class="dcl-price-month">/{% trans "Month" %}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 dcl-billing-sec">
|
|
||||||
<div class="col-xs-12 col-sm-5 col-md-6 billing dcl-billing">
|
|
||||||
<h3><b>{%trans "Billing Address"%}</b></h3>
|
|
||||||
<hr>
|
|
||||||
<form role="form" id="billing-form" method="post" action="" novalidate>
|
|
||||||
{% for field in form %}
|
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% bootstrap_field field show_label=False type='fields'%}
|
{% bootstrap_field field show_label=False type='fields'%}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<input type='hidden' name='next' value='{{request.path}}'/>
|
||||||
|
<div class="form-group text-right">
|
||||||
|
<button type="submit" class="btn btn-wide btn-vm-contact">{% trans "LOGIN" %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<p>
|
||||||
|
{% trans "Don't have an account yet?" %}<br>
|
||||||
|
{% trans "You can sign up by filling in the information below." %}<br>
|
||||||
|
<a href="{% url 'hosting:reset_password' %}">{% trans "Forgot password?" %}</a>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dcl-payment-box">
|
||||||
|
<div class="dcl-payment-section">
|
||||||
|
{% if not request.user.is_authenticated %}
|
||||||
|
<h3><b>{%trans "Sign up"%}</b></h3>
|
||||||
|
{% else %}
|
||||||
|
<h3><b>{%trans "Billing Address"%}</b></h3>
|
||||||
|
{% endif %}
|
||||||
|
<hr class="top-hr">
|
||||||
|
{% for message in messages %}
|
||||||
|
{% if 'duplicate_email' in message.tags %}
|
||||||
|
<p class="text-danger">{{message}}</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
<form role="form" id="billing-form" method="post" action="" novalidate>
|
||||||
|
{% csrf_token %}
|
||||||
|
{% for field in form %}
|
||||||
|
{% bootstrap_field field show_label=False type='fields'%}
|
||||||
|
{% endfor %}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-sm-7 col-md-6 creditcard-box dcl-creditcard">
|
|
||||||
<h3><b>{%trans "Credit Card"%}</b></h3>
|
|
||||||
<hr>
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<p>
|
|
||||||
{% blocktrans %}
|
|
||||||
Please fill in your credit card information below. We are using <a
|
|
||||||
href="https://stripe.com" target="_blank">Stripe</a> for payment and do not store
|
|
||||||
your information in our database.
|
|
||||||
{% endblocktrans %}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<div class="dcl-payment-box">
|
||||||
|
<div class="dcl-payment-section">
|
||||||
|
<h3>{%trans "Your Order" %}</h3>
|
||||||
|
<hr class="top-hr">
|
||||||
|
<div class="dcl-payment-order">
|
||||||
|
<p>{% trans "Cores"%} <strong class="pull-right">{{request.session.specs.cpu|floatformat}}</strong></p>
|
||||||
|
<hr>
|
||||||
|
<p>{% trans "Memory"%} <strong class="pull-right">{{request.session.specs.memory|floatformat}} GB</strong></p>
|
||||||
|
<hr>
|
||||||
|
<p>{% trans "Disk space"%} <strong class="pull-right">{{request.session.specs.disk_size|floatformat}} GB</strong></p>
|
||||||
|
<hr>
|
||||||
|
<p>{% trans "Configuration"%} <strong class="pull-right">{{request.session.template.name}}</strong></p>
|
||||||
|
<hr>
|
||||||
|
<p class="last-p"><strong>{%trans "Total" %}</strong> <small>({%trans "including VAT" %})</small> <strong class="pull-right">{{request.session.specs.price}} CHF/{% trans "Month" %}</strong></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dcl-payment-box">
|
||||||
|
<div class="dcl-payment-section">
|
||||||
|
<h3><b>{%trans "Credit Card"%}</b></h3>
|
||||||
|
<hr class="top-hr">
|
||||||
|
<p>
|
||||||
|
{% blocktrans %}Please fill in your credit card information below. We are using <a href="https://stripe.com" target="_blank">Stripe</a> for payment and do not store your information in our database.{% endblocktrans %}
|
||||||
|
</p>
|
||||||
<div>
|
<div>
|
||||||
{% if credit_card_data.last4 %}
|
{% if credit_card_data.last4 %}
|
||||||
<form role="form" id="payment-form-with-creditcard" novalidate>
|
<form role="form" id="payment-form-with-creditcard" novalidate>
|
||||||
|
@ -88,8 +96,6 @@
|
||||||
<h5 class="membership-lead">Type: {{credit_card_data.cc_brand}}</h5>
|
<h5 class="membership-lead">Type: {{credit_card_data.cc_brand}}</h5>
|
||||||
<input type="hidden" name="credit_card_needed" value="false"/>
|
<input type="hidden" name="credit_card_needed" value="false"/>
|
||||||
</form>
|
</form>
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12">
|
|
||||||
{% if not messages and not form.non_field_errors %}
|
{% if not messages and not form.non_field_errors %}
|
||||||
<p class="card-warning-content card-warning-addtional-margin">
|
<p class="card-warning-content card-warning-addtional-margin">
|
||||||
{% trans "You are not making any payment yet. After submitting your card information, you will be taken to the Confirm Order Page." %}
|
{% trans "You are not making any payment yet. After submitting your card information, you will be taken to the Confirm Order Page." %}
|
||||||
|
@ -109,55 +115,50 @@
|
||||||
</p>
|
</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<button id="payment_button_with_creditcard" class="btn btn-success btn-vm-contact" type="submit">{%trans "Submit" %}</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12">
|
|
||||||
<div class="col-xs-6 pull-right">
|
|
||||||
<button id="payment_button_with_creditcard" class="btn btn-success stripe-payment-btn"
|
|
||||||
type="submit">
|
|
||||||
{%trans "Submit" %}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<form action="" id="payment-form-new" method="POST">
|
<form action="" id="payment-form-new" method="POST">
|
||||||
<input type="hidden" name="token"/>
|
<input type="hidden" name="token"/>
|
||||||
<div class="group">
|
<div class="group">
|
||||||
<div class="col-xs-12 col-sm-12 col-md-10 col-lg-9 credit-card-goup">
|
<div class="credit-card-goup">
|
||||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 card-element card-number-element">
|
<div class="card-element card-number-element">
|
||||||
<label>{%trans "Card Number" %}</label>
|
<label>{%trans "Card Number" %}</label>
|
||||||
<div id="card-number-element" class="field my-input"></div>
|
<div id="card-number-element" class="field my-input"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-5 col-sm-3 col-md-3 col-lg-3 card-element card-expiry-element">
|
<div class="row">
|
||||||
|
<div class="col-xs-5 card-element card-expiry-element">
|
||||||
<label>{%trans "Expiry Date" %}</label>
|
<label>{%trans "Expiry Date" %}</label>
|
||||||
<div id="card-expiry-element" class="field my-input"></div>
|
<div id="card-expiry-element" class="field my-input"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-sm-2 col-md-6 col-lg-7 hide-mobile"></div>
|
<div class="col-xs-3 col-xs-offset-4 card-element card-cvc-element">
|
||||||
<div class="col-xs-3 col-sm-3 col-md-3 col-lg-2 card-element card-cvc-element">
|
|
||||||
<label>{%trans "CVC" %}</label>
|
<label>{%trans "CVC" %}</label>
|
||||||
<div id="card-cvc-element" class="field my-input"></div>
|
<div id="card-cvc-element" class="field my-input"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 card-element brand">
|
</div>
|
||||||
|
<div class="card-element brand">
|
||||||
<label>{%trans "Card Type" %}</label>
|
<label>{%trans "Card Type" %}</label>
|
||||||
<i class="pf pf-credit-card" id="brand-icon"></i>
|
<i class="pf pf-credit-card" id="brand-icon"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="card-errors" role="alert"></div>
|
<div id="card-errors"></div>
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12">
|
|
||||||
{% if not messages and not form.non_field_errors %}
|
{% if not messages and not form.non_field_errors %}
|
||||||
<p class="card-warning-content">
|
<p class="card-warning-content">
|
||||||
{% trans "You are not making any payment yet. After submitting your card information, you will be taken to the Confirm Order Page." %}
|
{% trans "You are not making any payment yet. After placing your order, you will be taken to the Submit Payment Page." %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div id='payment_error'>
|
<div id='payment_error'>
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
{% if 'failed_payment' or 'make_charge_error' in message.tags %}
|
{% if 'failed_payment' in message.tags or 'make_charge_error' in message.tags %}
|
||||||
<ul class="list-unstyled"><li>
|
<ul class="list-unstyled">
|
||||||
<p class="card-warning-content card-warning-error">{{ message|safe }}</p>
|
<li><p class="card-warning-content card-warning-error">{{ message|safe }}</p></li>
|
||||||
</li></ul>
|
</ul>
|
||||||
|
{% elif not form.non_field_errors %}
|
||||||
|
<p class="card-warning-content">
|
||||||
|
{% trans "You are not making any payment yet. After placing your order, you will be taken to the Submit Payment Page." %}
|
||||||
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
@ -167,29 +168,20 @@
|
||||||
</p>
|
</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="text-right">
|
||||||
<div class="col-xs-12">
|
<button class="btn btn-vm-contact btn-wide" type="submit">{%trans "SUBMIT" %}</button>
|
||||||
<div class="col-xs-6 pull-right">
|
|
||||||
<button class="btn btn-success stripe-payment-btn" type="submit">{%trans "Submit" %}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" style="display:none;">
|
<div style="display:none;">
|
||||||
<div class="col-xs-12">
|
|
||||||
<p class="payment-errors"></p>
|
<p class="payment-errors"></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- stripe key data -->
|
<!-- stripe key data -->
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="order-detail-container">
|
<div class="order-detail-container">
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-md-8 col-md-offset-2">
|
<div class="col-xs-12 col-md-8 col-md-offset-2">
|
||||||
|
@ -39,9 +39,9 @@
|
||||||
<div class="col-xs-12 col-sm-6">
|
<div class="col-xs-12 col-sm-6">
|
||||||
<address>
|
<address>
|
||||||
<h3><b>{% trans "Billed To:"%}</b></h3>
|
<h3><b>{% trans "Billed To:"%}</b></h3>
|
||||||
{% with request.session.billing_address_data as billing_address %}
|
{% with billing_address_data as billing_address %}
|
||||||
{{billing_address|get_value_from_dict:'cardholder_name'}}<br> {{billing_address|get_value_from_dict:'street_address'}}, {{billing_address|get_value_from_dict:'postal_code'}}<br>
|
{{billing_address.cardholder_name}}<br> {{billing_address.street_address}}, {{billing_address.postal_code}}<br>
|
||||||
{{billing_address|get_value_from_dict:'city'}}, {{billing_address|get_value_from_dict:'country'}}.
|
{{billing_address.city}}, {{billing_address.country}}.
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</address>
|
</address>
|
||||||
</div>
|
</div>
|
||||||
|
@ -76,31 +76,68 @@
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<form method="post">
|
<form method="post" id="virtual_machine_create_form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<p class="dcl-place-order-text">{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price }}CHF/month{% endblocktrans %}.</p>
|
<p class="dcl-place-order-text">{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price }}CHF/month{% endblocktrans %}.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4 content">
|
<div class="col-sm-4 content">
|
||||||
<a href="{{next_url}}" ><button class="btn btn-info pull-right">{% trans "Place order"%}</button></a>
|
<button class="btn btn-info pull-right"
|
||||||
|
id="btn-create-vm"
|
||||||
|
data-href="{% url 'hosting:order-confirmation' %}"
|
||||||
|
data-toggle="modal"
|
||||||
|
data-target="#createvm-modal">
|
||||||
|
{% trans "Place order"%}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<!-- Create VM Modal -->
|
||||||
|
<div class="modal fade" id="createvm-modal" tabindex="-1" role="dialog"
|
||||||
|
aria-hidden="true" data-backdrop="static" data-keyboard="false">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close hidden" data-dismiss="modal"
|
||||||
|
aria-label="create-vm-close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="modal-icon">
|
||||||
|
<i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
|
||||||
|
<span class="sr-only">{% trans "Processing..." %}</span>
|
||||||
|
</div>
|
||||||
|
<h4 class="modal-title" id="createvm-modal-title">
|
||||||
|
</h4>
|
||||||
|
<div class="modal-text" id="createvm-modal-body">
|
||||||
|
{% trans "Hold tight, we are processing your request" %}
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a id="createvm-modal-done-btn" class="btn btn-success btn-ok btn-wide hide" href="">{% trans "OK" %}</a>
|
||||||
|
<button id="createvm-modal-close-btn" type="button" class="btn btn-danger btn-ok btn-wide hide" data-dismiss="modal" aria-label="create-vm-close">{% trans "Close" %}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- / Create VM Modal -->
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
{% trans "Some problem encountered. Please try again later." as err_msg %}
|
||||||
|
var create_vm_error_message = '{{err_msg|safe}}.';
|
||||||
|
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
var locale_date = moment.utc(document.getElementById("order-created_at").textContent,'YYYY-MM-DD HH:mm').toDate();
|
var locale_date = moment.utc(document.getElementById("order-created_at").textContent,'YYYY-MM-DD HH:mm').toDate();
|
||||||
locale_date = moment(locale_date).format("YYYY-MM-DD h:mm:ss a");
|
locale_date = moment(locale_date).format("YYYY-MM-DD h:mm:ss a");
|
||||||
document.getElementById('order-created_at').innerHTML = locale_date;
|
document.getElementById('order-created_at').innerHTML = locale_date;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{%endblock%}
|
{%endblock%}
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
{% extends "datacenterlight/base.html" %}
|
|
||||||
{% load staticfiles i18n%}
|
|
||||||
{% get_current_language as LANGUAGE_CODE %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="intro-pricing">
|
|
||||||
|
|
||||||
<div class="intro-message">
|
|
||||||
<h2 class="section-heading">{% trans "We are cutting down the costs significantly!" %}</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="price-calc-section">
|
|
||||||
<div class="card">
|
|
||||||
<img class="img-beta" src="{% static 'datacenterlight/img/beta-img.png' %}" alt="">
|
|
||||||
<div class="caption">
|
|
||||||
<form method="POST" action="">
|
|
||||||
{% csrf_token %}
|
|
||||||
|
|
||||||
<div class="title">
|
|
||||||
<h3>{% trans "VM hosting" %} </h3>
|
|
||||||
</div>
|
|
||||||
<div class="price">
|
|
||||||
<span id="total">15</span>
|
|
||||||
<span>CHF</span>
|
|
||||||
<div class="price-text">
|
|
||||||
<p>{% trans "VAT included" %}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="descriptions">
|
|
||||||
<div class="description">
|
|
||||||
<p>{% trans "Hosted in Switzerland" %}</p>
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
<i class="fa fa-minus-circle left" data-minus="cpu" aria-hidden="true"></i>
|
|
||||||
<input class="input-price" type="number" min="1" max="42" id="coreValue" name="cpu">
|
|
||||||
<span> Core</span>
|
|
||||||
<i class="fa fa-plus-circle right" data-plus="cpu" aria-hidden="true"></i>
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
<i class="fa fa-minus-circle left" data-minus="ram" aria-hidden="true"></i>
|
|
||||||
<input id="ramValue" class="input-price" type="number" min="2" max="200" name="ram">
|
|
||||||
<span> GB RAM</span>
|
|
||||||
<i class="fa fa-plus-circle right" data-plus="ram" aria-hidden="true"></i>
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
<i class="fa fa-minus-circle left" data-minus="storage" aria-hidden="true"></i>
|
|
||||||
<input id="storageValue" class="input-price" type="number" min="10" max="500" step="10" name="storage">
|
|
||||||
<span>{% trans "GB Storage (SSD)" %}</span>
|
|
||||||
<i class="fa fa-plus-circle right" data-plus="storage" aria-hidden="true"></i>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="description select-configuration input">
|
|
||||||
<label for="name">OS</label>
|
|
||||||
<select name="config" id="">
|
|
||||||
{% for template in templates %}
|
|
||||||
<option value="{{template.id}}">{{template.name}} </option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<input type="hidden" name="total">
|
|
||||||
|
|
||||||
<!-- <div class="description input">
|
|
||||||
<label for="name">Name</label>
|
|
||||||
<input type="text" name="name" placeholder="Your Name">
|
|
||||||
</div>
|
|
||||||
<div class="description input">
|
|
||||||
<label for="email">Email</label>
|
|
||||||
<input type="email" name="email" placeholder="Your Email">
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!--<div class="description check-ip">
|
|
||||||
<input type="checkbox" name="ipv6"> Ipv6 Only<br>
|
|
||||||
</div>-->
|
|
||||||
</div>
|
|
||||||
<input type="submit" class="btn btn-primary" value="{% trans 'Order Now!' %}"></input>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text">
|
|
||||||
<h2 class="section-heading">{% trans "Simple and affordable: Try our virtual machine with featherlight price." %}</h2>
|
|
||||||
|
|
||||||
<div class="description">
|
|
||||||
<p>{% trans "Our VMs are hosted in Glarus, Switzerland, and our website is currently running in BETA mode. If you want more information that you did not find on our website, or if your order is more detailed, or if you encounter any technical hiccups, please contact us at support@datacenterlight.ch, our team will get in touch with you asap." %}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ from datacenterlight.models import VMTemplate
|
||||||
from datacenterlight.tasks import create_vm_task
|
from datacenterlight.tasks import create_vm_task
|
||||||
from membership.models import StripeCustomer
|
from membership.models import StripeCustomer
|
||||||
from opennebula_api.serializers import VMTemplateSerializer
|
from opennebula_api.serializers import VMTemplateSerializer
|
||||||
|
from utils.hosting_utils import get_vm_price
|
||||||
from utils.models import BillingAddress
|
from utils.models import BillingAddress
|
||||||
from utils.stripe_utils import StripeUtils
|
from utils.stripe_utils import StripeUtils
|
||||||
|
|
||||||
|
@ -94,12 +95,11 @@ class CeleryTaskTestCase(TestCase):
|
||||||
cpu = specs.get('cpu')
|
cpu = specs.get('cpu')
|
||||||
memory = specs.get('memory')
|
memory = specs.get('memory')
|
||||||
disk_size = specs.get('disk_size')
|
disk_size = specs.get('disk_size')
|
||||||
amount_to_be_charged = (cpu * 5) + (memory * 2) + (disk_size * 0.6)
|
amount_to_be_charged = get_vm_price(cpu=cpu, memory=memory,
|
||||||
plan_name = "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format(
|
disk_size=disk_size)
|
||||||
cpu=cpu,
|
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
|
||||||
memory=memory,
|
memory=memory,
|
||||||
disk_size=disk_size)
|
disk_size=disk_size)
|
||||||
|
|
||||||
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
||||||
ram=memory,
|
ram=memory,
|
||||||
ssd=disk_size,
|
ssd=disk_size,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from .views import IndexView, BetaProgramView, LandingProgramView, \
|
from .views import IndexView, BetaProgramView, LandingProgramView, \
|
||||||
BetaAccessView, PricingView, SuccessView, \
|
BetaAccessView, SuccessView, \
|
||||||
PaymentOrderView, OrderConfirmationView, \
|
PaymentOrderView, OrderConfirmationView, \
|
||||||
WhyDataCenterLightView, ContactUsView
|
WhyDataCenterLightView, ContactUsView
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ urlpatterns = [
|
||||||
name='whydatacenterlight'),
|
name='whydatacenterlight'),
|
||||||
url(r'^beta-program/?$', BetaProgramView.as_view(), name='beta'),
|
url(r'^beta-program/?$', BetaProgramView.as_view(), name='beta'),
|
||||||
url(r'^landing/?$', LandingProgramView.as_view(), name='landing'),
|
url(r'^landing/?$', LandingProgramView.as_view(), name='landing'),
|
||||||
url(r'^pricing/?$', PricingView.as_view(), name='pricing'),
|
|
||||||
url(r'^payment/?$', PaymentOrderView.as_view(), name='payment'),
|
url(r'^payment/?$', PaymentOrderView.as_view(), name='payment'),
|
||||||
url(r'^order-confirmation/?$', OrderConfirmationView.as_view(),
|
url(r'^order-confirmation/?$', OrderConfirmationView.as_view(),
|
||||||
name='order_confirmation'),
|
name='order_confirmation'),
|
||||||
|
|
|
@ -1,28 +1,35 @@
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth import login, authenticate
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect, HttpResponse
|
||||||
from django.shortcuts import redirect
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import get_language, ugettext_lazy as _
|
||||||
from django.views.decorators.cache import cache_control
|
from django.views.decorators.cache import cache_control
|
||||||
from django.views.generic import FormView, CreateView, TemplateView, DetailView
|
from django.views.generic import FormView, CreateView, TemplateView, DetailView
|
||||||
|
|
||||||
from datacenterlight.tasks import create_vm_task
|
from datacenterlight.tasks import create_vm_task
|
||||||
from hosting.models import HostingOrder
|
from hosting.models import HostingOrder
|
||||||
|
from hosting.forms import HostingUserLoginForm
|
||||||
from membership.models import CustomUser, StripeCustomer
|
from membership.models import CustomUser, StripeCustomer
|
||||||
from opennebula_api.models import OpenNebulaManager
|
from opennebula_api.serializers import VMTemplateSerializer
|
||||||
from opennebula_api.serializers import VirtualMachineTemplateSerializer, \
|
from utils.forms import (
|
||||||
VMTemplateSerializer
|
BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm
|
||||||
from utils.forms import BillingAddressForm
|
)
|
||||||
|
from utils.hosting_utils import get_vm_price
|
||||||
from utils.mailer import BaseEmail
|
from utils.mailer import BaseEmail
|
||||||
from utils.stripe_utils import StripeUtils
|
from utils.stripe_utils import StripeUtils
|
||||||
from utils.tasks import send_plain_email_task
|
from utils.tasks import send_plain_email_task
|
||||||
from .forms import BetaAccessForm, ContactForm
|
from .forms import BetaAccessForm, ContactForm
|
||||||
from .models import BetaAccess, BetaAccessVMType, BetaAccessVM, VMTemplate
|
from .models import BetaAccess, BetaAccessVMType, BetaAccessVM, VMTemplate
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ContactUsView(FormView):
|
class ContactUsView(FormView):
|
||||||
template_name = "datacenterlight/contact_form.html"
|
template_name = "datacenterlight/contact_form.html"
|
||||||
|
@ -88,56 +95,6 @@ class SuccessView(TemplateView):
|
||||||
return render(request, self.template_name)
|
return render(request, self.template_name)
|
||||||
|
|
||||||
|
|
||||||
class PricingView(TemplateView):
|
|
||||||
template_name = "datacenterlight/pricing.html"
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
try:
|
|
||||||
manager = OpenNebulaManager()
|
|
||||||
templates = manager.get_templates()
|
|
||||||
|
|
||||||
context = {
|
|
||||||
'templates': VirtualMachineTemplateSerializer(templates,
|
|
||||||
many=True).data,
|
|
||||||
}
|
|
||||||
except:
|
|
||||||
messages.error(request,
|
|
||||||
'We have a temporary problem to connect to our backend. \
|
|
||||||
Please try again in a few minutes'
|
|
||||||
)
|
|
||||||
context = {
|
|
||||||
'error': 'connection'
|
|
||||||
}
|
|
||||||
|
|
||||||
return render(request, self.template_name, context)
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
|
|
||||||
cores = request.POST.get('cpu')
|
|
||||||
memory = request.POST.get('ram')
|
|
||||||
storage = request.POST.get('storage')
|
|
||||||
price = request.POST.get('total')
|
|
||||||
|
|
||||||
template_id = int(request.POST.get('config'))
|
|
||||||
manager = OpenNebulaManager()
|
|
||||||
template = manager.get_template(template_id)
|
|
||||||
|
|
||||||
request.session['template'] = VirtualMachineTemplateSerializer(
|
|
||||||
template).data
|
|
||||||
|
|
||||||
if not request.user.is_authenticated():
|
|
||||||
request.session['next'] = reverse('hosting:payment')
|
|
||||||
|
|
||||||
request.session['specs'] = {
|
|
||||||
'cpu': cores,
|
|
||||||
'memory': memory,
|
|
||||||
'disk_size': storage,
|
|
||||||
'price': price,
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect(reverse('hosting:payment'))
|
|
||||||
|
|
||||||
|
|
||||||
class BetaAccessView(FormView):
|
class BetaAccessView(FormView):
|
||||||
template_name = "datacenterlight/beta_access.html"
|
template_name = "datacenterlight/beta_access.html"
|
||||||
form_class = BetaAccessForm
|
form_class = BetaAccessForm
|
||||||
|
@ -274,17 +231,11 @@ class IndexView(CreateView):
|
||||||
memory_field = forms.IntegerField(validators=[self.validate_memory])
|
memory_field = forms.IntegerField(validators=[self.validate_memory])
|
||||||
storage = request.POST.get('storage')
|
storage = request.POST.get('storage')
|
||||||
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
||||||
price = request.POST.get('total')
|
|
||||||
template_id = int(request.POST.get('config'))
|
template_id = int(request.POST.get('config'))
|
||||||
template = VMTemplate.objects.filter(
|
template = VMTemplate.objects.filter(
|
||||||
opennebula_vm_template_id=template_id).first()
|
opennebula_vm_template_id=template_id).first()
|
||||||
template_data = VMTemplateSerializer(template).data
|
template_data = VMTemplateSerializer(template).data
|
||||||
|
|
||||||
name = request.POST.get('name')
|
|
||||||
email = request.POST.get('email')
|
|
||||||
name_field = forms.CharField()
|
|
||||||
email_field = forms.EmailField()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cores = cores_field.clean(cores)
|
cores = cores_field.clean(cores)
|
||||||
except ValidationError as err:
|
except ValidationError as err:
|
||||||
|
@ -311,40 +262,16 @@ class IndexView(CreateView):
|
||||||
extra_tags='storage')
|
extra_tags='storage')
|
||||||
return HttpResponseRedirect(
|
return HttpResponseRedirect(
|
||||||
reverse('datacenterlight:index') + "#order_form")
|
reverse('datacenterlight:index') + "#order_form")
|
||||||
|
amount_to_be_charged = get_vm_price(cpu=cores, memory=memory,
|
||||||
try:
|
disk_size=storage)
|
||||||
name = name_field.clean(name)
|
|
||||||
except ValidationError as err:
|
|
||||||
msg = '{} {}.'.format(name, _('is not a proper name'))
|
|
||||||
messages.add_message(self.request, messages.ERROR, msg,
|
|
||||||
extra_tags='name')
|
|
||||||
return HttpResponseRedirect(
|
|
||||||
reverse('datacenterlight:index') + "#order_form")
|
|
||||||
|
|
||||||
try:
|
|
||||||
email = email_field.clean(email)
|
|
||||||
except ValidationError as err:
|
|
||||||
msg = '{} {}.'.format(email, _('is not a proper email'))
|
|
||||||
messages.add_message(self.request, messages.ERROR, msg,
|
|
||||||
extra_tags='email')
|
|
||||||
return HttpResponseRedirect(
|
|
||||||
reverse('datacenterlight:index') + "#order_form")
|
|
||||||
|
|
||||||
specs = {
|
specs = {
|
||||||
'cpu': cores,
|
'cpu': cores,
|
||||||
'memory': memory,
|
'memory': memory,
|
||||||
'disk_size': storage,
|
'disk_size': storage,
|
||||||
'price': price
|
'price': amount_to_be_charged
|
||||||
}
|
}
|
||||||
|
|
||||||
this_user = {
|
|
||||||
'name': name,
|
|
||||||
'email': email
|
|
||||||
}
|
|
||||||
|
|
||||||
request.session['specs'] = specs
|
request.session['specs'] = specs
|
||||||
request.session['template'] = template_data
|
request.session['template'] = template_data
|
||||||
request.session['user'] = this_user
|
|
||||||
return HttpResponseRedirect(reverse('datacenterlight:payment'))
|
return HttpResponseRedirect(reverse('datacenterlight:payment'))
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
|
@ -407,20 +334,19 @@ class WhyDataCenterLightView(IndexView):
|
||||||
|
|
||||||
class PaymentOrderView(FormView):
|
class PaymentOrderView(FormView):
|
||||||
template_name = 'datacenterlight/landing_payment.html'
|
template_name = 'datacenterlight/landing_payment.html'
|
||||||
form_class = BillingAddressForm
|
|
||||||
|
def get_form_class(self):
|
||||||
|
if self.request.user.is_authenticated():
|
||||||
|
return BillingAddressForm
|
||||||
|
else:
|
||||||
|
return BillingAddressFormSignup
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
form_kwargs = super(PaymentOrderView, self).get_form_kwargs()
|
form_kwargs = super(PaymentOrderView, self).get_form_kwargs()
|
||||||
billing_address_data = self.request.session.get('billing_address_data')
|
# if user is signed in, get billing address
|
||||||
if billing_address_data:
|
if self.request.user.is_authenticated():
|
||||||
form_kwargs.update({
|
form_kwargs.update({
|
||||||
'initial': {
|
'instance': self.request.user.billing_addresses.first()
|
||||||
'cardholder_name': billing_address_data['cardholder_name'],
|
|
||||||
'street_address': billing_address_data['street_address'],
|
|
||||||
'city': billing_address_data['city'],
|
|
||||||
'postal_code': billing_address_data['postal_code'],
|
|
||||||
'country': billing_address_data['country'],
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return form_kwargs
|
return form_kwargs
|
||||||
|
|
||||||
|
@ -428,48 +354,49 @@ class PaymentOrderView(FormView):
|
||||||
context = super(PaymentOrderView, self).get_context_data(**kwargs)
|
context = super(PaymentOrderView, self).get_context_data(**kwargs)
|
||||||
context.update({
|
context.update({
|
||||||
'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
|
'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
|
||||||
'site_url': reverse('datacenterlight:index')
|
'site_url': reverse('datacenterlight:index'),
|
||||||
|
'login_form': HostingUserLoginForm()
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
|
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
if 'specs' not in request.session or 'user' not in request.session:
|
# user is no longer added to session on the index page
|
||||||
|
if 'specs' not in request.session:
|
||||||
return HttpResponseRedirect(reverse('datacenterlight:index'))
|
return HttpResponseRedirect(reverse('datacenterlight:index'))
|
||||||
return self.render_to_response(self.get_context_data())
|
return self.render_to_response(self.get_context_data())
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
# Get billing address data
|
|
||||||
billing_address_data = form.cleaned_data
|
|
||||||
token = form.cleaned_data.get('token')
|
token = form.cleaned_data.get('token')
|
||||||
user = request.session.get('user')
|
if request.user.is_authenticated():
|
||||||
try:
|
this_user = {
|
||||||
CustomUser.objects.get(email=user.get('email'))
|
'email': request.user.email,
|
||||||
except CustomUser.DoesNotExist:
|
'name': request.user.name
|
||||||
password = CustomUser.get_random_password()
|
}
|
||||||
# Register the user, and do not send emails
|
customer = StripeCustomer.get_or_create(
|
||||||
CustomUser.register(user.get('name'),
|
email=this_user.get('email'),
|
||||||
password,
|
|
||||||
user.get('email'),
|
|
||||||
app='dcl',
|
|
||||||
base_url=None, send_email=False)
|
|
||||||
|
|
||||||
# Get or create stripe customer
|
|
||||||
customer = StripeCustomer.get_or_create(email=user.get('email'),
|
|
||||||
token=token)
|
token=token)
|
||||||
|
else:
|
||||||
|
this_user = {
|
||||||
|
'email': form.cleaned_data.get('email'),
|
||||||
|
'name': form.cleaned_data.get('name')
|
||||||
|
}
|
||||||
|
customer = StripeCustomer.create_stripe_api_customer(
|
||||||
|
email=this_user.get('email'),
|
||||||
|
token=token,
|
||||||
|
customer_name=form.cleaned_data.get('name'))
|
||||||
|
request.session['billing_address_data'] = form.cleaned_data
|
||||||
|
request.session['user'] = this_user
|
||||||
|
# Get or create stripe customer
|
||||||
if not customer:
|
if not customer:
|
||||||
form.add_error("__all__", "Invalid credit card")
|
form.add_error("__all__", "Invalid credit card")
|
||||||
return self.render_to_response(
|
return self.render_to_response(
|
||||||
self.get_context_data(form=form))
|
self.get_context_data(form=form))
|
||||||
|
|
||||||
# Create Billing Address
|
|
||||||
billing_address = form.save()
|
|
||||||
request.session['billing_address_data'] = billing_address_data
|
|
||||||
request.session['billing_address'] = billing_address.id
|
|
||||||
request.session['token'] = token
|
request.session['token'] = token
|
||||||
request.session['customer'] = customer.id
|
request.session[
|
||||||
|
'customer'] = customer.id if request.user.is_authenticated() else customer
|
||||||
return HttpResponseRedirect(
|
return HttpResponseRedirect(
|
||||||
reverse('datacenterlight:order_confirmation'))
|
reverse('datacenterlight:order_confirmation'))
|
||||||
else:
|
else:
|
||||||
|
@ -489,9 +416,15 @@ class OrderConfirmationView(DetailView):
|
||||||
if 'token' not in request.session:
|
if 'token' not in request.session:
|
||||||
return HttpResponseRedirect(reverse('datacenterlight:payment'))
|
return HttpResponseRedirect(reverse('datacenterlight:payment'))
|
||||||
stripe_customer_id = request.session.get('customer')
|
stripe_customer_id = request.session.get('customer')
|
||||||
customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
|
if request.user.is_authenticated():
|
||||||
|
customer = StripeCustomer.objects.filter(
|
||||||
|
id=stripe_customer_id).first()
|
||||||
|
stripe_api_cus_id = customer.stripe_id
|
||||||
|
else:
|
||||||
|
stripe_api_cus_id = stripe_customer_id
|
||||||
|
|
||||||
stripe_utils = StripeUtils()
|
stripe_utils = StripeUtils()
|
||||||
card_details = stripe_utils.get_card_details(customer.stripe_id,
|
card_details = stripe_utils.get_card_details(stripe_api_cus_id,
|
||||||
request.session.get(
|
request.session.get(
|
||||||
'token'))
|
'token'))
|
||||||
if not card_details.get('response_object'):
|
if not card_details.get('response_object'):
|
||||||
|
@ -503,7 +436,8 @@ class OrderConfirmationView(DetailView):
|
||||||
context = {
|
context = {
|
||||||
'site_url': reverse('datacenterlight:index'),
|
'site_url': reverse('datacenterlight:index'),
|
||||||
'cc_last4': card_details.get('response_object').get('last4'),
|
'cc_last4': card_details.get('response_object').get('last4'),
|
||||||
'cc_brand': card_details.get('response_object').get('brand')
|
'cc_brand': card_details.get('response_object').get('brand'),
|
||||||
|
'billing_address_data': request.session.get('billing_address_data')
|
||||||
}
|
}
|
||||||
return render(request, self.template_name, context)
|
return render(request, self.template_name, context)
|
||||||
|
|
||||||
|
@ -512,32 +446,43 @@ class OrderConfirmationView(DetailView):
|
||||||
specs = request.session.get('specs')
|
specs = request.session.get('specs')
|
||||||
user = request.session.get('user')
|
user = request.session.get('user')
|
||||||
stripe_customer_id = request.session.get('customer')
|
stripe_customer_id = request.session.get('customer')
|
||||||
customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
|
if request.user.is_authenticated():
|
||||||
billing_address_data = request.session.get('billing_address_data')
|
customer = StripeCustomer.objects.filter(
|
||||||
billing_address_id = request.session.get('billing_address')
|
id=stripe_customer_id).first()
|
||||||
|
stripe_api_cus_id = customer.stripe_id
|
||||||
|
else:
|
||||||
|
stripe_api_cus_id = stripe_customer_id
|
||||||
vm_template_id = template.get('id', 1)
|
vm_template_id = template.get('id', 1)
|
||||||
|
|
||||||
# Make stripe charge to a customer
|
|
||||||
stripe_utils = StripeUtils()
|
stripe_utils = StripeUtils()
|
||||||
card_details = stripe_utils.get_card_details(customer.stripe_id,
|
card_details = stripe_utils.get_card_details(stripe_api_cus_id,
|
||||||
request.session.get(
|
request.session.get(
|
||||||
'token'))
|
'token'))
|
||||||
if not card_details.get('response_object'):
|
if not card_details.get('response_object'):
|
||||||
msg = card_details.get('error')
|
msg = card_details.get('error')
|
||||||
messages.add_message(self.request, messages.ERROR, msg,
|
messages.add_message(self.request, messages.ERROR, msg,
|
||||||
extra_tags='failed_payment')
|
extra_tags='failed_payment')
|
||||||
return HttpResponseRedirect(
|
response = {
|
||||||
reverse('datacenterlight:payment') + '#payment_error')
|
'status': False,
|
||||||
|
'redirect': "{url}#{section}".format(
|
||||||
|
url=reverse('datacenterlight:payment'),
|
||||||
|
section='payment_error'),
|
||||||
|
'msg_title': str(_('Error.')),
|
||||||
|
'msg_body': str(
|
||||||
|
_('There was a payment related error.'
|
||||||
|
' On close of this popup, you will be redirected back to'
|
||||||
|
' the payment page.'))
|
||||||
|
}
|
||||||
|
return HttpResponse(json.dumps(response),
|
||||||
|
content_type="application/json")
|
||||||
card_details_dict = card_details.get('response_object')
|
card_details_dict = card_details.get('response_object')
|
||||||
cpu = specs.get('cpu')
|
cpu = specs.get('cpu')
|
||||||
memory = specs.get('memory')
|
memory = specs.get('memory')
|
||||||
disk_size = specs.get('disk_size')
|
disk_size = specs.get('disk_size')
|
||||||
amount_to_be_charged = (cpu * 5) + (memory * 2) + (disk_size * 0.6)
|
amount_to_be_charged = specs.get('price')
|
||||||
plan_name = "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format(
|
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
|
||||||
cpu=cpu,
|
|
||||||
memory=memory,
|
memory=memory,
|
||||||
disk_size=disk_size)
|
disk_size=disk_size)
|
||||||
|
|
||||||
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
||||||
ram=memory,
|
ram=memory,
|
||||||
ssd=disk_size,
|
ssd=disk_size,
|
||||||
|
@ -548,7 +493,7 @@ class OrderConfirmationView(DetailView):
|
||||||
name=plan_name,
|
name=plan_name,
|
||||||
stripe_plan_id=stripe_plan_id)
|
stripe_plan_id=stripe_plan_id)
|
||||||
subscription_result = stripe_utils.subscribe_customer_to_plan(
|
subscription_result = stripe_utils.subscribe_customer_to_plan(
|
||||||
customer.stripe_id,
|
stripe_api_cus_id,
|
||||||
[{"plan": stripe_plan.get(
|
[{"plan": stripe_plan.get(
|
||||||
'response_object').stripe_plan_id}])
|
'response_object').stripe_plan_id}])
|
||||||
stripe_subscription_obj = subscription_result.get('response_object')
|
stripe_subscription_obj = subscription_result.get('response_object')
|
||||||
|
@ -558,11 +503,96 @@ class OrderConfirmationView(DetailView):
|
||||||
msg = subscription_result.get('error')
|
msg = subscription_result.get('error')
|
||||||
messages.add_message(self.request, messages.ERROR, msg,
|
messages.add_message(self.request, messages.ERROR, msg,
|
||||||
extra_tags='failed_payment')
|
extra_tags='failed_payment')
|
||||||
return HttpResponseRedirect(
|
response = {
|
||||||
reverse('datacenterlight:payment') + '#payment_error')
|
'status': False,
|
||||||
|
'redirect': "{url}#{section}".format(
|
||||||
|
url=reverse('datacenterlight:payment'),
|
||||||
|
section='payment_error'),
|
||||||
|
'msg_title': str(_('Error.')),
|
||||||
|
'msg_body': str(
|
||||||
|
_('There was a payment related error.'
|
||||||
|
' On close of this popup, you will be redirected back to'
|
||||||
|
' the payment page.'))
|
||||||
|
}
|
||||||
|
return HttpResponse(json.dumps(response),
|
||||||
|
content_type="application/json")
|
||||||
|
|
||||||
|
# Create user if the user is not logged in and if he is not already
|
||||||
|
# registered
|
||||||
|
if not request.user.is_authenticated():
|
||||||
|
try:
|
||||||
|
custom_user = CustomUser.objects.get(
|
||||||
|
email=user.get('email'))
|
||||||
|
customer = StripeCustomer.objects.filter(
|
||||||
|
user_id=custom_user.id).first()
|
||||||
|
stripe_customer_id = customer.id
|
||||||
|
except CustomUser.DoesNotExist:
|
||||||
|
logger.debug(
|
||||||
|
"Customer {} does not exist.".format(user.get('email')))
|
||||||
|
password = CustomUser.get_random_password()
|
||||||
|
base_url = "{0}://{1}".format(self.request.scheme,
|
||||||
|
self.request.get_host())
|
||||||
|
custom_user = CustomUser.register(
|
||||||
|
user.get('name'), password,
|
||||||
|
user.get('email'),
|
||||||
|
app='dcl', base_url=base_url, send_email=True,
|
||||||
|
account_details=password
|
||||||
|
)
|
||||||
|
logger.debug("Created user {}.".format(user.get('email')))
|
||||||
|
stripe_customer = StripeCustomer.objects. \
|
||||||
|
create(user=custom_user, stripe_id=stripe_api_cus_id)
|
||||||
|
stripe_customer_id = stripe_customer.id
|
||||||
|
new_user = authenticate(username=custom_user.email,
|
||||||
|
password=password)
|
||||||
|
login(request, new_user)
|
||||||
|
else:
|
||||||
|
customer = StripeCustomer.objects.filter(
|
||||||
|
id=stripe_customer_id).first()
|
||||||
|
custom_user = customer.user
|
||||||
|
stripe_customer_id = customer.id
|
||||||
|
|
||||||
|
# Save billing address
|
||||||
|
billing_address_data = request.session.get('billing_address_data')
|
||||||
|
logger.debug('billing_address_data is {}'.format(billing_address_data))
|
||||||
|
billing_address_data.update({
|
||||||
|
'user': custom_user.id
|
||||||
|
})
|
||||||
|
billing_address_user_form = UserBillingAddressForm(
|
||||||
|
instance=custom_user.billing_addresses.first(),
|
||||||
|
data=billing_address_data)
|
||||||
|
billing_address = billing_address_user_form.save()
|
||||||
|
billing_address_id = billing_address.id
|
||||||
|
logger.debug("billing address id = {}".format(billing_address_id))
|
||||||
|
user = {
|
||||||
|
'name': custom_user.name,
|
||||||
|
'email': custom_user.email,
|
||||||
|
'pass': custom_user.password,
|
||||||
|
'request_scheme': request.scheme,
|
||||||
|
'request_host': request.get_host(),
|
||||||
|
'language': get_language(),
|
||||||
|
}
|
||||||
|
|
||||||
create_vm_task.delay(vm_template_id, user, specs, template,
|
create_vm_task.delay(vm_template_id, user, specs, template,
|
||||||
stripe_customer_id, billing_address_data,
|
stripe_customer_id, billing_address_data,
|
||||||
billing_address_id,
|
billing_address_id,
|
||||||
stripe_subscription_obj, card_details_dict)
|
stripe_subscription_obj, card_details_dict)
|
||||||
request.session['order_confirmation'] = True
|
for session_var in ['specs', 'template', 'billing_address',
|
||||||
return HttpResponseRedirect(reverse('datacenterlight:order_success'))
|
'billing_address_data',
|
||||||
|
'token', 'customer']:
|
||||||
|
if session_var in request.session:
|
||||||
|
del request.session[session_var]
|
||||||
|
|
||||||
|
response = {
|
||||||
|
'status': True,
|
||||||
|
'redirect': reverse(
|
||||||
|
'hosting:virtual_machines') if request.user.is_authenticated() else reverse(
|
||||||
|
'datacenterlight:index'),
|
||||||
|
'msg_title': str(_('Thank you for the order.')),
|
||||||
|
'msg_body': str(
|
||||||
|
_('Your VM will be up and running in a few moments.'
|
||||||
|
' We will send you a confirmation email as soon as'
|
||||||
|
' it is ready.'))
|
||||||
|
}
|
||||||
|
|
||||||
|
return HttpResponse(json.dumps(response),
|
||||||
|
content_type="application/json")
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2017-09-23 19:00+0530\n"
|
"POT-Creation-Date: 2017-09-24 12:34+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -268,7 +268,7 @@ msgstr ""
|
||||||
"Link klickst</a>.<br/>\n"
|
"Link klickst</a>.<br/>\n"
|
||||||
|
|
||||||
msgid "My VM page"
|
msgid "My VM page"
|
||||||
msgstr ""
|
msgstr "Meine VM page"
|
||||||
|
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
|
@ -306,6 +306,9 @@ msgstr "Registrieren"
|
||||||
msgid "Forgot your password ? "
|
msgid "Forgot your password ? "
|
||||||
msgstr "Passwort vergessen?"
|
msgstr "Passwort vergessen?"
|
||||||
|
|
||||||
|
msgid "Resend activation link"
|
||||||
|
msgstr "Aktivierungslink noch einmal senden"
|
||||||
|
|
||||||
msgid "Notifications"
|
msgid "Notifications"
|
||||||
msgstr "Benachrichtigungen"
|
msgstr "Benachrichtigungen"
|
||||||
|
|
||||||
|
@ -385,7 +388,7 @@ msgid "Processing..."
|
||||||
msgstr "Abarbeitung..."
|
msgstr "Abarbeitung..."
|
||||||
|
|
||||||
msgid "Hold tight, we are processing your request"
|
msgid "Hold tight, we are processing your request"
|
||||||
msgstr "Bitte warten - wir verbeiten Deine Anfrage gerade"
|
msgstr "Bitte warten - wir bearbeiten Deine Anfrage gerade"
|
||||||
|
|
||||||
msgid "Some problem encountered. Please try again later."
|
msgid "Some problem encountered. Please try again later."
|
||||||
msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
|
msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
|
||||||
|
@ -620,7 +623,7 @@ msgid "View Detail"
|
||||||
msgstr "Details anzeigen"
|
msgstr "Details anzeigen"
|
||||||
|
|
||||||
msgid "login"
|
msgid "login"
|
||||||
msgstr "Einloggen"
|
msgstr "anmelden"
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Thank you for signing up. We have sent an email to you. Please follow the "
|
"Thank you for signing up. We have sent an email to you. Please follow the "
|
||||||
|
|
34
hosting/migrations/0043_vmdetail.py
Normal file
34
hosting/migrations/0043_vmdetail.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2017-09-24 18:12
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('hosting', '0042_hostingorder_subscription_id'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='VMDetail',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('vm_id', models.IntegerField(default=0)),
|
||||||
|
('disk_size', models.FloatField(default=0.0)),
|
||||||
|
('cores', models.FloatField(default=0.0)),
|
||||||
|
('memory', models.FloatField(default=0.0)),
|
||||||
|
('configuration', models.CharField(default='', max_length=25)),
|
||||||
|
('ipv4', models.TextField(default='')),
|
||||||
|
('ipv6', models.TextField(default='')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('terminated_at', models.DateTimeField(null=True)),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
|
@ -159,3 +159,16 @@ class HostingBill(AssignPermissionsMixin, models.Model):
|
||||||
instance = cls.objects.create(customer=customer,
|
instance = cls.objects.create(customer=customer,
|
||||||
billing_address=billing_address)
|
billing_address=billing_address)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class VMDetail(models.Model):
|
||||||
|
user = models.ForeignKey(CustomUser)
|
||||||
|
vm_id = models.IntegerField(default=0)
|
||||||
|
disk_size = models.FloatField(default=0.0)
|
||||||
|
cores = models.FloatField(default=0.0)
|
||||||
|
memory = models.FloatField(default=0.0)
|
||||||
|
configuration = models.CharField(default='', max_length=25)
|
||||||
|
ipv4 = models.TextField(default='')
|
||||||
|
ipv6 = models.TextField(default='')
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
terminated_at = models.DateTimeField(null=True)
|
||||||
|
|
|
@ -857,6 +857,8 @@ a.list-group-item-danger:focus,
|
||||||
background: rgba(235, 204, 209, 0.2);
|
background: rgba(235, 204, 209, 0.2);
|
||||||
}
|
}
|
||||||
.has-error .form-control,
|
.has-error .form-control,
|
||||||
|
.has-error .form-control:focus,
|
||||||
|
.has-error .form-control:active,
|
||||||
.has-error .input-group-addon {
|
.has-error .input-group-addon {
|
||||||
color: #eb4d5c;
|
color: #eb4d5c;
|
||||||
border-color: #eb4d5c;
|
border-color: #eb4d5c;
|
||||||
|
@ -870,3 +872,48 @@ a.list-group-item-danger.active:focus {
|
||||||
.panel-danger > .panel-heading .badge {
|
.panel-danger > .panel-heading .badge {
|
||||||
background-color: #eb4d5c;
|
background-color: #eb4d5c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* bootstrap input box-shadom disable */
|
||||||
|
.has-error .form-control:focus,
|
||||||
|
.has-error .form-control:active,
|
||||||
|
.has-success .form-control:focus,
|
||||||
|
.has-success .form-control:active {
|
||||||
|
box-shadow: inset 0 0 1px rgba(0,0,0,0.25);
|
||||||
|
}
|
||||||
|
.checkmark {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.checkmark:after {
|
||||||
|
/*Add another block-level blank space*/
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
/*Make it a small rectangle so the border will create an L-shape*/
|
||||||
|
width: 25px;
|
||||||
|
height: 60px;
|
||||||
|
/*Add a white border on the bottom and left, creating that 'L' */
|
||||||
|
border: solid #777;
|
||||||
|
border-width: 0 3px 3px 0;
|
||||||
|
/*Rotate the L 45 degrees to turn it into a checkmark*/
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.closemark {
|
||||||
|
display: inline-block;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.closemark:before, .closemark:after {
|
||||||
|
position: absolute;
|
||||||
|
left: 25px;
|
||||||
|
content: ' ';
|
||||||
|
height: 50px;
|
||||||
|
width: 2px;
|
||||||
|
background-color: #777;
|
||||||
|
}
|
||||||
|
.closemark:before {
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
.closemark:after {
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
|
@ -22,7 +22,100 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.summary-box .content {
|
.summary-box .content {
|
||||||
|
|
||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* landing page payment new style */
|
||||||
|
.last-p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.dcl-payment-section {
|
||||||
|
max-width: 391px;
|
||||||
|
margin: 0 auto 30px;
|
||||||
|
padding: 0 10px 30px;
|
||||||
|
border-bottom: 1px solid #edebeb;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.dcl-payment-section hr{
|
||||||
|
margin-top: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.dcl-payment-section .top-hr {
|
||||||
|
margin-left: -10px;
|
||||||
|
}
|
||||||
|
.dcl-payment-section h3 {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.dcl-payment-section p {
|
||||||
|
padding: 0 5px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.dcl-payment-section .card-warning-content {
|
||||||
|
padding: 8px 10px;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
.dcl-payment-order strong{
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
.dcl-payment-order p {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
.dcl-payment-section .form-group {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.dcl-payment-section .form-control {
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 6px 12px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
.dcl-payment-user {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dcl-payment-user h4 {
|
||||||
|
font-weight: 600;
|
||||||
|
padding-left: 5px;
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.dcl-payment-grid {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.dcl-payment-box {
|
||||||
|
width: 50%;
|
||||||
|
position: relative;
|
||||||
|
padding: 0 30px;
|
||||||
|
}
|
||||||
|
.dcl-payment-box:nth-child(2) {
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
.dcl-payment-box:nth-child(4) {
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
.dcl-payment-section {
|
||||||
|
padding: 15px 10px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-bottom-width: 5px;
|
||||||
|
}
|
||||||
|
.dcl-payment-box:nth-child(2n) .dcl-payment-section {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.dcl-payment-box:nth-child(1):after,
|
||||||
|
.dcl-payment-box:nth-child(2):after {
|
||||||
|
content: ' ';
|
||||||
|
display: block;
|
||||||
|
background: #eee;
|
||||||
|
width: 1px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
z-index: 2;
|
||||||
|
top: 20px;
|
||||||
|
bottom: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -79,7 +79,6 @@ $(document).ready(function() {
|
||||||
$('html,body').scrollTop(scrollmem);
|
$('html,body').scrollTop(scrollmem);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.modal-text').removeClass('hide');
|
|
||||||
var create_vm_form = $('#virtual_machine_create_form');
|
var create_vm_form = $('#virtual_machine_create_form');
|
||||||
create_vm_form.submit(function () {
|
create_vm_form.submit(function () {
|
||||||
$('#btn-create-vm').prop('disabled', true);
|
$('#btn-create-vm').prop('disabled', true);
|
||||||
|
@ -90,26 +89,28 @@ $(document).ready(function() {
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
if (data.status === true) {
|
if (data.status === true) {
|
||||||
fa_icon = $('.modal-icon > .fa');
|
fa_icon = $('.modal-icon > .fa');
|
||||||
fa_icon.attr('class', 'fa fa-check');
|
fa_icon.attr('class', 'checkmark');
|
||||||
$('.modal-header > .close').attr('class', 'close');
|
// $('.modal-header > .close').removeClass('hidden');
|
||||||
$('#createvm-modal-title').text(data.msg_title);
|
$('#createvm-modal-title').text(data.msg_title);
|
||||||
$('#createvm-modal-body').text(data.msg_body);
|
$('#createvm-modal-body').text(data.msg_body);
|
||||||
$('#createvm-modal').on('hidden.bs.modal', function () {
|
$('#createvm-modal-done-btn')
|
||||||
window.location = data.redirect;
|
.attr('href', data.redirect)
|
||||||
})
|
.removeClass('hide');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function (xmlhttprequest, textstatus, message) {
|
error: function (xmlhttprequest, textstatus, message) {
|
||||||
fa_icon = $('.modal-icon > .fa');
|
fa_icon = $('.modal-icon > .fa');
|
||||||
fa_icon.attr('class', 'fa fa-times');
|
fa_icon.attr('class', 'fa fa-close');
|
||||||
$('.modal-header > .close').attr('class', 'close');
|
|
||||||
$('.modal-text').addClass('hide');
|
|
||||||
if (typeof(create_vm_error_message) !== 'undefined') {
|
if (typeof(create_vm_error_message) !== 'undefined') {
|
||||||
$('#createvm-modal-title').text(create_vm_error_message);
|
$('#createvm-modal-text').text(create_vm_error_message);
|
||||||
}
|
}
|
||||||
$('#btn-create-vm').prop('disabled', false);
|
$('#btn-create-vm').prop('disabled', false);
|
||||||
|
$('#createvm-modal-close-btn').removeClass('hide');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
$('#createvm-modal').on('hidden.bs.modal', function () {
|
||||||
|
$(this).find('.modal-footer .btn').addClass('hide');
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
|
@ -47,20 +47,5 @@
|
||||||
window.location.href = '{{next_url}}';
|
window.location.href = '{{next_url}}';
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
window.onload = function () {
|
|
||||||
{% for user_key in keys %}
|
|
||||||
var locale_date = moment.utc(document.getElementById("ssh-created_at-{{user_key.id}}").textContent,'YYYY-MM-DD HH:mm').toDate();
|
|
||||||
locale_date = moment(locale_date).format("YYYY-MM-DD h:mm:ss a");
|
|
||||||
document.getElementById('ssh-created_at-{{user_key.id}}').innerHTML = locale_date;
|
|
||||||
{% endfor %}
|
|
||||||
};
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
{%endblock%}
|
{%endblock%}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
<a class="unlink" href="{% url 'hosting:signup' %}">{% trans "Sign up"%}</a>
|
<a class="unlink" href="{% url 'hosting:signup' %}">{% trans "Sign up"%}</a>
|
||||||
<span class="text"> or </span>
|
<span class="text"> or </span>
|
||||||
<a class="unlink" href="{% url 'hosting:reset_password' %}">{% trans "Forgot your password ? "%}</a>
|
<a class="unlink" href="{% url 'hosting:reset_password' %}">{% trans "Forgot your password ? "%}</a>
|
||||||
|
<span class="text"> or </span><br/>
|
||||||
|
<a class="unlink" href="{% url 'hosting:resend_activation_link' %}">{% trans "Resend activation link"%}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -42,7 +42,9 @@
|
||||||
<p>
|
<p>
|
||||||
<strong>{% trans "Status" %}: </strong>
|
<strong>{% trans "Status" %}: </strong>
|
||||||
<strong>
|
<strong>
|
||||||
{% if order.status == 'Approved' %}
|
{% if vm.terminated_at %}
|
||||||
|
<span class="vm-color-failed">{% trans "Terminated" %}</span>
|
||||||
|
{% elif order.status == 'Approved' %}
|
||||||
<span class="vm-color-online">{% trans "Approved" %}</span>
|
<span class="vm-color-online">{% trans "Approved" %}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="vm-status-failed">{% trans "Declined" %}</span>
|
<span class="vm-status-failed">{% trans "Declined" %}</span>
|
||||||
|
@ -96,14 +98,14 @@
|
||||||
</p>
|
</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
{% comment %}
|
{% if vm.created_at %}
|
||||||
<p>
|
<p>
|
||||||
<span>{% trans "Period" %}</span>
|
<span>{% trans "Period" %}: </span>
|
||||||
<span class="pull-right">{{}}</span>
|
<span>{{ vm.created_at|date:'Y/m/d' }} - {% if vm.terminated_at %}{{ vm.terminated_at|date:'Y/m/d' }}{% else %}{% now 'Y/m/d' %}{% endif %}</span>
|
||||||
</p>
|
</p>
|
||||||
{% endcomment %}
|
{% endif %}
|
||||||
<p>
|
<p>
|
||||||
<span>{% trans "Cores" %}</span>
|
<span>{% trans "Cores" %}: </span>
|
||||||
{% if vm.cores %}
|
{% if vm.cores %}
|
||||||
<span class="pull-right">{{vm.cores|floatformat}}</span>
|
<span class="pull-right">{{vm.cores|floatformat}}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -111,11 +113,11 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span>{% trans "Memory" %}</span>
|
<span>{% trans "Memory" %}: </span>
|
||||||
<span class="pull-right">{{vm.memory}} GB</span>
|
<span class="pull-right">{{vm.memory}} GB</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span>{% trans "Disk space" %}</span>
|
<span>{% trans "Disk space" %}: </span>
|
||||||
<span class="pull-right">{{vm.disk_size}} GB</span>
|
<span class="pull-right">{{vm.disk_size}} GB</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
@ -160,22 +162,19 @@
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close hidden" data-dismiss="modal"
|
|
||||||
aria-label="create-vm-close">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="modal-icon">
|
<div class="modal-icon">
|
||||||
<i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
|
<i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
|
||||||
<span class="sr-only">{% trans "Processing..." %}</span>
|
<span class="sr-only">{% trans "Processing..." %}</span>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="modal-title" id="createvm-modal-title">
|
<h4 class="modal-title" id="createvm-modal-title"></h4>
|
||||||
</h4>
|
|
||||||
<div class="modal-text" id="createvm-modal-body">
|
<div class="modal-text" id="createvm-modal-body">
|
||||||
{% trans "Hold tight, we are processing your request" %}
|
{% trans "Hold tight, we are processing your request" %}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
<a id="createvm-modal-done-btn" class="btn btn-success btn-ok btn-wide hide" href="{% url 'hosting:virtual_machines' %}">{% trans "OK" %}</a>
|
||||||
|
<button id="createvm-modal-close-btn" type="button" class="btn btn-danger btn-ok btn-wide hide" data-dismiss="modal" aria-label="create-vm-close">{% trans "Close" %}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
36
hosting/templates/hosting/resend_activation_link.html
Normal file
36
hosting/templates/hosting/resend_activation_link.html
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{% extends "hosting/base_short.html" %}
|
||||||
|
{% load staticfiles bootstrap3%}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block navbar %}
|
||||||
|
{% include 'hosting/includes/_navbar_transparent.html' %}
|
||||||
|
{% endblock navbar %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="auth-container">
|
||||||
|
<div class="auth-bg"></div>
|
||||||
|
<div class="auth-center">
|
||||||
|
<div class="auth-title">
|
||||||
|
<h2>{% trans "Your VM hosted in Switzerland"%}</h2>
|
||||||
|
</div>
|
||||||
|
<div class="auth-content">
|
||||||
|
<div class="intro-message auth-box sign-up">
|
||||||
|
<h2 class="section-heading">{% trans "Resend activation link"%}</h2>
|
||||||
|
<form action="{% url 'hosting:resend_activation_link' %}" method="post" class="form" novalidate>
|
||||||
|
{% csrf_token %}
|
||||||
|
{% for field in form %}
|
||||||
|
{% bootstrap_field field show_label=False %}
|
||||||
|
{% endfor %}
|
||||||
|
{% buttons %}
|
||||||
|
<button type="submit" class="btn btn-block btn-success">
|
||||||
|
{% trans "Submit"%}
|
||||||
|
</button>
|
||||||
|
{% endbuttons %}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -101,21 +101,5 @@
|
||||||
window.location.href = '{{next_url}}';
|
window.location.href = '{{next_url}}';
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
window.onload = function () {
|
|
||||||
{% for user_key in keys %}
|
|
||||||
var locale_date = moment.utc(document.getElementById("ssh-created_at-{{user_key.id}}").textContent,'YYYY-MM-DD HH:mm').toDate();
|
|
||||||
locale_date = moment(locale_date).format("YYYY-MM-DD h:mm:ss a");
|
|
||||||
document.getElementById('ssh-created_at-{{user_key.id}}').innerHTML = locale_date;
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
};
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
{%endblock%}
|
{%endblock%}
|
||||||
|
|
||||||
|
|
|
@ -123,8 +123,9 @@
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="modal-icon"><i class="fa fa-check" aria-hidden="true"></i></div>
|
<div class="modal-icon"><i class="checkmark" aria-hidden="true"></i></div>
|
||||||
<h4 class="modal-title" id="ModalLabel">{% blocktrans with machine_name=virtual_machine.name %}Your Virtual Machine <strong>{{machine_name}}</strong> is successfully terminated!{% endblocktrans %}</h4>
|
<h4 class="modal-title"></h4>
|
||||||
|
<div class="modal-text" id="ModalLabel">{% blocktrans with machine_name=virtual_machine.name %}Your Virtual Machine <strong>{{machine_name}}</strong> is successfully terminated!{% endblocktrans %}</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a href="{% url 'hosting:virtual_machines' %}" class="btn btn-success btn-wide">{% trans "OK" %}</a>
|
<a href="{% url 'hosting:virtual_machines' %}" class="btn btn-success btn-wide">{% trans "OK" %}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,7 +13,12 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not error %}
|
{% if not error %}
|
||||||
<div class="dashboard-subtitle">
|
<div class="dashboard-subtitle">
|
||||||
<p>{% trans 'To create a new virtual machine, click "Create VM"' %}</p>
|
<p>{% trans 'To create a new virtual machine, click "Create VM"' %}
|
||||||
|
{% if show_create_ssh_key_msg %}
|
||||||
|
{% url 'hosting:create_ssh_key' as create_ssh_url %}
|
||||||
|
<br/>{% blocktrans %}To access your VM, add your SSH key <a href="{{create_ssh_url}}">here</a>{% endblocktrans %}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<a class="btn btn-vm" href="{% url 'hosting:create_virtual_machine' %}"><span class="css-plus"></span> <span>{% trans "CREATE VM" %}</span></a>
|
<a class="btn btn-vm" href="{% url 'hosting:create_virtual_machine' %}"><span class="css-plus"></span> <span>{% trans "CREATE VM" %}</span></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,8 +8,7 @@ from .views import (
|
||||||
MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView,
|
MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView,
|
||||||
HostingPricingView, CreateVirtualMachinesView, HostingBillListView,
|
HostingPricingView, CreateVirtualMachinesView, HostingBillListView,
|
||||||
HostingBillDetailView, SSHKeyDeleteView, SSHKeyCreateView, SSHKeyListView,
|
HostingBillDetailView, SSHKeyDeleteView, SSHKeyCreateView, SSHKeyListView,
|
||||||
SSHKeyChoiceView, DashboardView, SettingsView)
|
SSHKeyChoiceView, DashboardView, SettingsView, ResendActivationEmailView)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'index/?$', IndexView.as_view(), name='index'),
|
url(r'index/?$', IndexView.as_view(), name='index'),
|
||||||
|
@ -52,6 +51,8 @@ urlpatterns = [
|
||||||
url(r'signup/?$', SignupView.as_view(), name='signup'),
|
url(r'signup/?$', SignupView.as_view(), name='signup'),
|
||||||
url(r'signup-validate/?$', SignupValidateView.as_view(),
|
url(r'signup-validate/?$', SignupValidateView.as_view(),
|
||||||
name='signup-validate'),
|
name='signup-validate'),
|
||||||
|
url(r'resend-activation-link/?$', ResendActivationEmailView.as_view(),
|
||||||
|
name='resend_activation_link'),
|
||||||
url(r'reset-password/?$', PasswordResetView.as_view(),
|
url(r'reset-password/?$', PasswordResetView.as_view(),
|
||||||
name='reset_password'),
|
name='reset_password'),
|
||||||
url(r'reset-password-confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
|
url(r'reset-password-confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
|
from datetime import datetime
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
@ -33,20 +34,25 @@ from membership.models import CustomUser, StripeCustomer
|
||||||
from opennebula_api.models import OpenNebulaManager
|
from opennebula_api.models import OpenNebulaManager
|
||||||
from opennebula_api.serializers import VirtualMachineSerializer, \
|
from opennebula_api.serializers import VirtualMachineSerializer, \
|
||||||
VirtualMachineTemplateSerializer, VMTemplateSerializer
|
VirtualMachineTemplateSerializer, VMTemplateSerializer
|
||||||
from utils.forms import BillingAddressForm, PasswordResetRequestForm, \
|
from utils.forms import (
|
||||||
UserBillingAddressForm
|
BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm,
|
||||||
|
ResendActivationEmailForm
|
||||||
|
)
|
||||||
|
from utils.hosting_utils import get_vm_price
|
||||||
from utils.mailer import BaseEmail
|
from utils.mailer import BaseEmail
|
||||||
from utils.stripe_utils import StripeUtils
|
from utils.stripe_utils import StripeUtils
|
||||||
from utils.views import (
|
from utils.views import (
|
||||||
PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin
|
PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin,
|
||||||
|
ResendActivationLinkViewMixin
|
||||||
)
|
)
|
||||||
from .forms import HostingUserSignupForm, HostingUserLoginForm, \
|
from .forms import HostingUserSignupForm, HostingUserLoginForm, \
|
||||||
UserHostingKeyForm, generate_ssh_key_name
|
UserHostingKeyForm, generate_ssh_key_name
|
||||||
from .mixins import ProcessVMSelectionMixin
|
from .mixins import ProcessVMSelectionMixin
|
||||||
from .models import HostingOrder, HostingBill, HostingPlan, UserHostingKey
|
from .models import (
|
||||||
|
HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail
|
||||||
|
)
|
||||||
from datacenterlight.models import VMTemplate
|
from datacenterlight.models import VMTemplate
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a \
|
CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a \
|
||||||
|
@ -282,6 +288,14 @@ class SignupValidatedView(SignupValidateView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class ResendActivationEmailView(ResendActivationLinkViewMixin):
|
||||||
|
template_name = 'hosting/resend_activation_link.html'
|
||||||
|
form_class = ResendActivationEmailForm
|
||||||
|
success_url = reverse_lazy('hosting:login')
|
||||||
|
email_template_path = 'datacenterlight/emails/'
|
||||||
|
email_template_name = 'user_activation'
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetView(PasswordResetViewMixin):
|
class PasswordResetView(PasswordResetViewMixin):
|
||||||
site = 'dcl'
|
site = 'dcl'
|
||||||
template_name = 'hosting/reset_password.html'
|
template_name = 'hosting/reset_password.html'
|
||||||
|
@ -599,13 +613,6 @@ class PaymentVMView(LoginRequiredMixin, FormView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
if not UserHostingKey.objects.filter(user=self.request.user).exists():
|
|
||||||
messages.success(
|
|
||||||
request,
|
|
||||||
'In order to create a VM, you create/upload your SSH KEY first.'
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect(reverse('hosting:ssh_keys'))
|
|
||||||
|
|
||||||
if 'next' in request.session:
|
if 'next' in request.session:
|
||||||
del request.session['next']
|
del request.session['next']
|
||||||
|
|
||||||
|
@ -677,6 +684,11 @@ class OrdersHostingDetailView(LoginRequiredMixin,
|
||||||
|
|
||||||
if obj is not None:
|
if obj is not None:
|
||||||
# invoice for previous order
|
# invoice for previous order
|
||||||
|
try:
|
||||||
|
vm_detail = VMDetail.objects.get(vm_id=obj.vm_id)
|
||||||
|
context['vm'] = vm_detail.__dict__
|
||||||
|
context['vm']['name'] = '{}-{}'.format(context['vm']['configuration'], context['vm']['vm_id'])
|
||||||
|
except VMDetail.DoesNotExist:
|
||||||
try:
|
try:
|
||||||
manager = OpenNebulaManager(
|
manager = OpenNebulaManager(
|
||||||
email=owner.email, password=owner.password
|
email=owner.email, password=owner.password
|
||||||
|
@ -756,12 +768,11 @@ class OrdersHostingDetailView(LoginRequiredMixin,
|
||||||
cpu = specs.get('cpu')
|
cpu = specs.get('cpu')
|
||||||
memory = specs.get('memory')
|
memory = specs.get('memory')
|
||||||
disk_size = specs.get('disk_size')
|
disk_size = specs.get('disk_size')
|
||||||
amount_to_be_charged = (cpu * 5) + (memory * 2) + (disk_size * 0.6)
|
amount_to_be_charged = get_vm_price(cpu=cpu, memory=memory,
|
||||||
plan_name = "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format(
|
disk_size=disk_size)
|
||||||
cpu=cpu,
|
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
|
||||||
memory=memory,
|
memory=memory,
|
||||||
disk_size=disk_size)
|
disk_size=disk_size)
|
||||||
|
|
||||||
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
||||||
ram=memory,
|
ram=memory,
|
||||||
ssd=disk_size,
|
ssd=disk_size,
|
||||||
|
@ -806,7 +817,8 @@ class OrdersHostingDetailView(LoginRequiredMixin,
|
||||||
'status': True,
|
'status': True,
|
||||||
'redirect': reverse('hosting:virtual_machines'),
|
'redirect': reverse('hosting:virtual_machines'),
|
||||||
'msg_title': str(_('Thank you for the order.')),
|
'msg_title': str(_('Thank you for the order.')),
|
||||||
'msg_body': str(_('Your VM will be up and running in a few moments.'
|
'msg_body': str(
|
||||||
|
_('Your VM will be up and running in a few moments.'
|
||||||
' We will send you a confirmation email as soon as'
|
' We will send you a confirmation email as soon as'
|
||||||
' it is ready.'))
|
' it is ready.'))
|
||||||
}
|
}
|
||||||
|
@ -866,6 +878,10 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
|
||||||
context = {'error': 'connection'}
|
context = {'error': 'connection'}
|
||||||
else:
|
else:
|
||||||
context = super(ListView, self).get_context_data(**kwargs)
|
context = super(ListView, self).get_context_data(**kwargs)
|
||||||
|
if UserHostingKey.objects.filter(user=self.request.user).exists():
|
||||||
|
context['show_create_ssh_key_msg'] = False
|
||||||
|
else:
|
||||||
|
context['show_create_ssh_key_msg'] = True
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@ -886,15 +902,6 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
||||||
raise ValidationError(_('Invalid storage size'))
|
raise ValidationError(_('Invalid storage size'))
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
if not UserHostingKey.objects.filter(user=self.request.user).exists():
|
|
||||||
messages.success(
|
|
||||||
request,
|
|
||||||
_(
|
|
||||||
'In order to create a VM, you need to '
|
|
||||||
'create/upload your SSH KEY first.'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return HttpResponseRedirect(reverse('hosting:ssh_keys'))
|
|
||||||
context = {'templates': VMTemplate.objects.all()}
|
context = {'templates': VMTemplate.objects.all()}
|
||||||
return render(request, self.template_name, context)
|
return render(request, self.template_name, context)
|
||||||
|
|
||||||
|
@ -905,7 +912,6 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
||||||
memory_field = forms.IntegerField(validators=[self.validate_memory])
|
memory_field = forms.IntegerField(validators=[self.validate_memory])
|
||||||
storage = request.POST.get('storage')
|
storage = request.POST.get('storage')
|
||||||
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
||||||
price = request.POST.get('total')
|
|
||||||
template_id = int(request.POST.get('config'))
|
template_id = int(request.POST.get('config'))
|
||||||
template = VMTemplate.objects.filter(
|
template = VMTemplate.objects.filter(
|
||||||
opennebula_vm_template_id=template_id).first()
|
opennebula_vm_template_id=template_id).first()
|
||||||
|
@ -937,7 +943,8 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
||||||
extra_tags='storage')
|
extra_tags='storage')
|
||||||
return HttpResponseRedirect(
|
return HttpResponseRedirect(
|
||||||
reverse('datacenterlight:index') + "#order_form")
|
reverse('datacenterlight:index') + "#order_form")
|
||||||
|
price = get_vm_price(cpu=cores, memory=memory,
|
||||||
|
disk_size=storage)
|
||||||
specs = {
|
specs = {
|
||||||
'cpu': cores,
|
'cpu': cores,
|
||||||
'memory': memory,
|
'memory': memory,
|
||||||
|
@ -1043,6 +1050,10 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
||||||
except WrongIdError:
|
except WrongIdError:
|
||||||
response['status'] = True
|
response['status'] = True
|
||||||
response['text'] = ugettext('Terminated')
|
response['text'] = ugettext('Terminated')
|
||||||
|
vm_detail_obj = VMDetail.objects.filter(
|
||||||
|
vm_id=opennebula_vm_id).first()
|
||||||
|
vm_detail_obj.terminated_at = datetime.utcnow()
|
||||||
|
vm_detail_obj.save()
|
||||||
break
|
break
|
||||||
except BaseException:
|
except BaseException:
|
||||||
break
|
break
|
||||||
|
|
|
@ -82,7 +82,7 @@ class CustomUser(AbstractBaseUser, PermissionsMixin):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register(cls, name, password, email, app='digital_glarus',
|
def register(cls, name, password, email, app='digital_glarus',
|
||||||
base_url=None, send_email=True):
|
base_url=None, send_email=True, account_details=None):
|
||||||
user = cls.objects.filter(email=email).first()
|
user = cls.objects.filter(email=email).first()
|
||||||
if not user:
|
if not user:
|
||||||
user = cls.objects.create_user(name=name, email=email,
|
user = cls.objects.create_user(name=name, email=email,
|
||||||
|
@ -112,6 +112,9 @@ class CustomUser(AbstractBaseUser, PermissionsMixin):
|
||||||
'template_name': 'user_activation',
|
'template_name': 'user_activation',
|
||||||
'template_path': 'datacenterlight/emails/'
|
'template_path': 'datacenterlight/emails/'
|
||||||
}
|
}
|
||||||
|
if account_details:
|
||||||
|
email_data['context'][
|
||||||
|
'account_details'] = account_details
|
||||||
email = BaseEmail(**email_data)
|
email = BaseEmail(**email_data)
|
||||||
email.send()
|
email.send()
|
||||||
return user
|
return user
|
||||||
|
@ -176,6 +179,25 @@ class StripeCustomer(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s - %s" % (self.stripe_id, self.user.email)
|
return "%s - %s" % (self.stripe_id, self.user.email)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_stripe_api_customer(cls, email=None, token=None,
|
||||||
|
customer_name=None):
|
||||||
|
"""
|
||||||
|
This method creates a Stripe API customer with the given
|
||||||
|
email, token and customer_name. This is different from
|
||||||
|
get_or_create method below in that it does not create a
|
||||||
|
CustomUser and associate the customer created in stripe
|
||||||
|
with it, while get_or_create does that before creating the
|
||||||
|
stripe user.
|
||||||
|
"""
|
||||||
|
stripe_utils = StripeUtils()
|
||||||
|
stripe_data = stripe_utils.create_customer(token, email, customer_name)
|
||||||
|
if stripe_data.get('response_object'):
|
||||||
|
stripe_cus_id = stripe_data.get('response_object').get('id')
|
||||||
|
return stripe_cus_id
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_or_create(cls, email=None, token=None):
|
def get_or_create(cls, email=None, token=None):
|
||||||
"""
|
"""
|
||||||
|
@ -195,7 +217,6 @@ class StripeCustomer(models.Model):
|
||||||
|
|
||||||
except StripeCustomer.DoesNotExist:
|
except StripeCustomer.DoesNotExist:
|
||||||
user = CustomUser.objects.get(email=email)
|
user = CustomUser.objects.get(email=email)
|
||||||
|
|
||||||
stripe_utils = StripeUtils()
|
stripe_utils = StripeUtils()
|
||||||
stripe_data = stripe_utils.create_customer(token, email, user.name)
|
stripe_data = stripe_utils.create_customer(token, email, user.name)
|
||||||
if stripe_data.get('response_object'):
|
if stripe_data.get('response_object'):
|
||||||
|
|
|
@ -150,10 +150,11 @@ class OpenNebulaManager():
|
||||||
oca.User.METHODS['allocate'], email,
|
oca.User.METHODS['allocate'], email,
|
||||||
password, 'core')
|
password, 'core')
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"User {0} does not exist. Created the user. User id = {1}",
|
"User {} does not exist. Created the user. User id = {}".format(
|
||||||
email,
|
email,
|
||||||
opennebula_user
|
opennebula_user
|
||||||
)
|
)
|
||||||
|
)
|
||||||
return opennebula_user
|
return opennebula_user
|
||||||
except ConnectionRefusedError:
|
except ConnectionRefusedError:
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|
|
@ -96,5 +96,4 @@ pyflakes==1.5.0
|
||||||
billiard==3.5.0.3
|
billiard==3.5.0.3
|
||||||
amqp==2.2.1
|
amqp==2.2.1
|
||||||
vine==1.1.4
|
vine==1.1.4
|
||||||
#git+https://github.com/ungleich/cdist.git#egg=cdist
|
cdist==4.7.0
|
||||||
file:///home/app/cdist#egg=cdist
|
|
||||||
|
|
|
@ -18,7 +18,8 @@ class SignupFormMixin(forms.ModelForm):
|
||||||
model = CustomUser
|
model = CustomUser
|
||||||
fields = ['name', 'email', 'password']
|
fields = ['name', 'email', 'password']
|
||||||
widgets = {
|
widgets = {
|
||||||
'name': forms.TextInput(attrs={'placeholder': _('Enter your name or company name')}),
|
'name': forms.TextInput(
|
||||||
|
attrs={'placeholder': _('Enter your name or company name')}),
|
||||||
}
|
}
|
||||||
|
|
||||||
def clean_confirm_password(self):
|
def clean_confirm_password(self):
|
||||||
|
@ -42,7 +43,7 @@ class LoginFormMixin(forms.Form):
|
||||||
is_auth = authenticate(email=email, password=password)
|
is_auth = authenticate(email=email, password=password)
|
||||||
if not is_auth:
|
if not is_auth:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
"Your username and/or password were incorrect.")
|
_("Your username and/or password were incorrect."))
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
def clean_email(self):
|
def clean_email(self):
|
||||||
|
@ -51,7 +52,24 @@ class LoginFormMixin(forms.Form):
|
||||||
CustomUser.objects.get(email=email)
|
CustomUser.objects.get(email=email)
|
||||||
return email
|
return email
|
||||||
except CustomUser.DoesNotExist:
|
except CustomUser.DoesNotExist:
|
||||||
raise forms.ValidationError("User does not exist")
|
raise forms.ValidationError(_("User does not exist"))
|
||||||
|
|
||||||
|
|
||||||
|
class ResendActivationEmailForm(forms.Form):
|
||||||
|
email = forms.CharField(widget=forms.EmailInput())
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
fields = ['email']
|
||||||
|
|
||||||
|
def clean_email(self):
|
||||||
|
email = self.cleaned_data.get('email')
|
||||||
|
try:
|
||||||
|
c = CustomUser.objects.get(email=email)
|
||||||
|
if c.validated == 1:
|
||||||
|
raise forms.ValidationError(_("The account is already active."))
|
||||||
|
return email
|
||||||
|
except CustomUser.DoesNotExist:
|
||||||
|
raise forms.ValidationError(_("User does not exist"))
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetRequestForm(forms.Form):
|
class PasswordResetRequestForm(forms.Form):
|
||||||
|
@ -66,7 +84,7 @@ class PasswordResetRequestForm(forms.Form):
|
||||||
CustomUser.objects.get(email=email)
|
CustomUser.objects.get(email=email)
|
||||||
return email
|
return email
|
||||||
except CustomUser.DoesNotExist:
|
except CustomUser.DoesNotExist:
|
||||||
raise forms.ValidationError("User does not exist")
|
raise forms.ValidationError(_("User does not exist"))
|
||||||
|
|
||||||
|
|
||||||
class SetPasswordForm(forms.Form):
|
class SetPasswordForm(forms.Form):
|
||||||
|
@ -75,11 +93,11 @@ class SetPasswordForm(forms.Form):
|
||||||
password
|
password
|
||||||
"""
|
"""
|
||||||
error_messages = {
|
error_messages = {
|
||||||
'password_mismatch': ("The two password fields didn't match."),
|
'password_mismatch': _("The two password fields didn't match."),
|
||||||
}
|
}
|
||||||
new_password1 = forms.CharField(label=("New password"),
|
new_password1 = forms.CharField(label=_("New password"),
|
||||||
widget=forms.PasswordInput)
|
widget=forms.PasswordInput)
|
||||||
new_password2 = forms.CharField(label=("New password confirmation"),
|
new_password2 = forms.CharField(label=_("New password confirmation"),
|
||||||
widget=forms.PasswordInput)
|
widget=forms.PasswordInput)
|
||||||
|
|
||||||
def clean_new_password2(self):
|
def clean_new_password2(self):
|
||||||
|
@ -113,6 +131,12 @@ class BillingAddressForm(forms.ModelForm):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BillingAddressFormSignup(BillingAddressForm):
|
||||||
|
name = forms.CharField(label=_('Name'))
|
||||||
|
email = forms.EmailField(label=_('Email Address'))
|
||||||
|
field_order = ['name', 'email']
|
||||||
|
|
||||||
|
|
||||||
class UserBillingAddressForm(forms.ModelForm):
|
class UserBillingAddressForm(forms.ModelForm):
|
||||||
user = forms.ModelChoiceField(queryset=CustomUser.objects.all(),
|
user = forms.ModelChoiceField(queryset=CustomUser.objects.all(),
|
||||||
widget=forms.HiddenInput())
|
widget=forms.HiddenInput())
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
from hosting.models import UserHostingKey
|
import logging
|
||||||
|
from oca.pool import WrongIdError
|
||||||
|
|
||||||
|
from hosting.models import UserHostingKey, VMDetail
|
||||||
|
from opennebula_api.serializers import VirtualMachineSerializer
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_all_public_keys(customer):
|
def get_all_public_keys(customer):
|
||||||
|
@ -9,3 +15,48 @@ def get_all_public_keys(customer):
|
||||||
"""
|
"""
|
||||||
return UserHostingKey.objects.filter(user_id=customer.id).values_list(
|
return UserHostingKey.objects.filter(user_id=customer.id).values_list(
|
||||||
"public_key", flat=True)
|
"public_key", flat=True)
|
||||||
|
|
||||||
|
|
||||||
|
def get_or_create_vm_detail(user, manager, vm_id):
|
||||||
|
"""
|
||||||
|
Returns VMDetail object related to given vm_id. Creates the object
|
||||||
|
if it does not exist
|
||||||
|
|
||||||
|
:param vm_id: The ID of the VM which should be greater than 0.
|
||||||
|
:param user: The CustomUser object that owns this VM
|
||||||
|
:param manager: The OpenNebulaManager object
|
||||||
|
:return: The VMDetail object. None if vm_id is less than or equal to 0.
|
||||||
|
Also, for the cases where the VMDetail does not exist and we can not
|
||||||
|
fetch data about the VM from OpenNebula, the function returns None
|
||||||
|
"""
|
||||||
|
if vm_id <= 0:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
vm_detail_obj = VMDetail.objects.get(vm_id=vm_id)
|
||||||
|
except VMDetail.DoesNotExist:
|
||||||
|
try:
|
||||||
|
vm_obj = manager.get_vm(vm_id)
|
||||||
|
except (WrongIdError, ConnectionRefusedError) as e:
|
||||||
|
logger.error(str(e))
|
||||||
|
return None
|
||||||
|
vm = VirtualMachineSerializer(vm_obj).data
|
||||||
|
vm_detail_obj = VMDetail.objects.create(
|
||||||
|
user=user, vm_id=vm_id, disk_size=vm['disk_size'],
|
||||||
|
cores=vm['cores'], memory=vm['memory'],
|
||||||
|
configuration=vm['configuration'], ipv4=vm['ipv4'],
|
||||||
|
ipv6=vm['ipv6']
|
||||||
|
)
|
||||||
|
return vm_detail_obj
|
||||||
|
|
||||||
|
|
||||||
|
def get_vm_price(cpu, memory, disk_size):
|
||||||
|
"""
|
||||||
|
A helper function that computes price of a VM from given cpu, ram and
|
||||||
|
ssd parameters
|
||||||
|
|
||||||
|
:param cpu: Number of cores of the VM
|
||||||
|
:param memory: RAM of the VM
|
||||||
|
:param disk_size: Disk space of the VM
|
||||||
|
:return: The price of the VM
|
||||||
|
"""
|
||||||
|
return (cpu * 5) + (memory * 2) + (disk_size * 0.6)
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2017-09-02 11:50+0000\n"
|
"POT-Creation-Date: 2017-09-25 20:11+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -738,6 +738,24 @@ msgstr ""
|
||||||
msgid "Enter your name or company name"
|
msgid "Enter your name or company name"
|
||||||
msgstr "Geben Sie Ihren Namen oder der Ihrer Firma ein"
|
msgstr "Geben Sie Ihren Namen oder der Ihrer Firma ein"
|
||||||
|
|
||||||
|
msgid "Your username and/or password were incorrect."
|
||||||
|
msgstr "Dein Benutzername und/oder Dein Passwort ist falsch."
|
||||||
|
|
||||||
|
msgid "User does not exist"
|
||||||
|
msgstr "Der Benutzer existiert nicht"
|
||||||
|
|
||||||
|
msgid "The account is already active."
|
||||||
|
msgstr "Das Benutzerkonto ist bereits aktiv."
|
||||||
|
|
||||||
|
msgid "The two password fields didn't match."
|
||||||
|
msgstr "Die beiden Passwörter stimmen nicht überein."
|
||||||
|
|
||||||
|
msgid "New password"
|
||||||
|
msgstr "Neues Passwort"
|
||||||
|
|
||||||
|
msgid "New password confirmation"
|
||||||
|
msgstr "Neues Passwort Bestätigung"
|
||||||
|
|
||||||
msgid "Cardholder Name"
|
msgid "Cardholder Name"
|
||||||
msgstr "Name des Kartenbesitzer"
|
msgstr "Name des Kartenbesitzer"
|
||||||
|
|
||||||
|
@ -768,8 +786,16 @@ msgstr "Telefon"
|
||||||
msgid "Message"
|
msgid "Message"
|
||||||
msgstr "Nachricht"
|
msgstr "Nachricht"
|
||||||
|
|
||||||
|
msgid "An email with the activation link has been sent to your email"
|
||||||
|
msgstr ""
|
||||||
|
"Der Link zum Zurücksetzen deines Passwortes wurde an deine E-Mail gesendet"
|
||||||
|
|
||||||
|
msgid "Account Activation"
|
||||||
|
msgstr "Accountaktivierung"
|
||||||
|
|
||||||
msgid "The link to reset your email has been sent to your email"
|
msgid "The link to reset your email has been sent to your email"
|
||||||
msgstr "Der Link zum Zurücksetzen deines Passwortes wurde an deine E-Mail gesendet"
|
msgstr ""
|
||||||
|
"Der Link zum Zurücksetzen deines Passwortes wurde an deine E-Mail gesendet"
|
||||||
|
|
||||||
msgid "Password Reset"
|
msgid "Password Reset"
|
||||||
msgstr "Passwort zurücksetzen"
|
msgstr "Passwort zurücksetzen"
|
||||||
|
|
|
@ -238,7 +238,7 @@ class StripeUtils(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_stripe_plan_id(cpu, ram, ssd, version, app='dcl', hdd=None):
|
def get_stripe_plan_id(cpu, ram, ssd, version, app='dcl', hdd=None):
|
||||||
"""
|
"""
|
||||||
Returns the stripe plan id string of the form
|
Returns the Stripe plan id string of the form
|
||||||
`dcl-v1-cpu-2-ram-5gb-ssd-10gb` based on the input parameters
|
`dcl-v1-cpu-2-ram-5gb-ssd-10gb` based on the input parameters
|
||||||
|
|
||||||
:param cpu: The number of cores
|
:param cpu: The number of cores
|
||||||
|
@ -256,7 +256,19 @@ class StripeUtils(object):
|
||||||
if hdd is not None:
|
if hdd is not None:
|
||||||
dcl_plan_string = '{dcl_plan_string}-hdd-{hdd}gb'.format(
|
dcl_plan_string = '{dcl_plan_string}-hdd-{hdd}gb'.format(
|
||||||
dcl_plan_string=dcl_plan_string, hdd=hdd)
|
dcl_plan_string=dcl_plan_string, hdd=hdd)
|
||||||
stripe_plan_id_string = '{app}-v{version}-{plan}'.format(app=app,
|
stripe_plan_id_string = '{app}-v{version}-{plan}'.format(
|
||||||
|
app=app,
|
||||||
version=version,
|
version=version,
|
||||||
plan=dcl_plan_string)
|
plan=dcl_plan_string)
|
||||||
return stripe_plan_id_string
|
return stripe_plan_id_string
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_stripe_plan_name(cpu, memory, disk_size):
|
||||||
|
"""
|
||||||
|
Returns the Stripe plan name
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format(
|
||||||
|
cpu=cpu,
|
||||||
|
memory=memory,
|
||||||
|
disk_size=disk_size)
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth import authenticate, login
|
from django.contrib.auth import authenticate, login
|
||||||
from django.contrib.auth.tokens import default_token_generator
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
|
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
|
||||||
|
@ -63,9 +64,45 @@ class LoginViewMixin(FormView):
|
||||||
return super(LoginViewMixin, self).get(request, *args, **kwargs)
|
return super(LoginViewMixin, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ResendActivationLinkViewMixin(FormView):
|
||||||
|
success_message = _(
|
||||||
|
"An email with the activation link has been sent to your email")
|
||||||
|
|
||||||
|
def generate_email_context(self, user):
|
||||||
|
context = {
|
||||||
|
'base_url': "{0}://{1}".format(self.request.scheme,
|
||||||
|
self.request.get_host()),
|
||||||
|
'activation_link': reverse_lazy(
|
||||||
|
'hosting:validate',
|
||||||
|
kwargs={'validate_slug': user.validation_slug}
|
||||||
|
),
|
||||||
|
'dcl_text': settings.DCL_TEXT,
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
email = form.cleaned_data.get('email')
|
||||||
|
user = CustomUser.objects.get(email=email)
|
||||||
|
messages.add_message(self.request, messages.SUCCESS,
|
||||||
|
self.success_message)
|
||||||
|
context = self.generate_email_context(user)
|
||||||
|
email_data = {
|
||||||
|
'subject': '{dcl_text} {account_activation}'.format(
|
||||||
|
dcl_text=settings.DCL_TEXT,
|
||||||
|
account_activation=_('Account Activation')
|
||||||
|
),
|
||||||
|
'to': email,
|
||||||
|
'context': context,
|
||||||
|
'template_name': self.email_template_name,
|
||||||
|
'template_path': self.email_template_path,
|
||||||
|
'from_address': settings.DCL_SUPPORT_FROM_ADDRESS
|
||||||
|
}
|
||||||
|
email = BaseEmail(**email_data)
|
||||||
|
email.send()
|
||||||
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetViewMixin(FormView):
|
class PasswordResetViewMixin(FormView):
|
||||||
# template_name = 'hosting/reset_password.html'
|
|
||||||
# form_class = PasswordResetRequestForm
|
|
||||||
success_message = _(
|
success_message = _(
|
||||||
"The link to reset your email has been sent to your email")
|
"The link to reset your email has been sent to your email")
|
||||||
site = ''
|
site = ''
|
||||||
|
@ -78,7 +115,6 @@ class PasswordResetViewMixin(FormView):
|
||||||
'site_name': 'ungleich' if self.site != 'dcl' else settings.DCL_TEXT,
|
'site_name': 'ungleich' if self.site != 'dcl' else settings.DCL_TEXT,
|
||||||
'base_url': "{0}://{1}".format(self.request.scheme,
|
'base_url': "{0}://{1}".format(self.request.scheme,
|
||||||
self.request.get_host())
|
self.request.get_host())
|
||||||
|
|
||||||
}
|
}
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@ -104,11 +140,8 @@ class PasswordResetViewMixin(FormView):
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetConfirmViewMixin(FormView):
|
class PasswordResetConfirmViewMixin(FormView):
|
||||||
# template_name = 'hosting/confirm_reset_password.html'
|
|
||||||
form_class = SetPasswordForm
|
form_class = SetPasswordForm
|
||||||
|
|
||||||
# success_url = reverse_lazy('hosting:login')
|
|
||||||
|
|
||||||
def post(self, request, uidb64=None, token=None, *arg, **kwargs):
|
def post(self, request, uidb64=None, token=None, *arg, **kwargs):
|
||||||
try:
|
try:
|
||||||
uid = urlsafe_base64_decode(uidb64)
|
uid = urlsafe_base64_decode(uidb64)
|
||||||
|
|
Loading…
Reference in a new issue