diff --git a/Changelog b/Changelog index fcf3c16c..95b7dab8 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,11 @@ +1.0.18: 2017-07-02 + * [datacenterlight] Introduced the new flow. Landing page -> Payment -> Order confirmation -> Success + * [datacenterlight] Fixed issue showing local time to the user in order confirmation page, vm pages (like ssh keys) + * [hosting] Fixed responsive issue while user signup + * [hosting] Fixed 500 error when user requests for a vm whose id does not belong to him + * [datacenterlight] Refactored partially dcl text and dcl support email to be obtained from env parameters + * [datacenterlight] Updated DE translations + * [hosting] Updated email text for user activation 1.0.17: 2017-06-16 * [datacenterlight] Cleanup OrderView useless code * [datacenterlight] Replaced GiB to GB @@ -5,7 +13,7 @@ * [datacenterlight] Fixed translations * [datacenterlight] Added email confirmation feature * [datacenterlight] Changed logo on datacerlight dashboard - * [datacenterlight] Credit card input disappearance fix + * [datacenterlight] Credit card input disappearance fix 1.0.16: 2017-06-15 * [datacenterlight] .po file issue with multiple definition fixed * [datacenterlight] Navbar items in dcl user area rearranged diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index f6628b9f..a0c9a470 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-06-19 12:22+0530\n" +"POT-Creation-Date: 2017-07-02 23:08+0530\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,14 +18,6 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: templates/datacenterlight/base.html:72 -msgid "Please enter your name" -msgstr "Bitte gib Deinen Namen ein" - -#: templates/datacenterlight/base.html:73 -msgid "Please enter a valid email address" -msgstr "Bitte gib eine gültige E-Mail-Adresse ein" - #: templates/datacenterlight/beta_access.html:13 msgid "Enter name" msgstr "Name" @@ -99,15 +91,18 @@ msgid "" "datacenterlight account.
\n" " %(base_url)s%(activation_link)s\n" " " -msgstr "\n" -" Klicke hier um deinen %(dcl_text)s zu aktivieren.

\n" -" Oder kopiere den folgenden Link in die Adressleiste deines Browsers und folge dann dem Link um deinen %(dcl_text)s Account zu aktivieren.
\n" +msgstr "" +"\n" +" Klicke hier " +"um deinen %(dcl_text)s zu aktivieren.

