diff --git a/.gitignore b/.gitignore index 46bfbf54..cfef66a1 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ secret-key .env *.mo *.log +*.sql diff --git a/Changelog b/Changelog index 7ba5e634..fe9a1dde 100644 --- a/Changelog +++ b/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 * #3731: [dcl, hosting] Added cdist ssh key handler * #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 * #3728: [hosting] VM Termination animation added * #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 + * #3812: [hosting] Modal check icon made thin and font-size fixed * Feature: [cms, blog] Added /cms prefix for all the django-cms generated urls * Bugfix: [dcl, hosting] added host to celery error mails * Bugfix: [ungleich] Fixed wrong subdomain digitalglarus.ungleich.ch diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index 3c7869a1..cc72f397 100644 --- a/datacenterlight/locale/de/LC_MESSAGES/django.po +++ b/datacenterlight/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\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" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -65,24 +65,6 @@ msgstr "Bitte gib einen Wert von 10 bis 200 ein." msgid "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" msgstr "Weiter" @@ -95,6 +77,18 @@ msgstr "Vielen Dank für Deine Nachricht." msgid "Get in touch with us!" 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" msgstr "Nachricht" @@ -151,6 +145,15 @@ msgstr "" "Adressleiste deines Browsers.
\n" "%(base_url)s%(activation_link)s\n" +#, python-format +msgid "" +"Your account details are as follows:

\n" +"Username : Your email address
\n" +"Password : %(account_details)s

