diff --git a/Changelog b/Changelog index 58835832..fe9a1dde 100644 --- a/Changelog +++ b/Changelog @@ -4,7 +4,7 @@ Next: * #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 diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index b60a5e67..c0906022 100644 --- a/datacenterlight/locale/de/LC_MESSAGES/django.po +++ b/datacenterlight/locale/de/LC_MESSAGES/django.po @@ -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" @@ -347,6 +399,36 @@ 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" + +msgid "Order Now!" +msgstr "Bestelle jetzt!" + +msgid "" +"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." +msgstr "" +"Unsere VMs werden in der Schweiz im Kanton Glarus gehostet und befinden sich " +"zur Zeit noch in der BETA-Phase. Möchtest du mehr über uns erfahren und hast " +"auf unserer Website nicht genügend Informationen gefunden? Möchtest eine " +"detailliertere Bestellung aufgeben? Bist du auf technische Probleme " +"gestossen, die du uns mitteilen möchtest? Dann zögere nicht und kontaktiere " +"uns unter support@datacenterlight.ch. Unser Team wird sich umgehend um dein " +"Anliegen kümmern!" + msgid "Thank you for order! Our team will contact you via email" msgstr "" "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich mit " @@ -440,11 +522,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 "Confirm Order" msgstr "Bestellung Bestätigen" diff --git a/datacenterlight/templates/datacenterlight/calculator_form.html b/datacenterlight/templates/datacenterlight/calculator_form.html index 9a0fcaa3..1733a719 100644 --- a/datacenterlight/templates/datacenterlight/calculator_form.html +++ b/datacenterlight/templates/datacenterlight/calculator_form.html @@ -80,43 +80,6 @@ -
-
- - -
-
- {% 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 dbb5ebbf..ad53360e 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -17,4 +17,5 @@
-{% endblock submit_btn %} \ No newline at end of file +{% endblock submit_btn %} + diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 7d3559dd..ec87616f 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -1,19 +1,26 @@ +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.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.serializers import VMTemplateSerializer -from utils.forms import BillingAddressForm +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 @@ -21,6 +28,8 @@ 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" @@ -227,11 +236,6 @@ class IndexView(CreateView): 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: @@ -258,39 +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': 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): @@ -353,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 @@ -374,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: @@ -435,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'): @@ -452,6 +439,7 @@ class OrderConfirmationView(DetailView): 'cc_brand': card_details.get('response_object').get('brand'), 'vm': request.session.get('specs'), 'page_header_text': _('Confirm Order') + 'billing_address_data': request.session.get('billing_address_data') } return render(request, self.template_name, context) @@ -460,29 +448,40 @@ 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 = get_vm_price(cpu=cpu, memory=memory, - disk_size=disk_size) - specs['price'] = amount_to_be_charged + amount_to_be_charged = specs.get('price') plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu, memory=memory, disk_size=disk_size) @@ -496,7 +495,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') @@ -506,11 +505,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 74391147..6a50d7be 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -268,7 +268,7 @@ msgstr "" "Link klickst.
\n" msgid "My VM page" -msgstr "" +msgstr "Meine VM page" #, python-format msgid "" @@ -629,7 +629,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/static/hosting/css/landing-page.css b/hosting/static/hosting/css/landing-page.css index 5275dd97..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; @@ -871,6 +873,13 @@ a.list-group-item-danger.active:focus { 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; } 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/templates/hosting/virtual_machines.html b/hosting/templates/hosting/virtual_machines.html index 622e7a55..aac241e3 100644 --- a/hosting/templates/hosting/virtual_machines.html +++ b/hosting/templates/hosting/virtual_machines.html @@ -13,7 +13,12 @@ {% endif %} {% if not error %}
-

{% trans 'To create a new virtual machine, click "Create VM"' %}

+

{% 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 %} +
{% blocktrans %}To access your VM, add your SSH key here{% endblocktrans %} + {% endif %} +

diff --git a/hosting/views.py b/hosting/views.py index 4d736aa8..facc8c01 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -613,13 +613,6 @@ class PaymentVMView(LoginRequiredMixin, FormView): return context 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: del request.session['next'] @@ -885,6 +878,10 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView): context = {'error': 'connection'} else: 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 @@ -905,15 +902,6 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): raise ValidationError(_('Invalid storage size')) 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()} return render(request, self.template_name, context) diff --git a/membership/models.py b/membership/models.py index 5d7c7b11..d3f6372a 100644 --- a/membership/models.py +++ b/membership/models.py @@ -82,7 +82,7 @@ class CustomUser(AbstractBaseUser, PermissionsMixin): @classmethod 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() if not user: user = cls.objects.create_user(name=name, email=email, @@ -112,6 +112,9 @@ class CustomUser(AbstractBaseUser, PermissionsMixin): 'template_name': 'user_activation', 'template_path': 'datacenterlight/emails/' } + if account_details: + email_data['context'][ + 'account_details'] = account_details email = BaseEmail(**email_data) email.send() return user @@ -176,6 +179,25 @@ class StripeCustomer(models.Model): def __str__(self): 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 def get_or_create(cls, email=None, token=None): """ @@ -195,7 +217,6 @@ class StripeCustomer(models.Model): except StripeCustomer.DoesNotExist: user = CustomUser.objects.get(email=email) - stripe_utils = StripeUtils() stripe_data = stripe_utils.create_customer(token, email, user.name) if stripe_data.get('response_object'): diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 057139c0..33812c52 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -150,9 +150,10 @@ class OpenNebulaManager(): oca.User.METHODS['allocate'], email, password, 'core') logger.debug( - "User {0} does not exist. Created the user. User id = {1}", - email, - opennebula_user + "User {} does not exist. Created the user. User id = {}".format( + email, + opennebula_user + ) ) return opennebula_user except ConnectionRefusedError: diff --git a/utils/forms.py b/utils/forms.py index a12034dd..f8a6d103 100644 --- a/utils/forms.py +++ b/utils/forms.py @@ -131,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): user = forms.ModelChoiceField(queryset=CustomUser.objects.all(), widget=forms.HiddenInput())