\n" +" Oder kopiere den folgenden Link in die Adressleiste deines " +"Browsers und folge dann dem Link um deinen %(dcl_text)s Account zu " +"aktivieren.
\n" " %(base_url)s%(activation_link)s\n" " " #: templates/datacenterlight/emails/user_activation.html:123 #: templates/datacenterlight/emails/user_activation.txt:11 -#| msgid "Your Name" msgid "Your" msgstr "Dein" @@ -128,8 +123,9 @@ msgstr "" "\n" "Hallo,\n" "\n" -"Du kannst deinen %(dcl_text)s Account aktivieren, indem du hier klickst %(base_url)s" -"%(activation_link)s\n" +"Du kannst deinen %(dcl_text)s Account aktivieren, indem du hier klickst " +"%(base_url)s%(activation_link)s\n" + #: templates/datacenterlight/includes/_footer.html:11 #: templates/datacenterlight/includes/_footer.html:31 #: templates/datacenterlight/includes/_navbar.html:27 @@ -260,44 +256,112 @@ msgstr "MwSt. inklusive" msgid "Hosted in Switzerland" msgstr "Standort: Schweiz" -#: templates/datacenterlight/index.html:173 +#: templates/datacenterlight/index.html:161 +msgid "Please enter a value greater than or equal to 1." +msgstr "Bitte gib einen Wert größer oder gleich 1 ein." + +#: templates/datacenterlight/index.html:170 +msgid "Please enter a value greater than or equal to 2." +msgstr "Bitte gib einen Wert größer oder gleich 2 ein." + +#: templates/datacenterlight/index.html:179 +msgid "Please enter a value greater than or equal to 10." +msgstr "Bitte gib einen Wert größer oder gleich 10 ein" + +#: templates/datacenterlight/index.html:180 #: templates/datacenterlight/pricing.html:50 msgid "GB Storage (SSD)" msgstr "GB Storage (SSD)" -#: templates/datacenterlight/index.html:189 +#: templates/datacenterlight/index.html:199 msgid "Name" msgstr "" -#: templates/datacenterlight/index.html:190 +#: templates/datacenterlight/index.html:200 msgid "Your Name" msgstr "Dein Name" -#: templates/datacenterlight/index.html:193 +#: templates/datacenterlight/index.html:200 +msgid "Please enter your name" +msgstr "Bitte gib Deinen Namen ein" + +#: templates/datacenterlight/index.html:214 msgid "Email" msgstr "E-Mail-Adresse" -#: templates/datacenterlight/index.html:194 +#: templates/datacenterlight/index.html:215 msgid "Your Email" msgstr "Deine E-Mail" -#: templates/datacenterlight/index.html:197 +#: templates/datacenterlight/index.html:215 +msgid "Please enter a valid email address" +msgstr "Bitte gib eine gültige E-Mailadresse ein" + +#: templates/datacenterlight/index.html:228 #: templates/datacenterlight/pricing.html:79 msgid "Order Now!" msgstr "Bestelle jetzt!" -#: templates/datacenterlight/index.html:226 +#: templates/datacenterlight/index.html:254 msgid "Switzerland " msgstr "Schweiz" -#: templates/datacenterlight/index.html:243 +#: templates/datacenterlight/index.html:271 msgid "Questions?" msgstr "Fragen?" -#: templates/datacenterlight/index.html:243 +#: templates/datacenterlight/index.html:271 msgid "Contact us!" msgstr "Kontaktiere uns!" +#: templates/datacenterlight/order_detail.html:24 +msgid "Confirm Order" +msgstr "Bestellung Bestätigen" + +#: templates/datacenterlight/order_detail.html:30 +msgid "Billed To:" +msgstr "Rechnungsadresse" + +#: templates/datacenterlight/order_detail.html:39 +msgid "Date" +msgstr "Datum" + +#: templates/datacenterlight/order_detail.html:48 +msgid "Payment Method:" +msgstr "Bezahlmethode" + +#: templates/datacenterlight/order_detail.html:49 +msgid "ending" +msgstr "endend in" + +#: templates/datacenterlight/order_detail.html:59 +msgid "Order summary" +msgstr "Bestellungsübersicht" + +#: templates/datacenterlight/order_detail.html:63 +msgid "Cores" +msgstr "Prozessorkerne" + +#: templates/datacenterlight/order_detail.html:65 +msgid "Memory" +msgstr "Arbeitsspeicher" + +#: templates/datacenterlight/order_detail.html:67 +msgid "Disk space" +msgstr "Festplattenkapazität" + +#: templates/datacenterlight/order_detail.html:69 +msgid "Configuration" +msgstr "Konfiguration" + +#: templates/datacenterlight/order_detail.html:71 +msgid "Total" +msgstr "" + +#: templates/datacenterlight/order_detail.html:78 +msgid "Place order" +msgstr "Bestellen" + #: templates/datacenterlight/pricing.html:9 msgid "We are cutting down the costs significantly!" msgstr "Wir sorgen dafür, dass die Kosten für Dich signifikant abnehmen" @@ -328,6 +392,14 @@ msgstr "" msgid "as soon as possible!" msgstr "" +#: views.py:234 +msgid "is not a proper name" +msgstr "ist kein gültiger Name" + +#: views.py:241 +msgid "is not a proper email" +msgstr "ist keine gültige E-Mailadresse" + #~ msgid "Buy VM" #~ msgstr "VM Kaufen" diff --git a/datacenterlight/static/datacenterlight/css/landing-page.css b/datacenterlight/static/datacenterlight/css/landing-page.css index b406c752..c9fd76aa 100755 --- a/datacenterlight/static/datacenterlight/css/landing-page.css +++ b/datacenterlight/static/datacenterlight/css/landing-page.css @@ -7,7 +7,6 @@ font-family: 'Lato-Regular'; src: url('../fonts/Lato/Lato-Regular.ttf'); } - body, html { width: 100%; @@ -189,7 +188,6 @@ h6 { .nav-language:hover .drop-language{ display: block; } - .intro-header { height: 100vh; text-align: center; @@ -438,7 +436,6 @@ h6 { } .pricing-section .card .description{ padding: 12px; - border-bottom: 1px solid rgba(128, 128, 128, 0.3); } .pricing-section .card .descriptions{ padding: 10px 30px; @@ -789,11 +786,10 @@ tech-sub-sec h2 { } .price-calc-section .card .description{ padding: 12px; - border-bottom: 1px solid rgba(128, 128, 128, 0.3); position: relative; display: flex; - justify-content: space-around; - align-items: center; + justify-content: space-around !important; + align-items: center !important; } .price-calc-section .card .description span { @@ -804,10 +800,10 @@ tech-sub-sec h2 { width: 30%; text-align: left; } -.price-calc-section .card .description input{ - font-size: 20px; - text-align: center; - width: 60px; +.price-calc-section .card .description .select-number{ + font-size: 20px; + text-align: center; + width: 60px; } .price-calc-section .card .description i{ color: #29427A; @@ -844,8 +840,8 @@ tech-sub-sec h2 { .price-calc-section .card .check-ip{ font-size: 18px; } -.price-calc-section .card .description.input{ - justify-content: center; +.price-calc-section .card .justify-center{ + justify-content: center !important; } .price-calc-section .card .description.input label{ font-size: 15px; @@ -854,10 +850,11 @@ tech-sub-sec h2 { margin-bottom: 0; width: 40px; } -.price-calc-section .card .description.input input{ - width: 200px; - font-size: 14px; - text-align: left; +/*Changed class****.price-calc-section .card .description.input input*/ +.price-calc-section .card .description input{ + width: 200px; + font-size: 14px; + text-align: left; padding: 5px 10px; border-radius: 4px; border: 1px solid #d0d0d0; @@ -869,6 +866,18 @@ tech-sub-sec h2 { font-size: 17px; margin: 0 8px; } +.help-block.with-errors { + text-align: center; + margin: 0; + padding: 0; +} +.has-error .checkbox, .has-error .checkbox-inline, .has-error .control-label, .has-error .help-block, .has-error .radio, .has-error .radio-inline, .has-error.checkbox label, .has-error.checkbox-inline label, .has-error.radio label, .has-error.radio-inline label{ +color: #eb4d5c; +} +.form-group { + margin: 0; + border-bottom: 1px solid rgba(128, 128, 128, 0.3); +} @media(max-width:767px) { .percent-text { font-size: 50px; @@ -1125,7 +1134,7 @@ line-height: 40px; text-align: center; } - .price-calc-section .card .description input { + .price-calc-section .card .description .select-number{ font-size: 17px; text-align: center; width: 60px; diff --git a/datacenterlight/templates/datacenterlight/base.html b/datacenterlight/templates/datacenterlight/base.html index c62d92f7..2c3f0e5d 100644 --- a/datacenterlight/templates/datacenterlight/base.html +++ b/datacenterlight/templates/datacenterlight/base.html @@ -33,6 +33,9 @@ + + {% include "google_analytics.html" %} + @@ -60,53 +63,5 @@ - - - - - + diff --git a/datacenterlight/templates/datacenterlight/index.html b/datacenterlight/templates/datacenterlight/index.html index 6d63b29b..822858ae 100755 --- a/datacenterlight/templates/datacenterlight/index.html +++ b/datacenterlight/templates/datacenterlight/index.html @@ -139,7 +139,7 @@
-
+ {% csrf_token %}