\n" +"You can reset your password here:\n" +"%(base_url)s%(reset_password_url)s\n" +msgstr "" + #, python-format msgid "" "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" "%(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" msgstr "Home" @@ -264,6 +278,45 @@ msgstr "Kontaktiere uns" msgid "Switzerland " 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?
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" msgstr "Deine Bestellung" @@ -288,23 +341,15 @@ msgstr "inkl. Mehrwertsteuer" msgid "Month" msgstr "Monat" -msgid "Billing Address" -msgstr "Rechnungsadresse" - msgid "Credit Card" msgstr "Kreditkarte" msgid "" -"\n" -" Please fill in your credit card information " -"below. We are using Stripe for payment and do not store\n" -" your information in our database.\n" -" " +"Please fill in your credit card information below. We are using Stripe for payment and do not " +"store your information in our database." msgstr "" -"\n" -"Bitte füll Deine Kreditkarteninformationen unten aus. Wir nutzen Stripe für die Bezahlung und " "speichern keine Informationen in unserer Datenbank." @@ -330,6 +375,13 @@ msgstr "" msgid "Card Type" 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" msgstr "Weiter" @@ -365,6 +417,15 @@ msgstr "" msgid "Place order" 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!" 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" msgstr "Ungültige Speicher-Grösse" -msgid "is not a proper name" -msgstr "ist kein gültiger Name" +msgid "Error." +msgstr "" -msgid "is not a proper email" -msgstr "ist keine gültige E-Mailadresse" +msgid "" +"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 "" #~ "\n" diff --git a/datacenterlight/static/datacenterlight/css/landing-page.css b/datacenterlight/static/datacenterlight/css/landing-page.css index 6537dd0d..d50a864d 100755 --- a/datacenterlight/static/datacenterlight/css/landing-page.css +++ b/datacenterlight/static/datacenterlight/css/landing-page.css @@ -1653,3 +1653,20 @@ a.list-group-item-danger.active:focus { .panel-danger > .panel-heading .badge { 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); +} diff --git a/datacenterlight/static/datacenterlight/js/main.js b/datacenterlight/static/datacenterlight/js/main.js index ab37a68b..dd074397 100644 --- a/datacenterlight/static/datacenterlight/js/main.js +++ b/datacenterlight/static/datacenterlight/js/main.js @@ -155,9 +155,7 @@ function _calcPricing() { var total = (cardPricing['cpu'].value * 5) + (2 * cardPricing['ram'].value) + (0.6 * cardPricing['storage'].value); total = parseFloat(total.toFixed(2)); - $("#total").text(total); - $('input[name=total]').val(total); } function form_success() { @@ -191,4 +189,4 @@ }); }) } -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 1335869b..7d589570 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -13,7 +13,7 @@ from hosting.models import HostingOrder, HostingBill from membership.models import StripeCustomer, CustomUser from opennebula_api.models import OpenNebulaManager 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.mailer import BaseEmail 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, billing_address_id, 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 try: final_price = specs.get('price') @@ -142,9 +143,10 @@ def create_vm_task(self, vm_template_id, user, specs, template, email.send() if 'pass' in user: - lang = 'en-us' + lang = 'en-us' 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') translation.activate(lang) # 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)) if new_host is not None: 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: public_keys = get_all_public_keys(custom_user) keys = [{'value': key, 'state': True} for key in diff --git a/datacenterlight/templates/datacenterlight/beta_success.html b/datacenterlight/templates/datacenterlight/beta_success.html index 2512a05c..60df607c 100644 --- a/datacenterlight/templates/datacenterlight/beta_success.html +++ b/datacenterlight/templates/datacenterlight/beta_success.html @@ -8,7 +8,7 @@ diff --git a/datacenterlight/templates/datacenterlight/calculator_form.html b/datacenterlight/templates/datacenterlight/calculator_form.html index b5bac1f9..1733a719 100644 --- a/datacenterlight/templates/datacenterlight/calculator_form.html +++ b/datacenterlight/templates/datacenterlight/calculator_form.html @@ -77,47 +77,9 @@ {% endfor %} - -
-
- - -
-
- {% for message in messages %} - {% if 'name' in message.tags %} -
    -
  • - {{ message|safe }} -
  • -
- {% endif %} - {% endfor %} -
-
-
-
- - -
-
- {% for message in messages %} - {% if 'email' in message.tags %} -
    -
  • - {{ message|safe }} -
  • -
- {% endif %} - {% endfor %} -
-
diff --git a/datacenterlight/templates/datacenterlight/emails/user_activation.html b/datacenterlight/templates/datacenterlight/emails/user_activation.html index 955eed18..3c7fae74 100644 --- a/datacenterlight/templates/datacenterlight/emails/user_activation.html +++ b/datacenterlight/templates/datacenterlight/emails/user_activation.html @@ -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.
{{base_url}}{{activation_link}} {% 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 %} diff --git a/datacenterlight/templates/datacenterlight/emails/user_activation.txt b/datacenterlight/templates/datacenterlight/emails/user_activation.txt index 84ec50a9..7c833256 100644 --- a/datacenterlight/templates/datacenterlight/emails/user_activation.txt +++ b/datacenterlight/templates/datacenterlight/emails/user_activation.txt @@ -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. {{base_url}}{{activation_link}} {% 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 %} diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html index f4974a56..c7ada779 100644 --- a/datacenterlight/templates/datacenterlight/landing_payment.html +++ b/datacenterlight/templates/datacenterlight/landing_payment.html @@ -1,190 +1,182 @@ {% extends "hosting/base_short.html" %} {% load staticfiles bootstrap3 i18n %} +{% block css_extra %} + +{% endblock css_extra %} + {% block navbar %} {% include "datacenterlight/includes/_navbar.html" %} {% endblock navbar %} {% block content %} -
-
-
-

{%trans "Your Order" %}

-
-
- {%trans "Cores" %} -
-
- {%trans "Memory" %} -
-
- {%trans "Disk space" %} -
-
- {%trans "Configuration" %} -
-
-
-
- {{request.session.specs.cpu|floatformat}} -
-
- {{request.session.specs.memory|floatformat}} GB -
-
- {{request.session.specs.disk_size|floatformat}} GB -
-
- {{request.session.template.name}} -
-
-
-
- {%trans "Total" %} {%trans "including VAT" %} -
-
-
-
{{request.session.specs.price}} - CHF/{% trans "Month" %} +
+
+
+ {% if request.user.is_authenticated %} +
+

{% trans "Welcome back" %} {{request.user.name}}!

+

{% trans "Review your billing address and card details and proceed to make payment." %}

-
+ {% else %} +

{%trans "Log in" %}

+
+

{% blocktrans %}Already signed up?
By logging in you can retrieve saved billing information.{% endblocktrans %}

+
+ {% for field in login_form %} + {% csrf_token %} + {% bootstrap_field field show_label=False type='fields'%} + {% endfor %} + +
+ +
+
+

+ {% trans "Don't have an account yet?" %}
+ {% trans "You can sign up by filling in the information below." %}
+ {% trans "Forgot password?" %} +

+ {% endif %}
-
-
-
-
-

{%trans "Billing Address"%}

-
+
+
+ {% if not request.user.is_authenticated %} +

{%trans "Sign up"%}

+ {% else %} +

{%trans "Billing Address"%}

+ {% endif %} +
+ {% for message in messages %} + {% if 'duplicate_email' in message.tags %} +

{{message}}

+ {% endif %} + {% endfor %}
- {% for field in form %} {% csrf_token %} + {% for field in form %} {% bootstrap_field field show_label=False type='fields'%} {% endfor %}
-
+
+
+
+

{%trans "Your Order" %}

+
+
+

{% trans "Cores"%} {{request.session.specs.cpu|floatformat}}

+
+

{% trans "Memory"%} {{request.session.specs.memory|floatformat}} GB

+
+

{% trans "Disk space"%} {{request.session.specs.disk_size|floatformat}} GB

+
+

{% trans "Configuration"%} {{request.session.template.name}}

+
+

{%trans "Total" %}  ({%trans "including VAT" %}) {{request.session.specs.price}} CHF/{% trans "Month" %}

+
+
+
+
+

{%trans "Credit Card"%}

-
+
+

+ {% blocktrans %}Please fill in your credit card information below. We are using Stripe for payment and do not store your information in our database.{% endblocktrans %} +

-
-

- {% blocktrans %} - Please fill in your credit card information below. We are using Stripe for payment and do not store - your information in our database. - {% endblocktrans %} -

-
-
-
- {% if credit_card_data.last4 %} + {% if credit_card_data.last4 %}
Credit Card
Last 4: *****{{credit_card_data.last4}}
Type: {{credit_card_data.cc_brand}}
-
-
- {% if not messages and not form.non_field_errors %} -

- {% trans "You are not making any payment yet. After submitting your card information, you will be taken to the Confirm Order Page." %} -

- {% endif %} -
- {% for message in messages %} - {% if 'failed_payment' or 'make_charge_error' in message.tags %} -
  • -

    {{ message|safe }}

    -
- {% endif %} - {% endfor %} - {% for error in form.non_field_errors %} -

- {{ error|escape }} -

- {% endfor %} -
-
-
-
- -
-
+ {% if not messages and not form.non_field_errors %} +

+ {% trans "You are not making any payment yet. After submitting your card information, you will be taken to the Confirm Order Page." %} +

+ {% endif %} +
+ {% for message in messages %} + {% if 'failed_payment' or 'make_charge_error' in message.tags %} +
  • +

    {{ message|safe }}

    +
+ {% endif %} + {% endfor %} + {% for error in form.non_field_errors %} +

+ {{ error|escape }} +

+ {% endfor %}
- - {% else %} +
+ +
+ {% else %}
-
-
+
+
-
- -
+
+
+ +
+
+
+ +
+
-
-
- -
-
-
+
- -
-
- {% if not messages and not form.non_field_errors %} +
+ {% if not messages and not form.non_field_errors %} +

+ {% trans "You are not making any payment yet. After placing your order, you will be taken to the Submit Payment Page." %} +

+ {% endif %} +
+ {% for message in messages %} + {% if 'failed_payment' in message.tags or 'make_charge_error' in message.tags %} +
    +
  • {{ message|safe }}

  • +
+ {% elif not form.non_field_errors %}

- {% 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." %}

{% endif %} -
- {% for message in messages %} - {% if 'failed_payment' or 'make_charge_error' in message.tags %} -
  • -

    {{ message|safe }}

    -
- {% endif %} - {% endfor %} + {% endfor %} - {% for error in form.non_field_errors %} -

- {{ error|escape }} -

- {% endfor %} -
-
-
-
- -
-
+ {% for error in form.non_field_errors %} +

+ {{ error|escape }} +

+ {% endfor %} +
+
+
- + {% endif %}
diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 085b0c37..5e8af4ea 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -9,98 +9,135 @@ {% block content %} -
- {% if messages %} -
-
-
-
- {% for message in messages %} - {{ message }} - {% endfor %} -
+
+ {% if messages %} +
+
+
+
+ {% for message in messages %} + {{ message }} + {% endfor %}
- {% endif %} - {% if not error %} -
-
-
-

{% trans "Confirm Order"%}

-
-
-
-
-
- {% trans "Date"%}:
- {% now "Y-m-d H:i" %}

-
-
-
-
-

{% trans "Billed To:"%}

- {% with request.session.billing_address_data as billing_address %} - {{billing_address|get_value_from_dict:'cardholder_name'}}
{{billing_address|get_value_from_dict:'street_address'}}, {{billing_address|get_value_from_dict:'postal_code'}}
- {{billing_address|get_value_from_dict:'city'}}, {{billing_address|get_value_from_dict:'country'}}. - {% endwith %} -
-
-
-
-
-
- {% trans "Payment Method:"%}
- {{cc_brand}} {% trans "ending in" %} **** {{cc_last4}}
- {{request.session.user.email}} -
-
-
+
+ {% endif %} + {% if not error %} +
+
+
+

{% trans "Confirm Order"%}

-
- -
-
-

{% trans "Order summary"%}

-
-
- {% with request.session.specs as vm %} -

{% trans "Cores"%} {{vm.cpu}}

-
-

{% trans "Memory"%} {{vm.memory}} GB

-
-

{% trans "Disk space"%} {{vm.disk_size}} GB

-
-

{% trans "Configuration"%} {{request.session.template.name}}

-
-

{% trans "Total"%}

{{vm.price}} CHF /{% trans "Month" %}

+
+
+
+
+ {% trans "Date"%}:
+ {% now "Y-m-d H:i" %}

+
+
+
+
+

{% trans "Billed To:"%}

+ {% with billing_address_data as billing_address %} + {{billing_address.cardholder_name}}
{{billing_address.street_address}}, {{billing_address.postal_code}}
+ {{billing_address.city}}, {{billing_address.country}}. {% endwith %} +
+
+
+
+
+
+ {% trans "Payment Method:"%}
+ {{cc_brand}} {% trans "ending in" %} **** {{cc_last4}}
+ {{request.session.user.email}} +
-
-
- {% csrf_token %} -
-
-

{% 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 %}.

-
- -
-
- {% endif %} +
+ +
+
+

{% trans "Order summary"%}

+
+
+ {% with request.session.specs as vm %} +

{% trans "Cores"%} {{vm.cpu}}

+
+

{% trans "Memory"%} {{vm.memory}} GB

+
+

{% trans "Disk space"%} {{vm.disk_size}} GB

+
+

{% trans "Configuration"%} {{request.session.template.name}}

+
+

{% trans "Total"%}

{{vm.price}} CHF /{% trans "Month" %}

+ {% endwith %} +
+
+
+ {% csrf_token %} +
+
+

{% 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 %}.

+
+
+ +
+
+
+
+
+ {% endif %} +
+ + + - + {%endblock%} diff --git a/datacenterlight/templates/datacenterlight/pricing.html b/datacenterlight/templates/datacenterlight/pricing.html deleted file mode 100644 index 0724a6ce..00000000 --- a/datacenterlight/templates/datacenterlight/pricing.html +++ /dev/null @@ -1,96 +0,0 @@ -{% extends "datacenterlight/base.html" %} -{% load staticfiles i18n%} -{% get_current_language as LANGUAGE_CODE %} - -{% block content %} -
- -
-

{% trans "We are cutting down the costs significantly!" %}

-
- -
- -
-
- -
-
- {% csrf_token %} - -
-

{% trans "VM hosting" %}

-
-
- 15 - CHF -
-

{% trans "VAT included" %}

-
-
-
-
-

{% trans "Hosted in Switzerland" %}

-
-
- - - Core - -
-
- - - GB RAM - -
-
- - - {% trans "GB Storage (SSD)" %} - -
- - - -
- - -
- - - - - -
- - -
-
-
- -
-

{% trans "Simple and affordable: Try our virtual machine with featherlight price." %}

- -
-

{% 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." %}

-
-
-
-{% endblock %} - - - diff --git a/datacenterlight/tests.py b/datacenterlight/tests.py index c34c56ba..edde2db8 100644 --- a/datacenterlight/tests.py +++ b/datacenterlight/tests.py @@ -12,6 +12,7 @@ from datacenterlight.models import VMTemplate from datacenterlight.tasks import create_vm_task from membership.models import StripeCustomer from opennebula_api.serializers import VMTemplateSerializer +from utils.hosting_utils import get_vm_price from utils.models import BillingAddress from utils.stripe_utils import StripeUtils @@ -94,12 +95,11 @@ class CeleryTaskTestCase(TestCase): cpu = specs.get('cpu') memory = specs.get('memory') disk_size = specs.get('disk_size') - amount_to_be_charged = (cpu * 5) + (memory * 2) + (disk_size * 0.6) - plan_name = "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format( - cpu=cpu, - memory=memory, - disk_size=disk_size) - + amount_to_be_charged = get_vm_price(cpu=cpu, memory=memory, + disk_size=disk_size) + plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu, + memory=memory, + disk_size=disk_size) stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu, ram=memory, ssd=disk_size, diff --git a/datacenterlight/urls.py b/datacenterlight/urls.py index 772e691d..a8d8f49d 100644 --- a/datacenterlight/urls.py +++ b/datacenterlight/urls.py @@ -1,7 +1,7 @@ from django.conf.urls import url from .views import IndexView, BetaProgramView, LandingProgramView, \ - BetaAccessView, PricingView, SuccessView, \ + BetaAccessView, SuccessView, \ PaymentOrderView, OrderConfirmationView, \ WhyDataCenterLightView, ContactUsView @@ -15,7 +15,6 @@ urlpatterns = [ name='whydatacenterlight'), url(r'^beta-program/?$', BetaProgramView.as_view(), name='beta'), 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'^order-confirmation/?$', OrderConfirmationView.as_view(), name='order_confirmation'), diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 0521ffef..308913b0 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -1,28 +1,35 @@ +import logging +import json + from django import forms from django.conf import settings from django.contrib import messages +from django.contrib.auth import login, authenticate from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse -from django.http import HttpResponseRedirect -from django.shortcuts import redirect +from django.http import HttpResponseRedirect, HttpResponse 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.generic import FormView, CreateView, TemplateView, DetailView from datacenterlight.tasks import create_vm_task from hosting.models import HostingOrder +from hosting.forms import HostingUserLoginForm from membership.models import CustomUser, StripeCustomer -from opennebula_api.models import OpenNebulaManager -from opennebula_api.serializers import VirtualMachineTemplateSerializer, \ - VMTemplateSerializer -from utils.forms import BillingAddressForm +from opennebula_api.serializers import VMTemplateSerializer +from utils.forms import ( + BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm +) +from utils.hosting_utils import get_vm_price from utils.mailer import BaseEmail from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from .forms import BetaAccessForm, ContactForm from .models import BetaAccess, BetaAccessVMType, BetaAccessVM, VMTemplate +logger = logging.getLogger(__name__) + class ContactUsView(FormView): template_name = "datacenterlight/contact_form.html" @@ -88,56 +95,6 @@ class SuccessView(TemplateView): 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): template_name = "datacenterlight/beta_access.html" form_class = BetaAccessForm @@ -274,17 +231,11 @@ class IndexView(CreateView): memory_field = forms.IntegerField(validators=[self.validate_memory]) storage = request.POST.get('storage') storage_field = forms.IntegerField(validators=[self.validate_storage]) - price = request.POST.get('total') template_id = int(request.POST.get('config')) template = VMTemplate.objects.filter( opennebula_vm_template_id=template_id).first() template_data = VMTemplateSerializer(template).data - name = request.POST.get('name') - email = request.POST.get('email') - name_field = forms.CharField() - email_field = forms.EmailField() - try: cores = cores_field.clean(cores) except ValidationError as err: @@ -311,40 +262,16 @@ class IndexView(CreateView): extra_tags='storage') return HttpResponseRedirect( reverse('datacenterlight:index') + "#order_form") - - try: - 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") - + amount_to_be_charged = get_vm_price(cpu=cores, memory=memory, + disk_size=storage) specs = { 'cpu': cores, 'memory': memory, 'disk_size': storage, - 'price': price + 'price': amount_to_be_charged } - - this_user = { - 'name': name, - 'email': email - } - request.session['specs'] = specs request.session['template'] = template_data - request.session['user'] = this_user return HttpResponseRedirect(reverse('datacenterlight:payment')) def get_success_url(self): @@ -407,20 +334,19 @@ class WhyDataCenterLightView(IndexView): class PaymentOrderView(FormView): 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): form_kwargs = super(PaymentOrderView, self).get_form_kwargs() - billing_address_data = self.request.session.get('billing_address_data') - if billing_address_data: + # if user is signed in, get billing address + if self.request.user.is_authenticated(): form_kwargs.update({ - 'initial': { - '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'], - } + 'instance': self.request.user.billing_addresses.first() }) return form_kwargs @@ -428,48 +354,49 @@ class PaymentOrderView(FormView): context = super(PaymentOrderView, self).get_context_data(**kwargs) context.update({ 'stripe_key': settings.STRIPE_API_PUBLIC_KEY, - 'site_url': reverse('datacenterlight:index') + 'site_url': reverse('datacenterlight:index'), + 'login_form': HostingUserLoginForm() }) return context @cache_control(no_cache=True, must_revalidate=True, no_store=True) 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 self.render_to_response(self.get_context_data()) def post(self, request, *args, **kwargs): form = self.get_form() if form.is_valid(): - # Get billing address data - billing_address_data = form.cleaned_data token = form.cleaned_data.get('token') - user = request.session.get('user') - try: - CustomUser.objects.get(email=user.get('email')) - except CustomUser.DoesNotExist: - password = CustomUser.get_random_password() - # Register the user, and do not send emails - CustomUser.register(user.get('name'), - password, - user.get('email'), - app='dcl', - base_url=None, send_email=False) - + if request.user.is_authenticated(): + this_user = { + 'email': request.user.email, + 'name': request.user.name + } + customer = StripeCustomer.get_or_create( + email=this_user.get('email'), + 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 - customer = StripeCustomer.get_or_create(email=user.get('email'), - token=token) if not customer: form.add_error("__all__", "Invalid credit card") return self.render_to_response( 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['customer'] = customer.id + request.session[ + 'customer'] = customer.id if request.user.is_authenticated() else customer return HttpResponseRedirect( reverse('datacenterlight:order_confirmation')) else: @@ -489,9 +416,15 @@ class OrderConfirmationView(DetailView): if 'token' not in request.session: return HttpResponseRedirect(reverse('datacenterlight:payment')) 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() - card_details = stripe_utils.get_card_details(customer.stripe_id, + card_details = stripe_utils.get_card_details(stripe_api_cus_id, request.session.get( 'token')) if not card_details.get('response_object'): @@ -503,7 +436,8 @@ class OrderConfirmationView(DetailView): context = { 'site_url': reverse('datacenterlight:index'), '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) @@ -512,32 +446,43 @@ class OrderConfirmationView(DetailView): specs = request.session.get('specs') user = request.session.get('user') stripe_customer_id = request.session.get('customer') - customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() - billing_address_data = request.session.get('billing_address_data') - billing_address_id = request.session.get('billing_address') + 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 vm_template_id = template.get('id', 1) - # Make stripe charge to a customer 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( 'token')) if not card_details.get('response_object'): msg = card_details.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') - return HttpResponseRedirect( - reverse('datacenterlight:payment') + '#payment_error') + response = { + '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') cpu = specs.get('cpu') memory = specs.get('memory') disk_size = specs.get('disk_size') - amount_to_be_charged = (cpu * 5) + (memory * 2) + (disk_size * 0.6) - plan_name = "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format( - cpu=cpu, - memory=memory, - disk_size=disk_size) - + amount_to_be_charged = specs.get('price') + plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu, + memory=memory, + disk_size=disk_size) stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu, ram=memory, ssd=disk_size, @@ -548,7 +493,7 @@ class OrderConfirmationView(DetailView): name=plan_name, stripe_plan_id=stripe_plan_id) subscription_result = stripe_utils.subscribe_customer_to_plan( - customer.stripe_id, + stripe_api_cus_id, [{"plan": stripe_plan.get( 'response_object').stripe_plan_id}]) stripe_subscription_obj = subscription_result.get('response_object') @@ -558,11 +503,96 @@ class OrderConfirmationView(DetailView): msg = subscription_result.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') - return HttpResponseRedirect( - reverse('datacenterlight:payment') + '#payment_error') + response = { + '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, stripe_customer_id, billing_address_data, billing_address_id, stripe_subscription_obj, card_details_dict) - request.session['order_confirmation'] = True - return HttpResponseRedirect(reverse('datacenterlight:order_success')) + for session_var in ['specs', 'template', 'billing_address', + '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") diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index d6b65f95..c4c644f8 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\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" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -268,7 +268,7 @@ msgstr "" "Link klickst.
\n" msgid "My VM page" -msgstr "" +msgstr "Meine VM page" #, python-format msgid "" @@ -306,6 +306,9 @@ msgstr "Registrieren" msgid "Forgot your password ? " msgstr "Passwort vergessen?" +msgid "Resend activation link" +msgstr "Aktivierungslink noch einmal senden" + msgid "Notifications" msgstr "Benachrichtigungen" @@ -385,7 +388,7 @@ msgid "Processing..." msgstr "Abarbeitung..." 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." msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal." @@ -620,7 +623,7 @@ msgid "View Detail" msgstr "Details anzeigen" msgid "login" -msgstr "Einloggen" +msgstr "anmelden" msgid "" "Thank you for signing up. We have sent an email to you. Please follow the " diff --git a/hosting/migrations/0043_vmdetail.py b/hosting/migrations/0043_vmdetail.py new file mode 100644 index 00000000..66966233 --- /dev/null +++ b/hosting/migrations/0043_vmdetail.py @@ -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)), + ], + ), + ] diff --git a/hosting/models.py b/hosting/models.py index 478ed745..73c082bb 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -159,3 +159,16 @@ class HostingBill(AssignPermissionsMixin, models.Model): instance = cls.objects.create(customer=customer, billing_address=billing_address) 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) diff --git a/hosting/static/hosting/css/landing-page.css b/hosting/static/hosting/css/landing-page.css index ed8fb310..48a0c1a1 100644 --- a/hosting/static/hosting/css/landing-page.css +++ b/hosting/static/hosting/css/landing-page.css @@ -857,6 +857,8 @@ a.list-group-item-danger:focus, background: rgba(235, 204, 209, 0.2); } .has-error .form-control, +.has-error .form-control:focus, +.has-error .form-control:active, .has-error .input-group-addon { color: #eb4d5c; border-color: #eb4d5c; @@ -870,3 +872,48 @@ a.list-group-item-danger.active:focus { .panel-danger > .panel-heading .badge { 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); +} diff --git a/hosting/static/hosting/css/payment.css b/hosting/static/hosting/css/payment.css index 68a36837..b1b0460d 100644 --- a/hosting/static/hosting/css/payment.css +++ b/hosting/static/hosting/css/payment.css @@ -22,7 +22,100 @@ } .summary-box .content { - 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; + } } \ No newline at end of file diff --git a/hosting/static/hosting/js/virtual_machine_detail.js b/hosting/static/hosting/js/virtual_machine_detail.js index db2621c1..01a58127 100644 --- a/hosting/static/hosting/js/virtual_machine_detail.js +++ b/hosting/static/hosting/js/virtual_machine_detail.js @@ -79,7 +79,6 @@ $(document).ready(function() { $('html,body').scrollTop(scrollmem); }); - $('.modal-text').removeClass('hide'); var create_vm_form = $('#virtual_machine_create_form'); create_vm_form.submit(function () { $('#btn-create-vm').prop('disabled', true); @@ -90,26 +89,28 @@ $(document).ready(function() { success: function (data) { if (data.status === true) { fa_icon = $('.modal-icon > .fa'); - fa_icon.attr('class', 'fa fa-check'); - $('.modal-header > .close').attr('class', 'close'); + fa_icon.attr('class', 'checkmark'); + // $('.modal-header > .close').removeClass('hidden'); $('#createvm-modal-title').text(data.msg_title); $('#createvm-modal-body').text(data.msg_body); - $('#createvm-modal').on('hidden.bs.modal', function () { - window.location = data.redirect; - }) + $('#createvm-modal-done-btn') + .attr('href', data.redirect) + .removeClass('hide'); } }, error: function (xmlhttprequest, textstatus, message) { fa_icon = $('.modal-icon > .fa'); - fa_icon.attr('class', 'fa fa-times'); - $('.modal-header > .close').attr('class', 'close'); - $('.modal-text').addClass('hide'); + fa_icon.attr('class', 'fa fa-close'); 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); + $('#createvm-modal-close-btn').removeClass('hide'); } }); return false; }); + $('#createvm-modal').on('hidden.bs.modal', function () { + $(this).find('.modal-footer .btn').addClass('hide'); + }) }); diff --git a/hosting/templates/hosting/choice_ssh_keys.html b/hosting/templates/hosting/choice_ssh_keys.html index 3a377388..87224156 100644 --- a/hosting/templates/hosting/choice_ssh_keys.html +++ b/hosting/templates/hosting/choice_ssh_keys.html @@ -47,20 +47,5 @@ window.location.href = '{{next_url}}'; {% endif %} - - - - - {%endblock%} diff --git a/hosting/templates/hosting/login.html b/hosting/templates/hosting/login.html index 9f18fda9..82056d2f 100644 --- a/hosting/templates/hosting/login.html +++ b/hosting/templates/hosting/login.html @@ -44,6 +44,8 @@ {% trans "Sign up"%} or {% trans "Forgot your password ? "%} + or
+ {% trans "Resend activation link"%}
diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 345632d2..dc8de901 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -42,7 +42,9 @@

{% trans "Status" %}: - {% if order.status == 'Approved' %} + {% if vm.terminated_at %} + {% trans "Terminated" %} + {% elif order.status == 'Approved' %} {% trans "Approved" %} {% else %} {% trans "Declined" %} @@ -96,14 +98,14 @@

- {% comment %} + {% if vm.created_at %} +

+ {% trans "Period" %}: + {{ vm.created_at|date:'Y/m/d' }} - {% if vm.terminated_at %}{{ vm.terminated_at|date:'Y/m/d' }}{% else %}{% now 'Y/m/d' %}{% endif %} +

+ {% endif %}

- {% trans "Period" %} - {{}} -

- {% endcomment %} -

- {% trans "Cores" %} + {% trans "Cores" %}: {% if vm.cores %} {{vm.cores|floatformat}} {% else %} @@ -111,11 +113,11 @@ {% endif %}

- {% trans "Memory" %} + {% trans "Memory" %}: {{vm.memory}} GB

- {% trans "Disk space" %} + {% trans "Disk space" %}: {{vm.disk_size}} GB

@@ -160,22 +162,19 @@

@@ -123,8 +123,9 @@