{% trans "VM hosting" %}

@@ -152,28 +152,37 @@
-
+

{% trans "Hosted in Switzerland" %}

-
+
+
- + Core +
+
-
+
+
- + GB RAM +
+
-
+
+
- + {% trans "GB Storage (SSD)" %} +
+
-
+
-
-
- - -
+
+
+ + +
+
+ {% 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 %} +
+
-
-
-
@@ -250,4 +278,4 @@
- {% endblock %} \ No newline at end of file + {% endblock %} diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index cb412558..273074cf 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -1,7 +1,8 @@ {% extends "hosting/base_short.html" %} {% load staticfiles bootstrap3 %} {% load i18n %} -{% block content %} +{% load custom_tags %} +{% block content %}
{% if messages %} @@ -19,37 +20,38 @@ {% if not error %}
-
-

{% trans "Confirm Order"%}

{% trans "Order #"%} {{order.id}}

-
-
-
-
-
+
+

{% trans "Confirm Order"%}

+
+
+
+
+

{% trans "Billed To:"%}

- {{user.name}}
- {{order.billing_address.street_address}},{{order.billing_address.postal_code}}
- {{order.billing_address.city}}, {{order.billing_address.country}}. -
-
+ {% with request.session.billing_address_data as billing_address %} + {{request.session.user.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 "Date"%}:
- {{order.created_at}}

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

-
-
-
-
- {% trans "Payment Method:"%}
- {{order.cc_brand}} ending **** {{order.last4}}
- {{user.email}} -
-
-
-
+
+
+
+
+ {% trans "Payment Method:"%}
+ {{cc_brand}} {% trans "ending" %} **** {{cc_last4}}
+ {{request.session.user.email}} +
+
+
+
@@ -57,23 +59,39 @@

{% trans "Order summary"%}


-

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

-
-

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

-
-

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

-
-

{% trans "Total"%}

{{vm.price}} CHF

+ {% 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

+ {% endwith %}

- {% url 'datacenterlight:payment' as payment_url %} - {% if payment_url in request.META.HTTP_REFERER %} +
+ {% csrf_token %} - {% endif %} +
{% endif %} + + + {%endblock%} diff --git a/datacenterlight/templatetags/custom_tags.py b/datacenterlight/templatetags/custom_tags.py index 915e68fe..ed59af9a 100644 --- a/datacenterlight/templatetags/custom_tags.py +++ b/datacenterlight/templatetags/custom_tags.py @@ -21,4 +21,14 @@ def change_lang(context, lang=None, *args, **kwargs): activate(cur_language) - return "%s" % url \ No newline at end of file + return "%s" % url + +@register.filter('get_value_from_dict') +def get_value_from_dict(dict_data, key): + """ + usage example {{ your_dict|get_value_from_dict:your_key }} + """ + if key: + return dict_data.get(key) + else : + return "" diff --git a/datacenterlight/urls.py b/datacenterlight/urls.py index d880dc34..33ea644e 100644 --- a/datacenterlight/urls.py +++ b/datacenterlight/urls.py @@ -10,7 +10,7 @@ urlpatterns = [ 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/(?P\d+)/?$', OrderConfirmationView.as_view(), name='order_confirmation'), + url(r'^/order-confirmation/?$', OrderConfirmationView.as_view(), name='order_confirmation'), url(r'^/order-success/?$', SuccessView.as_view(), name='order_success'), url(r'^/beta_access?$', BetaAccessView.as_view(), name='beta_access'), ] diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 205216e9..51256053 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -12,7 +12,9 @@ from django import forms from django.core.exceptions import ValidationError from django.views.decorators.cache import cache_control from django.conf import settings +from django.utils.translation import ugettext_lazy as _ from utils.forms import BillingAddressForm, UserBillingAddressForm +from utils.models import BillingAddress from membership.models import StripeCustomer from hosting.models import HostingOrder, HostingBill from utils.stripe_utils import StripeUtils @@ -31,9 +33,14 @@ class SuccessView(TemplateView): def get(self, request, *args, **kwargs): if 'specs' not in request.session or 'user' not in request.session: return HttpResponseRedirect(reverse('datacenterlight:index')) - else : - del request.session['specs'] - del request.session['user'] + elif 'token' not in request.session: + return HttpResponseRedirect(reverse('datacenterlight:payment')) + elif 'order_confirmation' not in request.session: + return HttpResponseRedirect(reverse('datacenterlight:order_confirmation')) + else: + for session_var in ['specs', 'user', 'template', 'billing_address', 'billing_address_data', 'token', 'customer']: + if session_var in request.session: + del request.session[session_var] return render(request, self.template_name) class PricingView(TemplateView): @@ -187,15 +194,14 @@ class IndexView(CreateView): @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): - if 'specs' in request.session : - del request.session['specs'] - if 'user' in request.session : - del request.session['user'] + for session_var in ['specs', 'user', 'billing_address_data']: + if session_var in request.session: + del request.session[session_var] try: manager = OpenNebulaManager() templates = manager.get_templates() context = { - 'templates': VirtualMachineTemplateSerializer(templates, many=True).data, + 'templates': VirtualMachineTemplateSerializer(templates, many=True).data } except: messages.error( request, @@ -224,14 +230,16 @@ class IndexView(CreateView): try: name = name_field.clean(name) except ValidationError as err: - messages.add_message(self.request, messages.ERROR, '%(value) is not a proper name.'.format(name)) - return HttpResponseRedirect(reverse('datacenterlight:index')) + 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: - messages.add_message(self.request, messages.ERROR, '%(value) is not a proper email.'.format(email)) - return HttpResponseRedirect(reverse('datacenterlight:index')) + 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 = { 'cpu': cores, @@ -323,11 +331,27 @@ class WhyDataCenterLightView(IndexView): class PaymentOrderView(FormView): template_name = 'hosting/payment.html' form_class = BillingAddressForm + + 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: + form_kwargs.update({ + 'initial': { + '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 + def get_context_data(self, **kwargs): context = super(PaymentOrderView, self).get_context_data(**kwargs) context.update({ - 'stripe_key': settings.STRIPE_API_PUBLIC_KEY + 'stripe_key': settings.STRIPE_API_PUBLIC_KEY, + 'site_url': reverse('datacenterlight:index') }) return context @@ -342,13 +366,9 @@ class PaymentOrderView(FormView): if form.is_valid(): # Get billing address data billing_address_data = form.cleaned_data - context = self.get_context_data() - template = request.session.get('template') - specs = request.session.get('specs') - user = request.session.get('user') - vm_template_id = template.get('id', 1) - final_price = specs.get('price') token = form.cleaned_data.get('token') + user = request.session.get('user') + try: custom_user = CustomUser.objects.get(email=user.get('email')) except CustomUser.DoesNotExist: @@ -360,7 +380,6 @@ class PaymentOrderView(FormView): app='dcl', base_url=None, send_email=False) - # Get or create stripe customer customer = StripeCustomer.get_or_create(email=user.get('email'), token=token) @@ -370,115 +389,131 @@ class PaymentOrderView(FormView): # Create Billing Address billing_address = form.save() - - # Make stripe charge to a customer - stripe_utils = StripeUtils() - charge_response = stripe_utils.make_charge(amount=final_price, - customer=customer.stripe_id) - charge = charge_response.get('response_object') - - # Check if the payment was approved - if not charge: - context.update({ - 'paymentError': charge_response.get('error'), - 'form': form - }) - return render(request, self.template_name, context) - - charge = charge_response.get('response_object') - - # Create OpenNebulaManager - manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, - password=settings.OPENNEBULA_PASSWORD) - - # Create a vm using logged user - vm_id = manager.create_vm( - template_id=vm_template_id, - specs=specs, - vm_name="{email}-{template_name}-{date}".format( - email=user.get('email'), - template_name=template.get('name'), - date=int(datetime.now().strftime("%s"))) - ) - - # Create a Hosting Order - order = HostingOrder.create( - price=final_price, - vm_id=vm_id, - customer=customer, - billing_address=billing_address - ) - - # Create a Hosting Bill - bill = HostingBill.create( - customer=customer, billing_address=billing_address) - - # Create Billing Address for User if he does not have one - if not customer.user.billing_addresses.count(): - billing_address_data.update({ - 'user': customer.user.id - }) - billing_address_user_form = UserBillingAddressForm( - billing_address_data) - billing_address_user_form.is_valid() - billing_address_user_form.save() - - # Associate an order with a stripe payment - order.set_stripe_charge(charge) - - # If the Stripe payment was successed, set order status approved - order.set_approved() - - vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data - - context = { - 'name': user.get('name'), - 'email': user.get('email'), - 'cores': specs.get('cpu'), - 'memory': specs.get('memory'), - 'storage': specs.get('disk_size'), - 'price': specs.get('price'), - 'template': template.get('name'), - 'vm.name': vm['name'], - 'vm.id': vm['vm_id'], - 'order.id': order.id - } - email_data = { - 'subject': "Data Center Light Order from %s" % context['email'], - 'from_email': '(Data Center Light) Data Center Light Support ', - 'to': ['info@ungleich.ch'], - 'body': "\n".join(["%s=%s" % (k, v) for (k, v) in context.items()]), - 'reply_to': [context['email']], - } - email = EmailMessage(**email_data) - email.send() - return HttpResponseRedirect(reverse('datacenterlight:order_confirmation', kwargs={'pk': order.id})) + request.session['billing_address_data'] = billing_address_data + request.session['billing_address'] = billing_address.id + request.session['token'] = token + request.session['customer'] = customer.id + return HttpResponseRedirect(reverse('datacenterlight:order_confirmation')) else: return self.form_invalid(form) class OrderConfirmationView(DetailView): template_name = "datacenterlight/order_detail.html" + payment_template_name = 'hosting/payment.html' context_object_name = "order" model = HostingOrder - def get_context_data(self, **kwargs): - # Get context - context = super(DetailView, self).get_context_data(**kwargs) - obj = self.get_object() + + @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: + return HttpResponseRedirect(reverse('datacenterlight:index')) + 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() + stripe_utils = StripeUtils() + card_details = stripe_utils.get_card_details(customer.stripe_id, request.session.get('token')) + context = { + 'site_url': reverse('datacenterlight:index'), + 'cc_last4' : card_details.get('response_object').get('last4'), + 'cc_brand' : card_details.get('response_object').get('brand') + } + return render(request, self.template_name, context) + + def post(self, request, *args, **kwargs): + template = request.session.get('template') + 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') + billing_address = BillingAddress.objects.filter(id=billing_address_id).first() + token = request.session.get('token') + vm_template_id = template.get('id', 1) + final_price = specs.get('price') + + # Make stripe charge to a customer + stripe_utils = StripeUtils() + charge_response = stripe_utils.make_charge(amount=final_price, + customer=customer.stripe_id) + charge = charge_response.get('response_object') + + # Check if the payment was approved + if not charge: + context.update({ + 'paymentError': charge_response.get('error') + # TODO add logic in payment form to autofill data + #'form': form + }) + return render(request, self.payment_template_name, context) + + charge = charge_response.get('response_object') + + # Create OpenNebulaManager manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, password=settings.OPENNEBULA_PASSWORD) - try: - vm = manager.get_vm(obj.vm_id) - context['vm'] = VirtualMachineSerializer(vm).data - context['next_url'] = reverse('datacenterlight:order_success') - except WrongIdError: - messages.error(self.request, - 'The VM you are looking for is unavailable at the moment. \ - Please contact Data Center Light support.' - ) - self.kwargs['error'] = 'WrongIdError' - context['error'] = 'WrongIdError' - except ConnectionRefusedError: - messages.error(self.request, - 'In order to create a VM, you need to create/upload your SSH KEY first.' - ) - return context + + # Create a vm using oneadmin, also specify the name + vm_id = manager.create_vm( + template_id=vm_template_id, + specs=specs, + vm_name="{email}-{template_name}-{date}".format( + email=user.get('email'), + template_name=template.get('name'), + date=int(datetime.now().strftime("%s"))) + ) + + # Create a Hosting Order + order = HostingOrder.create( + price=final_price, + vm_id=vm_id, + customer=customer, + billing_address=billing_address + ) + + # Create a Hosting Bill + bill = HostingBill.create( + customer=customer, billing_address=billing_address) + + # Create Billing Address for User if he does not have one + if not customer.user.billing_addresses.count(): + billing_address_data.update({ + 'user': customer.user.id + }) + billing_address_user_form = UserBillingAddressForm( + billing_address_data) + billing_address_user_form.is_valid() + billing_address_user_form.save() + + # Associate an order with a stripe payment + order.set_stripe_charge(charge) + + # If the Stripe payment was successed, set order status approved + order.set_approved() + + vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data + + context = { + 'name': user.get('name'), + 'email': user.get('email'), + 'cores': specs.get('cpu'), + 'memory': specs.get('memory'), + 'storage': specs.get('disk_size'), + 'price': specs.get('price'), + 'template': template.get('name'), + 'vm.name': vm['name'], + 'vm.id': vm['vm_id'], + 'order.id': order.id + } + email_data = { + 'subject': settings.DCL_TEXT + " Order from %s" % context['email'], + 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, + 'to': ['info@ungleich.ch'], + 'body': "\n".join(["%s=%s" % (k, v) for (k, v) in context.items()]), + 'reply_to': [context['email']], + } + email = EmailMessage(**email_data) + email.send() + request.session['order_confirmation'] = True + return HttpResponseRedirect(reverse('datacenterlight:order_success')) diff --git a/digitalglarus/templates/base_glarus.html b/digitalglarus/templates/base_glarus.html index 424f3aac..b94c813e 100644 --- a/digitalglarus/templates/base_glarus.html +++ b/digitalglarus/templates/base_glarus.html @@ -39,27 +39,13 @@ - + + {% include 'google_analytics.html' %} + diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index d276f022..ba304ff9 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -118,6 +118,7 @@ INSTALLED_APPS = ( MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', + 'utils.middleware.MultipleProxyMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', @@ -138,6 +139,7 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(PROJECT_DIR, 'cms_templates/'), + os.path.join(PROJECT_DIR, 'templates'), os.path.join(PROJECT_DIR, 'cms_templates/djangocms_blog/'), os.path.join(PROJECT_DIR, 'membership'), os.path.join(PROJECT_DIR, 'hosting/templates/'), @@ -161,6 +163,7 @@ TEMPLATES = [ "django.contrib.messages.context_processors.messages", 'sekizai.context_processors.sekizai', 'cms.context_processors.cms_settings', + 'utils.context_processor.google_analytics', ], }, }, @@ -509,3 +512,12 @@ OPENNEBULA_ENDPOINT = env('OPENNEBULA_ENDPOINT') # dcl email configurations DCL_TEXT = env('DCL_TEXT') DCL_SUPPORT_FROM_ADDRESS = env('DCL_SUPPORT_FROM_ADDRESS') + +# Settings for Google analytics +GOOGLE_ANALYTICS_PROPERTY_IDS = { + 'datacenterlight.ch': 'UA-62285904-9', + 'digitalglarus.ch': 'UA-62285904-2', + '127.0.0.1:8000': 'localhost', + 'dynamicweb-development.ungleich.ch': 'development', + 'dynamicweb-staging.ungleich.ch': 'staging' +} diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 674f3db4..b3cc6c0b 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-06-23 02:28+0530\n" +"POT-Creation-Date: 2017-07-01 02:09+0530\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -26,47 +26,47 @@ msgstr "Dein Account wurde noch nicht aktiviert." msgid "Paste here your public key" msgstr "Fügen Sie Ihren public key ein" -#: templates/hosting/base_short.html:67 +#: templates/hosting/base_short.html:68 msgid "My Virtual Machines" msgstr "Meine virtuellen Maschinen" -#: templates/hosting/base_short.html:72 templates/hosting/orders.html.py:12 +#: templates/hosting/base_short.html:73 templates/hosting/orders.html.py:12 msgid "My Orders" msgstr "Meine Bestellungen" -#: templates/hosting/base_short.html:81 +#: templates/hosting/base_short.html:82 msgid "Keys" msgstr "Schlüssel" -#: templates/hosting/base_short.html:86 +#: templates/hosting/base_short.html:87 msgid "Notifications " msgstr "Benachrichtigungen" -#: templates/hosting/base_short.html:89 +#: templates/hosting/base_short.html:90 msgid "Logout" msgstr "Abmelden" -#: templates/hosting/base_short.html:94 +#: templates/hosting/base_short.html:95 msgid "How it works" msgstr "So funktioniert es" -#: templates/hosting/base_short.html:97 +#: templates/hosting/base_short.html:98 msgid "Your infrastructure" msgstr "deine Infrastruktur" -#: templates/hosting/base_short.html:100 +#: templates/hosting/base_short.html:101 msgid "Our inftrastructure" msgstr "Unsere Infrastruktur" -#: templates/hosting/base_short.html:103 +#: templates/hosting/base_short.html:104 msgid "Pricing" msgstr "Preise" -#: templates/hosting/base_short.html:106 +#: templates/hosting/base_short.html:107 msgid "Contact" msgstr "Kontakt" -#: templates/hosting/base_short.html:109 +#: templates/hosting/base_short.html:110 #: templates/hosting/confirm_reset_password.html:38 #: templates/hosting/login.html:17 templates/hosting/login.html.py:26 #: templates/hosting/reset_password.html:32 templates/hosting/signup.html:30 @@ -347,11 +347,11 @@ msgstr "Bestellung stornieren" msgid "Do You want to delete your order?" msgstr "Willst du deine Bestellung löschen?" -#: templates/hosting/orders.html:63 templates/hosting/user_keys.html.py:62 +#: templates/hosting/orders.html:63 templates/hosting/user_keys.html.py:63 msgid "Close" msgstr "Schliessen" -#: templates/hosting/orders.html:65 templates/hosting/user_keys.html.py:64 +#: templates/hosting/orders.html:65 templates/hosting/user_keys.html.py:65 msgid "Delete" msgstr "Löschen" @@ -359,31 +359,36 @@ msgstr "Löschen" msgid "Billing Amount" msgstr "Rechnungsbetrag" -#: templates/hosting/payment.html:35 +#: templates/hosting/payment.html:26 +#: templates/hosting/virtual_machine_detail.html:98 +msgid "Configuration" +msgstr "Konfiguration" + +#: templates/hosting/payment.html:38 msgid "Billing Address" msgstr "Rechnungsadresse" -#: templates/hosting/payment.html:49 +#: templates/hosting/payment.html:52 msgid "Payment Details" msgstr "Rechnungsdetails" -#: templates/hosting/payment.html:62 +#: templates/hosting/payment.html:65 msgid "Submit Payment" msgstr "Betrag überweisen" -#: templates/hosting/payment.html:81 +#: templates/hosting/payment.html:84 msgid "CARD NUMBER" msgstr "Kreditkartennummer" -#: templates/hosting/payment.html:86 +#: templates/hosting/payment.html:89 msgid "Valid Card Number" msgstr "Gültige Kreditkartennummer" -#: templates/hosting/payment.html:95 +#: templates/hosting/payment.html:98 msgid "EXPIRATION DATE" msgstr "Ablaufdatum" -#: templates/hosting/payment.html:106 +#: templates/hosting/payment.html:109 msgid "CV CODE" msgstr "CV Code" @@ -425,13 +430,13 @@ msgstr "Schlüssel hinzufügen" msgid "Created at" msgstr "Erstellt am" -#: templates/hosting/user_keys.html:42 +#: templates/hosting/user_keys.html:43 #, fuzzy #| msgid "Delete" msgid "Delete Key" msgstr "Löschen" -#: templates/hosting/user_keys.html:55 +#: templates/hosting/user_keys.html:56 #, fuzzy #| msgid "Do You want do delete your order?" msgid "Do You want to delete this key?" @@ -453,10 +458,6 @@ msgstr "Ip nicht zugewiesen" msgid "Disk" msgstr "Festplatte" -#: templates/hosting/virtual_machine_detail.html:98 -msgid "Configuration" -msgstr "Konfiguration" - #: templates/hosting/virtual_machine_detail.html:108 msgid "Current pricing" msgstr "Aktueller Preis" diff --git a/hosting/templates/hosting/base_short.html b/hosting/templates/hosting/base_short.html index 0ccfe5f9..c3b19c8c 100644 --- a/hosting/templates/hosting/base_short.html +++ b/hosting/templates/hosting/base_short.html @@ -57,7 +57,7 @@ - + {% if request.user.is_authenticated %} @@ -131,6 +131,8 @@ + {% else %} + {% include "datacenterlight/includes/_footer.html" %} {% endif %} @@ -162,6 +164,12 @@ + + + + + + diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index e859752e..761550fe 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -35,7 +35,7 @@
{% trans "Date"%}:
- {{order.created_at}}

+ {{order.created_at|date:'Y-m-d H:i'}}

{% trans "Status:"%}
{{vm.disk_size}} GB


-

{% trans "Total"%}

{{vm.price}} CHF

+

{% trans "Total"%}

{{vm.price}} CHF


{% url 'hosting:payment' as payment_url %} @@ -81,4 +81,17 @@ {% endif %} + + + {%endblock%} + diff --git a/hosting/templates/hosting/orders.html b/hosting/templates/hosting/orders.html index b52e7fc0..99aab23e 100644 --- a/hosting/templates/hosting/orders.html +++ b/hosting/templates/hosting/orders.html @@ -24,7 +24,7 @@ {% for order in orders %} {{ order.id }} - {{ order.created_at }} + {{ order.created_at | date:"M d, Y" }} {{ order.price }} CHF {% if order.approved %} {% trans "Approved"%} diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index e41f57d7..a09061bb 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -22,6 +22,9 @@

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

+
+

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


Total

{{request.session.specs.price }} CHF

diff --git a/hosting/templates/hosting/user_keys.html b/hosting/templates/hosting/user_keys.html index 6dd2c823..1dc45e54 100644 --- a/hosting/templates/hosting/user_keys.html +++ b/hosting/templates/hosting/user_keys.html @@ -32,7 +32,8 @@ {% for user_key in keys %} {{user_key.name}} - {{user_key.created_at}} + + {{user_key.created_at|date:'Y-m-d H:i' }} Active @@ -92,5 +93,21 @@ {% endif %} + + + + {%endblock%} diff --git a/templates/google_analytics.html b/templates/google_analytics.html new file mode 100644 index 00000000..64dbae40 --- /dev/null +++ b/templates/google_analytics.html @@ -0,0 +1,13 @@ +{% if GOOGLE_ANALYTICS_PROPERTY_ID %} + +{% else %} + +{% endif %} \ No newline at end of file diff --git a/utils/context_processor.py b/utils/context_processor.py new file mode 100644 index 00000000..740591ba --- /dev/null +++ b/utils/context_processor.py @@ -0,0 +1,15 @@ +from django.conf import settings + + +def google_analytics(request): + """ + Use the variables returned in this function to + render your Google Analytics tracking code template. + """ + host = request.get_host() + ga_prop_id = getattr(settings, 'GOOGLE_ANALYTICS_PROPERTY_IDS', False).get(host) + if not settings.DEBUG and ga_prop_id: + return { + 'GOOGLE_ANALYTICS_PROPERTY_ID': ga_prop_id + } + return {} diff --git a/utils/middleware.py b/utils/middleware.py new file mode 100644 index 00000000..d37bdbfd --- /dev/null +++ b/utils/middleware.py @@ -0,0 +1,17 @@ +class MultipleProxyMiddleware(object): + FORWARDED_FOR_FIELDS = [ + 'HTTP_X_FORWARDED_FOR', + 'HTTP_X_FORWARDED_HOST', + 'HTTP_X_FORWARDED_SERVER', + ] + + def process_request(self, request): + """ + Rewrites the proxy headers so that only the most + recent proxy is used. + """ + for field in self.FORWARDED_FOR_FIELDS: + if field in request.META: + if ',' in request.META[field]: + parts = request.META[field].split(',') + request.META[field] = parts[-1].strip() \ No newline at end of file diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index 29b45d8e..3b20b708 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -77,6 +77,16 @@ class StripeUtils(object): } return new_card_data + @handleStripeError + def get_card_details(self, customer_id, token): + customer = stripe.Customer.retrieve(customer_id) + credit_card_raw_data = customer.sources.data.pop() + card_details = { + 'last4': credit_card_raw_data.last4, + 'brand': credit_card_raw_data.brand + } + return card_details + def check_customer(self, id, user, token): customers = self.stripe.Customer.all() if not customers.get('data'):