From 6536991209fc25af113a91dfa93988ede3a00952 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 6 Oct 2017 01:17:35 +0530 Subject: [PATCH 001/866] list all cards, card_input template --- .../datacenterlight/landing_payment.html | 58 +-------------- hosting/templates/hosting/payment.html | 57 +------------- hosting/templates/hosting/settings.html | 74 +++---------------- hosting/views.py | 19 +++-- 4 files changed, 20 insertions(+), 188 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html index fa638d77..1a476797 100644 --- a/datacenterlight/templates/datacenterlight/landing_payment.html +++ b/datacenterlight/templates/datacenterlight/landing_payment.html @@ -121,63 +121,7 @@ {% else %} -
- -
-
-
- -
-
-
-
- -
-
-
- -
-
-
-
- - -
-
-
-
- {% 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 placing your order, you will be taken to the Submit Payment Page." %} -

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

- {{ error|escape }} -

- {% endfor %} -
-
- -
- -
-

-
-
+ {% include "hosting/includes/_card_input.html" %} {% endif %} diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index e9d6476f..a10cfbca 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -105,62 +105,7 @@ {% else %} -
- -
-
-
- -
-
-
-
- -
-
-
- -
-
-
-
- - -
-
-
-
- {% 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 %} -
-
- -
- - -
-

-
-
+ {% include "hosting/includes/_card_input.html" %} {% endif %} diff --git a/hosting/templates/hosting/settings.html b/hosting/templates/hosting/settings.html index 0bafe8e5..5e898b04 100644 --- a/hosting/templates/hosting/settings.html +++ b/hosting/templates/hosting/settings.html @@ -30,12 +30,13 @@

{%trans "Credit Card"%}


- {% if credit_card_data.last4 %} + {% for card in cards_list %}
{% trans "Credit Card" %}
-
{% trans "Last" %} 4: *****{{credit_card_data.last4}}
-
{% trans "Type" %}: {{credit_card_data.cc_brand}}
+
{% trans "Last" %} 4: *****{{card.last4}}
+
{% trans "Type" %}: {{card.cc_brand}}
{% comment %} + {% endcomment %}
@@ -46,75 +47,18 @@
- {% endcomment %}
+ {% endfor %} + {% if credit_card_data.last4 %} {% else %}

{% trans "No Credit Cards Added" %}

{% blocktrans %}We are using Stripe for payment and do not store your information in our database.{% endblocktrans %}

- {% comment %}

{% trans "Add a new 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 %} -

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

- {% blocktrans %}You are not making any payment here.{% endblocktrans %} -

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

-
-
+ {% include "hosting/includes/_card_input.html" %} + {% comment %} {% endcomment %} {% endif %}
@@ -123,7 +67,6 @@ - {% comment %} {% if stripe_key %} {% get_current_language as LANGUAGE_CODE %} @@ -138,6 +81,7 @@ {%endif%} + {% comment %} {% if credit_card_data.last4 and credit_card_data.cc_brand %} {%endif%} - -{% if credit_card_data.last4 and credit_card_data.cc_brand %} - -{%endif%} - {%endblock%} From 248283b369c55b3989c421a93921453d33248f42 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 28 Oct 2017 23:44:06 +0200 Subject: [PATCH 070/866] Show user's credit cards dynamically in landing payment page --- .../datacenterlight/landing_payment.html | 35 ++++++++++++++----- datacenterlight/views.py | 11 +++++- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html index 58246b6a..69c80d35 100644 --- a/datacenterlight/templates/datacenterlight/landing_payment.html +++ b/datacenterlight/templates/datacenterlight/landing_payment.html @@ -84,11 +84,35 @@
+ {% with card_list_len=cards_list|length %}

{%trans "Credit Card"%}


+ {% if card_list_len > 0 %} + {% blocktrans %}Please select one of the previous cards that you used before or fill in your credit card information below. We are using Stripe for payment and do not store your information in our database.{% endblocktrans %} + {% else %} {% 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 %} + {% endif %}

+
+ {% for card in cards_list %} +
+
+
{% trans "Credit Card" %}
+
{% trans "Last" %} 4: ***** {{card.last4}}
+
{% trans "Type" %}: {{card.brand}}
+
+ +
+ {% endfor %} + {% if card_list_len > 0 %} +
Use another card
+ {% endif %} + {% include "hosting/includes/_card_input.html" %} +
+ {% comment %}
{% if credit_card_data.last4 %}
@@ -125,6 +149,8 @@ {% include "hosting/includes/_card_input.html" %} {% endif %}
+ {% endcomment %} + {% endwith %}
@@ -144,13 +170,4 @@ })(); {%endif%} - -{% if credit_card_data.last4 and credit_card_data.cc_brand %} - -{%endif%} - {%endblock%} diff --git a/datacenterlight/views.py b/datacenterlight/views.py index bd1a7f51..861817e1 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -14,7 +14,7 @@ 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.models import HostingOrder, UserCardDetail from hosting.forms import HostingUserLoginForm from membership.models import CustomUser, StripeCustomer from opennebula_api.serializers import VMTemplateSerializer @@ -347,6 +347,14 @@ class PaymentOrderView(FormView): def get_context_data(self, **kwargs): context = super(PaymentOrderView, self).get_context_data(**kwargs) + user = self.request.user + if hasattr(user, 'stripecustomer'): + stripe_customer = user.stripecustomer + else: + stripe_customer = None + cards_list = UserCardDetail.get_all_cards_list( + stripe_customer=stripe_customer + ) if 'billing_address_data' in self.request.session: billing_address_data = self.request.session['billing_address_data'] else: @@ -380,6 +388,7 @@ class PaymentOrderView(FormView): ) context.update({ + 'cards_list': cards_list, 'stripe_key': settings.STRIPE_API_PUBLIC_KEY, 'site_url': reverse('datacenterlight:index'), 'login_form': HostingUserLoginForm(prefix='login_form'), From 2ffaee2d5b9722a50ed33e5d6a338df77af5232c Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 13:14:29 +0100 Subject: [PATCH 071/866] Add clear_items_from_list utility function --- utils/hosting_utils.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 3c193ad7..9541588d 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -60,3 +60,23 @@ def get_vm_price(cpu, memory, disk_size): :return: The price of the VM """ return (cpu * 5) + (memory * 2) + (disk_size * 0.6) + + +class HostingUtils: + @staticmethod + def clear_items_from_list(from_list, items_list): + """ + A utility function to clear items from a given list. + Useful when deleting items in bulk from session. + e.g.: + HostingUtils.clear_items_from_list( + request.session, + ['token', 'billing_address_data', 'card_id',] + ) + :param from_list: + :param items_list: + :return: + """ + for var in items_list: + if var in from_list: + del from_list[var] From 82ad9ac337a9f9ab83256debecc05160b7379274 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 13:17:26 +0100 Subject: [PATCH 072/866] Add selected card handling in landing flow "Order confirmation" page --- datacenterlight/views.py | 172 +++++++++++++++++++++++++++------------ 1 file changed, 122 insertions(+), 50 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 861817e1..baec4ee6 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -21,7 +21,7 @@ from opennebula_api.serializers import VMTemplateSerializer from utils.forms import ( BillingAddressForm, BillingAddressFormSignup ) -from utils.hosting_utils import get_vm_price +from utils.hosting_utils import get_vm_price, HostingUtils from utils.mailer import BaseEmail from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task @@ -401,6 +401,13 @@ class PaymentOrderView(FormView): # user is no longer added to session on the index page if 'specs' not in request.session: return HttpResponseRedirect(reverse('datacenterlight:index')) + HostingUtils.clear_items_from_list( + request.session, + ['token', 'billing_address_data', 'card_id', 'customer', + 'user'] + ) + if 'token' in request.session: + del request.session['token'] return self.render_to_response(self.get_context_data()) def post(self, request, *args, **kwargs): @@ -436,8 +443,40 @@ class PaymentOrderView(FormView): 'name': request.user.name } customer = StripeCustomer.get_or_create( - email=this_user.get('email'), - token=token) + email=this_user.get('email'), token=token + ) + if token is '': + # card selected case + card_id = address_form.cleaned_data.get('card') + try: + user_card_detail = UserCardDetail.objects.get( + id=card_id) + if not request.user.has_perm( + 'view_usercarddetail', user_card_detail + ): + raise UserCardDetail.DoesNotExist( + _( + "{user} does not have permission to access the " + "card").format(user=request.user.email) + ) + except UserCardDetail.DoesNotExist as e: + ex = str(e) + logger.error( + "Card Id: {card_id}, Exception: {ex}".format( + card_id=card_id, ex=ex + ) + ) + msg = _("An error occurred. Details: {}".format(ex)) + messages.add_message( + self.request, messages.ERROR, msg, + extra_tags='make_charge_error' + ) + return HttpResponseRedirect( + reverse('hosting:payment') + '#payment_error' + ) + request.session['card_id'] = user_card_detail.id + else: + request.session['token'] = token else: user_email = address_form.cleaned_data.get('email') user_name = address_form.cleaned_data.get('name') @@ -472,7 +511,7 @@ class PaymentOrderView(FormView): email=user_email, token=token, customer_name=user_name) - + request.session['token'] = token request.session['billing_address_data'] = address_form.cleaned_data request.session['user'] = this_user # Get or create stripe customer @@ -485,7 +524,6 @@ class PaymentOrderView(FormView): billing_address_form=address_form ) ) - request.session['token'] = token if type(customer) is StripeCustomer: request.session['customer'] = customer.stripe_id else: @@ -508,27 +546,33 @@ class OrderConfirmationView(DetailView): 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: + if 'token' not in request.session and 'card_id' not in request.session: return HttpResponseRedirect(reverse('datacenterlight:payment')) - stripe_api_cus_id = request.session.get('customer') - stripe_utils = StripeUtils() - 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') context = { 'site_url': reverse('datacenterlight:index'), - 'cc_last4': card_details.get('response_object').get('last4'), - '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') } + if 'token' in request.session: + stripe_utils = StripeUtils() + card_details = stripe_utils.get_cards_details_from_token( + request.session.get('token') + ) + card_detail_resp = card_details.get('response_object') + if not card_detail_resp: + msg = card_details.get('error') + messages.add_message(self.request, messages.ERROR, msg, + extra_tags='failed_payment') + return HttpResponseRedirect( + reverse('datacenterlight:payment') + '#payment_error') + context['cc_last4'] = card_detail_resp.get('last4') + context['cc_brand'] = card_detail_resp.get('brand') + else: + card_id = self.request.session.get('card_id') + card_detail = UserCardDetail.objects.get(id=card_id) + context['cc_last4'] = card_detail.last4 + context['cc_brand'] = card_detail.brand return render(request, self.template_name, context) def post(self, request, *args, **kwargs): @@ -538,43 +582,62 @@ class OrderConfirmationView(DetailView): stripe_api_cus_id = request.session.get('customer') vm_template_id = template.get('id', 1) stripe_utils = StripeUtils() - 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') - 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.')) + if 'token' in self.request.session: + card_details = stripe_utils.get_cards_details_from_token( + request.session['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') + 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') + ucd = UserCardDetail.contains( + request.user.stripecustomer, card_details_dict + ) + if not ucd: + stripe_utils.associate_customer_card( + stripe_api_cus_id, request.session['token'], + set_as_default=True + ) + else: + card_id = request.session.get('card_id') + user_card_detail = UserCardDetail.objects.get(id=card_id) + card_details_dict = { + 'last4': user_card_detail.last4, + 'brand': user_card_detail.brand } - return HttpResponse(json.dumps(response), - content_type="application/json") - card_details_dict = card_details.get('response_object') + if not user_card_detail.preferred: + UserCardDetail.set_default_card( + stripe_api_cus_id=stripe_api_cus_id, + stripe_source_id=user_card_detail.card_id + ) cpu = specs.get('cpu') memory = specs.get('memory') disk_size = specs.get('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, - version=1, - app='dcl') + 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, version=1, app='dcl' + ) stripe_plan = stripe_utils.get_or_create_stripe_plan( - amount=amount_to_be_charged, - name=plan_name, - stripe_plan_id=stripe_plan_id) + amount=amount_to_be_charged, name=plan_name, + stripe_plan_id=stripe_plan_id + ) subscription_result = stripe_utils.subscribe_customer_to_plan( stripe_api_cus_id, [{"plan": stripe_plan.get( @@ -644,6 +707,15 @@ class OrderConfirmationView(DetailView): billing_address_data.update({ 'user': custom_user.id }) + if 'token' in request.session: + ucd = UserCardDetail.get_or_create_user_card_detail( + stripe_customer=self.request.user.stripecustomer, + card_details=card_details_dict + ) + UserCardDetail.save_default_card_local( + self.request.user.stripecustomer.stripe_id, + ucd.card_id + ) user = { 'name': custom_user.name, 'email': custom_user.email, From 23e7edf7c28af39b0d8d93517a47538b5cd1911c Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 14:40:15 +0100 Subject: [PATCH 073/866] Return True if associate_customer_card runs ok --- utils/stripe_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index 2742045a..cb9ae6fb 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -86,6 +86,7 @@ class StripeUtils(object): if set_as_default: customer.default_source = card.id customer.save() + return True @handleStripeError def dissociate_customer_card(self, stripe_customer_id, card_id): From bea3477d848a8f87526e44ece06df5c0352ee801 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 14:45:14 +0100 Subject: [PATCH 074/866] Handle errors related to payment by redirecting to payment page --- datacenterlight/views.py | 39 ++++++++++++++++++++++++++--- hosting/views.py | 54 +++++++++++++++++++++++++++++++++++----- 2 files changed, 84 insertions(+), 9 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index baec4ee6..d9c0fbc1 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -603,21 +603,46 @@ class OrderConfirmationView(DetailView): } return HttpResponse(json.dumps(response), content_type="application/json") - card_details_dict = card_details.get('response_object') + card_details_response = card_details['response_object'] + card_details_dict = { + 'last4': card_details_response['last4'], + 'brand': card_details_response['brand'], + 'card_id': card_details_response['card_id'] + } ucd = UserCardDetail.contains( request.user.stripecustomer, card_details_dict ) if not ucd: - stripe_utils.associate_customer_card( + acc_result = stripe_utils.associate_customer_card( stripe_api_cus_id, request.session['token'], set_as_default=True ) + if acc_result['response_object'] is None: + 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.')) + } + logger.error( + "Card association failed. Error {error}".format( + error=acc_result['error'] + ) + ) + return HttpResponse(json.dumps(response), + content_type="application/json") else: card_id = request.session.get('card_id') user_card_detail = UserCardDetail.objects.get(id=card_id) card_details_dict = { 'last4': user_card_detail.last4, - 'brand': user_card_detail.brand + 'brand': user_card_detail.brand, + 'card_id': user_card_detail.card_id } if not user_card_detail.preferred: UserCardDetail.set_default_card( @@ -646,6 +671,14 @@ class OrderConfirmationView(DetailView): # Check if the subscription was approved and is active if (stripe_subscription_obj is None or stripe_subscription_obj.status != 'active'): + if request.user.is_authenticated(): + sac_id = request.user.stripecustomer.stripe_id + else: + sac_id = stripe_api_cus_id + stripe_utils.dissociate_customer_card( + sac_id, + card_details_dict['card_id'] + ) msg = subscription_result.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') diff --git a/hosting/views.py b/hosting/views.py index f61a4b19..6d5c9a78 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -632,6 +632,18 @@ class SettingsView(LoginRequiredMixin, FormView): msg = _('You seem to have already added this card') messages.add_message(request, messages.ERROR, msg) else: + acc_result = stripe_utils.associate_customer_card( + request.user.stripecustomer.stripe_id, token + ) + if acc_result['response_object'] is None: + msg = _( + 'An error occurred while associating the card.' + ' Details: {details}'.format( + details=acc_result['error'] + ) + ) + messages.add_message(request, messages.ERROR, msg) + return self.render_to_response(self.get_context_data()) preferred = False if stripe_customer.usercarddetail_set.count() == 0: preferred = True @@ -645,9 +657,6 @@ class SettingsView(LoginRequiredMixin, FormView): card_id=card['card_id'], preferred=preferred ) - stripe_utils.associate_customer_card( - request.user.stripecustomer.stripe_id, token - ) msg = _( "Successfully associated the card with your account" ) @@ -866,22 +875,48 @@ class OrdersHostingDetailView(LoginRequiredMixin, card_details_response = card_details['response_object'] card_details_dict = { 'last4': card_details_response['last4'], - 'brand': card_details_response['brand'] + 'brand': card_details_response['brand'], + 'card_id': card_details_response['card_id'] } ucd = UserCardDetail.contains( request.user.stripecustomer, card_details_response ) if not ucd: - stripe_utils.associate_customer_card( + acc_result = stripe_utils.associate_customer_card( stripe_api_cus_id, request.session['token'], set_as_default=True ) + if acc_result['response_object'] is None: + msg = _( + 'An error occurred while associating the card.' + ' Details: {details}'.format( + details=acc_result['error'] + ) + ) + messages.add_message(self.request, messages.ERROR, msg, + extra_tags='failed_payment') + response = { + 'status': False, + 'redirect': "{url}#{section}".format( + url=reverse('hosting: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" + ) else: card_id = request.session.get('card_id') user_card_detail = UserCardDetail.objects.get(id=card_id) card_details_dict = { 'last4': user_card_detail.last4, - 'brand': user_card_detail.brand + 'brand': user_card_detail.brand, + 'card_id': user_card_detail.card_id } if not user_card_detail.preferred: UserCardDetail.set_default_card( @@ -911,6 +946,13 @@ class OrdersHostingDetailView(LoginRequiredMixin, # Check if the subscription was approved and is active if (stripe_subscription_obj is None or stripe_subscription_obj.status != 'active'): + # At this point, we have created a Stripe API card, but and + # associated it with the customer; but the transaction failed + # due to some reason. So, we dissociate this card. + stripe_utils.dissociate_customer_card( + request.user.stripecustomer.stripe_id, + card_details_dict['card_id'] + ) msg = subscription_result.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') From abe8c9efa52b13f4d87eb07392b63c0d3494a4ad Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 15:05:32 +0100 Subject: [PATCH 075/866] Add error details to messages datacenterlight landing flow --- datacenterlight/views.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index d9c0fbc1..f58b8276 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -610,7 +610,7 @@ class OrderConfirmationView(DetailView): 'card_id': card_details_response['card_id'] } ucd = UserCardDetail.contains( - request.user.stripecustomer, card_details_dict + request.user.stripecustomer, card_details_response ) if not ucd: acc_result = stripe_utils.associate_customer_card( @@ -618,6 +618,11 @@ class OrderConfirmationView(DetailView): set_as_default=True ) if acc_result['response_object'] is None: + msg = acc_result.get('error') + messages.add_message( + self.request, messages.ERROR, msg, + extra_tags='failed_payment' + ) response = { 'status': False, 'redirect': "{url}#{section}".format( From 63eb7fc0e2278721932adebe114e3cfe7236313e Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 21:31:11 +0100 Subject: [PATCH 076/866] Rename contains to get_user_card_details --- hosting/models.py | 9 +++++---- hosting/views.py | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/hosting/models.py b/hosting/models.py index 9486eae7..7a181fe1 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -312,21 +312,22 @@ class UserCardDetail(AssignPermissionsMixin, models.Model): user_card_detail.save() @staticmethod - def contains(stripe_customer, card_details): + def get_user_card_details(stripe_customer, card_details): """ A utility function to check whether a StripeCustomer is already associated with the card having given details + :param stripe_customer: :param card_details: - :return: + :return: The UserCardDetails object if it exists, False otherwise """ try: - UserCardDetail.objects.get( + ucd = UserCardDetail.objects.get( stripe_customer=stripe_customer, fingerprint=card_details['fingerprint'], exp_month=card_details['exp_month'], exp_year=card_details['exp_year'] ) - return True + return ucd except UserCardDetail.DoesNotExist: return False diff --git a/hosting/views.py b/hosting/views.py index 6d5c9a78..5a9c2e2e 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -628,7 +628,7 @@ class SettingsView(LoginRequiredMixin, FormView): email=request.user.email, token=token ) card = card_details['response_object'] - if UserCardDetail.contains(stripe_customer, card): + if UserCardDetail.get_user_card_details(stripe_customer, card): msg = _('You seem to have already added this card') messages.add_message(request, messages.ERROR, msg) else: @@ -878,7 +878,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, 'brand': card_details_response['brand'], 'card_id': card_details_response['card_id'] } - ucd = UserCardDetail.contains( + ucd = UserCardDetail.get_user_card_details( request.user.stripecustomer, card_details_response ) if not ucd: From af1690b84621322c86fa27a07031d72388da614f Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 21:36:12 +0100 Subject: [PATCH 077/866] Fix: obtaining stripe_customer in landing flow --- datacenterlight/views.py | 82 ++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index f58b8276..1a817310 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -403,8 +403,7 @@ class PaymentOrderView(FormView): return HttpResponseRedirect(reverse('datacenterlight:index')) HostingUtils.clear_items_from_list( request.session, - ['token', 'billing_address_data', 'card_id', 'customer', - 'user'] + ['token', 'card_id', 'customer', 'user'] ) if 'token' in request.session: del request.session['token'] @@ -609,38 +608,49 @@ class OrderConfirmationView(DetailView): 'brand': card_details_response['brand'], 'card_id': card_details_response['card_id'] } - ucd = UserCardDetail.contains( - request.user.stripecustomer, card_details_response - ) - if not ucd: - acc_result = stripe_utils.associate_customer_card( - stripe_api_cus_id, request.session['token'], - set_as_default=True - ) - if acc_result['response_object'] is None: - msg = acc_result.get('error') - messages.add_message( - self.request, messages.ERROR, msg, - extra_tags='failed_payment' + s_cus = None + try: + s_cus = StripeCustomer.objects.get(stripe_id=stripe_api_cus_id) + except StripeCustomer.DoesNotExist: + pass + if s_cus: + ucd = UserCardDetail.get_user_card_details(s_cus, card_details_response) + if not ucd: + acc_result = stripe_utils.associate_customer_card( + stripe_api_cus_id, request.session['token'], + set_as_default=True ) - 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.')) - } - logger.error( - "Card association failed. Error {error}".format( - error=acc_result['error'] + if acc_result['response_object'] is None: + msg = acc_result.get('error') + messages.add_message( + self.request, messages.ERROR, msg, + extra_tags='failed_payment' + ) + 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.') + ) + } + logger.error( + "Card association failed. Error {error}".format( + error=acc_result['error'] + ) + ) + return HttpResponse(json.dumps(response), + content_type="application/json") + else: + if not ucd.preferred: + UserCardDetail.set_default_card( + stripe_api_cus_id=stripe_api_cus_id, + stripe_source_id=ucd.card_id ) - ) - return HttpResponse(json.dumps(response), - content_type="application/json") else: card_id = request.session.get('card_id') user_card_detail = UserCardDetail.objects.get(id=card_id) @@ -745,13 +755,13 @@ class OrderConfirmationView(DetailView): billing_address_data.update({ 'user': custom_user.id }) - if 'token' in request.session: + if 'token' in request.session and s_cus is not None: ucd = UserCardDetail.get_or_create_user_card_detail( - stripe_customer=self.request.user.stripecustomer, - card_details=card_details_dict + stripe_customer=s_cus, + card_details=card_details_response ) UserCardDetail.save_default_card_local( - self.request.user.stripecustomer.stripe_id, + s_cus.stripe_id, ucd.card_id ) user = { From 618d0004f2456b236d4d427e98631234ea355922 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 21:41:54 +0100 Subject: [PATCH 078/866] Rearrange code --- datacenterlight/views.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 1a817310..36ca81d6 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -451,12 +451,11 @@ class PaymentOrderView(FormView): user_card_detail = UserCardDetail.objects.get( id=card_id) if not request.user.has_perm( - 'view_usercarddetail', user_card_detail + 'view_usercarddetail', user_card_detail ): raise UserCardDetail.DoesNotExist( - _( - "{user} does not have permission to access the " - "card").format(user=request.user.email) + _("{user} does not have permission to access" + " the card").format(user=request.user.email) ) except UserCardDetail.DoesNotExist as e: ex = str(e) @@ -496,9 +495,9 @@ class PaymentOrderView(FormView): ) ) customer = StripeCustomer.create_stripe_api_customer( - email=user_email, - token=token, - customer_name=user_name) + email=user_email, token=token, + customer_name=user_name + ) except CustomUser.DoesNotExist: logger.debug( ("StripeCustomer does not exist for {email}." @@ -597,8 +596,8 @@ class OrderConfirmationView(DetailView): '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.')) + ' On close of this popup, you will be redirected ' + 'back to the payment page.')) } return HttpResponse(json.dumps(response), content_type="application/json") From 3d8f81339b41218e58a2d8f2659d37781188838d Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 21:49:15 +0100 Subject: [PATCH 079/866] Refactor, reorganize some code --- datacenterlight/views.py | 42 ++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 36ca81d6..51cd18fd 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -560,10 +560,13 @@ class OrderConfirmationView(DetailView): card_detail_resp = card_details.get('response_object') if not card_detail_resp: msg = card_details.get('error') - messages.add_message(self.request, messages.ERROR, msg, - extra_tags='failed_payment') + messages.add_message( + self.request, messages.ERROR, msg, + extra_tags='failed_payment' + ) return HttpResponseRedirect( - reverse('datacenterlight:payment') + '#payment_error') + reverse('datacenterlight:payment') + '#payment_error' + ) context['cc_last4'] = card_detail_resp.get('last4') context['cc_brand'] = card_detail_resp.get('brand') else: @@ -586,8 +589,10 @@ class OrderConfirmationView(DetailView): ) if not card_details.get('response_object'): msg = card_details.get('error') - messages.add_message(self.request, messages.ERROR, msg, - extra_tags='failed_payment') + messages.add_message( + self.request, messages.ERROR, msg, + extra_tags='failed_payment' + ) response = { 'status': False, 'redirect': "{url}#{section}".format( @@ -599,8 +604,9 @@ class OrderConfirmationView(DetailView): ' On close of this popup, you will be redirected ' 'back to the payment page.')) } - return HttpResponse(json.dumps(response), - content_type="application/json") + return HttpResponse( + json.dumps(response), content_type="application/json" + ) card_details_response = card_details['response_object'] card_details_dict = { 'last4': card_details_response['last4'], @@ -709,7 +715,6 @@ class OrderConfirmationView(DetailView): } 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(): @@ -747,7 +752,6 @@ class OrderConfirmationView(DetailView): # object already exists stripe_customer_id = request.user.stripecustomer.id custom_user = request.user - # Save billing address billing_address_data = request.session.get('billing_address_data') logger.debug('billing_address_data is {}'.format(billing_address_data)) @@ -771,16 +775,16 @@ class OrderConfirmationView(DetailView): 'request_host': request.get_host(), 'language': get_language(), } - - create_vm_task.delay(vm_template_id, user, specs, template, - stripe_customer_id, billing_address_data, - stripe_subscription_obj.id, card_details_dict) - for session_var in ['specs', 'template', 'billing_address', - 'billing_address_data', - 'token', 'customer']: - if session_var in request.session: - del request.session[session_var] - + create_vm_task.delay( + vm_template_id, user, specs, template, stripe_customer_id, + billing_address_data, stripe_subscription_obj.id, + card_details_dict + ) + HostingUtils.clear_items_from_list( + request.session, + ['specs', 'template', 'billing_address', 'billing_address_data', + 'token', 'customer'] + ) response = { 'status': True, 'redirect': reverse( From edac806c118f642b737c7999c02d99643a67b639 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 21:56:29 +0100 Subject: [PATCH 080/866] Some more refactoring --- datacenterlight/views.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 51cd18fd..50148418 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -785,17 +785,18 @@ class OrderConfirmationView(DetailView): ['specs', 'template', 'billing_address', 'billing_address_data', 'token', 'customer'] ) + if request.user.is_authenticated(): + redirect_url = reverse('hosting:virtual_machines') + else: + redirect_url = reverse('datacenterlight:index') response = { 'status': True, - 'redirect': reverse( - 'hosting:virtual_machines') if request.user.is_authenticated() else reverse( - 'datacenterlight:index'), + 'redirect': redirect_url, '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") From 62f30bf03c37301061b357ab14daa90ea363b648 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 22:03:19 +0100 Subject: [PATCH 081/866] Remove some commented and unnecessary code --- .../datacenterlight/landing_payment.html | 38 ------------------- datacenterlight/views.py | 15 -------- hosting/templates/hosting/settings.html | 10 ----- 3 files changed, 63 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html index 69c80d35..5ac601ce 100644 --- a/datacenterlight/templates/datacenterlight/landing_payment.html +++ b/datacenterlight/templates/datacenterlight/landing_payment.html @@ -112,44 +112,6 @@ {% endif %} {% include "hosting/includes/_card_input.html" %} - {% comment %} -
- {% 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 %} -
-
- -
- {% else %} - {% include "hosting/includes/_card_input.html" %} - {% endif %} -
- {% endcomment %} {% endwith %} diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 50148418..3a8bdac6 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -359,7 +359,6 @@ class PaymentOrderView(FormView): billing_address_data = self.request.session['billing_address_data'] else: billing_address_data = {} - if self.request.user.is_authenticated(): if billing_address_data: billing_address_form = BillingAddressForm( @@ -369,24 +368,10 @@ class PaymentOrderView(FormView): billing_address_form = BillingAddressForm( instance=self.request.user.billing_addresses.first() ) - # Get user last order - last_hosting_order = HostingOrder.objects.filter( - customer__user=self.request.user - ).last() - - # If user has already an hosting order, get the credit card - # data from it - if last_hosting_order: - credit_card_data = last_hosting_order.get_cc_data() - if credit_card_data: - context['credit_card_data'] = credit_card_data - else: - context['credit_card_data'] = None else: billing_address_form = BillingAddressFormSignup( initial=billing_address_data ) - context.update({ 'cards_list': cards_list, 'stripe_key': settings.STRIPE_API_PUBLIC_KEY, diff --git a/hosting/templates/hosting/settings.html b/hosting/templates/hosting/settings.html index 6b5acbee..64057747 100644 --- a/hosting/templates/hosting/settings.html +++ b/hosting/templates/hosting/settings.html @@ -123,14 +123,4 @@ })(); {%endif%} - - {% comment %} - {% if credit_card_data.last4 and credit_card_data.cc_brand %} - - {%endif%} - {% endcomment %} {%endblock%} From 4be27962703cdb2774e1b0d13842ba3df3b22846 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 22:40:21 +0100 Subject: [PATCH 082/866] Clean up some unnecessary code --- datacenterlight/views.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 3a8bdac6..fb34f5b4 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -348,10 +348,9 @@ class PaymentOrderView(FormView): def get_context_data(self, **kwargs): context = super(PaymentOrderView, self).get_context_data(**kwargs) user = self.request.user + stripe_customer = None if hasattr(user, 'stripecustomer'): stripe_customer = user.stripecustomer - else: - stripe_customer = None cards_list = UserCardDetail.get_all_cards_list( stripe_customer=stripe_customer ) @@ -390,8 +389,6 @@ class PaymentOrderView(FormView): request.session, ['token', 'card_id', 'customer', 'user'] ) - if 'token' in request.session: - del request.session['token'] return self.render_to_response(self.get_context_data()) def post(self, request, *args, **kwargs): From 83dbae74e6a661b649f79d2b691ea8dc73a2e8d2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 22:54:33 +0100 Subject: [PATCH 083/866] Add missing card_id to session variables to be cleared --- datacenterlight/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index fb34f5b4..f9d1dede 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -765,7 +765,7 @@ class OrderConfirmationView(DetailView): HostingUtils.clear_items_from_list( request.session, ['specs', 'template', 'billing_address', 'billing_address_data', - 'token', 'customer'] + 'token', 'customer', 'card_id'] ) if request.user.is_authenticated(): redirect_url = reverse('hosting:virtual_machines') From 16b6ecb38cdc87737c54e9f51e3ffa7eff51014a Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 29 Oct 2017 23:48:33 +0100 Subject: [PATCH 084/866] Clear session variables on payment error --- hosting/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 5a9c2e2e..37d6eabb 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -39,7 +39,7 @@ from utils.forms import ( BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm, ResendActivationEmailForm ) -from utils.hosting_utils import get_vm_price +from utils.hosting_utils import get_vm_price, HostingUtils from utils.mailer import BaseEmail from utils.stripe_utils import StripeUtils from utils.views import ( @@ -707,6 +707,10 @@ class PaymentVMView(LoginRequiredMixin, FormView): def get(self, request, *args, **kwargs): if 'next' in request.session: del request.session['next'] + HostingUtils.clear_items_from_list( + request.session, + ['token', 'card_id', 'customer', 'user'] + ) return self.render_to_response(self.get_context_data()) def post(self, request, *args, **kwargs): From 24d904288fd0724178af021fcbeec01fadf54947 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 30 Oct 2017 00:26:18 +0100 Subject: [PATCH 085/866] Clear token and card form variables explicitly --- datacenterlight/views.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index f9d1dede..f2af35cd 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -356,6 +356,10 @@ class PaymentOrderView(FormView): ) if 'billing_address_data' in self.request.session: billing_address_data = self.request.session['billing_address_data'] + if 'token' in billing_address_data: + billing_address_data.pop('token') + if 'card' in billing_address_data: + billing_address_data.pop('card') else: billing_address_data = {} if self.request.user.is_authenticated(): From a2a35a9475eb6bafd7d6d70d991416a21caab0f4 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 30 Oct 2017 08:26:35 +0100 Subject: [PATCH 086/866] Fix error accessing stripecustomer for user when it doesn't exist --- hosting/views.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 37d6eabb..3b259242 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -552,8 +552,11 @@ class SettingsView(LoginRequiredMixin, FormView): def get_context_data(self, **kwargs): context = super(SettingsView, self).get_context_data(**kwargs) user = self.request.user + stripe_customer = None + if hasattr(user, 'stripecustomer'): + stripe_customer = user.stripecustomer cards_list = UserCardDetail.get_all_cards_list( - stripe_customer=user.stripecustomer + stripe_customer=stripe_customer ) context.update({ 'cards_list': cards_list, From 3e08760e044bfed66cb1363a4bfcb8de95b93d52 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 31 Oct 2017 08:58:43 +0100 Subject: [PATCH 087/866] Change letter-spacing to 2px for settings-container buttons --- hosting/static/hosting/css/commons.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/static/hosting/css/commons.css b/hosting/static/hosting/css/commons.css index 33381370..32a1338e 100644 --- a/hosting/static/hosting/css/commons.css +++ b/hosting/static/hosting/css/commons.css @@ -363,7 +363,7 @@ } .settings-container .choice-btn { - letter-spacing: 1px; + letter-spacing: 2px; min-width: 127px; } From d8ce0f95c5d92d2c9648051e5cef5f54f03ce04b Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 31 Oct 2017 09:02:07 +0100 Subject: [PATCH 088/866] Change letter-spacing to 2px for btn-vm-contact --- hosting/static/hosting/css/virtual-machine.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/static/hosting/css/virtual-machine.css b/hosting/static/hosting/css/virtual-machine.css index 3329d6fe..5f91fe2f 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -413,7 +413,7 @@ border: 2px solid #A3C0E2; padding: 5px 25px; font-size: 12px; - letter-spacing: 1.3px; + letter-spacing: 2px; } .btn-vm-contact:hover, .btn-vm-contact:focus { background: #fff; From f9bd8493330cd809f4c47ea98e7c21ba8b1fb1cd Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 31 Oct 2017 09:05:49 +0100 Subject: [PATCH 089/866] Remove redundant "previous" text --- .../templates/datacenterlight/landing_payment.html | 4 ++-- hosting/templates/hosting/payment.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html index 5ac601ce..e0bd56a9 100644 --- a/datacenterlight/templates/datacenterlight/landing_payment.html +++ b/datacenterlight/templates/datacenterlight/landing_payment.html @@ -89,7 +89,7 @@

{% if card_list_len > 0 %} - {% blocktrans %}Please select one of the previous cards that you used before or 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 select one of the cards that you used before or fill in your credit card information below. We are using Stripe for payment and do not store your information in our database.{% endblocktrans %} {% else %} {% 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 %} {% endif %} @@ -108,7 +108,7 @@ {% endfor %} {% if card_list_len > 0 %} -

Use another card
+
{% trans "Use another card" %}
{% endif %} {% include "hosting/includes/_card_input.html" %} diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index 10fd4674..a321b44c 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -72,7 +72,7 @@

{% if card_list_len > 0 %} - {% blocktrans %}Please select one of the previous cards that you used before or 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 select one of the cards that you used before or fill in your credit card information below. We are using Stripe for payment and do not store your information in our database.{% endblocktrans %} {% else %} {% 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 %} {% endif %} @@ -91,7 +91,7 @@

{% endfor %} {% if card_list_len > 0 %} -
Use another card
+
{% trans "Use another card" %}
{% endif %} {% include "hosting/includes/_card_input.html" %} From c438c0d8cbab7d8518d929cc675fa5d8fc5d1d13 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 31 Oct 2017 09:38:36 +0100 Subject: [PATCH 090/866] Add some de translations --- .../locale/de/LC_MESSAGES/django.po | 48 +++++++--- hosting/locale/de/LC_MESSAGES/django.po | 87 +++++++++++++++---- 2 files changed, 108 insertions(+), 27 deletions(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index 3b8c9ca6..2fe8dba8 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-10-17 00:32+0530\n" +"POT-Creation-Date: 2017-10-31 08:23+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -345,6 +345,17 @@ msgstr "Monat" msgid "Credit Card" msgstr "Kreditkarte" +msgid "" +"Please select one of the cards that you used before or fill in your credit " +"card information below. We are using Stripe for payment and do not store your information in our " +"database." +msgstr "" +"Bitte wähle Seine der Karten aus, die du zuvor verwendet hast, oder gib " +"deine Kreditkartendaten unten ein. Wir verwenden Stripe als " +"Zahlungdiensleister und speichern deine Daten nicht in unserer Datenbank." + msgid "" "Please fill in your credit card information below. We are using Stripe for payment and do not " @@ -354,12 +365,17 @@ msgstr "" "\"https://stripe.com\" target=\"_blank\">Stripe für die Bezahlung und " "speichern keine Informationen in unserer Datenbank." -msgid "" -"You are not making any payment yet. After submitting your card information, " -"you will be taken to the Confirm Order 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 "Last" +msgstr "Vor" + +msgid "Type" +msgstr "Kartentyp" + +msgid "SELECT" +msgstr "Auswählen" + +msgid "Use another card" +msgstr "Benutze eine andere Kreditkarte" msgid "Processing" msgstr "Weiter" @@ -471,6 +487,13 @@ msgstr "Ungültige RAM-Grösse" msgid "Invalid storage size" msgstr "Ungültige Speicher-Grösse" +#, python-brace-format +msgid "{user} does not have permission to access the card" +msgstr "" + +msgid "An error occurred. Details: {}" +msgstr "" + msgid "Confirm Order" msgstr "Bestellung Bestätigen" @@ -494,15 +517,20 @@ msgstr "" "Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du " "auf sie zugreifen kannst." +#~ msgid "" +#~ "You are not making any payment yet. After submitting your card " +#~ "information, you will be taken to the Confirm Order 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 "Card Number" #~ msgstr "Kreditkartennummer" #~ msgid "Expiry Date" #~ msgstr "Ablaufdatum" -#~ msgid "Card Type" -#~ msgstr "Kartentyp" - #~ msgid "Processing..." #~ msgstr "Abarbeitung..." diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 64e4a73e..3614dc49 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-10-17 00:32+0530\n" +"POT-Creation-Date: 2017-10-31 08:23+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -437,6 +437,17 @@ msgstr "inkl. Mehrwertsteuer" msgid "Billing Address" msgstr "Rechnungsadresse" +msgid "" +"Please select one of the cards that you used before or fill in your credit " +"card information below. We are using Stripe for payment and do not store your information in our " +"database." +msgstr "" +"Bitte wähle Seine der Karten aus, die du zuvor verwendet hast, oder gib " +"deine Kreditkartendaten unten ein. Wir verwenden Stripe als " +"Zahlungdiensleister und speichern deine Daten nicht in unserer Datenbank." + msgid "" "Please fill in your credit card information below. We are using Stripe for payment and do not " @@ -446,12 +457,17 @@ msgstr "" "\"https://stripe.com\" target=\"_blank\">Stripe für die Bezahlung und " "speichern keine Informationen in unserer Datenbank." -msgid "" -"You are not making any payment yet. After submitting your card information, " -"you will be taken to the Confirm Order 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 "Last" +msgstr "Vor" + +msgid "Type" +msgstr "Kartentyp" + +msgid "SELECT" +msgstr "Auswählen" + +msgid "Use another card" +msgstr "Benutze eine andere Kreditkarte" msgid "Processing" msgstr "Weiter" @@ -468,16 +484,19 @@ msgstr "Passwort zurücksetzen" msgid "UPDATE" msgstr "" -msgid "Last" -msgstr "" - -msgid "Type" -msgstr "Kartentyp" - msgid "REMOVE CARD" msgstr "KARTE ENTFERNEN" -msgid "SELECT" +msgid "Remove Card" +msgstr "Karte Entfernen" + +msgid "Do you want to remove this associated card?" +msgstr "" + +msgid "Delete" +msgstr "Löschen" + +msgid "DEFAULT" msgstr "" msgid "No Credit Cards Added" @@ -534,9 +553,6 @@ msgstr "" msgid "Private Key" msgstr "" -msgid "Delete" -msgstr "Löschen" - msgid "Delete SSH Key" msgstr "SSH Key löschen" @@ -668,6 +684,35 @@ msgstr "Dein Passwort konnte nicht zurückgesetzt werden." msgid "The reset password link is no longer valid." msgstr "Der Link zum Zurücksetzen Deines Passwortes ist nicht mehr gültig." +msgid "Card deassociation successful" +msgstr "" + +msgid "You are not permitted to do this operation" +msgstr "" + +msgid "The selected card does not exist" +msgstr "" + +msgid "Billing address updated successfully" +msgstr "" + +msgid "You seem to have already added this card" +msgstr "" + +#, python-brace-format +msgid "An error occurred while associating the card. Details: {details}" +msgstr "Beim Zuordnen der Karte ist ein Fehler aufgetreten. Details: {details}" + +msgid "Successfully associated the card with your account" +msgstr "" + +#, python-brace-format +msgid "{user} does not have permission to access the card" +msgstr "" + +msgid "An error occurred. Details: {}" +msgstr "" + msgid "Invalid credit card" msgstr "Ungültige Kreditkarte" @@ -735,6 +780,14 @@ msgstr "" "Es gab einen Fehler bei der Bearbeitung Deine Anfrage. Bitte versuche es " "noch einmal." +#~ msgid "" +#~ "You are not making any payment yet. After submitting your card " +#~ "information, you will be taken to the Confirm Order 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 "Reset your password" #~ msgstr "Passwort zurücksetzen" From 272e14f71253dbf2dcac22a1a8f0e7cf889be301 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 27 Nov 2017 08:43:34 +0100 Subject: [PATCH 091/866] Update hosting django.po --- hosting/locale/de/LC_MESSAGES/django.po | 42 +++---------------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 6721a75c..6774fa8d 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-10-31 08:23+0000\n" +"POT-Creation-Date: 2017-11-27 07:43+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -453,9 +453,9 @@ msgid "" "database." msgstr "" "Bitte wähle Seine der Karten aus, die du zuvor verwendet hast, oder gib " -"deine Kreditkartendaten unten ein. Wir verwenden Stripe als " -"Zahlungdiensleister und speichern deine Daten nicht in unserer Datenbank." +"deine Kreditkartendaten unten ein. Wir verwenden Stripe als Zahlungdiensleister und speichern deine " +"Daten nicht in unserer Datenbank." msgid "" "Please fill in your credit card information below. We are using Stripe für die Bezahlung und " "speichern keine Informationen in unserer Datenbank." -msgid "" -"You are not making any payment yet. After submitting your card information, " -"you will be taken to the Confirm Order 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 "SUBMIT" -msgstr "ABSENDEN" - -msgid "Card Number" -msgstr "Kreditkartennummer" - -msgid "Expiry Date" -msgstr "Ablaufdatum" - -msgid "CVC" -msgstr "" - -msgid "Card Type" -msgstr "Kartentyp" - msgid "Last" msgstr "Vor" @@ -524,12 +502,6 @@ msgstr "Karte Entfernen" msgid "Do you want to remove this associated card?" msgstr "" -msgid "Last" -msgstr "" - -msgid "Type" -msgstr "Kartentyp" - msgid "Delete" msgstr "Löschen" @@ -590,9 +562,6 @@ msgstr "" msgid "Private Key" msgstr "" -msgid "Delete" -msgstr "Löschen" - msgid "Delete SSH Key" msgstr "SSH Key löschen" @@ -894,9 +863,6 @@ msgstr "" #~ msgid "Notifications " #~ msgstr "Benachrichtigungen" -#~ msgid "REMOVE CARD" -#~ msgstr "KARTE ENTFERNEN" - #~ msgid "EDIT CARD" #~ msgstr "BEARBEITEN" From 28de423a1475f1fc4f167311be292a4ab41d34eb Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 6 Apr 2018 00:51:44 +0200 Subject: [PATCH 092/866] Add VMPricing model --- datacenterlight/models.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/datacenterlight/models.py b/datacenterlight/models.py index 6fcf24a9..01ddfdcf 100644 --- a/datacenterlight/models.py +++ b/datacenterlight/models.py @@ -12,6 +12,19 @@ class VMTemplate(models.Model): return vm_template +class VMPricing(models.Model): + name = models.CharField(max_length=255, unique=True) + vat_inclusive = models.BooleanField(default=True) + vat_percentage = models.DecimalField(decimal_places=2, blank=True) + cores_unit_price = models.DecimalField(decimal_places=2, default=0) + ram_unit_price = models.DecimalField(decimal_places=2, default= 0) + ssd_unit_price = models.DecimalField(decimal_places=2, default=0) + hdd_unit_price = models.DecimalField(decimal_places=2, default=0) + + def __str__(self): + return self.name + + class StripePlan(models.Model): """ A model to store Data Center Light's created Stripe plans From d07cc41d0a102dd65938185331e3662fc9059cc4 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 9 Apr 2018 21:17:48 +0200 Subject: [PATCH 093/866] Update VMPricing and add get_default_pricing class method --- datacenterlight/models.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/datacenterlight/models.py b/datacenterlight/models.py index 01ddfdcf..3a376747 100644 --- a/datacenterlight/models.py +++ b/datacenterlight/models.py @@ -15,14 +15,33 @@ class VMTemplate(models.Model): class VMPricing(models.Model): name = models.CharField(max_length=255, unique=True) vat_inclusive = models.BooleanField(default=True) - vat_percentage = models.DecimalField(decimal_places=2, blank=True) - cores_unit_price = models.DecimalField(decimal_places=2, default=0) - ram_unit_price = models.DecimalField(decimal_places=2, default= 0) - ssd_unit_price = models.DecimalField(decimal_places=2, default=0) - hdd_unit_price = models.DecimalField(decimal_places=2, default=0) + vat_percentage = models.DecimalField( + max_digits=7, decimal_places=2, blank=True, default=0 + ) + cores_unit_price = models.DecimalField( + max_digits=7, decimal_places=2, default=0 + ) + ram_unit_price = models.DecimalField( + max_digits=7, decimal_places=2, default=0 + ) + ssd_unit_price = models.DecimalField( + max_digits=7, decimal_places=2, default=0 + ) + hdd_unit_price = models.DecimalField( + max_digits=7, decimal_places=2, default=0 + ) def __str__(self): - return self.name + return self.name + '-' + 'VAT' if self.vat_inclusive else 'NO_VAT' + + @classmethod + def get_default_pricing(cls): + """ Returns the default pricing or None """ + try: + default_pricing = VMPricing.objects.get(name='default') + except: + default_pricing = None + return default_pricing class StripePlan(models.Model): From c7afbb32c0d8f5d237bd96e480be30a5e64077bd Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 9 Apr 2018 21:18:18 +0200 Subject: [PATCH 094/866] Add DCLCalculatorPluginModel --- datacenterlight/cms_models.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index 9eb55e0c..583ac6a5 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -6,6 +6,8 @@ from django.utils.safestring import mark_safe from djangocms_text_ckeditor.fields import HTMLField from filer.fields.image import FilerImageField +from datacenterlight.models import VMPricing + class CMSIntegration(models.Model): name = models.CharField( @@ -275,3 +277,12 @@ class DCLSectionPromoPluginModel(CMSPlugin): if self.background_image: extra_classes += ' promo-with-bg' return extra_classes + + +class DCLCalculatorPluginModel(DCLSectionPluginModel): + pricing = models.ForeignKey( + VMPricing, + default=VMPricing.get_default_pricing(), + help_text='Choose a pricing that will be associated with this ' + 'Calculator' + ) From dd30542f9f14737cf59186f06a7ef3f2fbdac245 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 9 Apr 2018 21:18:50 +0200 Subject: [PATCH 095/866] Use DCLCalculatorPluginModel in DCLCalculatorPlugin --- datacenterlight/cms_plugins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index a1a3833d..26ee9162 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -6,7 +6,7 @@ from .cms_models import ( DCLFooterPluginModel, DCLLinkPluginModel, DCLNavbarDropdownPluginModel, DCLSectionIconPluginModel, DCLSectionImagePluginModel, DCLSectionPluginModel, DCLNavbarPluginModel, - DCLSectionPromoPluginModel + DCLSectionPromoPluginModel, DCLCalculatorPluginModel ) from .models import VMTemplate @@ -76,7 +76,7 @@ class DCLSectionPromoPlugin(CMSPluginBase): class DCLCalculatorPlugin(CMSPluginBase): module = "Datacenterlight" name = "DCL Calculator Plugin" - model = DCLSectionPluginModel + model = DCLCalculatorPluginModel render_template = "datacenterlight/cms/calculator.html" cache = False allow_children = True From 4d6fdf2de97d6ba35397a9b83e71f82a2ed4b8f5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 9 Apr 2018 21:32:53 +0200 Subject: [PATCH 096/866] Add DCLCalculatorPluginModel and VMPricing models --- .../migrations/0019_auto_20180409_1923.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 datacenterlight/migrations/0019_auto_20180409_1923.py diff --git a/datacenterlight/migrations/0019_auto_20180409_1923.py b/datacenterlight/migrations/0019_auto_20180409_1923.py new file mode 100644 index 00000000..4766cb5e --- /dev/null +++ b/datacenterlight/migrations/0019_auto_20180409_1923.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-04-09 19:23 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0018_auto_20180403_1930'), + ] + + operations = [ + migrations.CreateModel( + name='DCLCalculatorPluginModel', + fields=[ + ('dclsectionpluginmodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='datacenterlight.DCLSectionPluginModel')), + ], + options={ + 'abstract': False, + }, + bases=('datacenterlight.dclsectionpluginmodel',), + ), + migrations.CreateModel( + name='VMPricing', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, unique=True)), + ('vat_inclusive', models.BooleanField(default=True)), + ('vat_percentage', models.DecimalField(blank=True, decimal_places=2, default=0, max_digits=7)), + ('cores_unit_price', models.DecimalField(decimal_places=2, default=0, max_digits=7)), + ('ram_unit_price', models.DecimalField(decimal_places=2, default=0, max_digits=7)), + ('ssd_unit_price', models.DecimalField(decimal_places=2, default=0, max_digits=7)), + ('hdd_unit_price', models.DecimalField(decimal_places=2, default=0, max_digits=7)), + ], + ), + migrations.AddField( + model_name='dclcalculatorpluginmodel', + name='pricing', + field=models.ForeignKey(default=None, help_text='Choose a pricing that will be associated with this Calculator', on_delete=django.db.models.deletion.CASCADE, to='datacenterlight.VMPricing'), + ), + ] From 76c9b20cc9534e603b6caa6df59607c5eda706bd Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 9 Apr 2018 21:34:09 +0200 Subject: [PATCH 097/866] Add VMPricing init migration --- .../migrations/0020_auto_20180409_1928.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 datacenterlight/migrations/0020_auto_20180409_1928.py diff --git a/datacenterlight/migrations/0020_auto_20180409_1928.py b/datacenterlight/migrations/0020_auto_20180409_1928.py new file mode 100644 index 00000000..9a659acc --- /dev/null +++ b/datacenterlight/migrations/0020_auto_20180409_1928.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-04-09 19:28 +from __future__ import unicode_literals + +from django.db import migrations + +DEFAULT_VMPRICING_NAME='default' + + +def create_default_pricing(apps, schema_editor): + """ + Create default pricing + :param apps: + :param schema_editor: + :return: + """ + VMPricing = apps.get_model('datacenterlight', 'VMPricing') + if not VMPricing.objects.count(): + vm_pricing = VMPricing( + name=DEFAULT_VMPRICING_NAME, + vat_inclusive=True, + cores_unit_price=5, + ram_unit_price=2, + ssd_unit_price=0.6, + hdd_unit_price=0.1, + ) + vm_pricing.save() + + +def undo_vm_pricing(apps, schema_editor): + """Deleting all entries for this model""" + + VMPricing = apps.get_model("datacenterlight", "VMPricing") + VMPricing.objects.all().delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0019_auto_20180409_1923'), + ] + + operations = [ + migrations.RunPython( + create_default_pricing, + reverse_code=undo_vm_pricing + ), + ] From 1116812a994291c35493e12036db0fefb217289d Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 9 Apr 2018 21:40:03 +0200 Subject: [PATCH 098/866] Correct hdd price in VMPricing init --- datacenterlight/migrations/0020_auto_20180409_1928.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/migrations/0020_auto_20180409_1928.py b/datacenterlight/migrations/0020_auto_20180409_1928.py index 9a659acc..cea83a4c 100644 --- a/datacenterlight/migrations/0020_auto_20180409_1928.py +++ b/datacenterlight/migrations/0020_auto_20180409_1928.py @@ -22,7 +22,7 @@ def create_default_pricing(apps, schema_editor): cores_unit_price=5, ram_unit_price=2, ssd_unit_price=0.6, - hdd_unit_price=0.1, + hdd_unit_price=0.01, ) vm_pricing.save() From be72b9628cdb0ef1895c79fc18e44d6d9da7221d Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Tue, 10 Apr 2018 13:21:58 +0200 Subject: [PATCH 099/866] Add djangocms_blog namespace and urls --- dynamicweb/urls.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dynamicweb/urls.py b/dynamicweb/urls.py index ec43d1a5..edb7e3b7 100644 --- a/dynamicweb/urls.py +++ b/dynamicweb/urls.py @@ -67,6 +67,7 @@ urlpatterns += i18n_patterns( include('ungleich_page.urls', namespace='ungleich_page'), name='ungleich_page'), + url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')), ) urlpatterns += [ From ebba6d3795076c4e06ac6fc66079013cd2bd16dc Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Thu, 12 Apr 2018 08:56:24 +0530 Subject: [PATCH 100/866] cms page extension favicon --- datacenterlight/admin.py | 8 ++++- datacenterlight/cms_models.py | 11 ++++++- datacenterlight/cms_toolbar.py | 24 +++++++++++++++ .../migrations/0019_cmsfaviconextension.py | 29 +++++++++++++++++++ .../templates/datacenterlight/cms/base.html | 12 +++++--- 5 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 datacenterlight/cms_toolbar.py create mode 100644 datacenterlight/migrations/0019_cmsfaviconextension.py diff --git a/datacenterlight/admin.py b/datacenterlight/admin.py index acb93fff..b8dc2f32 100644 --- a/datacenterlight/admin.py +++ b/datacenterlight/admin.py @@ -1,10 +1,16 @@ from django.contrib import admin from cms.admin.placeholderadmin import PlaceholderAdminMixin -from .cms_models import CMSIntegration +from cms.extensions import PageExtensionAdmin +from .cms_models import CMSIntegration, CMSFaviconExtension class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin): list_display = ('name', 'domain') +class CMSFaviconExtensionAdmin(PageExtensionAdmin): + pass + + admin.site.register(CMSIntegration, CMSIntegrationAdmin) +admin.site.register(CMSFaviconExtension, CMSFaviconExtensionAdmin) diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index 9eb55e0c..4aec69b1 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -1,9 +1,12 @@ +from cms.extensions import PageExtension +from cms.extensions.extension_pool import extension_pool from cms.models.fields import PlaceholderField from cms.models.pluginmodel import CMSPlugin from django.contrib.sites.models import Site from django.db import models from django.utils.safestring import mark_safe from djangocms_text_ckeditor.fields import HTMLField +from filer.fields.file import FilerFileField from filer.fields.image import FilerImageField @@ -30,9 +33,15 @@ class CMSIntegration(models.Model): return self.name -# Models for CMS Plugins +class CMSFaviconExtension(PageExtension): + favicon = FilerFileField(related_name="cms_favicon_image") +extension_pool.register(CMSFaviconExtension) + + +# Models for CMS Plugins + class DCLSectionPluginModel(CMSPlugin): heading = models.CharField( blank=True, null=True, max_length=100, diff --git a/datacenterlight/cms_toolbar.py b/datacenterlight/cms_toolbar.py new file mode 100644 index 00000000..15a8cb4b --- /dev/null +++ b/datacenterlight/cms_toolbar.py @@ -0,0 +1,24 @@ +from cms.extensions.toolbar import ExtensionToolbar +from cms.toolbar_pool import toolbar_pool +from django.utils.translation import ugettext_lazy as _ + +from .cms_models import CMSFaviconExtension + + +@toolbar_pool.register +class CMSFaviconExtensionToolbar(ExtensionToolbar): + # defineds the model for the current toolbar + model = CMSFaviconExtension + + def populate(self): + # setup the extension toolbar with permissions and sanity checks + current_page_menu = self._setup_extension_toolbar() + # if it's all ok + if current_page_menu: + # retrieves the instance of the current extension (if any) and the toolbar item url + page_extension, url = self.get_page_extension_admin() + if url: + # adds a toolbar item + current_page_menu.add_modal_item( + _('CMS Favicon'), url=url, disabled=not self.toolbar.edit_mode + ) diff --git a/datacenterlight/migrations/0019_cmsfaviconextension.py b/datacenterlight/migrations/0019_cmsfaviconextension.py new file mode 100644 index 00000000..7b350a70 --- /dev/null +++ b/datacenterlight/migrations/0019_cmsfaviconextension.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-04-12 03:16 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import filer.fields.file + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0018_auto_20180403_1930'), + ] + + operations = [ + migrations.CreateModel( + name='CMSFaviconExtension', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('extended_object', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='cms.Page')), + ('favicon', filer.fields.file.FilerFileField(on_delete=django.db.models.deletion.CASCADE, related_name='cms_favicon_image', to='filer.File')), + ('public_extension', models.OneToOneField(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='draft_extension', to='datacenterlight.CMSFaviconExtension')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/datacenterlight/templates/datacenterlight/cms/base.html b/datacenterlight/templates/datacenterlight/cms/base.html index 0c356735..942a0ad4 100644 --- a/datacenterlight/templates/datacenterlight/cms/base.html +++ b/datacenterlight/templates/datacenterlight/cms/base.html @@ -8,9 +8,9 @@ - + - {% page_attribute page_title %} + {% page_attribute "page_title" %} @@ -30,7 +30,11 @@ - + {% if request.current_page.cmsfaviconextension %} + + {% else %} + + {% endif %} @@ -52,7 +56,7 @@ {% placeholder 'Datacenterlight Header' or %}
-

{% page_attribute page_title %}

+

{% page_attribute "page_title" %}

{% endplaceholder %} From c0c2dc5c37aef76021d490bf62d62f5c1f00ee1d Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Thu, 12 Apr 2018 09:26:30 +0530 Subject: [PATCH 101/866] larger image used in email --- .../datacenterlight/img/datacenterlight.png | Bin 5135 -> 5897 bytes .../emails/user_activation.html | 2 +- .../datacenterlight/emails/welcome_user.html | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/static/datacenterlight/img/datacenterlight.png b/datacenterlight/static/datacenterlight/img/datacenterlight.png index 1ae6ff53c6e1876d7ff5c2dc598020e32774f37e..9097af9bfa6894bb2da2fe71d2360b5215718ddd 100644 GIT binary patch literal 5897 zcmaKQcRXBc*Y*&E1W}@dV2l=Z^cYbxT8Q53Xk#))8wO#B-fKh+g6JVi2!jwM5y9w* zo`{IvGf1Mpc+NS``+dLfeEW}kuf6tK*L|&Zul>iqV)S(GQC(rV0ssK0G}KiL005$@ zGuw!q>};Ki`=oz%yWyp3=4FU};N@e7K>(l*XnO=u!_^LnFhJNjU_E*easa?3PLz?E zmzlPfEFA4BVs{=R;_K>umJI;NDf+tG!JQFaKzjrdYHkAoQ4aDTQz>mxZFgma zBTC&5gD~{dF@pO!!yyhJMFpUouk0CsE5geT= z=PT^y$@Mpa3c?ePLAiUO&~CtUL_2%5x0gKVjOl+$aCQF&*3I*;Hk~yL>}%%^784OY zFX`_CI|DCIXhI_jr+`RtIb@*>C^j~w&C&AVI zY-be&2K5l(poT%a0xv3-Mg2RM+y9F9SFXdqbCLPiT<{qg@cFs^uXFy}bv8Wb=08(= z_VCZ#Bizo09dkC-fzhJM004cUh6>ckcWNE>_TjV|Bl}@v#;R+09ja3YvFuQec9!17C&NrZHttCoTfsEkBB#|Hn7i@>?sk^>ft~~zBcVs zn=e~4cYHQm4t{N9pMQ42HM3H0&JBPE(fJyE^?bc+^?%!dIpTl(|FHoyOy@xt{=e+t z#xqB7%^cM^VmpV32pCC#7d%Rv>083ne$HN%Gi*!QON3foX+8b<)-}rI`xH@z&&_u(kPI-=s8|6g>m4@+Y!o&bE@l6 zb=|_h&8?SqZLk3dAGQl3%-qRYTLxZN&#SPF9y8%qOmsTAp6`It!WcIaK+w3AyG#lFACn+w{<5iHb+a@&F*(xZ&ScC16r>xo2r4@>7g{Tj)- ze@f*?+DQrkh>{Uo<}=;33hVf-*a{9`h-_6n{j0_CAni|38X^z11 z5iIb|A3S-+o8%VWe(qA=7bxG?hQUnU*fN}Vy*=CrWwb|n17 z>|N2ERl0%ofJ58yQS@M2nuN3_3s z+*f??xUwRdjJXV!k@EKp<~A-lq@))Olq-@#dk7+yZ&@&A-#Ixk+D*b;xhVh9NEWmD zT3Ab9W9FdEFwg06PK(HgA1tZ&tzGqI?y(kq>shPJkEcU@SOtOnUs`QBZDK7wQ53Is zSjNv9_PDdKI$hSJQb_D*bx<)7MrAkKluNJA0WBWG@~=J8VU#qr*={xu}^Cs@cq?TGsZ1@sPiPN@fF{Qw1k-M*k|gU*l5v7_ z@BrDHq`?C2=Njq}KbCA5i+Px1mM%wbR@^-}I&MJGK3{)|k9SIqr^>Jiztx;?jac@` ziLx#G>`XHgPrV`Z(Q1i;-}lHL{jy@(C^O@UI0r#~*WX|VwwMGGSO|3Ex`xiK;5=yN zu6DhQWld}}3md$1rE`z~xotqh8=@XZc57!MWcQi#z7@;vN)jxZy{nYEsJQaQu|g%7 z(Tx8p(y(qM6EkR&{`~q)&1(og#+~q~pvu&odIk%^jc=>18H4`DWtLsBVcK~~N-5HN zQAe<`=ggx51_XsdaUOhv<*$fJjIpcpm`P01Zzh%x1lN5n6XxDGIgX3U8YKY*_g9@Y z7$v5&A@uS+kuMn$(|&&GYz#hpW~9hhdO~s(sw4BBc?m$lxKDxsl${4i`*v3G$$sCh z%sL~2h%dDlj$6j?y=h>K&doI_@o{cA%~kAptwiy9AtNGa0U18DZCEuO@uQ{XnsF(O zNqmd?@}Om(vC~?HG<@s%P^$R6gDuRICtTY4B`&uLYO$BlS)7!vtJJu?2P_WEpFF9v zV{@`8N^2M@nNBjD(HGS+6Kp2zg23jU4Z_XapLHE#gv{^!=8^Pddmp#DUUQnA{MC8&w3HO| zQM%gci}Ni#Q+n$7QoRBsgF<(lurNpLmk8k}zqKvCoHFHpJjoru)of2G?q=g~+f35j zY7)@D*cS4B14WGX7Z7JPJJc|fr>x)5y15e^$~j6Ktz=PBTekJ^&`@AJDl$->A+Vt2 z(LiaYuI3PZRw`MnY>5$6(fAF-WN zZY4A(8iwI?I_WZge@=BBDO#EN42)hrjITei;`%bsJ(n`LtfX*HDpZdCJ~!KMQkX+d z&TYB4>zS+X0^FFRG9OTXA%E@f3l}?)cz;yjQQ9g&dSl*2e^7Em1U3N+C!pRR8i1w> z=}z^^mg*wj?TAOe4CDRN4(?G~d9^b{g#v=Jf+cd=5ll}j7ri_6A4WvDKR=kLdK(~m z5kDF~fV`%HzB{_FBZdqsz4OexgfS)J#g- zpC!kA{y{5r$DA{-;RVXDjt>HZ1>Grr3rk$nEt3JZ43iV!-#1fushljhhVV-Z`4sUz zf(*K4`s$8+-rnV3GVq6tN0@0d8;`W2RcUfh(Wnh*viV1xX^(U1aPJmUJ3wvY`>#Ap z+*cF#%-kg1I3 z<*l2bI!~`t!NyZZe{D?m-$G-s1xWZuGFJ zu^1I`k<*nEvZT!zTD#-O>UJONW~pmd7KWwWh$9M}PD&k9r$^O)?$(i)+Zs!gGjkvM zGt`=m3Y-0u@Z(M8V$VWx?o5WPct6`}?e#tWV55eynf7cS?@-L@eG5j<)o}beF&Aos zzJ=&d)d9PKT6~Gtx|RjU<*{5@()OOTm9WM!gS$E|(o40*!&kBTLrQT}?{V=QF(IKs zx7Z&S{|FG&&l7z;0D8zgqH(;Rv*>HV%TsZZMB1tWRK%|-lSzr0R>ZghJ*dHAQ z?tUxLExy_)bcwU-c6kiam10FD{(4JEG43`AMfT!g1s7Hl%GW*y8;4FH%pBDrj2i`% z=;_Ai%k1VE^$$cgg=>dq2b@8cme*N66vZ)dvmW?sx=HeP_oNv0)3o6?x!A8^B<1Iz zl+Jk;^$N|x)U{L#WxWeCZzf_JWwM5(dp8H9_Fon}30Q%un|ik9#4wTYE)y^bx!0*! z0)~?t3GMxfRK$aq(^)#cjPHsNQ97}^~=71TZcrka1HY-m@*dV!_1|5;&uP52H2bs1Z+ z^Q+OW-Kr%IZ;i`yFw1$k)&qG|{%}$_wPo-A-HP5%@>eKelT7c>zsSJMKO;>{?MprO zYfEdj$-m>QEz)af2g5{M!cPVwx+F*l0RYAq7Aih)x4!H|7IKP#mWcL*M@icFN3jhR z3|l(mm1JhyP1f)j1+R_6df)rn^<|rTb6OmnkJTrU#1r-8Tjh^d+>{JCQn5m?7rHW9C*{;%PlUzJ-F{ z$1l6?Hs;G4!MAAGJesneaPZ&EkuY8+*)jT~7=zovUpISEbu)17z+jp#Cn3#Tjql(} z>Fxc&PwgbNqP~}@R~@4`LvKG-T-QKB)ZJe+@qb*S75uqSEI$zSONUJQ!Vh1XRm*SLfr9&gcCO@n{jmmb6kkKg)|e%9xlTVtJ6i1;2_Z! z2kbQRc1}!Z=+401tQ;XrtfD-8;rsm8{6ft`uOFIKb9L}j-DS#}s=*taSj5~ZXXuh zW-oPO9z2f>Fxq5zBk{IzT}1-NgN5X@6+uFL2KIlC&?5VW_T+ggwbyhUaLoR-(aQ>7 z$6_HI4>KXVr$%y$`q^FZckrg`KR$$Zui2a)MG1!-3&YiT);is^q>$DTyu!(4WiI!& z>-$RjZp1K~sC`FX+H~wMYn`2)tt8D)reLZVeM6&*c5Qg3Xp~WHusw7`6Dx)0pl793 zHu)5#MwnwdoDsdIfIqDNSZ7M|fVU4qgU7M6C}21SorH8{X)sQ}>i9#QarF4n&DCku z_oy+WlP2h}Wi(OVlW`LM18H<>MCxg2j7Pi&&!L$NKFpN!wCP>-k^d$+~@b+hw>QZnyvT{b=-MU{_MxB{t+ z_sn!+=+BfbKmI*IVv5b=#vZw?WXYYx2u)GWzIiFyz-0d_^G`Z#y(b-plg&f+7T+ML zTw1^ZnFXAt&)GCf z@h3lmri2OpuIi)SJdMX!tM0V$NlR?dkoOhdaSd`JXgX7Ioju0OgUvt@hLNq9O-U@c z46*q-oqoLCUPRmEG{rriT8o}e*Y=E$*?!)h{u1y1*89;(BtycM?>cPZV>-p!9lrsh zJpU3uy()fNW$WQ~^|*?TG+)d@U~~KqdeMcs#y5}{sX_i(29&DG5n5x= zU{+@7U441fxo6K}Pky_l)Tw;ek}bCO)M~BUdjwC>?)%f915;mvdIC>hKC`(B=JtvW5Aa z*o0QXc1y}$>|vEA--SREnnYr5y~A=%qbuZQ0KJJo?%1pmSmjG;9L>cKy#nDNuDyGe zN`qy+K+B2Txw?q-JdIfz`(e%_tlllIo9guKHWcIn)7~F(J!pM`;5l4px{xAX^~4F~ zSxF+_<~N2aZLe%fvu0xD6LNL)Jd|%VUqT%|G!8viQMxp37f@YQth+sxruAJ`hHbR` zyvcPY%F9JnQqZuaptYCS3mxbl*1Ro^C&{5BkY&#V!+lQ@ww*HLP;0b`Dv!P zF^oseK{b8U{+0;zA|20Jt1jnS0Ul&G(tj|=7iy6x%q4#zWic~Y7hXtO=W%Be&^eb2 nk>>xj0p}9UUw#1eXAlt?Ak0s)A0)rib?(6(j|ytnZezx-t+t3!!dL7nS~X&CReKYSS(QqS4y!_~+I!EK zMeQH`3!cyO=3MuS`?~IP&h_U0oG5)=bxLw(asU883DHn7000Quug^xL#MgKb3mUmD z_q4|^Cp1a9X9Gl1DS1bF_0$pZkiwGfr3M*b69R?WVPQVbDZ zrI<~4PrVCT{>^&tEi;s1hC8OVR3q)XchbXl=S;W`BT7B^^;W;$BSeaCx#MJ7-q#pG zn+{USL;#sa@!z1;U1v3hJ@wt-V{1!GR(T~QifbYOK-gn|0O8F+gZc)aMBgC5fanGj zIdAYa9l=d0CcQy+)f@ajwHz@pzI*3&BKoW)b9#D4n55tR#ewC8Zi5`#{zrf5;*Q|s zA0iEJn=cO%Y~J|~0{~2U+pzqm^F0<+x_TbcZAP!+uKBul3gb({5qye>@+_a%b*1GBMl;_&SDe(+FmL zaY<+AeLjCb;<_jQD*ux*B=&fr-KT<+giFSZr(x5tY@kN}jDqrY5*7grSZ}wS&A+oWbC0;V7DM&O{`!rZ zVZLE`!PZ8G$!T4|SMW@`uz!X^!IC`YQLM_e3{B}7<+In;l-@n=+=o^KM5Nx19T60l zT&*|{nkcB^s``N5F|!s7pqDolOJOma(F%%9t>*0Y@P-RuiP_%VtR>Hsy$}r~ zS70=-SHUUsgLvb%gU;TU2ffsg@8XJ_`F_Rt^H@@Za&`1BGlb$nf z`P1C2uprS@Y3R4I04RqRD3|nO^+{&Pmjx2>S*h_ZEY2B)Dk(K;I7yn!9&a$8NkL+s z87C=Q3}q_On#1CQ@^%lR#!Ku1XKD3TWJqZMFQGgpqri2AxXrq`-PuxYODm+OV>GBl zHq}R$J|&sXFKVv1JaN9~8>^CI-Fs?Jji+DM>{vdq)10tZ8>Oo+B|e`@LAsZj*o>a{ z%96+Y3|Ik&G5e9_;C(r8g<7R@5$Wk zQ~o{OUnO>KwK{J05Wf-5SgNdqb8_FGTRK0-%$SZhpu|MDV2TcMOvfq*RvU>7?j4I$ zYHTyo=AM82AhCM)m++4uNkR$eqM8eCTw@AYitM-A%fG13NabZq9Kr>xbGBu2xckbR z9bcD!c~;MC&h*P{cU1LL&cow#*f-6B`ak-9_M&>_SEHKKE*5Y2asDGgA~?D7dLdXz zo1?add8TAs`TxpO#h+^etse1l9$NbWLe1M-c)b@=O=szz#!grv=9Jq#lM^r3i1Bfx;AnD=Q|_!7 z0G^_jCZeTG?7R6)1+TgRA+YtpS=dqw~AwqO~|E!fI zHegr-2IJ3`-u&0?q_j8If^g48+U3V}R?s&ZzjaX4i*8cH02ThwoAPSZmtd=8qJ3f? z&mVS6f%)tV_EB$!vYX&_$#md90=wntDH74T@HJ&fQ;jiAgAP*uk})^$g3+3GJDQ{G zvlaFF9QAht?Z7q7zbyVZl4e#tiwZHk`4t4Hc=TyUTN6VN{;Nktmz-l{SO4|Z zXYM#u`5RSrAd|PEC}gJRY@op}n=|=SH3;wW(-4hl7}#1;KUCOq)=r6iEk0zf@IyV_E6bUxNrk?TXY&b zbMDVjk!Uf?gJx=s=W{3w=+eHdkcr&{0kD21TuW9g_V)WPY7))~tI{Gp*{}zcZ5k2Nk2A&VP2Ye<;Bm0GXj+h&3;XQv=u6 z0JfKcqAFqibD=z1!=@RRf%hT&&?IrlXwn5*GrIdic0nx8W}yAyg0J;(7ZTN*vFB0R z#EUYF*X@FD?PYw3Lnkf^Nd!gva!-EP%;Br|8}MwdmPU;dL!z6~*RaG-GG~pH8DIhC z&Ikwl;>eH>Kyaa};c|=i4951G9aOTznD95eYV|AYq;kYid{8ybq8jbH*}klf*wY^u zN|>!LmHKGGzVM8-#KBO`M&- zuh_QidR{E48N1A8JG8I+2u0qb+My0Tspxa2gFf&arP*uE}Gv?;z0Et(#eL@YggypUhAeHhZH|i))KS#lj@feboZ^bQKL&~$8kW{N7ZZ| z!$+S5=@m!m7WG9WsJW`=@jcnqq&||62BGo$F>gXgfy0*(?0>N*;yW7JaC)rqs1KiS z(F*bR1l2Fjzt5!HWw7cEFjz*6T+EY7Pfsc8Mj^!8!J+oRl%tDgp3y>Xvi;Y_dfj6V z(=;NH=OgsbC`ZO6EAZoPACrReWvpr}t$klr1#2V~>B5UdA6U{F3uZ2@3IU%;MlPY4 zs58!SILU4k2{Xp3TbMcWR$u$>}6 z=@F#3%g70qF^4Ga7wAv@P$^Wc{le=Vb&GIUAmR9rzl^yZtub{B2IdpIqUo;PQeN>R zoV33}Cdz_Sw+CYq54RTN+l2?fGoLIKAHHbe!OLunj!8V19o>*b`>WhP4&hKz+qXm3f)Lu++m{$Ui436 z%^FE%;NXroA9Llat{A^tfdPm-!fdbC)=275p~c3vkctZXygX&C~bP3{Tm2RoS^^Ka4S#W5X``P1CJ(NA$u@1W#W;BH&gJLdQ zWfqSo%1NiB^WW2b>SI$L$^`P!D~CGJ!M*#mpOs_}N1(q^Ex-ZR8-j^2?c%kCmCp;@ zQw<}%Q&2g$O?@Nm?3HU>QqeMF8v-$=xBlU(k#BRxY1)&jkC~NLfSl?C(iyF?#)T8f zU>!{z)M;D`x_a?Qaoagz&W!jMp8z&<D^VNO{@{SApQT$)ak5%a*^NM0;rc_Fc!UUny z?cvZ>Lsm9d>p3)hT*qmmRzJ9$V@a2xu_>>cUq1WqjGy;<-ug-M*{c!Hb3SikhfxHv zMl9<5F)hAO&4qFg;TEf?g>y{=pQAfJQPY^pdP24wLav#G4%SkIaC{8CWf57Oj|-wqBwjuHme3FIcncSan+;=?Q=!iQpfxUoEBO0MKnb`(A8G&9mZwQ{E^ zn`U)DkP3!-URo-WQ|ryJM2J+tJ@|e>9W0+0Sfv2U(EIqf@;!;}>gsUzxvu)X-b}Ia zuCqMKbo^724?ll*vP^1NEYCUKpTQwkgeFZ)s|;Bhn*>tu1!|(YV{!wI=wH4$FA^XQ zr_~{sTyU=t9+n=p0nu!8ChUW(A4@Q$mmhsuIg4~SPt(ksqr34dyfh~2dgYkS@l>z6Dk$42-{!U>8lar*+P)1PlF|9z~W>-?U=fe%J ziu9mq=~r{2xvn5bS)B_zKhrFQK;fe}rBsjZ_LErcs?I&x|(~6lCbRDA$E-?JWa@U$!r{>aBNo zbEP7gNbD{ihJB_9KF&YRqB92!#T-C8f9inu6jIQA4FhjwHu=4kPTk)Pw#WkoQZ)%0 zL0Im;LAKxMwZDps>W1a1v>bl?^_&LXbDj(PkEE%Ums+<2?Nxz|odML^S!D)5q}5 zQkf3!FQ&FJlgrJ)2vA6`W)J3k%WAokHf0`T^ouRe;ypjqOXr*Pe)LNSPfvCOZNg!X$!ORLFl&T~jK!z2)!*3hmwSrPcY+)B&cv=&d&9!Ol){dqm{ z-QwzmnxaB6NJ%WUxjZ+y7QV|S?sJ)uOy?GA>(=1@c*KgBxGf^@HtI{Ceg}_KPYyzx z!72(eVtgE5vE$O>VPbb{-3WuAhWwgzAK!JkAN}s@Q%Rb?Pa6ccH_hC0ZbF6E>_jRI zP)S6lyqo~ZvRl?2rv;y0!^7ORAn&Zun+qLJV?&a-%r`m6qNW?7BJPkpMgGf9F1R|( znz;EM!40vCt*eTp(Uso?Y)Zq({OYP4NEbdZKQ-)S(?7h@e3Dm{e+vdM9Mbwa zcy6w}%+x}8ITO_YPO_v~1d`9||C@9HY$^V>0&W2g`$IxmD%JO|;~xM-Rad17Y#aJN DnJB5u diff --git a/datacenterlight/templates/datacenterlight/emails/user_activation.html b/datacenterlight/templates/datacenterlight/emails/user_activation.html index 6e70100f..403482a4 100644 --- a/datacenterlight/templates/datacenterlight/emails/user_activation.html +++ b/datacenterlight/templates/datacenterlight/emails/user_activation.html @@ -14,7 +14,7 @@ diff --git a/datacenterlight/templates/datacenterlight/emails/welcome_user.html b/datacenterlight/templates/datacenterlight/emails/welcome_user.html index e947ac97..f18f9750 100644 --- a/datacenterlight/templates/datacenterlight/emails/welcome_user.html +++ b/datacenterlight/templates/datacenterlight/emails/welcome_user.html @@ -14,7 +14,7 @@
- +
From 588f513f2a1330a30829d3cb9f575f1acd814a39 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 12 Apr 2018 07:59:04 +0200 Subject: [PATCH 102/866] Remove initial VMPricing code from migration --- ...409_1923.py => 0019_auto_20180410_1948.py} | 2 +- .../migrations/0020_auto_20180409_1928.py | 48 ------------------- 2 files changed, 1 insertion(+), 49 deletions(-) rename datacenterlight/migrations/{0019_auto_20180409_1923.py => 0019_auto_20180410_1948.py} (97%) delete mode 100644 datacenterlight/migrations/0020_auto_20180409_1928.py diff --git a/datacenterlight/migrations/0019_auto_20180409_1923.py b/datacenterlight/migrations/0019_auto_20180410_1948.py similarity index 97% rename from datacenterlight/migrations/0019_auto_20180409_1923.py rename to datacenterlight/migrations/0019_auto_20180410_1948.py index 4766cb5e..64a13128 100644 --- a/datacenterlight/migrations/0019_auto_20180409_1923.py +++ b/datacenterlight/migrations/0019_auto_20180410_1948.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-04-09 19:23 +# Generated by Django 1.9.4 on 2018-04-10 19:48 from __future__ import unicode_literals from django.db import migrations, models diff --git a/datacenterlight/migrations/0020_auto_20180409_1928.py b/datacenterlight/migrations/0020_auto_20180409_1928.py deleted file mode 100644 index cea83a4c..00000000 --- a/datacenterlight/migrations/0020_auto_20180409_1928.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-04-09 19:28 -from __future__ import unicode_literals - -from django.db import migrations - -DEFAULT_VMPRICING_NAME='default' - - -def create_default_pricing(apps, schema_editor): - """ - Create default pricing - :param apps: - :param schema_editor: - :return: - """ - VMPricing = apps.get_model('datacenterlight', 'VMPricing') - if not VMPricing.objects.count(): - vm_pricing = VMPricing( - name=DEFAULT_VMPRICING_NAME, - vat_inclusive=True, - cores_unit_price=5, - ram_unit_price=2, - ssd_unit_price=0.6, - hdd_unit_price=0.01, - ) - vm_pricing.save() - - -def undo_vm_pricing(apps, schema_editor): - """Deleting all entries for this model""" - - VMPricing = apps.get_model("datacenterlight", "VMPricing") - VMPricing.objects.all().delete() - - -class Migration(migrations.Migration): - - dependencies = [ - ('datacenterlight', '0019_auto_20180409_1923'), - ] - - operations = [ - migrations.RunPython( - create_default_pricing, - reverse_code=undo_vm_pricing - ), - ] From d50f282057a1a73c0ad81c1ae2ab388e69697e24 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 12 Apr 2018 08:01:43 +0200 Subject: [PATCH 103/866] Add create_vm_pricing management command --- .../commands/create_default_vm_pricing.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 datacenterlight/management/commands/create_default_vm_pricing.py diff --git a/datacenterlight/management/commands/create_default_vm_pricing.py b/datacenterlight/management/commands/create_default_vm_pricing.py new file mode 100644 index 00000000..c1b36eea --- /dev/null +++ b/datacenterlight/management/commands/create_default_vm_pricing.py @@ -0,0 +1,36 @@ +from django.core.management.base import BaseCommand + +from datacenterlight.models import VMPricing + + +class Command(BaseCommand): + help = '''Creates default VMPricing object''' + DEFAULT_VMPRICING_NAME = 'default' + + def handle(self, *args, **options): + self.create_default_vm_pricing() + + def create_default_vm_pricing(self): + obj, created = VMPricing.objects.get_or_create( + name=self.DEFAULT_VMPRICING_NAME, + defaults={ + "vat_inclusive": True, + "cores_unit_price": 5, + "ram_unit_price": 2, + "ssd_unit_price": 0.6, + "hdd_unit_price": 0.01 + } + ) + + if created: + print( + 'Successfully created {} VMPricing object'.format( + self.DEFAULT_VMPRICING_NAME + ) + ) + else: + print( + '{} VMPricing exists already.'.format( + self.DEFAULT_VMPRICING_NAME + ) + ) From 3e1d5ba0e20e37c80a1dc66f7de42354cc3fd404 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 12 Apr 2018 08:03:12 +0200 Subject: [PATCH 104/866] Improve string representation of VMPricing object --- datacenterlight/models.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/datacenterlight/models.py b/datacenterlight/models.py index 3a376747..a67d108c 100644 --- a/datacenterlight/models.py +++ b/datacenterlight/models.py @@ -1,5 +1,9 @@ +import logging + from django.db import models +logger = logging.getLogger(__name__) + class VMTemplate(models.Model): name = models.CharField(max_length=50) @@ -32,14 +36,22 @@ class VMPricing(models.Model): ) def __str__(self): - return self.name + '-' + 'VAT' if self.vat_inclusive else 'NO_VAT' + return self.name + '-' + ' - '.join([ + '{}/Core'.format(self.cores_unit_price), + '{}/GB RAM'.format(self.ram_unit_price), + '{}/GB SSD'.format(self.ssd_unit_price), + '{}/GB HDD'.format(self.hdd_unit_price), + '{}% VAT'.format(self.vat_percentage) + if not self.vat_inclusive else 'NO_VAT', ] + ) @classmethod def get_default_pricing(cls): """ Returns the default pricing or None """ try: default_pricing = VMPricing.objects.get(name='default') - except: + except Exception as e: + logger.error(str(e)) default_pricing = None return default_pricing From 0ea9051de119fc937150e840110401bec5f532b0 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 12 Apr 2018 08:38:10 +0200 Subject: [PATCH 105/866] Change the name of the DCL Calculator Plugin DCL Caclulator Plugin -> DCL Calculator Section Plugin Note: We do not change the plugin name itself because it causes data loss --- datacenterlight/cms_plugins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 26ee9162..fbc32b00 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -75,8 +75,8 @@ class DCLSectionPromoPlugin(CMSPluginBase): @plugin_pool.register_plugin class DCLCalculatorPlugin(CMSPluginBase): module = "Datacenterlight" - name = "DCL Calculator Plugin" - model = DCLCalculatorPluginModel + name = "DCL Calculator Section Plugin" + model = DCLSectionPluginModel render_template = "datacenterlight/cms/calculator.html" cache = False allow_children = True From 56792893299cb24b965a67496a879197b9885704 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Thu, 12 Apr 2018 22:24:50 +0530 Subject: [PATCH 106/866] replaced cms_menu with static menu --- .../blog.ungleich.ch/css/clean-blog.css | 2 +- dynamicweb/urls.py | 33 +++++++---------- ungleich/templates/cms/ungleichch/_menu.html | 35 +++++++++++-------- 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/digitalglarus/static/blog.ungleich.ch/css/clean-blog.css b/digitalglarus/static/blog.ungleich.ch/css/clean-blog.css index 3c8f4e29..66e4c54d 100755 --- a/digitalglarus/static/blog.ungleich.ch/css/clean-blog.css +++ b/digitalglarus/static/blog.ungleich.ch/css/clean-blog.css @@ -70,7 +70,7 @@ hr.small { } .navbar-custom .navbar-brand { color: white; - padding: 20px; + padding: 5px 20px; } .navbar-custom .navbar-brand:hover, .navbar-custom .navbar-brand:focus { diff --git a/dynamicweb/urls.py b/dynamicweb/urls.py index ec43d1a5..c3d15c04 100644 --- a/dynamicweb/urls.py +++ b/dynamicweb/urls.py @@ -18,8 +18,8 @@ import debug_toolbar urlpatterns = [ url(r'^index.html$', LandingView.as_view()), - url(r'^open_api/', include('opennebula_api.urls', - namespace='opennebula_api')), + url(r'^open_api/', + include('opennebula_api.urls', namespace='opennebula_api')), url(r'^railshosting/', RailsHostingView.as_view(), name="rails.hosting"), url(r'^nodehosting/', NodeJSHostingView.as_view(), @@ -28,8 +28,7 @@ urlpatterns = [ name="django.hosting"), url(r'^nosystemd/', include('nosystemd.urls', namespace="nosystemd")), url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')), - url(r'^jsi18n/(?P\S+?)/$', - i18n.javascript_catalog), + url(r'^jsi18n/(?P\S+?)/$', i18n.javascript_catalog), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += i18n_patterns( @@ -45,28 +44,22 @@ urlpatterns += i18n_patterns( url(r'^admin/', include(admin.site.urls)), url(r'^datacenterlight/', include('datacenterlight.urls', namespace="datacenterlight")), - url(r'^hosting/', RedirectView.as_view( - url=reverse_lazy('hosting:login')), name='redirect_hosting_login'), + url(r'^hosting/', RedirectView.as_view(url=reverse_lazy('hosting:login')), + name='redirect_hosting_login'), url(r'^alplora/', include('alplora.urls', namespace="alplora")), url(r'^membership/', include(membership_urls)), - url(r'^digitalglarus/', include('digitalglarus.urls', - namespace="digitalglarus")), - url(r'^cms/blog/', - include('ungleich.urls', namespace='ungleich')), - url( - r'^blog/(?P\d{4})/(?P\d{1,2})/(?P\d{1,2})/(?P\w[-\w]*)/$', + url(r'^digitalglarus/', + include('digitalglarus.urls', namespace="digitalglarus")), + url(r'^cms/blog/', include('ungleich.urls', namespace='ungleich')), + url(r'^blog/(?P\d{4})/(?P\d{1,2})/(?P\d{1,2})/(?P\w[-\w]*)/$', RedirectView.as_view(pattern_name='ungleich:post-detail')), - url(r'^blog/$', RedirectView.as_view( - url=reverse_lazy('ungleich:post-list') - ), name='blog_list_view' - ), + url(r'^blog/$', + RedirectView.as_view(url=reverse_lazy('ungleich:post-list')), name='blog_list_view'), url(r'^cms/', include('cms.urls')), + url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')), url(r'^$', RedirectView.as_view(url='/cms') if REDIRECT_TO_CMS else LandingView.as_view()), - url(r'^', - include('ungleich_page.urls', - namespace='ungleich_page'), - name='ungleich_page'), + url(r'^', include('ungleich_page.urls', namespace='ungleich_page')), ) urlpatterns += [ diff --git a/ungleich/templates/cms/ungleichch/_menu.html b/ungleich/templates/cms/ungleichch/_menu.html index e17e90d6..6ccb043b 100644 --- a/ungleich/templates/cms/ungleichch/_menu.html +++ b/ungleich/templates/cms/ungleichch/_menu.html @@ -5,29 +5,34 @@ From 479d6fbd4fa13c62c15dd98b061f591c7f7fc4ed Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 13 Apr 2018 15:45:06 +0530 Subject: [PATCH 107/866] change promo link color --- datacenterlight/static/datacenterlight/css/landing-page.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/datacenterlight/static/datacenterlight/css/landing-page.css b/datacenterlight/static/datacenterlight/css/landing-page.css index 33bf6425..f1d110d3 100755 --- a/datacenterlight/static/datacenterlight/css/landing-page.css +++ b/datacenterlight/static/datacenterlight/css/landing-page.css @@ -1231,6 +1231,10 @@ footer { background-position: center; } +.promo-section.promo-with-bg a { + color: #87B6EA; +} + .promo-section h3 { font-weight: 700; font-size: 36px; From ca480ce9c8e9775bc22addf480737236f71e033f Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 13 Apr 2018 16:23:17 +0530 Subject: [PATCH 108/866] darker shade for hover --- datacenterlight/static/datacenterlight/css/landing-page.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/datacenterlight/static/datacenterlight/css/landing-page.css b/datacenterlight/static/datacenterlight/css/landing-page.css index f1d110d3..8e9f2c2d 100755 --- a/datacenterlight/static/datacenterlight/css/landing-page.css +++ b/datacenterlight/static/datacenterlight/css/landing-page.css @@ -1235,6 +1235,11 @@ footer { color: #87B6EA; } +.promo-section.promo-with-bg a:hover, +.promo-section.promo-with-bg a:focus { + color: #77a6da; +} + .promo-section h3 { font-weight: 700; font-size: 36px; From 2536f9405863b7e44cbf3d3fa035d7a1dbb42ac8 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 13 Apr 2018 17:44:00 +0530 Subject: [PATCH 109/866] django parler config fix --- dynamicweb/settings/base.py | 2 +- ungleich_page/static/ungleich_page/css/agency.css | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 5db5a498..58c6b8e2 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -516,7 +516,7 @@ META_INCLUDE_KEYWORDS = ["ungleich", "hosting", "switzerland", "Schweiz", "Swiss", "cdist"] META_USE_SITES = True -PARLER_LANGUAGES = {1: ({'code': 'en-us'}, {'code': 'de'},)} +PARLER_LANGUAGES = {SITE_ID: ({'code': 'en-us'}, {'code': 'de'},)} AUTH_USER_MODEL = 'membership.CustomUser' # PAYMENT diff --git a/ungleich_page/static/ungleich_page/css/agency.css b/ungleich_page/static/ungleich_page/css/agency.css index 2b40158a..a4d2f4d3 100755 --- a/ungleich_page/static/ungleich_page/css/agency.css +++ b/ungleich_page/static/ungleich_page/css/agency.css @@ -235,13 +235,14 @@ fieldset[disabled] .btn-xl.active { .navbar-default .navbar-nav>.active>a { border-radius: 0; color: #fff; - background-color: #fed136; + background-color: transparent; + border-bottom: 2px solid #fed136; } .navbar-default .navbar-nav>.active>a:hover, .navbar-default .navbar-nav>.active>a:focus { color: #fff; - background-color: #fec503; + background-color: transparent; } .navbar-default .navbar-brand { @@ -266,8 +267,8 @@ fieldset[disabled] .btn-xl.active { } .navbar-default .navbar-nav>.active>a { - border-radius: 3px; - } + /* border-radius: 3px; */ + } .navbar-default.navbar-shrink { padding: 10px 0; From 5b8b40f9673b22bc7ad2beacb1819b0d90114f3d Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 13 Apr 2018 17:49:27 +0530 Subject: [PATCH 110/866] restore ungleich style --- ungleich_page/static/ungleich_page/css/agency.css | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ungleich_page/static/ungleich_page/css/agency.css b/ungleich_page/static/ungleich_page/css/agency.css index a4d2f4d3..2b40158a 100755 --- a/ungleich_page/static/ungleich_page/css/agency.css +++ b/ungleich_page/static/ungleich_page/css/agency.css @@ -235,14 +235,13 @@ fieldset[disabled] .btn-xl.active { .navbar-default .navbar-nav>.active>a { border-radius: 0; color: #fff; - background-color: transparent; - border-bottom: 2px solid #fed136; + background-color: #fed136; } .navbar-default .navbar-nav>.active>a:hover, .navbar-default .navbar-nav>.active>a:focus { color: #fff; - background-color: transparent; + background-color: #fec503; } .navbar-default .navbar-brand { @@ -267,8 +266,8 @@ fieldset[disabled] .btn-xl.active { } .navbar-default .navbar-nav>.active>a { - /* border-radius: 3px; */ - } + border-radius: 3px; + } .navbar-default.navbar-shrink { padding: 10px 0; From 403f9b5a08119e2247b6afee0887fbafa3c4da54 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 13 Apr 2018 17:55:08 +0530 Subject: [PATCH 111/866] change logo in hosting emails --- hosting/templates/hosting/emails/new_booked_vm.html | 2 +- hosting/templates/hosting/emails/password_reset_email.html | 2 +- hosting/templates/hosting/emails/vm_canceled.html | 2 +- hosting/templates/hosting/emails/vm_charged.html | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hosting/templates/hosting/emails/new_booked_vm.html b/hosting/templates/hosting/emails/new_booked_vm.html index b80aebe0..7bc0cf3a 100644 --- a/hosting/templates/hosting/emails/new_booked_vm.html +++ b/hosting/templates/hosting/emails/new_booked_vm.html @@ -14,7 +14,7 @@
- +
diff --git a/hosting/templates/hosting/emails/password_reset_email.html b/hosting/templates/hosting/emails/password_reset_email.html index 57831228..c7c1310a 100644 --- a/hosting/templates/hosting/emails/password_reset_email.html +++ b/hosting/templates/hosting/emails/password_reset_email.html @@ -14,7 +14,7 @@
- +
diff --git a/hosting/templates/hosting/emails/vm_canceled.html b/hosting/templates/hosting/emails/vm_canceled.html index 3142f6bc..9c2ec4c2 100644 --- a/hosting/templates/hosting/emails/vm_canceled.html +++ b/hosting/templates/hosting/emails/vm_canceled.html @@ -14,7 +14,7 @@
- +
diff --git a/hosting/templates/hosting/emails/vm_charged.html b/hosting/templates/hosting/emails/vm_charged.html index 33568d05..3a6c4f95 100644 --- a/hosting/templates/hosting/emails/vm_charged.html +++ b/hosting/templates/hosting/emails/vm_charged.html @@ -74,7 +74,7 @@
- +
@@ -100,7 +100,7 @@ From 7d05ab5f5f31e0abe54ba38b2c28b0a63827d5ba Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 13 Apr 2018 18:53:21 +0530 Subject: [PATCH 112/866] Update Changelog --- Changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog b/Changelog index 8442789d..912a466f 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,6 @@ +next: + * #4367: [dcl] email logo resolution fix + * #4376: [cms] dcl promo section plugin link color changed to brighter shade 1.6.5: 2018-04-08 * #4396: [ungleich] add favicon to ungleich blog * #4327: [dcl] fix navbar logo repeat From 3f4156ed8e64bcd9f8dd511eeb7593db9cccabf2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 13 Apr 2018 20:52:58 +0200 Subject: [PATCH 113/866] Update Changelog --- Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog b/Changelog index 912a466f..46b2534b 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,5 @@ next: + * bgfix: [all] Make /blog available on all domains * #4367: [dcl] email logo resolution fix * #4376: [cms] dcl promo section plugin link color changed to brighter shade 1.6.5: 2018-04-08 From 283a0d25d183366ce3465891c693c58da1562a6a Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 15 Apr 2018 13:31:55 +0200 Subject: [PATCH 114/866] Update get_vm_price method to use pricing defined in VMPricing --- utils/hosting_utils.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 3c193ad7..d8c49b53 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -1,6 +1,7 @@ import logging from oca.pool import WrongIdError +from datacenterlight.models import VMPricing from hosting.models import UserHostingKey, VMDetail from opennebula_api.serializers import VirtualMachineSerializer @@ -49,14 +50,29 @@ def get_or_create_vm_detail(user, manager, vm_id): return vm_detail_obj -def get_vm_price(cpu, memory, disk_size): +def get_vm_price(cpu, memory, disk_size, hdd_size=0, pricing_name='default'): """ A helper function that computes price of a VM from given cpu, ram and ssd parameters :param cpu: Number of cores of the VM :param memory: RAM of the VM - :param disk_size: Disk space of the VM + :param disk_size: Disk space of the VM (SSD) + :param hdd_size: The HDD size + :param pricing_name: The pricing name to be used :return: The price of the VM """ - return (cpu * 5) + (memory * 2) + (disk_size * 0.6) + try: + pricing = VMPricing.objects.get(name=pricing_name) + except Exception as ex: + logger.error( + "Error getting VMPricing object for {pricing_name}." + "Details: {details}".format( + pricing_name=pricing_name, details=str(ex) + ) + ) + return None + return ((cpu * pricing.cores_unit_price) + + (memory * pricing.ram_unit_price) + + (disk_size * pricing.sdd_unit_price) + + (hdd_size * pricing.hdd_unit_price)) From 74393ac6ace4ab766e5a0abc43a423237a42a2b8 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 15 Apr 2018 13:32:53 +0200 Subject: [PATCH 115/866] Optimize imports --- datacenterlight/cms_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index fbc32b00..9e3376eb 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -6,7 +6,7 @@ from .cms_models import ( DCLFooterPluginModel, DCLLinkPluginModel, DCLNavbarDropdownPluginModel, DCLSectionIconPluginModel, DCLSectionImagePluginModel, DCLSectionPluginModel, DCLNavbarPluginModel, - DCLSectionPromoPluginModel, DCLCalculatorPluginModel + DCLSectionPromoPluginModel ) from .models import VMTemplate From 82a2014fa5b7b458b675045f5b0024c57e562d59 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 15 Apr 2018 13:34:55 +0200 Subject: [PATCH 116/866] Pass vm_pricing context from default VMPricing object --- datacenterlight/cms_plugins.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 9e3376eb..9bb87bd8 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -8,7 +8,7 @@ from .cms_models import ( DCLSectionPluginModel, DCLNavbarPluginModel, DCLSectionPromoPluginModel ) -from .models import VMTemplate +from .models import VMTemplate, VMPricing @plugin_pool.register_plugin @@ -91,6 +91,8 @@ class DCLCalculatorPlugin(CMSPluginBase): context['templates'] = VMTemplate.objects.all() context['children_to_side'] = [] context['children_to_content'] = [] + context['vm_pricing'] = VMPricing.get_default_pricing() + if instance.child_plugin_instances is not None: context['children_to_content'].extend( instance.child_plugin_instances From aa55c1e868bdc5df1cb7f0956213c0b82cda30a8 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 15 Apr 2018 13:37:06 +0200 Subject: [PATCH 117/866] Update main.js to compute total from the unitprice's defined in the window context --- datacenterlight/static/datacenterlight/js/main.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/datacenterlight/static/datacenterlight/js/main.js b/datacenterlight/static/datacenterlight/js/main.js index 6753695c..35f2b247 100644 --- a/datacenterlight/static/datacenterlight/js/main.js +++ b/datacenterlight/static/datacenterlight/js/main.js @@ -171,7 +171,18 @@ } function _calcPricing() { - var total = (cardPricing['cpu'].value * 5) + (2 * cardPricing['ram'].value) + (0.6 * cardPricing['storage'].value); + if(typeof window.coresUnitPrice === 'undefined'){ + window.coresUnitPrice = 5; + } + if(typeof window.ramUnitPrice === 'undefined'){ + window.coresUnitPrice = 2; + } + if(typeof window.ssdUnitPrice === 'undefined'){ + window.ssdUnitPrice = 0.6; + } + var total = (cardPricing['cpu'].value * window.coresUnitPrice) + + (cardPricing['ram'].value * window.ramUnitPrice) + + (cardPricing['storage'].value * window.ssdUnitPrice); total = parseFloat(total.toFixed(2)); $("#total").text(total); } From c738888ab2dcb118816607938eff3c61583e4c4f Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 15 Apr 2018 13:39:01 +0200 Subject: [PATCH 118/866] Set vm unit price parameters from the passed context --- .../datacenterlight/includes/_calculator_form.html | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html index f38150bb..05201b11 100644 --- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html +++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html @@ -1,4 +1,14 @@ {% load staticfiles i18n%} +{% if vm_pricing %} + +{% endif %}
{% csrf_token %}
@@ -7,9 +17,11 @@
15 CHF/{% trans "month" %} + {% if vm_pricing.vat_inclusive %}

{% trans "VAT included" %}

+ {% endif %}
@@ -78,5 +90,6 @@
+ From 558e187e11ac749ba8cb0bfecf7d5ade0a7672a7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 15 Apr 2018 13:39:41 +0200 Subject: [PATCH 119/866] Update text: including/excluding VAT --- datacenterlight/templates/datacenterlight/landing_payment.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html index f21dc54b..b808e033 100644 --- a/datacenterlight/templates/datacenterlight/landing_payment.html +++ b/datacenterlight/templates/datacenterlight/landing_payment.html @@ -78,7 +78,7 @@

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


-

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

+

{%trans "Total" %}  ({% if vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %}) {{request.session.specs.price|intcomma}} CHF/{% trans "Month" %}

From 63a12ffe0636b5cdb1a11671e004044c0057201b Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 15 Apr 2018 13:42:39 +0200 Subject: [PATCH 120/866] Use updated get_vm_price method --- datacenterlight/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index af3b774c..1089ceed 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -106,6 +106,7 @@ class IndexView(CreateView): storage = request.POST.get('storage') storage_field = forms.IntegerField(validators=[self.validate_storage]) template_id = int(request.POST.get('config')) + vm_pricing_name = request.POST.get('pricing_name') template = VMTemplate.objects.filter( opennebula_vm_template_id=template_id ).first() @@ -140,7 +141,10 @@ class IndexView(CreateView): return HttpResponseRedirect(referer_url + "#order_form") amount_to_be_charged = get_vm_price( - cpu=cores, memory=memory, disk_size=storage + cpu=cores, + memory=memory, + disk_size=storage, + pricing_name=vm_pricing_name ) specs = { 'cpu': cores, From 962c96067fa9b30d2f8093ad4c1602d9d06bba1f Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 15 Apr 2018 20:55:39 +0200 Subject: [PATCH 121/866] Add get_vm_pricing_by_name VMPricing method --- datacenterlight/models.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/datacenterlight/models.py b/datacenterlight/models.py index a67d108c..86158394 100644 --- a/datacenterlight/models.py +++ b/datacenterlight/models.py @@ -45,6 +45,18 @@ class VMPricing(models.Model): if not self.vat_inclusive else 'NO_VAT', ] ) + @classmethod + def get_vm_pricing_by_name(cls, name): + try: + pricing = VMPricing.objects.get(name=name) + except Exception as e: + logger.error( + "Error getting VMPricing with name {name}. " + "Details: {details}".format(name=name, details=str(e)) + ) + pricing = VMPricing.get_default_pricing() + return pricing + @classmethod def get_default_pricing(cls): """ Returns the default pricing or None """ From e9a883bf2e45ec064c4a4307c74726a73fee2dde Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 15 Apr 2018 20:57:10 +0200 Subject: [PATCH 122/866] Fix a bug: use ssd_unit_price instead of sdd_unit_price --- utils/hosting_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index d8c49b53..26fdeb94 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -74,5 +74,5 @@ def get_vm_price(cpu, memory, disk_size, hdd_size=0, pricing_name='default'): return None return ((cpu * pricing.cores_unit_price) + (memory * pricing.ram_unit_price) + - (disk_size * pricing.sdd_unit_price) + + (disk_size * pricing.ssd_unit_price) + (hdd_size * pricing.hdd_unit_price)) From 957cec00a0c7540ad6071af1f137ddc4c1c43980 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 15 Apr 2018 20:59:31 +0200 Subject: [PATCH 123/866] Add get_vm_price_with_vat method --- utils/hosting_utils.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 26fdeb94..1367138c 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -76,3 +76,38 @@ def get_vm_price(cpu, memory, disk_size, hdd_size=0, pricing_name='default'): (memory * pricing.ram_unit_price) + (disk_size * pricing.ssd_unit_price) + (hdd_size * pricing.hdd_unit_price)) + + +def get_vm_price_with_vat(cpu, memory, disk_size, hdd_size=0, + pricing_name='default'): + """ + A helper function that computes price of a VM from given cpu, ram and + ssd, hdd and the pricing parameters + + :param cpu: Number of cores of the VM + :param memory: RAM of the VM + :param disk_size: Disk space of the VM (SSD) + :param hdd_size: The HDD size + :param pricing_name: The pricing name to be used + :return: The a tuple containing the price of the VM and the VAT + """ + try: + pricing = VMPricing.objects.get(name=pricing_name) + except Exception as ex: + logger.error( + "Error getting VMPricing object for {pricing_name}." + "Details: {details}".format( + pricing_name=pricing_name, details=str(ex) + ) + ) + return None + + price = float((cpu * pricing.cores_unit_price) + + (memory * pricing.ram_unit_price) + + (disk_size * pricing.ssd_unit_price) + + (hdd_size * pricing.hdd_unit_price)) + if pricing.vat_inclusive: + vat = 0 + else: + vat = price * float(pricing.vat_percentage) * 0.01 + return price, vat From 23bd0fa147b5234caeb369f6bd9aefc4cac1846c Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 15 Apr 2018 21:03:31 +0200 Subject: [PATCH 124/866] Pass context params to various landing templates --- datacenterlight/views.py | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 1089ceed..e2d28245 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -19,11 +19,11 @@ from hosting.models import HostingOrder from membership.models import CustomUser, StripeCustomer from opennebula_api.serializers import VMTemplateSerializer from utils.forms import BillingAddressForm, BillingAddressFormSignup -from utils.hosting_utils import get_vm_price +from utils.hosting_utils import get_vm_price, get_vm_price_with_vat from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from .forms import ContactForm -from .models import VMTemplate +from .models import VMTemplate, VMPricing from .utils import get_cms_integration logger = logging.getLogger(__name__) @@ -93,7 +93,8 @@ class IndexView(CreateView): @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): - for session_var in ['specs', 'user', 'billing_address_data']: + for session_var in ['specs', 'user', 'billing_address_data', + 'pricing_name']: if session_var in request.session: del request.session[session_var] return HttpResponseRedirect(reverse('datacenterlight:cms_index')) @@ -106,13 +107,30 @@ class IndexView(CreateView): storage = request.POST.get('storage') storage_field = forms.IntegerField(validators=[self.validate_storage]) template_id = int(request.POST.get('config')) - vm_pricing_name = request.POST.get('pricing_name') + pricing_name = request.POST.get('pricing_name') + vm_pricing = VMPricing.get_vm_pricing_by_name(pricing_name) + template = VMTemplate.objects.filter( opennebula_vm_template_id=template_id ).first() template_data = VMTemplateSerializer(template).data referer_url = request.META['HTTP_REFERER'] + if vm_pricing is None: + vm_pricing_name_msg = _( + "Incorrect pricing name. Please contact support" + "{support_email}".format( + support_email=settings.DCL_SUPPORT_FROM_ADDRESS + ) + ) + messages.add_message( + self.request, messages.ERROR, vm_pricing_name_msg, + extra_tags='pricing' + ) + return HttpResponseRedirect(referer_url + "#order_form") + else: + vm_pricing_name = vm_pricing.name + try: cores = cores_field.clean(cores) except ValidationError as err: @@ -140,7 +158,7 @@ class IndexView(CreateView): ) return HttpResponseRedirect(referer_url + "#order_form") - amount_to_be_charged = get_vm_price( + amount_to_be_charged, vat = get_vm_price_with_vat( cpu=cores, memory=memory, disk_size=storage, @@ -150,7 +168,10 @@ class IndexView(CreateView): 'cpu': cores, 'memory': memory, 'disk_size': storage, - 'price': amount_to_be_charged + 'price': amount_to_be_charged, + 'vat': vat, + 'total_price': amount_to_be_charged + vat, + 'pricing_name': vm_pricing_name } request.session['specs'] = specs request.session['template'] = template_data @@ -224,7 +245,10 @@ class PaymentOrderView(FormView): 'site_url': reverse('datacenterlight:index'), 'login_form': HostingUserLoginForm(prefix='login_form'), 'billing_address_form': billing_address_form, - 'cms_integration': get_cms_integration('default') + 'cms_integration': get_cms_integration('default'), + 'vm_pricing': VMPricing.get_vm_pricing_by_name( + self.request.session['specs']['pricing_name'] + ) }) return context @@ -493,7 +517,7 @@ class OrderConfirmationView(DetailView): stripe_subscription_obj.id, card_details_dict) for session_var in ['specs', 'template', 'billing_address', 'billing_address_data', - 'token', 'customer']: + 'token', 'customer', 'pricing_name']: if session_var in request.session: del request.session[session_var] From 40b984be1506e2eab0aca0eee0a99091522aaa76 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 15 Apr 2018 21:04:06 +0200 Subject: [PATCH 125/866] Update order_detail landing template --- .../templates/datacenterlight/order_detail.html | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 78ed43c0..20ff4db3 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -65,9 +65,15 @@ {% trans "Disk space" %}: {{vm.disk_size|intcomma}} GB

+ {% if vm.vat > 0 %} +

+ {% trans "VAT" %}: + {{vm.vat|floatformat|intcomma}} CHF +

+ {% endif %}

{% trans "Total" %} - {{vm.price|intcomma}} CHF + {{vm.total_price|floatformat|intcomma}} CHF

@@ -78,7 +84,7 @@ {% 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 %}.
+
{% blocktrans with vm_total_price=vm.total_price|floatformat|intcomma %}By clicking "Place order" this plan will charge your credit card account with the fee of {{vm_total_price}} CHF/month{% endblocktrans %}.
@@ -84,7 +84,7 @@ {% csrf_token %}
-
{% blocktrans with vm_total_price=vm.total_price|floatformat|intcomma %}By clicking "Place order" this plan will charge your credit card account with the fee of {{vm_total_price}} CHF/month{% endblocktrans %}.
+
{% blocktrans with vm_total_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with the fee of {{vm_total_price}} CHF/month{% endblocktrans %}.
- + From 5738dc8e1b5b2d2323a2e6a4c20f9039511a7715 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 16 Apr 2018 04:31:42 +0200 Subject: [PATCH 153/866] Virtual machine detail: use hostingorder for obtaining the price, rather than the serializer --- hosting/templates/hosting/virtual_machine_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index b77e1dca..61b16112 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -45,7 +45,7 @@

{% trans "Billing" %}

{% trans "Current Pricing" %}
-
{{virtual_machine.price|floatformat|intcomma}} CHF/{% trans "Month" %}
+
{{order.price|floatformat|intcomma}} CHF/{% trans "Month" %}
{% trans "See Invoice" %}
From 602ad1b2c07986a3e65a48c4fbfedc4735f32c3e Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 16 Apr 2018 04:32:27 +0200 Subject: [PATCH 154/866] Reformat code --- hosting/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index ca40f205..56f9386a 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1103,7 +1103,8 @@ class VirtualMachineView(LoginRequiredMixin, View): context = { 'virtual_machine': serializer.data, 'order': HostingOrder.objects.get( - vm_id=serializer.data['vm_id']) + vm_id=serializer.data['vm_id'] + ) } except Exception as ex: logger.debug("Exception generated {}".format(str(ex))) From 6cc40cb67f68c0da20370748a7fd1ec94fb38367 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 16 Apr 2018 04:34:40 +0200 Subject: [PATCH 155/866] virtual machine detail: show price upto 2 decimal places --- hosting/templates/hosting/virtual_machine_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index 61b16112..68894851 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -45,7 +45,7 @@

{% trans "Billing" %}

{% trans "Current Pricing" %}
-
{{order.price|floatformat|intcomma}} CHF/{% trans "Month" %}
+
{{order.price|floatformat:2|intcomma}} CHF/{% trans "Month" %}
{% trans "See Invoice" %}
From 731fef8ad9c8735f3b746a5e9caeacd63192ae93 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 16 Apr 2018 04:39:21 +0200 Subject: [PATCH 156/866] Show VAT details in hosting/order_details if it is set --- hosting/templates/hosting/order_detail.html | 6 ++++++ hosting/views.py | 1 + 2 files changed, 7 insertions(+) diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index f5ee80b6..099aaab8 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -127,6 +127,12 @@ {% trans "Disk space" %}: {{vm.disk_size}} GB

+ {% if vm.vat > 0 %} +

+ {% trans "VAT" %}: + {{vm.vat|floatformat:2|intcomma}} CHF +

+ {% endif %}

{% trans "Total" %} {{vm.price|intcomma}} CHF diff --git a/hosting/views.py b/hosting/views.py index 56f9386a..1f531784 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -756,6 +756,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): pricing_name=(obj.vm_pricing.name if obj.vm_pricing else 'default') ) + context['vm']['vat'] = vat context['vm']['price'] = price + vat context['subscription_end_date'] = vm_detail.end_date() except VMDetail.DoesNotExist: From 1e768648217b8a2120161858e106a7e3372bce05 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 16 Apr 2018 04:46:43 +0200 Subject: [PATCH 157/866] Get vm price and vat and pass it to context --- hosting/views.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hosting/views.py b/hosting/views.py index 1f531784..88593969 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -766,6 +766,15 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): ) vm = manager.get_vm(obj.vm_id) context['vm'] = VirtualMachineSerializer(vm).data + price, vat = get_vm_price_with_vat( + cpu=context['vm']['cores'], + ssd_size=context['vm']['disk_size'], + memory=context['vm']['memory'], + pricing_name=(obj.vm_pricing.name + if obj.vm_pricing else 'default') + ) + context['vm']['vat'] = vat + context['vm']['price'] = price + vat except WrongIdError: messages.error( self.request, From 3fca9dbb0df0d564f73a111d13c9c8eae825836f Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 16 Apr 2018 05:03:48 +0200 Subject: [PATCH 158/866] Fix a bug creating hostingorder --- datacenterlight/tasks.py | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index c97c6c54..db479b43 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -97,30 +97,22 @@ def create_vm_task(self, vm_template_id, user, specs, template, if vm_id is None: raise Exception("Could not create VM") - if 'pricing_name' in specs: - vm_pricing = VMPricing.get_vm_pricing_by_name( - name=specs['pricing_name'] - ) - # Create a Hosting Order - order = HostingOrder.create( - price=final_price, - vm_id=vm_id, - customer=customer, - billing_address=billing_address, - vm_pricing=vm_pricing - ) - else: - # Create a Hosting Order - order = HostingOrder.create( - price=final_price, - vm_id=vm_id, - customer=customer, - billing_address=billing_address - ) + vm_pricing = VMPricing.get_vm_pricing_by_name( + name=specs['pricing_name'] + ) if 'pricing_name' in specs else VMPricing.get_default_pricing() + # Create a Hosting Order + order = HostingOrder.create( + price=final_price, + vm_id=vm_id, + customer=customer, + billing_address=billing_address, + vm_pricing=vm_pricing + ) # Create a Hosting Bill HostingBill.create( - customer=customer, billing_address=billing_address) + customer=customer, billing_address=billing_address + ) # Create Billing Address for User if he does not have one if not customer.user.billing_addresses.count(): From b218ee1662677955759005ec2c95089f0d928414 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 17 Apr 2018 11:47:56 +0200 Subject: [PATCH 159/866] Add urlconfs for comic --- dynamicweb/urls.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dynamicweb/urls.py b/dynamicweb/urls.py index edb7e3b7..8f9f5b1c 100644 --- a/dynamicweb/urls.py +++ b/dynamicweb/urls.py @@ -11,6 +11,7 @@ from hosting.views import ( RailsHostingView, DjangoHostingView, NodeJSHostingView ) from membership import urls as membership_urls +from ungleich import views as ungleich_views from ungleich_page.views import LandingView from django.views.generic import RedirectView from django.core.urlresolvers import reverse_lazy @@ -60,6 +61,13 @@ urlpatterns += i18n_patterns( url=reverse_lazy('ungleich:post-list') ), name='blog_list_view' ), + url(r'^comic/(?P\d{4})/(?P\d{1,2})/(?P\d{1,2})/(?P\w[-\w]*)/$', + RedirectView.as_view(pattern_name='ungleich:post-detail')), + url(r'^comic/$', + ungleich_views.PostListViewUngleich.as_view( + tags='comic' + ), + name='blog_list_view'), url(r'^cms/', include('cms.urls')), url(r'^$', RedirectView.as_view(url='/cms') if REDIRECT_TO_CMS else LandingView.as_view()), From ee35fbd7849f5d2d1ea49773038024928ba84351 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 17 Apr 2018 11:49:01 +0200 Subject: [PATCH 160/866] Attempt to filter blogs by category --- ungleich/views.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ungleich/views.py b/ungleich/views.py index 3610d1bc..36583a04 100644 --- a/ungleich/views.py +++ b/ungleich/views.py @@ -7,6 +7,7 @@ from djangocms_blog.models import Post from djangocms_blog.views import PostListView from djangocms_blog.settings import get_setting from django.utils.translation import ugettext_lazy as _ +from djangocms_blog.models import BlogCategory def blog(request): @@ -20,6 +21,7 @@ def blog(request): class PostListViewUngleich(PostListView): + tags = None model = Post context_object_name = 'post_list' base_template_name = 'post_list_ungleich.html' @@ -38,7 +40,17 @@ class PostListViewUngleich(PostListView): def get_queryset(self): language = get_language() - queryset = self.model.objects.filter(publish=True).translated(language) + if self.tags: + queryset = (self.model + .objects + .filter(tags__name__in=[self.tags], publish=True) + .translated(language)) + else: + queryset = (self.model + .objects + .filter(publish=True) + .translated(language) + ) setattr(self.request, get_setting('CURRENT_NAMESPACE'), self.config) return queryset From ff1d4f1a6fb211b7d72366e344d40dd3124b6328 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Tue, 17 Apr 2018 16:15:49 +0200 Subject: [PATCH 161/866] Rename tags to category, because thats how we filter posts by --- dynamicweb/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynamicweb/urls.py b/dynamicweb/urls.py index 8f9f5b1c..20e9df3b 100644 --- a/dynamicweb/urls.py +++ b/dynamicweb/urls.py @@ -65,7 +65,7 @@ urlpatterns += i18n_patterns( RedirectView.as_view(pattern_name='ungleich:post-detail')), url(r'^comic/$', ungleich_views.PostListViewUngleich.as_view( - tags='comic' + category='comic' ), name='blog_list_view'), url(r'^cms/', include('cms.urls')), From 7d211b33333ebca6e559f2c46f7bd2dc220dc309 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Tue, 17 Apr 2018 16:17:25 +0200 Subject: [PATCH 162/866] Modify PostListViewUngleich: Filter posts by category if specified --- ungleich/views.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ungleich/views.py b/ungleich/views.py index 36583a04..c1d7445d 100644 --- a/ungleich/views.py +++ b/ungleich/views.py @@ -21,7 +21,7 @@ def blog(request): class PostListViewUngleich(PostListView): - tags = None + category = None model = Post context_object_name = 'post_list' base_template_name = 'post_list_ungleich.html' @@ -40,10 +40,20 @@ class PostListViewUngleich(PostListView): def get_queryset(self): language = get_language() - if self.tags: + if self.category: + blog_category = ( + BlogCategory + ._default_manager + .language(language) + .filter( + translations__language_code=language, + translations__slug=self.category + ) + ) + queryset = (self.model .objects - .filter(tags__name__in=[self.tags], publish=True) + .filter(categories=blog_category, publish=True) .translated(language)) else: queryset = (self.model From 80a568b2b382a763f108d809249f2e71bf69ff0c Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Tue, 17 Apr 2018 17:14:39 +0200 Subject: [PATCH 163/866] Remove unwanted /comic/... urlconf --- dynamicweb/urls.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/dynamicweb/urls.py b/dynamicweb/urls.py index 20e9df3b..09aa4fa8 100644 --- a/dynamicweb/urls.py +++ b/dynamicweb/urls.py @@ -61,8 +61,6 @@ urlpatterns += i18n_patterns( url=reverse_lazy('ungleich:post-list') ), name='blog_list_view' ), - url(r'^comic/(?P\d{4})/(?P\d{1,2})/(?P\d{1,2})/(?P\w[-\w]*)/$', - RedirectView.as_view(pattern_name='ungleich:post-detail')), url(r'^comic/$', ungleich_views.PostListViewUngleich.as_view( category='comic' From 8b5b353e59a663cc5d9c3a678b549ca0aa1af2d1 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Tue, 17 Apr 2018 17:15:23 +0200 Subject: [PATCH 164/866] Reformat code and give proper name to comic urlconf --- dynamicweb/urls.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dynamicweb/urls.py b/dynamicweb/urls.py index 09aa4fa8..50bc10ec 100644 --- a/dynamicweb/urls.py +++ b/dynamicweb/urls.py @@ -62,10 +62,8 @@ urlpatterns += i18n_patterns( ), name='blog_list_view' ), url(r'^comic/$', - ungleich_views.PostListViewUngleich.as_view( - category='comic' - ), - name='blog_list_view'), + ungleich_views.PostListViewUngleich.as_view(category='comic'), + name='comic_post_list_view'), url(r'^cms/', include('cms.urls')), url(r'^$', RedirectView.as_view(url='/cms') if REDIRECT_TO_CMS else LandingView.as_view()), From d15a4da84061798ca824dd271dad704459e8d159 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Tue, 17 Apr 2018 18:03:10 +0200 Subject: [PATCH 165/866] Check if child plugin instances exist before looping over them --- datacenterlight/cms_plugins.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 2ad07249..6533adc7 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -92,13 +92,14 @@ class DCLCalculatorPlugin(CMSPluginBase): context['children_to_side'] = [] context['children_to_content'] = [] pricing_plugin_model = None - for child in instance.child_plugin_instances: - if child.__class__.__name__ == 'DCLCustomPricingModel': - # The second clause is just to make sure we pick up the most - # recent CustomPricing, if more than one is present - if (pricing_plugin_model is None or child.pricing_id > - pricing_plugin_model.model.pricing_id): - pricing_plugin_model = child + if instance.child_plugin_instances: + for child in instance.child_plugin_instances: + if child.__class__.__name__ == 'DCLCustomPricingModel': + # The second clause is just to make sure we pick up the + # most recent CustomPricing, if more than one is present + if (pricing_plugin_model is None or child.pricing_id > + pricing_plugin_model.model.pricing_id): + pricing_plugin_model = child if pricing_plugin_model: context['vm_pricing'] = VMPricing.get_vm_pricing_by_name( From b3d36c1be3bf9485d8bedb89f0faf20a5ee6ded4 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Tue, 17 Apr 2018 18:12:45 +0200 Subject: [PATCH 166/866] Reformat code --- ungleich/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ungleich/views.py b/ungleich/views.py index c1d7445d..af7cb304 100644 --- a/ungleich/views.py +++ b/ungleich/views.py @@ -59,8 +59,7 @@ class PostListViewUngleich(PostListView): queryset = (self.model .objects .filter(publish=True) - .translated(language) - ) + .translated(language)) setattr(self.request, get_setting('CURRENT_NAMESPACE'), self.config) return queryset From e4e7d93275c2306154704152e9ce1b06c3d5c282 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 17 Apr 2018 20:50:41 +0200 Subject: [PATCH 167/866] Fix flake8 errors --- datacenterlight/admin.py | 1 + utils/hosting_utils.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/datacenterlight/admin.py b/datacenterlight/admin.py index 3e5927e8..28adf28b 100644 --- a/datacenterlight/admin.py +++ b/datacenterlight/admin.py @@ -7,5 +7,6 @@ from .models import VMPricing class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin): list_display = ('name', 'domain') + admin.site.register(CMSIntegration, CMSIntegrationAdmin) admin.site.register(VMPricing) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 87b69534..c267cc0b 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -74,9 +74,9 @@ def get_vm_price(cpu, memory, disk_size, hdd_size=0, pricing_name='default'): ) return None price = ((decimal.Decimal(cpu) * pricing.cores_unit_price) + - (decimal.Decimal(memory) * pricing.ram_unit_price) + - (decimal.Decimal(disk_size) * pricing.ssd_unit_price) + - (decimal.Decimal(hdd_size) * pricing.hdd_unit_price)) + (decimal.Decimal(memory) * pricing.ram_unit_price) + + (decimal.Decimal(disk_size) * pricing.ssd_unit_price) + + (decimal.Decimal(hdd_size) * pricing.hdd_unit_price)) cents = decimal.Decimal('.01') price = price.quantize(cents, decimal.ROUND_HALF_UP) return float(price) From a50fa77c8a9f7598b7a12e7741ad9b02a8236b13 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 17 Apr 2018 21:36:08 +0200 Subject: [PATCH 168/866] Update get_vm_price_with_vat: Return vat_percentage also --- utils/hosting_utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index c267cc0b..04ed658a 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -93,7 +93,8 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, :param ssd_size: Disk space of the VM (SSD) :param hdd_size: The HDD size :param pricing_name: The pricing name to be used - :return: The a tuple containing the price of the VM and the VAT + :return: The a tuple containing the price of the VM, the VAT and the + VAT percentage """ try: pricing = VMPricing.objects.get(name=pricing_name) @@ -112,10 +113,12 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, (decimal.Decimal(hdd_size) * pricing.hdd_unit_price)) if pricing.vat_inclusive: vat = decimal.Decimal(0) + vat_percent = decimal.Decimal(0) else: vat = price * pricing.vat_percentage * decimal.Decimal(0.01) + vat_percent = pricing.vat_percentage cents = decimal.Decimal('.01') price = price.quantize(cents, decimal.ROUND_HALF_UP) vat = vat.quantize(cents, decimal.ROUND_HALF_UP) - return float(price), float(vat) + return float(price), float(vat), float(vat_percent) From c2513dc7c3527afc4f94cb577062a47c9a906d74 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 17 Apr 2018 21:38:28 +0200 Subject: [PATCH 169/866] Show vat_percent and subtotal for vat exclusive case --- datacenterlight/templates/datacenterlight/order_detail.html | 6 +++++- datacenterlight/views.py | 3 ++- hosting/views.py | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index f26bc450..543f3934 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -67,7 +67,11 @@

{% if vm.vat > 0 %}

- {% trans "VAT" %}: + {% trans "Subtotal" %}: + {{vm.price|floatformat:2|intcomma}} CHF +

+

+ {% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%): {{vm.vat|floatformat:2|intcomma}} CHF

{% endif %} diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 87a0e660..cccd4277 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -158,7 +158,7 @@ class IndexView(CreateView): ) return HttpResponseRedirect(referer_url + "#order_form") - price, vat = get_vm_price_with_vat( + price, vat, vat_percent = get_vm_price_with_vat( cpu=cores, memory=memory, ssd_size=storage, @@ -170,6 +170,7 @@ class IndexView(CreateView): 'disk_size': storage, 'price': price, 'vat': vat, + 'vat_percent': vat_percent, 'total_price': price + vat, 'pricing_name': vm_pricing_name } diff --git a/hosting/views.py b/hosting/views.py index 88593969..a7aeca1e 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -749,7 +749,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): context['vm'] = vm_detail.__dict__ context['vm']['name'] = '{}-{}'.format( context['vm']['configuration'], context['vm']['vm_id']) - price, vat = get_vm_price_with_vat( + price, vat, vat_percent = get_vm_price_with_vat( cpu=context['vm']['cores'], ssd_size=context['vm']['disk_size'], memory=context['vm']['memory'], @@ -766,7 +766,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): ) vm = manager.get_vm(obj.vm_id) context['vm'] = VirtualMachineSerializer(vm).data - price, vat = get_vm_price_with_vat( + price, vat, vat_percent = get_vm_price_with_vat( cpu=context['vm']['cores'], ssd_size=context['vm']['disk_size'], memory=context['vm']['memory'], From a454cd252280c383da0b475433773340c1b73abc Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 17 Apr 2018 21:38:53 +0200 Subject: [PATCH 170/866] Update datacenterlight's django.po --- datacenterlight/locale/de/LC_MESSAGES/django.po | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index 3dc4650b..b937805c 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: 2018-04-15 23:37+0000\n" +"POT-Creation-Date: 2018-04-17 19:26+0000\n" "PO-Revision-Date: 2018-03-30 23:22+0000\n" "Last-Translator: b'Anonymous User '\n" "Language-Team: LANGUAGE \n" @@ -97,6 +97,7 @@ msgstr "Deine E-Mail-Adresse" msgid "Password" msgstr "Passwort" +#, python-format msgid "" "You can reset your password here." @@ -379,15 +380,20 @@ msgstr "Bestellungsübersicht" msgid "Product" msgstr "Produkt" +#, fuzzy +msgid "Subtotal" +msgstr "Zwischensumme" + msgid "VAT" msgstr "Mehrwertsteuer" +#, python-format msgid "" "By clicking \"Place order\" this plan will charge your credit card account " "with the fee of %(vm_total_price)s CHF/month" msgstr "" -"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_total_price)s CHF " -"pro Monat belastet" +"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit " +"%(vm_total_price)s CHF pro Monat belastet" msgid "Place order" msgstr "Bestellen" From 4c21110c00807b5c44849768c4bcaac174f13b50 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 17 Apr 2018 21:47:02 +0200 Subject: [PATCH 171/866] Remove fuzzy and python-format --- datacenterlight/locale/de/LC_MESSAGES/django.po | 2 -- 1 file changed, 2 deletions(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index b937805c..50dbfbe8 100644 --- a/datacenterlight/locale/de/LC_MESSAGES/django.po +++ b/datacenterlight/locale/de/LC_MESSAGES/django.po @@ -380,14 +380,12 @@ msgstr "Bestellungsübersicht" msgid "Product" msgstr "Produkt" -#, fuzzy msgid "Subtotal" msgstr "Zwischensumme" msgid "VAT" msgstr "Mehrwertsteuer" -#, python-format msgid "" "By clicking \"Place order\" this plan will charge your credit card account " "with the fee of %(vm_total_price)s CHF/month" From 2ac1ac7d9728db5ec30e434aae0495c19385c3a2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 17 Apr 2018 22:20:36 +0200 Subject: [PATCH 172/866] Add subtotal and VAT to hosting order detail too --- hosting/templates/hosting/order_detail.html | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 099aaab8..45b68cae 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -129,13 +129,17 @@

{% if vm.vat > 0 %}

- {% trans "VAT" %}: + {% trans "Subtotal" %}: + {{vm.price|floatformat:2|intcomma}} CHF +

+

+ {% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%): {{vm.vat|floatformat:2|intcomma}} CHF

{% endif %}

{% trans "Total" %} - {{vm.price|intcomma}} CHF + {% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF

From 36c0b9a0a67a474dcb395dafdc75cd3329312e74 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 17 Apr 2018 22:23:46 +0200 Subject: [PATCH 173/866] Differentiate price and total_price in hosting order_detail --- hosting/views.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index a7aeca1e..ec36836a 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -757,7 +757,9 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): if obj.vm_pricing else 'default') ) context['vm']['vat'] = vat - context['vm']['price'] = price + vat + context['vm']['price'] = price + context['vm']['vat_percent'] = vat_percent + context['vm']['total_price'] = price + vat context['subscription_end_date'] = vm_detail.end_date() except VMDetail.DoesNotExist: try: @@ -774,7 +776,9 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): if obj.vm_pricing else 'default') ) context['vm']['vat'] = vat - context['vm']['price'] = price + vat + context['vm']['price'] = price + context['vm']['vat_percent'] = vat_percent + context['vm']['total_price'] = price + vat except WrongIdError: messages.error( self.request, From 4e3211b62fb08ae6835f71a2fba6aae015bb83ed Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 18 Apr 2018 21:37:12 +0200 Subject: [PATCH 174/866] Make total and subtotal texts bold --- datacenterlight/templates/datacenterlight/order_detail.html | 4 ++-- hosting/templates/hosting/order_detail.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 543f3934..95bfa3c6 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -67,7 +67,7 @@

{% if vm.vat > 0 %}

- {% trans "Subtotal" %}: + {% trans "Subtotal" %}: {{vm.price|floatformat:2|intcomma}} CHF

@@ -76,7 +76,7 @@

{% endif %}

- {% trans "Total" %} + {% trans "Total" %} {{vm.total_price|floatformat:2|intcomma}} CHF

diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 45b68cae..2568aafc 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -129,7 +129,7 @@

{% if vm.vat > 0 %}

- {% trans "Subtotal" %}: + {% trans "Subtotal" %}: {{vm.price|floatformat:2|intcomma}} CHF

@@ -138,7 +138,7 @@

{% endif %}

- {% trans "Total" %} + {% trans "Total" %} {% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF

From bd875ffe7deb21c8d99eaa0501a43ef75285141d Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 18 Apr 2018 23:50:52 +0200 Subject: [PATCH 175/866] Create hostingorder outside celery task --- datacenterlight/tasks.py | 69 +++++++++++++++------------------------- datacenterlight/tests.py | 11 +++++-- datacenterlight/views.py | 52 +++++++++++++++++++++++++++--- hosting/views.py | 48 ++++++++++++++++++++++++++-- 4 files changed, 126 insertions(+), 54 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 3db6eb54..67b00c5d 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -49,24 +49,11 @@ def retry_task(task, exception=None): @app.task(bind=True, max_retries=settings.CELERY_MAX_RETRIES) -def create_vm_task(self, vm_template_id, user, specs, template, - stripe_customer_id, billing_address_data, - stripe_subscription_id, cc_details): +def create_vm_task(self, vm_template_id, user, specs, template, order_id): logger.debug( "Running create_vm_task on {}".format(current_task.request.hostname)) vm_id = None try: - final_price = specs.get('price') - billing_address = BillingAddress( - 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'] - ) - billing_address.save() - customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() - if 'pass' in user: on_user = user.get('email') on_pass = user.get('pass') @@ -94,33 +81,26 @@ def create_vm_task(self, vm_template_id, user, specs, template, if vm_id is None: raise Exception("Could not create VM") - # Create a Hosting Order - order = HostingOrder.create( - price=final_price, - vm_id=vm_id, - customer=customer, - billing_address=billing_address - ) - - # Create a Hosting 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 subscription - order.set_subscription_id(stripe_subscription_id, cc_details) - - # If the Stripe payment succeeds, set order status approved - order.set_approved() + # Update HostingOrder with the created vm_id + hosting_order = HostingOrder.objects.filter(id=order_id).first() + error_msg = None + if hosting_order: + logger.debug( + "Updating hosting_order {} with vm_id={}".format( + hosting_order.id, vm_id + ) + ) + hosting_order.vm_id = vm_id + hosting_order.save() + else: + error_msg = ( + "HostingOrder with id {order_id} not found. This means that " + "the hosting order was not created and/or it is/was not " + "associated with VM with id {vm_id}".format( + order_id=order_id, vm_id=vm_id + ) + ) + logger.error(error_msg) vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data @@ -134,8 +114,11 @@ def create_vm_task(self, vm_template_id, user, specs, template, 'template': template.get('name'), 'vm_name': vm.get('name'), 'vm_id': vm['vm_id'], - 'order_id': order.id + 'order_id': order_id } + + if error_msg: + context['errors'] = error_msg email_data = { 'subject': settings.DCL_TEXT + " Order from %s" % context['email'], 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, @@ -159,7 +142,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, 'base_url': "{0}://{1}".format(user.get('request_scheme'), user.get('request_host')), 'order_url': reverse('hosting:orders', - kwargs={'pk': order.id}), + kwargs={'pk': order_id}), 'page_header': _( 'Your New VM %(vm_name)s at Data Center Light') % { 'vm_name': vm.get('name')}, diff --git a/datacenterlight/tests.py b/datacenterlight/tests.py index d1ce9785..25c0bf4c 100644 --- a/datacenterlight/tests.py +++ b/datacenterlight/tests.py @@ -122,10 +122,15 @@ class CeleryTaskTestCase(TestCase): msg = subscription_result.get('error') raise Exception("Creating subscription failed: {}".format(msg)) + order = HostingOrder.create( + price=specs['price'], + vm_id=0, + customer=customer, + billing_address=billing_address + ) + async_task = create_vm_task.delay( - vm_template_id, self.user, specs, template_data, - stripe_customer.id, billing_address_data, - stripe_subscription_obj.id, card_details_dict + vm_template_id, self.user, specs, template_data, order.id ) new_vm_id = 0 res = None diff --git a/datacenterlight/views.py b/datacenterlight/views.py index af3b774c..a855b3cc 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -15,11 +15,14 @@ from django.views.generic import FormView, CreateView, DetailView from datacenterlight.tasks import create_vm_task from hosting.forms import HostingUserLoginForm -from hosting.models import HostingOrder +from hosting.models import HostingOrder, HostingBill from membership.models import CustomUser, StripeCustomer from opennebula_api.serializers import VMTemplateSerializer -from utils.forms import BillingAddressForm, BillingAddressFormSignup +from utils.forms import ( + BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm +) from utils.hosting_utils import get_vm_price +from utils.models import BillingAddress from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from .forms import ContactForm @@ -484,9 +487,48 @@ class OrderConfirmationView(DetailView): 'language': get_language(), } - create_vm_task.delay(vm_template_id, user, specs, template, - stripe_customer_id, billing_address_data, - stripe_subscription_obj.id, card_details_dict) + billing_address = BillingAddress( + 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'] + ) + billing_address.save() + + customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() + + # Create a Hosting Order with vm_id = 0, we shall set it later in + # celery task once the VM instance is up and running + order = HostingOrder.create( + price=specs['price'], + vm_id=0, + customer=customer, + billing_address=billing_address + ) + + # Create a Hosting 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 subscription + order.set_subscription_id( + stripe_subscription_obj.id, card_details_dict + ) + + # If the Stripe payment succeeds, set order status approved + order.set_approved() + + create_vm_task.delay(vm_template_id, user, specs, template, order.id) for session_var in ['specs', 'template', 'billing_address', 'billing_address_data', 'token', 'customer']: diff --git a/hosting/views.py b/hosting/views.py index 6e143760..c7c7655a 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -44,6 +44,7 @@ from utils.forms import ( ) from utils.hosting_utils import get_vm_price from utils.mailer import BaseEmail +from utils.models import BillingAddress from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from utils.views import ( @@ -882,9 +883,50 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): 'request_host': request.get_host(), 'language': get_language(), } - create_vm_task.delay(vm_template_id, user, specs, template, - stripe_customer_id, billing_address_data, - stripe_subscription_obj.id, card_details_dict) + + billing_address = BillingAddress( + 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'] + ) + billing_address.save() + + customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() + + # Create a Hosting Order with vm_id = 0, we shall set it later in + # celery task once the VM instance is up and running + order = HostingOrder.create( + price=specs['price'], + vm_id=0, + customer=customer, + billing_address=billing_address + ) + + # Create a Hosting 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 subscription + order.set_subscription_id( + stripe_subscription_obj.id, card_details_dict + ) + + # If the Stripe payment succeeds, set order status approved + order.set_approved() + + create_vm_task.delay(vm_template_id, user, specs, template, order.id) for session_var in ['specs', 'template', 'billing_address', 'billing_address_data', From 1a7412f8ff3bda668adaa0bc74d78e50b6c40c9a Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 19 Apr 2018 00:51:55 +0200 Subject: [PATCH 176/866] stripe_utils: Add set_subscription_metadata method --- utils/stripe_utils.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index 79bca243..3809e138 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -233,6 +233,12 @@ class StripeUtils(object): ) return subscription_result + @handleStripeError + def set_subscription_metadata(self, subscription_id, metadata): + subscription = stripe.Subscription.retrieve(subscription_id) + subscription.metadata = metadata + subscription.save() + @handleStripeError def unsubscribe_customer(self, subscription_id): """ From 8a659c153e85e9a1a4f43cc761617e7c79d2aa93 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 19 Apr 2018 00:52:38 +0200 Subject: [PATCH 177/866] Set VM_ID metadata to the created subscription --- datacenterlight/tasks.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 67b00c5d..8c63c84c 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -18,6 +18,7 @@ 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 +from utils.stripe_utils import StripeUtils logger = get_task_logger(__name__) @@ -102,6 +103,22 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): ) logger.error(error_msg) + stripe_utils = StripeUtils() + result = stripe_utils.set_subscription_metadata( + subscription_id=hosting_order.subscription_id, + metadata={"VM_ID": str(vm_id)} + ) + stripe_subscription_obj = result.get('response_object') + if stripe_subscription_obj is not None: + emsg = "Could not update subscription metadata for {sub}".format( + sub=hosting_order.subscription_id + ) + logger.error(emsg) + if error_msg: + error_msg += emsg + else: + error_msg = emsg + vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data context = { From 791f48513af985a617250c10788e571325ef1d4a Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 19 Apr 2018 01:07:59 +0200 Subject: [PATCH 178/866] Organize imports --- datacenterlight/tasks.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 8c63c84c..758778b6 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -10,14 +10,12 @@ from django.utils import translation from django.utils.translation import ugettext_lazy as _ from dynamicweb.celery import app -from hosting.models import HostingOrder, HostingBill -from membership.models import StripeCustomer, CustomUser +from hosting.models import HostingOrder +from membership.models import CustomUser from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer 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 from utils.stripe_utils import StripeUtils logger = get_task_logger(__name__) From a3a8227007ac517a08fe856bc4a038d9e016e4f7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 19 Apr 2018 01:08:52 +0200 Subject: [PATCH 179/866] Check if subscription metadata update response does not have errors --- datacenterlight/tasks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 758778b6..12fabc1e 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -106,8 +106,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): subscription_id=hosting_order.subscription_id, metadata={"VM_ID": str(vm_id)} ) - stripe_subscription_obj = result.get('response_object') - if stripe_subscription_obj is not None: + + if result.get('error') is not None: emsg = "Could not update subscription metadata for {sub}".format( sub=hosting_order.subscription_id ) From 80c3ac5346a449bc4d7f2863a818c5c8542250c0 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 19 Apr 2018 01:09:25 +0200 Subject: [PATCH 180/866] Improve create_vm_task test --- datacenterlight/tests.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/datacenterlight/tests.py b/datacenterlight/tests.py index 25c0bf4c..d6cd6adf 100644 --- a/datacenterlight/tests.py +++ b/datacenterlight/tests.py @@ -12,9 +12,11 @@ from unittest import skipIf from datacenterlight.models import VMTemplate from datacenterlight.tasks import create_vm_task +from hosting.models import HostingOrder 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 @@ -81,11 +83,14 @@ class CeleryTaskTestCase(TestCase): stripe_customer = StripeCustomer.get_or_create( email=self.customer_email, - token=self.token) + token=self.token + ) card_details = self.stripe_utils.get_card_details( stripe_customer.stripe_id, - self.token) - card_details_dict = card_details.get('response_object') + self.token + ) + card_details_dict = card_details.get('error') + self.assertEquals(card_details_dict, None) billing_address_data = {'cardholder_name': self.customer_name, 'postal_code': '1231', 'country': 'CH', @@ -122,10 +127,19 @@ class CeleryTaskTestCase(TestCase): msg = subscription_result.get('error') raise Exception("Creating subscription failed: {}".format(msg)) + billing_address = BillingAddress( + 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'] + ) + billing_address.save() + order = HostingOrder.create( price=specs['price'], vm_id=0, - customer=customer, + customer=stripe_customer, billing_address=billing_address ) From fae1c7fbeb74a634fbc0618228b190f4535277b4 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Thu, 19 Apr 2018 07:26:34 +0200 Subject: [PATCH 181/866] Update hosting_order in a pythonic way --- datacenterlight/tasks.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 12fabc1e..815d627d 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -83,20 +83,21 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): # Update HostingOrder with the created vm_id hosting_order = HostingOrder.objects.filter(id=order_id).first() error_msg = None - if hosting_order: + + try: + hosting_order.vm_id = vm_id + hosting_order.save() logger.debug( - "Updating hosting_order {} with vm_id={}".format( + "Updated hosting_order {} with vm_id={}".format( hosting_order.id, vm_id ) ) - hosting_order.vm_id = vm_id - hosting_order.save() - else: + except Exception as ex: error_msg = ( "HostingOrder with id {order_id} not found. This means that " "the hosting order was not created and/or it is/was not " - "associated with VM with id {vm_id}".format( - order_id=order_id, vm_id=vm_id + "associated with VM with id {vm_id}. Details {details}".format( + order_id=order_id, vm_id=vm_id, details=str(ex) ) ) logger.error(error_msg) From a7f1f14dc779173fc9f4dc9411748b886170c69a Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Thu, 19 Apr 2018 09:12:54 +0200 Subject: [PATCH 182/866] Raise Http404 when we do not have a post for a given language --- digitalglarus/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/digitalglarus/views.py b/digitalglarus/views.py index 32d8e1f5..a450b413 100644 --- a/digitalglarus/views.py +++ b/digitalglarus/views.py @@ -2,7 +2,7 @@ import logging from django.conf import settings from django.shortcuts import render -from django.http import HttpResponseRedirect +from django.http import HttpResponseRedirect, Http404 from django.core.urlresolvers import reverse_lazy, reverse from django.utils.translation import ugettext_lazy as _ from django.views.generic import TemplateView, UpdateView @@ -846,6 +846,8 @@ def blog_detail(request, slug): # post = Post.objects.filter_by_language(get_language()).filter(slug=slug).first() post = Post.objects.translated(get_language(), slug=slug).first() + if post is None: + raise Http404() context = { 'post': post, } From 0b97ae69f5d3d33f5a136b6b19cc71c1681dc3b6 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Thu, 19 Apr 2018 09:23:22 +0200 Subject: [PATCH 183/866] Cleanup and reformat some code --- digitalglarus/views.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/digitalglarus/views.py b/digitalglarus/views.py index a450b413..f99577c5 100644 --- a/digitalglarus/views.py +++ b/digitalglarus/views.py @@ -834,8 +834,9 @@ class ContactView(FormView): def blog(request): tags = ["digitalglarus"] - posts = Post.objects.filter(tags__name__in=tags, publish=True).translated(get_language()) - # posts = Post.objects.filter_by_language(get_language()).filter(tags__name__in=tags, publish=True) + posts = (Post.objects + .filter(tags__name__in=tags, publish=True) + .translated(get_language())) context = { 'post_list': posts, } @@ -843,8 +844,6 @@ def blog(request): def blog_detail(request, slug): - # post = Post.objects.filter_by_language(get_language()).filter(slug=slug).first() - post = Post.objects.translated(get_language(), slug=slug).first() if post is None: raise Http404() From da13903ba2ebb1c83b784cf4fb14e5eb1b4170c0 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 20 Apr 2018 16:42:50 +0530 Subject: [PATCH 184/866] remove padding --- datacenterlight/static/datacenterlight/css/header-slider.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/datacenterlight/static/datacenterlight/css/header-slider.css b/datacenterlight/static/datacenterlight/css/header-slider.css index e21e2b49..d01f02a7 100644 --- a/datacenterlight/static/datacenterlight/css/header-slider.css +++ b/datacenterlight/static/datacenterlight/css/header-slider.css @@ -120,6 +120,11 @@ .header_slider .intro-cap { font-size: 3.25em; } + + .header_slider > .carousel .item .container { + padding-left: 0; + padding-right: 0; + } } .header_slider .intro_lead { From a25bcc807f6b09ef295edd677d52a0a60aa453a8 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 20 Apr 2018 17:04:02 +0530 Subject: [PATCH 185/866] change header slider to container fluid --- .../static/datacenterlight/css/header-slider.css | 11 +++-------- .../_header_with_background_video_slider_item.html | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/datacenterlight/static/datacenterlight/css/header-slider.css b/datacenterlight/static/datacenterlight/css/header-slider.css index d01f02a7..ea01edf7 100644 --- a/datacenterlight/static/datacenterlight/css/header-slider.css +++ b/datacenterlight/static/datacenterlight/css/header-slider.css @@ -55,7 +55,7 @@ flex: 1; } -.header_slider > .carousel .item .container { +.header_slider > .carousel .item .container-fluid { overflow: auto; padding: 50px 20px 60px; height: 100%; @@ -104,9 +104,9 @@ .header_slider .carousel-control .fa { font-size: 4em; } - .header_slider > .carousel .item .container { + .header_slider > .carousel .item .container-fluid { overflow: auto; - padding: 75px 50px; + padding: 75px; } .header_slider .btn-trans { padding: 8px 15px; @@ -120,11 +120,6 @@ .header_slider .intro-cap { font-size: 3.25em; } - - .header_slider > .carousel .item .container { - padding-left: 0; - padding-right: 0; - } } .header_slider .intro_lead { diff --git a/ungleich_page/templates/ungleich_page/ungleich/_header_with_background_video_slider_item.html b/ungleich_page/templates/ungleich_page/ungleich/_header_with_background_video_slider_item.html index f1edba16..4761cdc5 100644 --- a/ungleich_page/templates/ungleich_page/ungleich/_header_with_background_video_slider_item.html +++ b/ungleich_page/templates/ungleich_page/ungleich/_header_with_background_video_slider_item.html @@ -13,7 +13,7 @@ {% endif %} -
+
{% if instance.heading %}
{{ instance.heading }}
{% endif %} From 1e97d0ba380a382a92f6e76a6e2014fb84b36141 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 20 Apr 2018 17:51:46 +0530 Subject: [PATCH 186/866] Update cms_plugins.py --- datacenterlight/cms_plugins.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 6533adc7..19dc0b39 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -89,10 +89,12 @@ class DCLCalculatorPlugin(CMSPluginBase): context, instance, placeholder ) context['templates'] = VMTemplate.objects.all() - context['children_to_side'] = [] context['children_to_content'] = [] pricing_plugin_model = None - if instance.child_plugin_instances: + if instance.child_plugin_instances is not None: + context['children_to_content'].extend( + instance.child_plugin_instances + ) for child in instance.child_plugin_instances: if child.__class__.__name__ == 'DCLCustomPricingModel': # The second clause is just to make sure we pick up the @@ -108,10 +110,6 @@ class DCLCalculatorPlugin(CMSPluginBase): else: context['vm_pricing'] = VMPricing.get_default_pricing() - if instance.child_plugin_instances is not None: - context['children_to_content'].extend( - instance.child_plugin_instances - ) return context From 8f6260b063269d127dbfff2eda68ad930672c77d Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 20 Apr 2018 17:54:23 +0530 Subject: [PATCH 187/866] Update _calculator_form.html --- .../includes/_calculator_form.html | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html index 05201b11..e3fe8676 100644 --- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html +++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html @@ -1,14 +1,16 @@ {% load staticfiles i18n%} + {% if vm_pricing %} - + {% endif %} +
{% csrf_token %}
From 3debf3411858df959c96362ecf9a073649464583 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 20 Apr 2018 20:25:24 +0530 Subject: [PATCH 188/866] remove vm creation to util function --- datacenterlight/utils.py | 59 ++++++++++++++++++++++++++++++ datacenterlight/views.py | 64 ++++++--------------------------- hosting/views.py | 77 +++++++--------------------------------- 3 files changed, 82 insertions(+), 118 deletions(-) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 2efade8e..6c7a25bd 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -1,5 +1,10 @@ from django.contrib.sites.models import Site +from datacenterlight.tasks import create_vm_task +from hosting.models import HostingOrder, HostingBill +from membership.models import StripeCustomer +from utils.forms import UserBillingAddressForm +from utils.models import BillingAddress from .cms_models import CMSIntegration @@ -12,3 +17,57 @@ def get_cms_integration(name): except CMSIntegration.DoesNotExist: cms_integration = CMSIntegration.objects.get(name=name, domain=None) return cms_integration + + +def create_vm(billing_address_data, stripe_customer_id, specs, + stripe_subscription_obj, card_details_dict, request, + vm_template_id, template, user): + billing_address = BillingAddress( + 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'] + ) + billing_address.save() + + customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() + + # Create a Hosting Order with vm_id = 0, we shall set it later in + # celery task once the VM instance is up and running + order = HostingOrder.create( + price=specs['price'], + vm_id=0, + customer=customer, + billing_address=billing_address + ) + + # Create a Hosting 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 subscription + order.set_subscription_id( + stripe_subscription_obj.id, card_details_dict + ) + + # If the Stripe payment succeeds, set order status approved + order.set_approved() + + create_vm_task.delay(vm_template_id, user, specs, template, order.id) + + for session_var in ['specs', 'template', 'billing_address', + 'billing_address_data', + 'token', 'customer']: + if session_var in request.session: + del request.session[session_var] diff --git a/datacenterlight/views.py b/datacenterlight/views.py index a855b3cc..203846fb 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -7,7 +7,7 @@ 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, HttpResponse +from django.http import HttpResponseRedirect, HttpResponse, JsonResponse from django.shortcuts import render from django.utils.translation import get_language, ugettext_lazy as _ from django.views.decorators.cache import cache_control @@ -27,7 +27,7 @@ from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from .forms import ContactForm from .models import VMTemplate -from .utils import get_cms_integration +from .utils import get_cms_integration, create_vm logger = logging.getLogger(__name__) @@ -390,8 +390,8 @@ class OrderConfirmationView(DetailView): ' On close of this popup, you will be redirected back to' ' the payment page.')) } - return HttpResponse(json.dumps(response), - content_type="application/json") + return JsonResponse(response) + card_details_dict = card_details.get('response_object') cpu = specs.get('cpu') memory = specs.get('memory') @@ -431,8 +431,7 @@ class OrderConfirmationView(DetailView): ' On close of this popup, you will be redirected back to' ' the payment page.')) } - return HttpResponse(json.dumps(response), - content_type="application/json") + return JsonResponse(response) # Create user if the user is not logged in and if he is not already # registered @@ -487,53 +486,11 @@ class OrderConfirmationView(DetailView): 'language': get_language(), } - billing_address = BillingAddress( - 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'] + create_vm( + billing_address_data, stripe_customer_id, specs, + stripe_subscription_obj, card_details_dict, request, + vm_template_id, template, user ) - billing_address.save() - - customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() - - # Create a Hosting Order with vm_id = 0, we shall set it later in - # celery task once the VM instance is up and running - order = HostingOrder.create( - price=specs['price'], - vm_id=0, - customer=customer, - billing_address=billing_address - ) - - # Create a Hosting 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 subscription - order.set_subscription_id( - stripe_subscription_obj.id, card_details_dict - ) - - # If the Stripe payment succeeds, set order status approved - order.set_approved() - - create_vm_task.delay(vm_template_id, user, specs, template, order.id) - 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, @@ -549,5 +506,4 @@ class OrderConfirmationView(DetailView): ' it is ready.')) } - return HttpResponse(json.dumps(response), - content_type="application/json") + return JsonResponse(response) diff --git a/hosting/views.py b/hosting/views.py index c7c7655a..5d5051e4 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1,4 +1,3 @@ -import json import logging import uuid from datetime import datetime @@ -12,7 +11,9 @@ from django.contrib.auth.tokens import default_token_generator from django.core.exceptions import ValidationError from django.core.files.base import ContentFile from django.core.urlresolvers import reverse_lazy, reverse -from django.http import Http404, HttpResponseRedirect, HttpResponse +from django.http import ( + Http404, HttpResponseRedirect, HttpResponse, JsonResponse +) from django.shortcuts import redirect, render from django.utils.http import urlsafe_base64_decode from django.utils.safestring import mark_safe @@ -31,7 +32,7 @@ from stored_messages.models import Message from stored_messages.settings import stored_messages_settings from datacenterlight.models import VMTemplate -from datacenterlight.tasks import create_vm_task +from datacenterlight.utils import create_vm from membership.models import CustomUser, StripeCustomer from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import ( @@ -44,7 +45,6 @@ from utils.forms import ( ) from utils.hosting_utils import get_vm_price from utils.mailer import BaseEmail -from utils.models import BillingAddress from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from utils.views import ( @@ -873,8 +873,8 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): ' On close of this popup, you will be redirected back to' ' the payment page.')) } - return HttpResponse(json.dumps(response), - content_type="application/json") + return JsonResponse(response) + user = { 'name': self.request.user.name, 'email': self.request.user.email, @@ -884,55 +884,11 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): 'language': get_language(), } - billing_address = BillingAddress( - 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'] + create_vm( + billing_address_data, stripe_customer_id, specs, + stripe_subscription_obj, card_details_dict, request, + vm_template_id, template, user ) - billing_address.save() - - customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() - - # Create a Hosting Order with vm_id = 0, we shall set it later in - # celery task once the VM instance is up and running - order = HostingOrder.create( - price=specs['price'], - vm_id=0, - customer=customer, - billing_address=billing_address - ) - - # Create a Hosting 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 subscription - order.set_subscription_id( - stripe_subscription_obj.id, card_details_dict - ) - - # If the Stripe payment succeeds, set order status approved - order.set_approved() - - create_vm_task.delay(vm_template_id, user, specs, template, order.id) - - 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, @@ -944,8 +900,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): ' it is ready.')) } - return HttpResponse(json.dumps(response), - content_type="application/json") + return JsonResponse(response) class OrdersHostingListView(LoginRequiredMixin, ListView): @@ -1128,10 +1083,7 @@ class VirtualMachineView(LoginRequiredMixin, View): for m in storage: pass storage.used = True - return HttpResponse( - json.dumps({'text': ugettext('Terminated')}), - content_type="application/json" - ) + return JsonResponse({'text': ugettext('Terminated')}) else: return redirect(reverse('hosting:virtual_machines')) elif self.request.is_ajax(): @@ -1262,10 +1214,7 @@ class VirtualMachineView(LoginRequiredMixin, View): ["%s=%s" % (k, v) for (k, v) in admin_email_body.items()]), } send_plain_email_task.delay(email_to_admin_data) - return HttpResponse( - json.dumps(response), - content_type="application/json" - ) + return JsonResponse(response) class HostingBillListView(PermissionRequiredMixin, LoginRequiredMixin, From 736253fedacd424eb709fc0c4e0d78a8435d2749 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 20 Apr 2018 20:27:26 +0530 Subject: [PATCH 189/866] refactor imports --- datacenterlight/views.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 203846fb..49e4d15a 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -1,4 +1,3 @@ -import json import logging from django import forms @@ -7,22 +6,18 @@ 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, HttpResponse, JsonResponse +from django.http import HttpResponseRedirect, JsonResponse from django.shortcuts import render 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, DetailView -from datacenterlight.tasks import create_vm_task from hosting.forms import HostingUserLoginForm -from hosting.models import HostingOrder, HostingBill +from hosting.models import HostingOrder from membership.models import CustomUser, StripeCustomer from opennebula_api.serializers import VMTemplateSerializer -from utils.forms import ( - BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm -) +from utils.forms import BillingAddressForm, BillingAddressFormSignup from utils.hosting_utils import get_vm_price -from utils.models import BillingAddress from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from .forms import ContactForm From 564f7a5be8401a51c4606a4a0cbf65fa4e5777fb Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 20 Apr 2018 20:36:28 +0530 Subject: [PATCH 190/866] merge migrations --- datacenterlight/migrations/0020_merge.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 datacenterlight/migrations/0020_merge.py diff --git a/datacenterlight/migrations/0020_merge.py b/datacenterlight/migrations/0020_merge.py new file mode 100644 index 00000000..6bbe0086 --- /dev/null +++ b/datacenterlight/migrations/0020_merge.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-04-20 15:04 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0019_auto_20180415_2236'), + ('datacenterlight', '0019_cmsfaviconextension'), + ] + + operations = [ + ] From 45bd853a2094a6cbbc61d2e9897bb58cbc6e112a Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 20 Apr 2018 21:09:16 +0530 Subject: [PATCH 191/866] Update Changelog --- Changelog | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Changelog b/Changelog index 46b2534b..471f0720 100644 --- a/Changelog +++ b/Changelog @@ -1,7 +1,10 @@ -next: +1.7: 2018-04-20 * bgfix: [all] Make /blog available on all domains * #4367: [dcl] email logo resolution fix * #4376: [cms] dcl promo section plugin link color changed to brighter shade + * #4379: [dcl] pricing without VAT + * bgfix: [blog] fix top menu items to show only one item + * #4297: [cms] favicon as a page attribute for dcl template 1.6.5: 2018-04-08 * #4396: [ungleich] add favicon to ungleich blog * #4327: [dcl] fix navbar logo repeat From 3b6c2b9d4e578b60c8e9264ce591034d3737569c Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Sat, 21 Apr 2018 22:27:43 +0530 Subject: [PATCH 192/866] fix vm_id default --- hosting/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/models.py b/hosting/models.py index 09c6eb2a..de4d3aec 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -72,7 +72,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model): return self.ORDER_APPROVED_STATUS if self.approved else self.ORDER_DECLINED_STATUS @classmethod - def create(cls, price=None, vm_id=None, customer=None, + def create(cls, price=None, vm_id=0, customer=None, billing_address=None, vm_pricing=None): instance = cls.objects.create( price=price, From 67a6c8f2c29616982b5142ca82a4efaa8f601d5d Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Sat, 21 Apr 2018 22:59:00 +0530 Subject: [PATCH 193/866] Update Changelog --- Changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog b/Changelog index 471f0720..cc53af19 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,6 @@ +1.7.1: 2018-04-21 + * #4481: [digitalglarus] Make /blog available on all domains + * #4370: [comic] new url /comic to show only comic blogs 1.7: 2018-04-20 * bgfix: [all] Make /blog available on all domains * #4367: [dcl] email logo resolution fix From 91f1c1ef0670be10e4df36e1671aa5e2408a19c3 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Sat, 21 Apr 2018 23:05:23 +0530 Subject: [PATCH 194/866] Update Changelog --- Changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog b/Changelog index cc53af19..86c21db0 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,5 @@ 1.7.1: 2018-04-21 - * #4481: [digitalglarus] Make /blog available on all domains + * #4481: [blog] fix de blog pages 500 error * #4370: [comic] new url /comic to show only comic blogs 1.7: 2018-04-20 * bgfix: [all] Make /blog available on all domains From 3bf064a017d400f50ec72a8d652f4d72791eb812 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 25 Apr 2018 14:52:25 +0530 Subject: [PATCH 195/866] add calculator placeholder to cms_integration --- datacenterlight/cms_models.py | 6 +- datacenterlight/cms_plugins.py | 67 ++++++++-------- ...1_cmsintegration_calculator_placeholder.py | 28 +++++++ .../datacenterlight/css/landing-page.css | 4 +- .../datacenterlight/cms/calculator.html | 17 +--- .../datacenterlight/cms/section.html | 15 +++- .../includes/_calculator_form.html | 4 +- dynamicweb/settings/base.py | 12 +++ .../static/hosting/css/price_calculator.css | 6 +- hosting/static/hosting/js/initial.js | 80 +++++++++++++++++++ hosting/templates/hosting/base_short.html | 9 ++- .../hosting/create_virtual_machine.html | 14 ++-- hosting/views.py | 7 +- 13 files changed, 195 insertions(+), 74 deletions(-) create mode 100644 datacenterlight/migrations/0021_cmsintegration_calculator_placeholder.py diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index dd6a165f..5a8d7ac8 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -26,6 +26,10 @@ class CMSIntegration(models.Model): navbar_placeholder = PlaceholderField( 'datacenterlight_navbar', related_name='dcl-navbar-placeholder+' ) + calculator_placeholder = PlaceholderField( + 'datacenterlight_calculator', + related_name='dcl-calculator-placeholder+' + ) domain = models.ForeignKey(Site, null=True, blank=True) class Meta: @@ -288,7 +292,7 @@ class DCLSectionPromoPluginModel(CMSPlugin): return extra_classes -class DCLCustomPricingModel(CMSPlugin): +class DCLCalculatorPluginModel(CMSPlugin): pricing = models.ForeignKey( VMPricing, related_name="dcl_custom_pricing_vm_pricing", diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 19dc0b39..0096faa5 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -1,3 +1,4 @@ +from cms.models.pluginmodel import CMSPlugin from cms.plugin_base import CMSPluginBase from cms.plugin_pool import plugin_pool @@ -6,7 +7,7 @@ from .cms_models import ( DCLFooterPluginModel, DCLLinkPluginModel, DCLNavbarDropdownPluginModel, DCLSectionIconPluginModel, DCLSectionImagePluginModel, DCLSectionPluginModel, DCLNavbarPluginModel, - DCLSectionPromoPluginModel, DCLCustomPricingModel + DCLSectionPromoPluginModel, DCLCalculatorPluginModel ) from .models import VMTemplate, VMPricing @@ -21,7 +22,7 @@ class DCLSectionPlugin(CMSPluginBase): allow_children = True child_classes = [ 'DCLSectionIconPlugin', 'DCLSectionImagePlugin', - 'DCLSectionPromoPlugin', 'UngleichHTMLPlugin' + 'DCLSectionPromoPlugin', 'UngleichHTMLPlugin', 'DCLCalculatorPlugin' ] def render(self, context, instance, placeholder): @@ -30,14 +31,18 @@ class DCLSectionPlugin(CMSPluginBase): ) context['children_to_side'] = [] context['children_to_content'] = [] + context['children_calculator'] = [] if instance.child_plugin_instances is not None: right_children = [ 'DCLSectionImagePluginModel', - 'DCLSectionIconPluginModel' + 'DCLSectionIconPluginModel', ] for child in instance.child_plugin_instances: + print(child.__dict__) if child.__class__.__name__ in right_children: context['children_to_side'].append(child) + elif child.__class__.__name__ == 'CMSPlugin': + context['children_calculator'].append(child) else: context['children_to_content'].append(child) return context @@ -75,50 +80,42 @@ class DCLSectionPromoPlugin(CMSPluginBase): @plugin_pool.register_plugin class DCLCalculatorPlugin(CMSPluginBase): module = "Datacenterlight" - name = "DCL Calculator Section Plugin" - model = DCLSectionPluginModel + name = "DCL Calculator Plugin" + model = DCLCalculatorPluginModel render_template = "datacenterlight/cms/calculator.html" cache = False - allow_children = True - child_classes = [ - 'DCLSectionPromoPlugin', 'UngleichHTMLPlugin', 'DCLCustomPricingPlugin' - ] + require_parent = True def render(self, context, instance, placeholder): context = super(DCLCalculatorPlugin, self).render( context, instance, placeholder ) context['templates'] = VMTemplate.objects.all() - context['children_to_content'] = [] - pricing_plugin_model = None - if instance.child_plugin_instances is not None: - context['children_to_content'].extend( - instance.child_plugin_instances - ) - for child in instance.child_plugin_instances: - if child.__class__.__name__ == 'DCLCustomPricingModel': - # The second clause is just to make sure we pick up the - # most recent CustomPricing, if more than one is present - if (pricing_plugin_model is None or child.pricing_id > - pricing_plugin_model.model.pricing_id): - pricing_plugin_model = child - - if pricing_plugin_model: - context['vm_pricing'] = VMPricing.get_vm_pricing_by_name( - name=pricing_plugin_model.pricing.name - ) - else: - context['vm_pricing'] = VMPricing.get_default_pricing() + # pricing_plugin_model = None + # if instance.child_plugin_instances is not None: + # for child in instance.child_plugin_instances: + # if child.__class__.__name__ == 'DCLCustomPricingModel': + # # The second clause is just to make sure we pick up the + # # most recent CustomPricing, if more than one is present + # if (pricing_plugin_model is None or child.pricing_id > + # pricing_plugin_model.model.pricing_id): + # pricing_plugin_model = child + # if pricing_plugin_model: + # context['vm_pricing'] = VMPricing.get_vm_pricing_by_name( + # name=pricing_plugin_model.pricing.name + # ) + # else: + # context['vm_pricing'] = VMPricing.get_default_pricing() return context -@plugin_pool.register_plugin -class DCLCustomPricingPlugin(CMSPluginBase): - module = "Datacenterlight" - name = "DCL Custom Pricing Plugin" - model = DCLCustomPricingModel - render_plugin = False +# @plugin_pool.register_plugin +# class DCLCustomPricingPlugin(CMSPluginBase): +# module = "Datacenterlight" +# name = "DCL Custom Pricing Plugin" +# model = DCLCustomPricingModel +# render_plugin = False @plugin_pool.register_plugin diff --git a/datacenterlight/migrations/0021_cmsintegration_calculator_placeholder.py b/datacenterlight/migrations/0021_cmsintegration_calculator_placeholder.py new file mode 100644 index 00000000..3ebbb469 --- /dev/null +++ b/datacenterlight/migrations/0021_cmsintegration_calculator_placeholder.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-04-25 09:20 +from __future__ import unicode_literals + +import cms.models.fields +from django.db import migrations +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0020_merge'), + ('cms', '0014_auto_20160404_1908'), + ] + + operations = [ + migrations.AddField( + model_name='cmsintegration', + name='calculator_placeholder', + field=cms.models.fields.PlaceholderField(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, + related_name='dcl-calculator-placeholder+', slotname='datacenterlight_calculator', to='cms.Placeholder'), + ), + migrations.RenameModel( + old_name='DCLCustomPricingModel', + new_name='DCLCalculatorPluginModel', + ), + ] diff --git a/datacenterlight/static/datacenterlight/css/landing-page.css b/datacenterlight/static/datacenterlight/css/landing-page.css index 8e9f2c2d..f241ed71 100755 --- a/datacenterlight/static/datacenterlight/css/landing-page.css +++ b/datacenterlight/static/datacenterlight/css/landing-page.css @@ -776,7 +776,7 @@ textarea { width: 100%; margin: 0 auto; background: #fff; - box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1), 0 0 6px rgba(0, 0, 0, 0.15); padding-bottom: 40px; border-radius: 7px; text-align: center; @@ -929,7 +929,7 @@ textarea { } -@media(max-width:991px) { +@media(max-width:767px) { .section-sm-center .split-text, .section-sm-center .space { text-align: center !important; diff --git a/datacenterlight/templates/datacenterlight/cms/calculator.html b/datacenterlight/templates/datacenterlight/cms/calculator.html index 27d1f89c..7b123a72 100644 --- a/datacenterlight/templates/datacenterlight/cms/calculator.html +++ b/datacenterlight/templates/datacenterlight/cms/calculator.html @@ -1,16 +1,5 @@ -
-
-
-
- {% include "datacenterlight/cms/includes/_section_split_content.html" %} -
-
-
-
- {% include "datacenterlight/includes/_calculator_form.html" %} -
-
-
-
+
+
+ {% include "datacenterlight/includes/_calculator_form.html" with vm_pricing=instance.pricing %}
\ No newline at end of file diff --git a/datacenterlight/templates/datacenterlight/cms/section.html b/datacenterlight/templates/datacenterlight/cms/section.html index 5a420a99..4438cf7d 100644 --- a/datacenterlight/templates/datacenterlight/cms/section.html +++ b/datacenterlight/templates/datacenterlight/cms/section.html @@ -2,17 +2,24 @@
- {% if children_to_side|length %} + {% if children_to_side|length or children_calculator|length %}
{% include "datacenterlight/cms/includes/_section_split_content.html" %}
-
- {% for plugin in children_to_side %} + {% if children_calculator|length %} + {% for plugin in children_calculator %} {% render_plugin plugin %} {% endfor %} -
+ {% endif %} + {% if children_to_side %} +
+ {% for plugin in children_to_side %} + {% render_plugin plugin %} + {% endfor %} +
+ {% endif %}
{% else %} diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html index e3fe8676..656e78e7 100644 --- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html +++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html @@ -17,7 +17,7 @@

{% trans "VM hosting" %}

- 15 + CHF/{% trans "month" %} {% if vm_pricing.vat_inclusive %}
@@ -94,4 +94,4 @@
- + \ No newline at end of file diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 58c6b8e2..da3f0941 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -352,6 +352,18 @@ CMS_PLACEHOLDER_CONF = { }, ] }, + 'datacenterlight_calculator': { + 'name': _('Datacenterlight Calculator'), + 'plugins': ['DCLCalculatorPlugin'], + 'default_plugins': [ + { + 'plugin_type': 'DCLCalculatorPlugin', + 'values': { + 'pricing_id': 1 + }, + }, + ] + }, } CMS_PERMISSION = True diff --git a/hosting/static/hosting/css/price_calculator.css b/hosting/static/hosting/css/price_calculator.css index 316b12ca..61fb277b 100644 --- a/hosting/static/hosting/css/price_calculator.css +++ b/hosting/static/hosting/css/price_calculator.css @@ -1,7 +1,7 @@ /* Create VM calculator */ .price-calc-section { - padding: 80px 40px !important; + padding: 20px 0 !important; } @media (max-width: 768px) { @@ -40,7 +40,7 @@ } .price-calc-section .card { - width: 50%; + /* width: 50%; */ margin: 0 auto; background: #fff; box-shadow: 1px 3px 6px 2px rgba(0, 0, 0, 0.2); @@ -52,7 +52,7 @@ @media (min-width: 768px) { .price-calc-section .card { - margin-left: 0; + /* margin-left: 0; */ } } diff --git a/hosting/static/hosting/js/initial.js b/hosting/static/hosting/js/initial.js index 1fca9735..35ecaadf 100644 --- a/hosting/static/hosting/js/initial.js +++ b/hosting/static/hosting/js/initial.js @@ -153,4 +153,84 @@ $( document ).ready(function() { $('.navbar-fixed-top.topnav').css('padding-right', topnavPadding-scrollbarWidth); } }); + + /* --------------------------------------------- + Scripts initialization + --------------------------------------------- */ + var cardPricing = { + 'cpu': { + 'id': 'coreValue', + 'value': 1, + 'min': 1, + 'max': 48, + 'interval': 1 + }, + 'ram': { + 'id': 'ramValue', + 'value': 2, + 'min': 1, + 'max': 200, + 'interval': 1 + }, + 'storage': { + 'id': 'storageValue', + 'value': 10, + 'min': 10, + 'max': 2000, + 'interval': 10 + } + }; + + function _initPricing() { + _fetchPricing(); + + $('.fa-minus-circle.left').click(function(event) { + var data = $(this).data('minus'); + + if (cardPricing[data].value > cardPricing[data].min) { + cardPricing[data].value = Number(cardPricing[data].value) - cardPricing[data].interval; + } + _fetchPricing(); + }); + $('.fa-plus-circle.right').click(function(event) { + var data = $(this).data('plus'); + if (cardPricing[data].value < cardPricing[data].max) { + cardPricing[data].value = Number(cardPricing[data].value) + cardPricing[data].interval; + } + _fetchPricing(); + }); + + $('.input-price').change(function() { + var data = $(this).attr("name"); + cardPricing[data].value = $('input[name=' + data + ']').val(); + _fetchPricing(); + }); + } + + function _fetchPricing() { + Object.keys(cardPricing).map(function(element) { + $('input[name=' + element + ']').val(cardPricing[element].value); + }); + _calcPricing(); + } + + function _calcPricing() { + if(typeof window.coresUnitPrice === 'undefined'){ + window.coresUnitPrice = 5; + } + if(typeof window.ramUnitPrice === 'undefined'){ + window.coresUnitPrice = 2; + } + if(typeof window.ssdUnitPrice === 'undefined'){ + window.ssdUnitPrice = 0.6; + } + console.log(coresUnitPrice, ramUnitPrice, ssdUnitPrice, cardPricing) + var total = (cardPricing['cpu'].value * window.coresUnitPrice) + + (cardPricing['ram'].value * window.ramUnitPrice) + + (cardPricing['storage'].value * window.ssdUnitPrice); + total = parseFloat(total.toFixed(2)); + $("#total").text(total); + } + + _initPricing(); }); \ No newline at end of file diff --git a/hosting/templates/hosting/base_short.html b/hosting/templates/hosting/base_short.html index 9c1538db..63f2b499 100644 --- a/hosting/templates/hosting/base_short.html +++ b/hosting/templates/hosting/base_short.html @@ -1,5 +1,5 @@ -{% load staticfiles bootstrap3%} -{% load i18n %} +{% load staticfiles i18n cms_tags sekizai_tags %} + @@ -29,6 +29,9 @@ {% block css_extra %} {% endblock css_extra %} + {% render_block "css" postprocessor "compressor.contrib.sekizai.compress" %} + {% render_block "js" postprocessor "compressor.contrib.sekizai.compress" %} + @@ -48,7 +51,7 @@ - + {% cms_toolbar %} {% block navbar %} {% include "hosting/includes/_navbar_user.html" %} diff --git a/hosting/templates/hosting/create_virtual_machine.html b/hosting/templates/hosting/create_virtual_machine.html index a614dd78..5c4bc3cf 100644 --- a/hosting/templates/hosting/create_virtual_machine.html +++ b/hosting/templates/hosting/create_virtual_machine.html @@ -1,7 +1,9 @@ {% extends "hosting/base_short.html" %} -{% load staticfiles bootstrap3 i18n %} +{% load staticfiles bootstrap3 i18n cms_tags %} {% block content %} + +
@@ -17,14 +19,8 @@ {% endif %}
-
-
-
-
- {% include "hosting/calculator_form.html" %} -
-
-
+
+ {% render_placeholder cms_integration.calculator_placeholder %}
diff --git a/hosting/views.py b/hosting/views.py index ec36836a..b288be3b 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -32,6 +32,7 @@ from stored_messages.settings import stored_messages_settings from datacenterlight.models import VMTemplate from datacenterlight.tasks import create_vm_task +from datacenterlight.utils import get_cms_integration from membership.models import CustomUser, StripeCustomer from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import ( @@ -1003,7 +1004,11 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): @method_decorator(decorators) def get(self, request, *args, **kwargs): - context = {'templates': VMTemplate.objects.all()} + print(get_cms_integration('default')) + context = { + 'templates': VMTemplate.objects.all(), + 'cms_integration': get_cms_integration('default'), + } return render(request, self.template_name, context) @method_decorator(decorators) From 3b3b73a2ce7278db39052e71f22bbbef98e87e2f Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 25 Apr 2018 15:08:28 +0530 Subject: [PATCH 196/866] alignment calculator plugin --- datacenterlight/cms_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 0096faa5..4c8be828 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -41,7 +41,7 @@ class DCLSectionPlugin(CMSPluginBase): print(child.__dict__) if child.__class__.__name__ in right_children: context['children_to_side'].append(child) - elif child.__class__.__name__ == 'CMSPlugin': + elif child.plugin_type == 'DCLCalculatorPlugin': context['children_calculator'].append(child) else: context['children_to_content'].append(child) From f66d768ecb3733612d1c9e2410a8d951a8a74c53 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 25 Apr 2018 15:55:58 +0530 Subject: [PATCH 197/866] hosting payment page --- .../static/datacenterlight/js/main.js | 2 +- .../templates/datacenterlight/cms/base.html | 1 + .../includes/_calculator_form.html | 2 +- hosting/static/hosting/js/initial.js | 3 +- hosting/templates/hosting/payment.html | 5 ++- hosting/views.py | 44 ++++++++++++++----- 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/datacenterlight/static/datacenterlight/js/main.js b/datacenterlight/static/datacenterlight/js/main.js index 35f2b247..f6ba036b 100644 --- a/datacenterlight/static/datacenterlight/js/main.js +++ b/datacenterlight/static/datacenterlight/js/main.js @@ -175,7 +175,7 @@ window.coresUnitPrice = 5; } if(typeof window.ramUnitPrice === 'undefined'){ - window.coresUnitPrice = 2; + window.ramUnitPrice = 2; } if(typeof window.ssdUnitPrice === 'undefined'){ window.ssdUnitPrice = 0.6; diff --git a/datacenterlight/templates/datacenterlight/cms/base.html b/datacenterlight/templates/datacenterlight/cms/base.html index 942a0ad4..a614db67 100644 --- a/datacenterlight/templates/datacenterlight/cms/base.html +++ b/datacenterlight/templates/datacenterlight/cms/base.html @@ -61,6 +61,7 @@
{% endplaceholder %} + {% url 'datacenterlight:index' as calculator_form_url %} {% placeholder 'Datacenterlight Content' %} {% placeholder 'datacenterlight_footer'%} diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html index 656e78e7..8335c7ec 100644 --- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html +++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html @@ -11,7 +11,7 @@ {% endif %} -
+ {% csrf_token %}

{% trans "VM hosting" %}

diff --git a/hosting/static/hosting/js/initial.js b/hosting/static/hosting/js/initial.js index 35ecaadf..7159da9a 100644 --- a/hosting/static/hosting/js/initial.js +++ b/hosting/static/hosting/js/initial.js @@ -219,12 +219,11 @@ $( document ).ready(function() { window.coresUnitPrice = 5; } if(typeof window.ramUnitPrice === 'undefined'){ - window.coresUnitPrice = 2; + window.ramUnitPrice = 2; } if(typeof window.ssdUnitPrice === 'undefined'){ window.ssdUnitPrice = 0.6; } - console.log(coresUnitPrice, ramUnitPrice, ssdUnitPrice, cardPricing) var total = (cardPricing['cpu'].value * window.coresUnitPrice) + (cardPricing['ram'].value * window.ramUnitPrice) + (cardPricing['storage'].value * window.ssdUnitPrice); diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index 4878831e..d6574bdf 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -42,12 +42,13 @@
+ {%trans "Total" %} {%trans "including VAT" %}
-
{{request.session.specs.price|intcomma}} - CHF/{% trans "Month" %} +
+ {{request.session.specs.price|intcomma}} CHF/{% trans "Month" %}
diff --git a/hosting/views.py b/hosting/views.py index b288be3b..63d99a91 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -30,7 +30,7 @@ from stored_messages.api import mark_read from stored_messages.models import Message from stored_messages.settings import stored_messages_settings -from datacenterlight.models import VMTemplate +from datacenterlight.models import VMTemplate, VMPricing from datacenterlight.tasks import create_vm_task from datacenterlight.utils import get_cms_integration from membership.models import CustomUser, StripeCustomer @@ -1020,18 +1020,34 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): storage = request.POST.get('storage') storage_field = forms.IntegerField(validators=[self.validate_storage]) template_id = int(request.POST.get('config')) + pricing_name = request.POST.get('pricing_name') + vm_pricing = VMPricing.get_vm_pricing_by_name(pricing_name) template = VMTemplate.objects.filter( opennebula_vm_template_id=template_id).first() template_data = VMTemplateSerializer(template).data + if vm_pricing is None: + vm_pricing_name_msg = _( + "Incorrect pricing name. Please contact support" + "{support_email}".format( + support_email=settings.DCL_SUPPORT_FROM_ADDRESS + ) + ) + messages.add_message( + self.request, messages.ERROR, vm_pricing_name_msg, + extra_tags='pricing' + ) + return redirect(CreateVirtualMachinesView.as_view()) + else: + vm_pricing_name = vm_pricing.name + try: cores = cores_field.clean(cores) except ValidationError as err: msg = '{} : {}.'.format(cores, str(err)) messages.add_message(self.request, messages.ERROR, msg, extra_tags='cores') - return HttpResponseRedirect( - reverse('datacenterlight:index') + "#order_form") + return redirect(CreateVirtualMachinesView.as_view()) try: memory = memory_field.clean(memory) @@ -1039,8 +1055,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): msg = '{} : {}.'.format(memory, str(err)) messages.add_message(self.request, messages.ERROR, msg, extra_tags='memory') - return HttpResponseRedirect( - reverse('datacenterlight:index') + "#order_form") + return redirect(CreateVirtualMachinesView.as_view()) try: storage = storage_field.clean(storage) @@ -1048,15 +1063,24 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): msg = '{} : {}.'.format(storage, str(err)) messages.add_message(self.request, messages.ERROR, msg, extra_tags='storage') - return HttpResponseRedirect( - reverse('datacenterlight:index') + "#order_form") - price = get_vm_price(cpu=cores, memory=memory, - disk_size=storage) + return redirect(CreateVirtualMachinesView.as_view()) + + price, vat, vat_percent = get_vm_price_with_vat( + cpu=cores, + memory=memory, + ssd_size=storage, + pricing_name=vm_pricing_name + ) + specs = { 'cpu': cores, 'memory': memory, 'disk_size': storage, - 'price': price + 'price': price, + 'vat': vat, + 'vat_percent': vat_percent, + 'total_price': price + vat, + 'pricing_name': vm_pricing_name } request.session['specs'] = specs From 91a65e88ec35caa79885ac3726cf577a8149a687 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 25 Apr 2018 16:08:05 +0530 Subject: [PATCH 198/866] hosting vm payment price fix --- hosting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 63d99a91..d0d2a99c 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -855,7 +855,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): cpu = specs.get('cpu') memory = specs.get('memory') disk_size = specs.get('disk_size') - amount_to_be_charged = specs.get('price') + amount_to_be_charged = specs.get('total_price') plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu, memory=memory, disk_size=disk_size) From 14548b2f0158c3842bb68f422a8e6f394aa969e0 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 25 Apr 2018 17:26:31 +0530 Subject: [PATCH 199/866] flake8 refacoring --- datacenterlight/cms_plugins.py | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 4c8be828..ecc0a355 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -1,4 +1,3 @@ -from cms.models.pluginmodel import CMSPlugin from cms.plugin_base import CMSPluginBase from cms.plugin_pool import plugin_pool @@ -9,7 +8,7 @@ from .cms_models import ( DCLSectionPluginModel, DCLNavbarPluginModel, DCLSectionPromoPluginModel, DCLCalculatorPluginModel ) -from .models import VMTemplate, VMPricing +from .models import VMTemplate @plugin_pool.register_plugin @@ -91,33 +90,9 @@ class DCLCalculatorPlugin(CMSPluginBase): context, instance, placeholder ) context['templates'] = VMTemplate.objects.all() - # pricing_plugin_model = None - # if instance.child_plugin_instances is not None: - # for child in instance.child_plugin_instances: - # if child.__class__.__name__ == 'DCLCustomPricingModel': - # # The second clause is just to make sure we pick up the - # # most recent CustomPricing, if more than one is present - # if (pricing_plugin_model is None or child.pricing_id > - # pricing_plugin_model.model.pricing_id): - # pricing_plugin_model = child - - # if pricing_plugin_model: - # context['vm_pricing'] = VMPricing.get_vm_pricing_by_name( - # name=pricing_plugin_model.pricing.name - # ) - # else: - # context['vm_pricing'] = VMPricing.get_default_pricing() return context -# @plugin_pool.register_plugin -# class DCLCustomPricingPlugin(CMSPluginBase): -# module = "Datacenterlight" -# name = "DCL Custom Pricing Plugin" -# model = DCLCustomPricingModel -# render_plugin = False - - @plugin_pool.register_plugin class DCLBannerListPlugin(CMSPluginBase): module = "Datacenterlight" From 0c4c945ec3d3ad1ef640c305c9ff4d26157a9414 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 25 Apr 2018 17:51:36 +0530 Subject: [PATCH 200/866] flake8 fix --- hosting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index d0d2a99c..495efd5c 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -43,7 +43,7 @@ from utils.forms import ( BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm, ResendActivationEmailForm ) -from utils.hosting_utils import get_vm_price, get_vm_price_with_vat +from utils.hosting_utils import get_vm_price_with_vat from utils.mailer import BaseEmail from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task From 89ed869780b32c4515ed524042506aca512e1b23 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Wed, 25 Apr 2018 23:31:27 +0530 Subject: [PATCH 201/866] hosting calculator styles --- .../static/hosting/css/price_calculator.css | 23 +++++++++++-------- hosting/templates/hosting/payment.html | 3 +-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/hosting/static/hosting/css/price_calculator.css b/hosting/static/hosting/css/price_calculator.css index 61fb277b..68961d33 100644 --- a/hosting/static/hosting/css/price_calculator.css +++ b/hosting/static/hosting/css/price_calculator.css @@ -2,6 +2,8 @@ .price-calc-section { padding: 20px 0 !important; + font-weight: 300; + font-size: 18px; } @media (max-width: 768px) { @@ -40,13 +42,13 @@ } .price-calc-section .card { - /* width: 50%; */ + border-radius: 7px; margin: 0 auto; background: #fff; box-shadow: 1px 3px 6px 2px rgba(0, 0, 0, 0.2); padding-bottom: 30px; text-align: center; - max-width: 320px; + max-width: 4000px; position: relative; } @@ -85,7 +87,7 @@ } .price-calc-section .card .description { - padding: 7px 8px 2px; + padding: 12px; position: relative; display: flex; justify-content: space-around !important; @@ -93,7 +95,7 @@ } .price-calc-section .card .description span { - font-size: 14px; + font-size: 16px; margin-left: 5px; /* margin-left: 0px; */ /* justify-self: start; */ @@ -104,17 +106,18 @@ } .price-calc-section .card .description .select-number{ - font-size: 16px; + font-size: 18px; text-align: center; width: 85px; + padding: 5px 10px; } .price-calc-section .card .description i { color: #29427a; cursor: pointer; font-size: 20px; - border: 1px solid #ccc; - padding: 5px 6px 3px; + /* border: 1px solid #ccc; */ + /* padding: 5px 6px 3px; */ border-radius: 5px; } @@ -193,7 +196,7 @@ .price-calc-section .help-block.with-errors { text-align: center; margin: 0 0; - padding: 0 0 5px; + padding: 0 0; } .price-calc-section .help-block.with-errors ul { margin-bottom: 0; @@ -209,10 +212,10 @@ display: block; position: absolute; bottom: 0; - left: 18%; + left: 0; z-index: 20; height: 1px; - width: 65%; + width: 100%; background: rgba(128, 128, 128, 0.2); } diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index d6574bdf..ab6c6a65 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -42,8 +42,7 @@
- - {%trans "Total" %} {%trans "including VAT" %} + {%trans "Total" %} {% if vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %}
From 34df86fb90fdfd2547d02fb0f71322f6f230dad9 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Sat, 28 Apr 2018 00:56:11 +0530 Subject: [PATCH 202/866] add favicon extension to ungleich_template --- .../templates/ungleich_page/ungleich_cms_page.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ungleich_page/templates/ungleich_page/ungleich_cms_page.html b/ungleich_page/templates/ungleich_page/ungleich_cms_page.html index f8d32f07..42293b04 100644 --- a/ungleich_page/templates/ungleich_page/ungleich_cms_page.html +++ b/ungleich_page/templates/ungleich_page/ungleich_cms_page.html @@ -33,7 +33,11 @@ {% include "google_analytics.html" %} - + {% if request.current_page.cmsfaviconextension %} + + {% else %} + + {% endif %} From 35fb872dc1ff5f143daa12f1d3434f235c4d577a Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Tue, 1 May 2018 00:00:05 +0530 Subject: [PATCH 203/866] Update Changelog --- Changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog b/Changelog index 86c21db0..e4558a9d 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,6 @@ +1.7.2: 2018-04-30 + * bgfix: [cms] add favicon extension to ungleich cms pages + * #4474: [cms] reduce heading slider side padding 1.7.1: 2018-04-21 * #4481: [blog] fix de blog pages 500 error * #4370: [comic] new url /comic to show only comic blogs From d6db984156c2aa4c813c9799d02d369d1fe7948c Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Tue, 1 May 2018 00:48:31 +0530 Subject: [PATCH 204/866] Update ungleich.css --- ungleich_page/static/ungleich_page/css/ungleich.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ungleich_page/static/ungleich_page/css/ungleich.css b/ungleich_page/static/ungleich_page/css/ungleich.css index 2537a921..af71e692 100644 --- a/ungleich_page/static/ungleich_page/css/ungleich.css +++ b/ungleich_page/static/ungleich_page/css/ungleich.css @@ -195,7 +195,7 @@ flex: 1; } -.header_slider > .carousel .item .container { +.header_slider > .carousel .item .container-fluid { overflow: auto; padding: 50px 20px 60px; height: 100%; @@ -236,7 +236,7 @@ .header_slider .carousel-control .fa { font-size: 4em; } - .header_slider > .carousel .item .container { + .header_slider > .carousel .item .container-fluid { overflow: auto; padding: 75px 50px; } @@ -403,4 +403,4 @@ left: 0; bottom: 0; background: rgba(0,0,0,0.35); -} \ No newline at end of file +} From ccf55acbaf85561345ac9e50e93cdb3642f57146 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Tue, 1 May 2018 04:36:29 +0530 Subject: [PATCH 205/866] Update navbar_dropdown.html --- .../templates/datacenterlight/cms/navbar_dropdown.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/cms/navbar_dropdown.html b/datacenterlight/templates/datacenterlight/cms/navbar_dropdown.html index 051e8914..70926874 100644 --- a/datacenterlight/templates/datacenterlight/cms/navbar_dropdown.html +++ b/datacenterlight/templates/datacenterlight/cms/navbar_dropdown.html @@ -1,10 +1,10 @@ {% load cms_tags %} \ No newline at end of file +
From 4d2d337651c91857d65f587dc87042b238e330ce Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Tue, 1 May 2018 16:44:15 +0530 Subject: [PATCH 206/866] Release 1.8 --- Changelog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog b/Changelog index e4558a9d..2c2877ab 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,7 @@ +1.8: 2018-05-01 + * #4527: [hosting] cms calculator on non-cms pages for the hosting app + * bgfix: [dcl] navbar dropdown target fix + * bgfix: [hosting] login/signup pages footer link fix 1.7.2: 2018-04-30 * bgfix: [cms] add favicon extension to ungleich cms pages * #4474: [cms] reduce heading slider side padding From 25ef657c621c2a16a668e096c784663c1ac50915 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Tue, 1 May 2018 18:15:56 +0530 Subject: [PATCH 207/866] Update cms_plugins.py --- datacenterlight/cms_plugins.py | 1 - 1 file changed, 1 deletion(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index ecc0a355..12de0daf 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -37,7 +37,6 @@ class DCLSectionPlugin(CMSPluginBase): 'DCLSectionIconPluginModel', ] for child in instance.child_plugin_instances: - print(child.__dict__) if child.__class__.__name__ in right_children: context['children_to_side'].append(child) elif child.plugin_type == 'DCLCalculatorPlugin': From f8dc2c6bbee5a8a48c259bf55ef4c2b48a42b062 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Mon, 7 May 2018 05:07:58 +0530 Subject: [PATCH 208/866] discount option added to calculator --- datacenterlight/cms_plugins.py | 1 - .../migrations/0022_auto_20180506_1950.py | 25 ++ datacenterlight/models.py | 12 +- .../static/datacenterlight/css/common.css | 9 + .../static/datacenterlight/js/main.js | 6 +- .../includes/_calculator_form.html | 11 +- .../datacenterlight/landing_payment.html | 19 +- .../datacenterlight/order_detail.html | 8 + datacenterlight/views.py | 5 +- hosting/static/hosting/css/commons.css | 9 + hosting/static/hosting/css/landing-page.css | 224 ------------ hosting/static/hosting/css/order.css | 4 + hosting/static/hosting/css/payment.css | 333 +++++++++++++----- hosting/static/hosting/js/initial.js | 6 +- hosting/templates/hosting/order_detail.html | 8 + hosting/templates/hosting/payment.html | 331 +++++++++-------- hosting/views.py | 8 +- utils/hosting_utils.py | 11 +- 18 files changed, 554 insertions(+), 476 deletions(-) create mode 100644 datacenterlight/migrations/0022_auto_20180506_1950.py diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index ecc0a355..12de0daf 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -37,7 +37,6 @@ class DCLSectionPlugin(CMSPluginBase): 'DCLSectionIconPluginModel', ] for child in instance.child_plugin_instances: - print(child.__dict__) if child.__class__.__name__ in right_children: context['children_to_side'].append(child) elif child.plugin_type == 'DCLCalculatorPlugin': diff --git a/datacenterlight/migrations/0022_auto_20180506_1950.py b/datacenterlight/migrations/0022_auto_20180506_1950.py new file mode 100644 index 00000000..dd79b825 --- /dev/null +++ b/datacenterlight/migrations/0022_auto_20180506_1950.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-05-06 14:20 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0021_cmsintegration_calculator_placeholder'), + ] + + operations = [ + migrations.AddField( + model_name='vmpricing', + name='discount_amount', + field=models.DecimalField(decimal_places=2, default=0, max_digits=4), + ), + migrations.AddField( + model_name='vmpricing', + name='discount_name', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/datacenterlight/models.py b/datacenterlight/models.py index eceb7617..56a19f03 100644 --- a/datacenterlight/models.py +++ b/datacenterlight/models.py @@ -34,6 +34,10 @@ class VMPricing(models.Model): hdd_unit_price = models.DecimalField( max_digits=7, decimal_places=6, default=0 ) + discount_name = models.CharField(max_length=255, null=True, blank=True) + discount_amount = models.DecimalField( + max_digits=4, decimal_places=2, default=0 + ) def __str__(self): return self.name + ' => ' + ' - '.join([ @@ -42,8 +46,12 @@ class VMPricing(models.Model): '{}/GB SSD'.format(self.ssd_unit_price.normalize()), '{}/GB HDD'.format(self.hdd_unit_price.normalize()), '{}% VAT'.format(self.vat_percentage.normalize()) - if not self.vat_inclusive else 'VAT-Incl', ] - ) + if not self.vat_inclusive else 'VAT-Incl', + '{} {}'.format( + self.discount_amount if self.discount_amount else '', + self.discount_name if self.discount_name else 'Discount' + ), + ]) @classmethod def get_vm_pricing_by_name(cls, name): diff --git a/datacenterlight/static/datacenterlight/css/common.css b/datacenterlight/static/datacenterlight/css/common.css index 895256ef..b6eabd75 100644 --- a/datacenterlight/static/datacenterlight/css/common.css +++ b/datacenterlight/static/datacenterlight/css/common.css @@ -150,3 +150,12 @@ footer .dcl-link-separator::before { border-radius: 100%; background: #777; } + +.mb-0 { + margin-bottom: 0; +} + +.thin-hr { + margin-top: 10px; + margin-bottom: 10px; +} \ No newline at end of file diff --git a/datacenterlight/static/datacenterlight/js/main.js b/datacenterlight/static/datacenterlight/js/main.js index f6ba036b..292e8c16 100644 --- a/datacenterlight/static/datacenterlight/js/main.js +++ b/datacenterlight/static/datacenterlight/js/main.js @@ -180,9 +180,13 @@ if(typeof window.ssdUnitPrice === 'undefined'){ window.ssdUnitPrice = 0.6; } + if(typeof window.discountAmount === 'undefined'){ + window.discountAmount = 0; + } var total = (cardPricing['cpu'].value * window.coresUnitPrice) + (cardPricing['ram'].value * window.ramUnitPrice) + - (cardPricing['storage'].value * window.ssdUnitPrice); + (cardPricing['storage'].value * window.ssdUnitPrice) - + window.discountAmount; total = parseFloat(total.toFixed(2)); $("#total").text(total); } diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html index 8335c7ec..dfc0bf22 100644 --- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html +++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html @@ -8,6 +8,7 @@ window.ramUnitPrice = {{vm_pricing.ram_unit_price|default:0}}; window.ssdUnitPrice = {{vm_pricing.ssd_unit_price|default:0}}; window.hddUnitPrice = {{vm_pricing.hdd_unit_price|default:0}}; + window.discountAmount = {{vm_pricing.discount_amount|default:0}}; {% endif %} @@ -19,11 +20,15 @@
CHF/{% trans "month" %} - {% if vm_pricing.vat_inclusive %}
-

{% trans "VAT included" %}

+

+ {% if vm_pricing.vat_inclusive %}{% trans "VAT included" %}
{% endif %} + {% if vm_pricing.discount_amount %} + {% trans "Discount" as discount_name %} + {{ vm_pricing.discount_amount }} CHF {{ vm_pricing.discount_name|default:discount_name }} included + {% endif %} +

- {% endif %}
diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html index b808e033..4d111fa1 100644 --- a/datacenterlight/templates/datacenterlight/landing_payment.html +++ b/datacenterlight/templates/datacenterlight/landing_payment.html @@ -78,7 +78,24 @@

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


-

{%trans "Total" %}  ({% if vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %}) {{request.session.specs.price|intcomma}} CHF/{% trans "Month" %}

+

+ {%trans "Total" %}   + + ({% if vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %}) + + {{request.session.specs.price|intcomma}} CHF/{% trans "Month" %} +

+
+ {% if vm_pricing.discount_amount %} +

+ {%trans "Discount" as discount_name %} + {{ vm_pricing.discount_name|default:discount_name }}   + - {{ vm_pricing.discount_amount }} CHF/{% trans "Month" %} +

+

+ ({% trans "Will be applied at checkout" %}) +

+ {% endif %}
diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 95bfa3c6..13d2c61e 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -65,6 +65,7 @@ {% trans "Disk space" %}: {{vm.disk_size|intcomma}} GB

+
{% if vm.vat > 0 %}

{% trans "Subtotal" %}: @@ -75,6 +76,13 @@ {{vm.vat|floatformat:2|intcomma}} CHF

{% endif %} + {% if vm_pricing.discount_amount %} +

+ {%trans "Discount" as discount_name %} + {{ vm_pricing.discount_name|default:discount_name }}: + - {{ vm_pricing.discount_amount }} CHF +

+ {% endif %}

{% trans "Total" %} {{vm.total_price|floatformat:2|intcomma}} CHF diff --git a/datacenterlight/views.py b/datacenterlight/views.py index cccd4277..bc5ea49e 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -387,7 +387,10 @@ class OrderConfirmationView(DetailView): 'billing_address_data': ( request.session.get('billing_address_data') ), - 'cms_integration': get_cms_integration('default') + 'cms_integration': get_cms_integration('default'), + 'vm_pricing': VMPricing.get_vm_pricing_by_name( + self.request.session['specs']['pricing_name'] + ), } return render(request, self.template_name, context) diff --git a/hosting/static/hosting/css/commons.css b/hosting/static/hosting/css/commons.css index 59ca56eb..0abfd499 100644 --- a/hosting/static/hosting/css/commons.css +++ b/hosting/static/hosting/css/commons.css @@ -361,4 +361,13 @@ .locale_date.done{ opacity: 1; +} + +.mb-0 { + margin-bottom: 0; +} + +.thin-hr { + margin-top: 10px; + margin-bottom: 10px; } \ No newline at end of file diff --git a/hosting/static/hosting/css/landing-page.css b/hosting/static/hosting/css/landing-page.css index d5236324..389e6999 100644 --- a/hosting/static/hosting/css/landing-page.css +++ b/hosting/static/hosting/css/landing-page.css @@ -449,230 +449,6 @@ a.unlink:hover { color: inherit; } -/***** DCL payment page **********/ -.dcl-order-container { - font-weight: 300; -} - -.dcl-order-table-header { - border-bottom: 1px solid #eee; - padding-top: 15px; - padding-bottom: 15px; - font-size: 16px; - color: #333; - text-align: center; - font-weight: 300; -} - -.dcl-order-table-content { - border-bottom: 1px solid #eee; - padding-top: 15px; - padding-bottom: 15px; - font-size: 18px; - font-weight: 600; - text-align: center; -} - -.tbl-content { -} - -.dcl-order-table-total { - border-bottom: 4px solid #eee; - padding-top: 15px; - padding-bottom: 20px; - font-size: 20px; - font-weight: 600; - color: #999; -} - -.dcl-order-table-total span { - font-size: 13px; - color: #999; - font-weight: 400; - padding-left: 5px; -} - -.dcl-place-order-text{ - color: #808080; -} - -.dcl-order-table-total .tbl-total { - text-align: center; - color: #000; - padding-left: 44px; -} - -.tbl-total .dcl-price-month { - font-size: 16px; - text-transform: capitalize; - color: #000; -} - -.tbl-no-padding { - padding: 0px; -} - -.dcl-billing-sec { - margin-top: 50px; -} - -.dcl-order-sec { - padding: 0 30px; -} - -.card-warning-content { - font-weight: 300; - border: 1px solid #a1a1a1; - border-radius: 3px; - padding: 5px; - margin-bottom: 15px; -} -.card-warning-error { - border: 1px solid #EB4D5C; - color: #EB4D5C; -} - -.card-warning-addtional-margin { - margin-top: 15px; -} - -.stripe-payment-btn { - outline: none; - width: auto; - float: right; - font-style: normal; - font-weight: 300; - position: absolute; - padding-left: 30px; - padding-right: 30px; - right: 0; -} - -.card-cvc-element label { - padding-left: 10px; -} - -.card-element { - margin-bottom: 10px; -} - -.card-element label{ - width:100%; - margin-bottom:0px; -} - -.my-input { - border-bottom: 1px solid #ccc; - } - -.card-cvc-element .my-input { - padding-left: 10px; -} - -#card-errors { - clear: both; - padding: 0 0 10px; - color: #eb4d5c; -} - -.credit-card-goup{ - padding: 0; -} - -@media (max-width: 767px) { - .dcl-order-table-total span { - padding-left: 3px; - } - - .dcl-order-sec { - padding: 10px 20px 30px 20px; - border-bottom: 4px solid #eee; - } - - .tbl-header { - border-bottom: 1px solid #eee; - padding: 10px 0; - } - - .tbl-content { - border-bottom: 1px solid #eee; - padding: 10px 0; - } - - .dcl-order-table-header { - border-bottom: 0px solid #eee; - padding: 10px 0; - text-align: left; - } - - .dcl-order-table-content { - border-bottom: 0px solid #eee; - padding: 10px 0; - text-align: right; - font-size: 16px; - } - - .dcl-order-table-total { - font-size: 18px; - color: #000; - padding: 10px 0; - border-bottom: 0px solid #eee; - } - - .dcl-order-table-total .tbl-total { - padding: 0px; - text-align: right; - } - - .dcl-billing-sec { - margin-top: 30px; - margin-bottom: 30px; - } - - .card-expiry-element { - padding-right: 10px; - } - - .card-cvc-element { - padding-left: 10px; - } - - #billing-form .form-control { - box-shadow: none !important; - font-weight: 400; - } -} - -@media (min-width: 1200px) { - .dcl-order-container { - width: 990px; - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; - } -} - -@media (min-width: 768px) { - .dcl-billing { - padding-right: 65px; - border-right: 1px solid #eee; - } - - .dcl-creditcard { - padding-left: 65px; - } - - .tbl-tot { - padding-left: 17px; - } - - .content-dashboard { - /*width: auto !important;*/ - } - -} - @media only screen and (max-width: 1040px) and (min-width: 768px) { .content-dashboard { width: 96% !important; diff --git a/hosting/static/hosting/css/order.css b/hosting/static/hosting/css/order.css index 0cd22c21..27a67f3e 100644 --- a/hosting/static/hosting/css/order.css +++ b/hosting/static/hosting/css/order.css @@ -96,4 +96,8 @@ #virtual_machine_create_form { padding: 15px 0; +} + +.dcl-place-order-text { + color: #808080; } \ No newline at end of file diff --git a/hosting/static/hosting/css/payment.css b/hosting/static/hosting/css/payment.css index de89afd0..8a1bc70f 100644 --- a/hosting/static/hosting/css/payment.css +++ b/hosting/static/hosting/css/payment.css @@ -1,19 +1,35 @@ - -.payment-container {padding-top:70px; padding-bottom: 11%;} -.creditcard-box .panel-title {display: inline;font-weight: bold; font-size:17px;} -.creditcard-box .checkbox.pull-right { margin: 0; } -.creditcard-box .pl-ziro { padding-left: 0px; } -.creditcard-box .form-control.error { - border-color: red; - outline: 0; - box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(255,0,0,0.6); +.payment-container { + padding-top: 70px; + padding-bottom: 11%; } + +.creditcard-box .panel-title { + display: inline; + font-weight: bold; + font-size: 17px; +} + +.creditcard-box .checkbox.pull-right { + margin: 0; +} + +.creditcard-box .pl-ziro { + padding-left: 0px; +} + +.creditcard-box .form-control.error { + border-color: red; + outline: 0; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(255, 0, 0, 0.6); +} + .creditcard-box label.error { font-weight: bold; color: red; padding: 2px 8px; margin-top: 2px; } + .creditcard-box .payment-errors { font-weight: bold; color: red; @@ -21,96 +37,221 @@ margin-top: 2px; } -/* 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-order-sec { + padding: 0 30px; } -.dcl-payment-user h4 { - font-weight: 600; - font-size: 17px; +.dcl-billing-sec { + margin-top: 50px; +} + +.dcl-order-container { + font-weight: 300; +} + +.dcl-order-table-header { + border-bottom: 1px solid #eee; + padding: 15px 10px; + font-size: 16px; + color: #333; + font-weight: 300; +} + +.dcl-order-table-content { + border-bottom: 1px solid #eee; + padding: 15px 10px; + font-size: 18px; + font-weight: 600; +} + +.dcl-order-table-total { + border-bottom: 4px solid #eee; + padding-top: 15px; + padding-bottom: 20px; + font-size: 20px; + font-weight: 600; +} + +.dcl-order-table-total span { + font-size: 13px; + color: #999; + font-weight: 400; +} + +.dcl-order-table-total .tbl-total { + text-align: right; + color: #000; +} + +.tbl-no-padding { + padding: 0px; +} + +.card-warning-content { + font-weight: 300; + border: 1px solid #a1a1a1; + border-radius: 3px; + padding: 5px; + margin-bottom: 15px; +} + +.card-warning-error { + border: 1px solid #EB4D5C; + color: #EB4D5C; +} + +.card-warning-addtional-margin { + margin-top: 15px; +} + +.stripe-payment-btn { + outline: none; + width: auto; + float: right; + font-style: normal; + font-weight: 300; + position: absolute; + padding-left: 30px; + padding-right: 30px; + right: 0; +} + +.card-cvc-element label { + padding-left: 10px; +} + +.card-element { + margin-bottom: 10px; +} + +.card-element label { + width: 100%; + margin-bottom: 0px; +} + +.my-input { + border-bottom: 1px solid #ccc; +} + +.card-cvc-element .my-input { + padding-left: 10px; +} + +#card-errors { + clear: both; + padding: 0 0 10px; + color: #eb4d5c; +} + +.credit-card-goup { + padding: 0; +} + +@media (max-width: 767px) { + .dcl-order-sec { + padding: 10px 5px 30px; + border-bottom: 4px solid #eee; + } + + .dcl-billing-sec { + margin-top: 30px; + margin-bottom: 30px; + padding: 5px; + } + + .dcl-billing { + margin-top: 20px; + margin-bottom: 40px; + } + + .tbl-header { + border-bottom: 1px solid #eee; + padding-top: 10px; + padding-bottom: 10px; + margin-right: -15px; + } + + .dcl-order-table-total .tbl-total { + margin-left: -15px; + } + + .dcl-order-table-total .tbl-tot { + margin-right: -15px; + } + + .tbl-content { + border-bottom: 1px solid #eee; + padding-top: 10px; + padding-bottom: 10px; + margin-left: -15px; + } + + .dcl-order-table-header { + border-bottom: 0px solid #eee; + padding: 10px 0; + text-align: left; + } + + .dcl-order-table-content { + border-bottom: 0px solid #eee; + padding: 10px 0; + text-align: right; + font-size: 16px; + } + + .dcl-order-table-total { + font-size: 18px; + color: #000; + padding: 10px 0; + border-bottom: 0px solid #eee; + } + + .card-expiry-element { + padding-right: 10px; + } + + .card-cvc-element { + padding-left: 10px; + } + + #billing-form .form-control { + box-shadow: none !important; + font-weight: 400; + } } @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; - } + .dcl-billing { + padding-right: 65px; + border-right: 1px solid #eee; + } + + .dcl-creditcard { + padding-left: 65px; + } + + .dcl-order-table-total .tbl-total, + .dcl-order-table-total .tbl-tot { + padding: 0 10px; + } + + .tbl-header-center, + .tbl-content-center { + text-align: center; + } + + .tbl-header-right, + .tbl-content-right { + text-align: right; + } } + +@media (min-width: 1200px) { + .dcl-order-container { + width: 990px; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; + } +} \ No newline at end of file diff --git a/hosting/static/hosting/js/initial.js b/hosting/static/hosting/js/initial.js index 7159da9a..9c1c226e 100644 --- a/hosting/static/hosting/js/initial.js +++ b/hosting/static/hosting/js/initial.js @@ -224,9 +224,13 @@ $( document ).ready(function() { if(typeof window.ssdUnitPrice === 'undefined'){ window.ssdUnitPrice = 0.6; } + if(typeof window.discountAmount === 'undefined'){ + window.discountAmount = 0; + } var total = (cardPricing['cpu'].value * window.coresUnitPrice) + (cardPricing['ram'].value * window.ramUnitPrice) + - (cardPricing['storage'].value * window.ssdUnitPrice); + (cardPricing['storage'].value * window.ssdUnitPrice) - + window.discountAmount; total = parseFloat(total.toFixed(2)); $("#total").text(total); } diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 2568aafc..ec50528d 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -127,6 +127,7 @@ {% trans "Disk space" %}: {{vm.disk_size}} GB

+
{% if vm.vat > 0 %}

{% trans "Subtotal" %}: @@ -137,6 +138,13 @@ {{vm.vat|floatformat:2|intcomma}} CHF

{% endif %} + {% if vm_pricing.discount_amount %} +

+ {%trans "Discount" as discount_name %} + {{ vm_pricing.discount_name|default:discount_name }}: + - {{ vm_pricing.discount_amount }} CHF +

+ {% endif %}

{% trans "Total" %} {% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index ab6c6a65..afcf6373 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -9,159 +9,208 @@

-
-
-

{%trans "Your Order" %}

-
-
- {%trans "Cores" %} -
-
- {%trans "Memory" %} -
-
- {%trans "Disk space" %} -
-
- {%trans "Configuration" %} +
+

{%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|intcomma}} GB -
-
- {{request.session.template.name}} -
-
-
-
- {%trans "Total" %} {% if vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %} -
-
-
-
- {{request.session.specs.price|intcomma}} CHF/{% trans "Month" %} +
+
+
+
+
+ {{request.session.specs.cpu|floatformat}} +
+
+
+
+ {{request.session.specs.memory|floatformat}} GB +
+
+
+
+ {{request.session.specs.disk_size|floatformat|intcomma}} GB +
+
+
+
+ {{request.session.template.name}} +
+
-
-
-
-
-

{%trans "Billing Address"%}

-
- - {% for field in form %} - {% csrf_token %} - {% bootstrap_field field show_label=False type='fields'%} - {% endfor %} - +
+
+
+
+ {%trans "Total" %}  + {% if vm_pricing.vat_inclusive %}{%trans "including VAT" %}{% else %}{%trans "excluding VAT" %}{% endif %} +
+
+
+
+ {{request.session.specs.price|intcomma}} 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 %} -

+ {% if vm_pricing.discount_amount %} +
+
+
+
+ {%trans "Discount" as discount_name %} + {{ vm_pricing.discount_name|default:discount_name }}  
+ ({% trans "Will be applied at checkout" %}) +
+
+
+
+
- {{ vm_pricing.discount_amount }} CHF/{% trans "Month" %}
+
+
+
+ {% endif %} +
+
+
+
+
+
+

{%trans "Billing Address"%}

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

{%trans "Credit Card"%}

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

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

- {% endfor %} -
-
- -
- {% else %} -
- -
-
-
- -
-
-
-
- -
-
-
- -
-
-
-
- - -
-
-
-
- {% 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 }}

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

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

    {{ message|safe }}

    +
  • +
+ {% endif %} + {% endfor %} -
-

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

+ {{ error|escape }} +

+ {% endfor %} +
+
+ +
+
+ +
+

+
+ + {% endif %} +
diff --git a/hosting/views.py b/hosting/views.py index 495efd5c..1353229a 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -652,7 +652,10 @@ class PaymentVMView(LoginRequiredMixin, FormView): }) context.update({ - 'stripe_key': settings.STRIPE_API_PUBLIC_KEY + 'stripe_key': settings.STRIPE_API_PUBLIC_KEY, + 'vm_pricing': VMPricing.get_vm_pricing_by_name( + self.request.session['specs']['pricing_name'] + ) }) return context @@ -806,6 +809,9 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): context['cc_brand'] = card_details.get('response_object').get( 'cc_brand') context['vm'] = self.request.session.get('specs') + context['vm_pricing'] = VMPricing.get_vm_pricing_by_name( + self.request.session['specs']['pricing_name'] + ), return context @method_decorator(decorators) diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 04ed658a..b6e267a2 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -107,10 +107,13 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, ) return None - price = ((decimal.Decimal(cpu) * pricing.cores_unit_price) + - (decimal.Decimal(memory) * pricing.ram_unit_price) + - (decimal.Decimal(ssd_size) * pricing.ssd_unit_price) + - (decimal.Decimal(hdd_size) * pricing.hdd_unit_price)) + price = ( + (decimal.Decimal(cpu) * pricing.cores_unit_price) + + (decimal.Decimal(memory) * pricing.ram_unit_price) + + (decimal.Decimal(ssd_size) * pricing.ssd_unit_price) + + (decimal.Decimal(hdd_size) * pricing.hdd_unit_price) - + pricing.discount_amount + ) if pricing.vat_inclusive: vat = decimal.Decimal(0) vat_percent = decimal.Decimal(0) From 7a72cc02abc9db3dc7d13322f2e5bafd8bc48fca Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Mon, 7 May 2018 05:22:05 +0530 Subject: [PATCH 209/866] translations --- .../locale/de/LC_MESSAGES/django.po | 12 +++++++- dynamicweb/settings/base.py | 4 +++ hosting/locale/de/LC_MESSAGES/django.po | 28 +++++++++++++++++-- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index 50dbfbe8..cd92b339 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: 2018-04-17 19:26+0000\n" +"POT-Creation-Date: 2018-05-07 05:15+0530\n" "PO-Revision-Date: 2018-03-30 23:22+0000\n" "Last-Translator: b'Anonymous User '\n" "Language-Team: LANGUAGE \n" @@ -19,6 +19,9 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Translated-Using: django-rosetta 0.8.1\n" +msgid "CMS Favicon" +msgstr "" + #, python-format msgid "Your New VM %(vm_name)s at Data Center Light" msgstr "Deine neue VM %(vm_name)s bei Data Center Light" @@ -140,6 +143,9 @@ msgstr "Monat" msgid "VAT included" msgstr "MwSt. inklusive" +msgid "Discount" +msgstr "Rabatt" + msgid "Hosted in Switzerland" msgstr "Standort: Schweiz" @@ -314,6 +320,9 @@ msgstr "exkl. Mehrwertsteuer" msgid "Month" msgstr "Monat" +msgid "Will be applied at checkout" +msgstr "wird an der Kasse angewendet" + msgid "Credit Card" msgstr "Kreditkarte" @@ -386,6 +395,7 @@ msgstr "Zwischensumme" msgid "VAT" msgstr "Mehrwertsteuer" +#, python-format msgid "" "By clicking \"Place order\" this plan will charge your credit card account " "with the fee of %(vm_total_price)s CHF/month" diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index da3f0941..f540e998 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -267,6 +267,10 @@ LANGUAGES = ( LANGUAGE_CODE = 'en-us' +LOCALE_PATHS = [ + os.path.join(PROJECT_DIR, 'digitalglarus/locale'), +] + CMS_PLACEHOLDER_CONF = { 'logo_image': { 'name': 'Logo Image', diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 118245e5..42e46314 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-12-21 00:23+0000\n" +"POT-Creation-Date: 2018-05-07 05:15+0530\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -365,13 +365,25 @@ msgstr "Arbeitsspeicher" msgid "Disk space" msgstr "Festplattenkapazität" +msgid "Subtotal" +msgstr "Zwischensumme" + +msgid "VAT" +msgstr "Mehrwertsteuer" + +msgid "Discount" +msgstr "Rabatt" + msgid "Total" msgstr "Gesamt" -#, python-format +#, fuzzy, python-format +#| msgid "" +#| "By clicking \"Place order\" this plan will charge your credit card " +#| "account with the fee of %(vm_price)sCHF/month" msgid "" "By clicking \"Place order\" this plan will charge your credit card account " -"with the fee of %(vm_price)sCHF/month" +"with the fee of %(vm_price|intcomma)sCHF/month" msgstr "" "Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_price)sCHF " "pro Monat belastet" @@ -421,6 +433,12 @@ msgstr "Konfiguration" msgid "including VAT" msgstr "inkl. Mehrwertsteuer" +msgid "excluding VAT" +msgstr "exkl. Mehrwertsteuer" + +msgid "Will be applied at checkout" +msgstr "wird an der Kasse angewendet" + msgid "Billing Address" msgstr "Rechnungsadresse" @@ -699,6 +717,10 @@ msgstr "Ungültige RAM-Grösse" msgid "Invalid storage size" msgstr "Ungültige Speicher-Grösse" +#, python-brace-format +msgid "Incorrect pricing name. Please contact support{support_email}" +msgstr "" + msgid "" "We could not find the requested VM. Please " "contact Data Center Light Support." From 2ff8c250340e8710958711c441894e2e11f5c371 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Mon, 7 May 2018 06:11:44 +0530 Subject: [PATCH 210/866] exclude discount from total price --- datacenterlight/models.py | 17 +++++++++++------ .../datacenterlight/order_detail.html | 2 +- datacenterlight/views.py | 8 +++----- hosting/views.py | 19 +++++++++---------- utils/hosting_utils.py | 6 +++--- 5 files changed, 27 insertions(+), 25 deletions(-) diff --git a/datacenterlight/models.py b/datacenterlight/models.py index 56a19f03..ff7eeb8d 100644 --- a/datacenterlight/models.py +++ b/datacenterlight/models.py @@ -36,22 +36,27 @@ class VMPricing(models.Model): ) discount_name = models.CharField(max_length=255, null=True, blank=True) discount_amount = models.DecimalField( - max_digits=4, decimal_places=2, default=0 + max_digits=6, decimal_places=2, default=0 ) def __str__(self): - return self.name + ' => ' + ' - '.join([ + display_str = self.name + ' => ' + ' - '.join([ '{}/Core'.format(self.cores_unit_price.normalize()), '{}/GB RAM'.format(self.ram_unit_price.normalize()), '{}/GB SSD'.format(self.ssd_unit_price.normalize()), '{}/GB HDD'.format(self.hdd_unit_price.normalize()), '{}% VAT'.format(self.vat_percentage.normalize()) if not self.vat_inclusive else 'VAT-Incl', - '{} {}'.format( - self.discount_amount if self.discount_amount else '', - self.discount_name if self.discount_name else 'Discount' - ), ]) + if self.discount_amount: + display_str = ' - '.join([ + display_str, + '{} {}'.format( + self.discount_amount, + self.discount_name if self.discount_name else 'Discount' + ) + ]) + return display_str @classmethod def get_vm_pricing_by_name(cls, name): diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 13d2c61e..3b269377 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -76,7 +76,7 @@ {{vm.vat|floatformat:2|intcomma}} CHF

{% endif %} - {% if vm_pricing.discount_amount %} + {% if vm.discount > 0 %}

{%trans "Discount" as discount_name %} {{ vm_pricing.discount_name|default:discount_name }}: diff --git a/datacenterlight/views.py b/datacenterlight/views.py index bc5ea49e..8f4c886f 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -158,7 +158,7 @@ class IndexView(CreateView): ) return HttpResponseRedirect(referer_url + "#order_form") - price, vat, vat_percent = get_vm_price_with_vat( + price, vat, vat_percent, discount = get_vm_price_with_vat( cpu=cores, memory=memory, ssd_size=storage, @@ -171,7 +171,8 @@ class IndexView(CreateView): 'price': price, 'vat': vat, 'vat_percent': vat_percent, - 'total_price': price + vat, + 'discount': discount, + 'total_price': price + vat - discount, 'pricing_name': vm_pricing_name } request.session['specs'] = specs @@ -388,9 +389,6 @@ class OrderConfirmationView(DetailView): request.session.get('billing_address_data') ), 'cms_integration': get_cms_integration('default'), - 'vm_pricing': VMPricing.get_vm_pricing_by_name( - self.request.session['specs']['pricing_name'] - ), } return render(request, self.template_name, context) diff --git a/hosting/views.py b/hosting/views.py index 1353229a..99897841 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -655,7 +655,7 @@ class PaymentVMView(LoginRequiredMixin, FormView): 'stripe_key': settings.STRIPE_API_PUBLIC_KEY, 'vm_pricing': VMPricing.get_vm_pricing_by_name( self.request.session['specs']['pricing_name'] - ) + ), }) return context @@ -753,7 +753,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): context['vm'] = vm_detail.__dict__ context['vm']['name'] = '{}-{}'.format( context['vm']['configuration'], context['vm']['vm_id']) - price, vat, vat_percent = get_vm_price_with_vat( + price, vat, vat_percent, discount = get_vm_price_with_vat( cpu=context['vm']['cores'], ssd_size=context['vm']['disk_size'], memory=context['vm']['memory'], @@ -762,8 +762,9 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): ) context['vm']['vat'] = vat context['vm']['price'] = price + context['vm']['discount'] = discount context['vm']['vat_percent'] = vat_percent - context['vm']['total_price'] = price + vat + context['vm']['total_price'] = price + vat - discount context['subscription_end_date'] = vm_detail.end_date() except VMDetail.DoesNotExist: try: @@ -772,7 +773,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): ) vm = manager.get_vm(obj.vm_id) context['vm'] = VirtualMachineSerializer(vm).data - price, vat, vat_percent = get_vm_price_with_vat( + price, vat, vat_percent, discount = get_vm_price_with_vat( cpu=context['vm']['cores'], ssd_size=context['vm']['disk_size'], memory=context['vm']['memory'], @@ -781,8 +782,9 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): ) context['vm']['vat'] = vat context['vm']['price'] = price + context['vm']['discount'] = discount context['vm']['vat_percent'] = vat_percent - context['vm']['total_price'] = price + vat + context['vm']['total_price'] = price + vat - discount except WrongIdError: messages.error( self.request, @@ -809,9 +811,6 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): context['cc_brand'] = card_details.get('response_object').get( 'cc_brand') context['vm'] = self.request.session.get('specs') - context['vm_pricing'] = VMPricing.get_vm_pricing_by_name( - self.request.session['specs']['pricing_name'] - ), return context @method_decorator(decorators) @@ -1071,7 +1070,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): extra_tags='storage') return redirect(CreateVirtualMachinesView.as_view()) - price, vat, vat_percent = get_vm_price_with_vat( + price, vat, vat_percent, discount = get_vm_price_with_vat( cpu=cores, memory=memory, ssd_size=storage, @@ -1085,7 +1084,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): 'price': price, 'vat': vat, 'vat_percent': vat_percent, - 'total_price': price + vat, + 'total_price': price + vat - discount, 'pricing_name': vm_pricing_name } diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index b6e267a2..9e96634f 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -111,8 +111,7 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, (decimal.Decimal(cpu) * pricing.cores_unit_price) + (decimal.Decimal(memory) * pricing.ram_unit_price) + (decimal.Decimal(ssd_size) * pricing.ssd_unit_price) + - (decimal.Decimal(hdd_size) * pricing.hdd_unit_price) - - pricing.discount_amount + (decimal.Decimal(hdd_size) * pricing.hdd_unit_price) ) if pricing.vat_inclusive: vat = decimal.Decimal(0) @@ -124,4 +123,5 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, cents = decimal.Decimal('.01') price = price.quantize(cents, decimal.ROUND_HALF_UP) vat = vat.quantize(cents, decimal.ROUND_HALF_UP) - return float(price), float(vat), float(vat_percent) + discount = pricing.discount_amount + return float(price), float(vat), float(vat_percent), float(discount) From eeed9b2e7214aacd2e1e3152de3d5628434083fb Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Mon, 7 May 2018 06:25:50 +0530 Subject: [PATCH 211/866] discount name in templates --- .../templates/datacenterlight/order_detail.html | 4 ++-- datacenterlight/views.py | 2 +- hosting/templates/hosting/order_detail.html | 6 +++--- hosting/views.py | 7 ++++--- utils/hosting_utils.py | 7 +++++-- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 3b269377..1bedbb44 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -79,8 +79,8 @@ {% if vm.discount > 0 %}

{%trans "Discount" as discount_name %} - {{ vm_pricing.discount_name|default:discount_name }}: - - {{ vm_pricing.discount_amount }} CHF + {{ vm.discount.name|default:discount_name }}: + - {{ discount.amount }} CHF

{% endif %}

diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 8f4c886f..79411fbb 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -172,7 +172,7 @@ class IndexView(CreateView): 'vat': vat, 'vat_percent': vat_percent, 'discount': discount, - 'total_price': price + vat - discount, + 'total_price': price + vat - discount.amount, 'pricing_name': vm_pricing_name } request.session['specs'] = specs diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index ec50528d..1ed75bd9 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -138,11 +138,11 @@ {{vm.vat|floatformat:2|intcomma}} CHF

{% endif %} - {% if vm_pricing.discount_amount %} + {% if vm.discount > 0 %}

{%trans "Discount" as discount_name %} - {{ vm_pricing.discount_name|default:discount_name }}: - - {{ vm_pricing.discount_amount }} CHF + {{ vm.discount.name|default:discount_name }}: + - {{ discount.amount }} CHF

{% endif %}

diff --git a/hosting/views.py b/hosting/views.py index 99897841..7b3cc357 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -764,7 +764,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): context['vm']['price'] = price context['vm']['discount'] = discount context['vm']['vat_percent'] = vat_percent - context['vm']['total_price'] = price + vat - discount + context['vm']['total_price'] = price + vat - discount.amount context['subscription_end_date'] = vm_detail.end_date() except VMDetail.DoesNotExist: try: @@ -784,7 +784,8 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): context['vm']['price'] = price context['vm']['discount'] = discount context['vm']['vat_percent'] = vat_percent - context['vm']['total_price'] = price + vat - discount + context['vm']['total_price'] = price + \ + vat - discount.amount except WrongIdError: messages.error( self.request, @@ -1084,7 +1085,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): 'price': price, 'vat': vat, 'vat_percent': vat_percent, - 'total_price': price + vat - discount, + 'total_price': price + vat - discount.amount, 'pricing_name': vm_pricing_name } diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 9e96634f..36964867 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -123,5 +123,8 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, cents = decimal.Decimal('.01') price = price.quantize(cents, decimal.ROUND_HALF_UP) vat = vat.quantize(cents, decimal.ROUND_HALF_UP) - discount = pricing.discount_amount - return float(price), float(vat), float(vat_percent), float(discount) + discount = { + 'name': pricing.discount_name, + 'amount': float(pricing.discount_amount), + } + return float(price), float(vat), float(vat_percent), discount From 3d2ce279548809c1de71ab6b13d4fdb343a20931 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Mon, 7 May 2018 06:29:53 +0530 Subject: [PATCH 212/866] fix discount amount --- datacenterlight/views.py | 2 +- hosting/views.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 79411fbb..ec10a341 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -172,7 +172,7 @@ class IndexView(CreateView): 'vat': vat, 'vat_percent': vat_percent, 'discount': discount, - 'total_price': price + vat - discount.amount, + 'total_price': price + vat - discount['amount'], 'pricing_name': vm_pricing_name } request.session['specs'] = specs diff --git a/hosting/views.py b/hosting/views.py index 7b3cc357..ec583a9b 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -764,7 +764,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): context['vm']['price'] = price context['vm']['discount'] = discount context['vm']['vat_percent'] = vat_percent - context['vm']['total_price'] = price + vat - discount.amount + context['vm']['total_price'] = price + vat - discount['amount'] context['subscription_end_date'] = vm_detail.end_date() except VMDetail.DoesNotExist: try: @@ -785,7 +785,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): context['vm']['discount'] = discount context['vm']['vat_percent'] = vat_percent context['vm']['total_price'] = price + \ - vat - discount.amount + vat - discount['amount'] except WrongIdError: messages.error( self.request, @@ -1085,7 +1085,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): 'price': price, 'vat': vat, 'vat_percent': vat_percent, - 'total_price': price + vat - discount.amount, + 'total_price': price + vat - discount['amount'], 'pricing_name': vm_pricing_name } From 0fdb88b8aa5659dafdd709f81edffb95bb7c4e80 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Mon, 7 May 2018 07:50:32 +0530 Subject: [PATCH 213/866] invoice discount amount fix --- datacenterlight/migrations/0022_auto_20180506_1950.py | 5 +++-- datacenterlight/templates/datacenterlight/order_detail.html | 4 ++-- hosting/templates/hosting/order_detail.html | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/datacenterlight/migrations/0022_auto_20180506_1950.py b/datacenterlight/migrations/0022_auto_20180506_1950.py index dd79b825..a5554a58 100644 --- a/datacenterlight/migrations/0022_auto_20180506_1950.py +++ b/datacenterlight/migrations/0022_auto_20180506_1950.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-05-06 14:20 +# Generated by Django 1.9.4 on 2018-05-07 02:19 from __future__ import unicode_literals from django.db import migrations, models @@ -15,7 +15,8 @@ class Migration(migrations.Migration): migrations.AddField( model_name='vmpricing', name='discount_amount', - field=models.DecimalField(decimal_places=2, default=0, max_digits=4), + field=models.DecimalField( + decimal_places=2, default=0, max_digits=6), ), migrations.AddField( model_name='vmpricing', diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 1bedbb44..fbe7ff0f 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -76,11 +76,11 @@ {{vm.vat|floatformat:2|intcomma}} CHF

{% endif %} - {% if vm.discount > 0 %} + {% if vm.discount.amount > 0 %}

{%trans "Discount" as discount_name %} {{ vm.discount.name|default:discount_name }}: - - {{ discount.amount }} CHF + - {{ vm.discount.amount }} CHF

{% endif %}

diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 1ed75bd9..11aa3474 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -138,11 +138,11 @@ {{vm.vat|floatformat:2|intcomma}} CHF

{% endif %} - {% if vm.discount > 0 %} + {% if vm.discount.amount > 0 %}

{%trans "Discount" as discount_name %} {{ vm.discount.name|default:discount_name }}: - - {{ discount.amount }} CHF + - {{ vm.discount.amount }} CHF

{% endif %}

From 55cbe3244a4625558804f491ea296ce26b48d3ff Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Mon, 7 May 2018 09:14:31 +0530 Subject: [PATCH 214/866] fix testing error --- hosting/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index ec583a9b..7623ed90 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -654,7 +654,7 @@ class PaymentVMView(LoginRequiredMixin, FormView): context.update({ 'stripe_key': settings.STRIPE_API_PUBLIC_KEY, 'vm_pricing': VMPricing.get_vm_pricing_by_name( - self.request.session['specs']['pricing_name'] + self.request.session.get('specs', {}).get('pricing_name') ), }) @@ -1010,7 +1010,6 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): @method_decorator(decorators) def get(self, request, *args, **kwargs): - print(get_cms_integration('default')) context = { 'templates': VMTemplate.objects.all(), 'cms_integration': get_cms_integration('default'), From b351cb9aa04d6d9f1f3675c3c99646144660cf75 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Thu, 10 May 2018 21:25:38 +0530 Subject: [PATCH 215/866] translation fix --- hosting/locale/de/LC_MESSAGES/django.po | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 42e46314..1404b594 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -377,10 +377,7 @@ msgstr "Rabatt" msgid "Total" msgstr "Gesamt" -#, fuzzy, python-format -#| msgid "" -#| "By clicking \"Place order\" this plan will charge your credit card " -#| "account with the fee of %(vm_price)sCHF/month" +#, python-format msgid "" "By clicking \"Place order\" this plan will charge your credit card account " "with the fee of %(vm_price|intcomma)sCHF/month" From f3ffbd96e5d1d31b60d51e378c4c9e23188bdcef Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Thu, 10 May 2018 21:26:47 +0530 Subject: [PATCH 216/866] translation fix --- hosting/locale/de/LC_MESSAGES/django.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 1404b594..b981d408 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -382,7 +382,7 @@ msgid "" "By clicking \"Place order\" this plan will charge your credit card account " "with the fee of %(vm_price|intcomma)sCHF/month" msgstr "" -"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_price)sCHF " +"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit %(vm_price|intcomma)sCHF " "pro Monat belastet" msgid "Place order" From 73e3dce8d495196005359aa8cb2880595cb2152f Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 11 May 2018 17:18:19 +0530 Subject: [PATCH 217/866] order detail page font format --- .../static/datacenterlight/css/hosting.css | 2 +- .../templates/datacenterlight/order_detail.html | 12 ++++++------ hosting/static/hosting/css/order.css | 2 +- hosting/templates/hosting/order_detail.html | 16 ++++++++-------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/datacenterlight/static/datacenterlight/css/hosting.css b/datacenterlight/static/datacenterlight/css/hosting.css index b4c5909c..87c40329 100644 --- a/datacenterlight/static/datacenterlight/css/hosting.css +++ b/datacenterlight/static/datacenterlight/css/hosting.css @@ -482,6 +482,7 @@ margin: 100px auto 40px; border: 1px solid #ccc; padding: 30px 30px 20px; + color: #595959; } .order-detail-container .dashboard-title-thin { @@ -515,7 +516,6 @@ .order-detail-container p { margin-bottom: 5px; - color: #595959; } .order-detail-container hr { diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index fbe7ff0f..4b52d4d5 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -59,33 +59,33 @@

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

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


{% if vm.vat > 0 %}

{% trans "Subtotal" %}: - {{vm.price|floatformat:2|intcomma}} CHF + {{vm.price|floatformat:2|intcomma}} CHF

{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%): - {{vm.vat|floatformat:2|intcomma}} CHF + {{vm.vat|floatformat:2|intcomma}} CHF

{% endif %} {% if vm.discount.amount > 0 %}

{%trans "Discount" as discount_name %} {{ vm.discount.name|default:discount_name }}: - - {{ vm.discount.amount }} CHF + - {{ vm.discount.amount }} CHF

{% endif %}

{% trans "Total" %} - {{vm.total_price|floatformat:2|intcomma}} CHF + {{vm.total_price|floatformat:2|intcomma}} CHF

diff --git a/hosting/static/hosting/css/order.css b/hosting/static/hosting/css/order.css index 27a67f3e..5d39f24b 100644 --- a/hosting/static/hosting/css/order.css +++ b/hosting/static/hosting/css/order.css @@ -3,6 +3,7 @@ margin: 100px auto 40px; border: 1px solid #ccc; padding: 15px; + color: #595959; } @media(min-width: 768px) { @@ -60,7 +61,6 @@ .order-detail-container p { margin-bottom: 5px; - color: #595959; } .order-detail-container hr { diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 11aa3474..d08a7151 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -114,40 +114,40 @@

{% trans "Cores" %}: {% if vm.cores %} - {{vm.cores|floatformat}} + {{vm.cores|floatformat}} {% else %} - {{vm.cpu|floatformat}} + {{vm.cpu|floatformat}} {% endif %}

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

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


{% if vm.vat > 0 %}

{% trans "Subtotal" %}: - {{vm.price|floatformat:2|intcomma}} CHF + {{vm.price|floatformat:2|intcomma}} CHF

{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%): - {{vm.vat|floatformat:2|intcomma}} CHF + {{vm.vat|floatformat:2|intcomma}} CHF

{% endif %} {% if vm.discount.amount > 0 %}

{%trans "Discount" as discount_name %} {{ vm.discount.name|default:discount_name }}: - - {{ vm.discount.amount }} CHF + - {{ vm.discount.amount }} CHF

{% endif %}

{% trans "Total" %} - {% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF + {% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF

From a14407182ff3983859b43e05bcc5fc790a42773c Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 11 May 2018 17:21:02 +0530 Subject: [PATCH 218/866] font weight for discount name --- datacenterlight/templates/datacenterlight/order_detail.html | 4 ++-- hosting/templates/hosting/order_detail.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 4b52d4d5..dfa6634e 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -55,7 +55,7 @@

{% trans "Cores" %}: - {{vm.cpu|floatformat}} + {{vm.cpu|floatformat}}

{% trans "Memory" %}: @@ -79,7 +79,7 @@ {% if vm.discount.amount > 0 %}

{%trans "Discount" as discount_name %} - {{ vm.discount.name|default:discount_name }}: + {{ vm.discount.name|default:discount_name }}: - {{ vm.discount.amount }} CHF

{% endif %} diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index d08a7151..9dea5359 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -141,7 +141,7 @@ {% if vm.discount.amount > 0 %}

{%trans "Discount" as discount_name %} - {{ vm.discount.name|default:discount_name }}: + {{ vm.discount.name|default:discount_name }}: - {{ vm.discount.amount }} CHF

{% endif %} From 30deae5a201ec75ad0e7047d8e9949e2cfde142a Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 11 May 2018 17:25:44 +0530 Subject: [PATCH 219/866] strong color fix --- datacenterlight/static/datacenterlight/css/hosting.css | 4 ---- hosting/static/hosting/css/order.css | 4 ---- 2 files changed, 8 deletions(-) diff --git a/datacenterlight/static/datacenterlight/css/hosting.css b/datacenterlight/static/datacenterlight/css/hosting.css index 87c40329..047f4922 100644 --- a/datacenterlight/static/datacenterlight/css/hosting.css +++ b/datacenterlight/static/datacenterlight/css/hosting.css @@ -504,10 +504,6 @@ margin-bottom: 15px; } -.order-detail-container .order-details strong { - color: #595959; -} - .order-detail-container h4 { font-size: 16px; font-weight: bold; diff --git a/hosting/static/hosting/css/order.css b/hosting/static/hosting/css/order.css index 5d39f24b..fa932798 100644 --- a/hosting/static/hosting/css/order.css +++ b/hosting/static/hosting/css/order.css @@ -49,10 +49,6 @@ margin-bottom: 15px; } -.order-detail-container .order-details strong { - color: #595959; -} - .order-detail-container h4 { font-size: 16px; font-weight: bold; From 8044e0c2a0f30c057d1ed69d61b2410e3e608c38 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 11 May 2018 17:47:27 +0530 Subject: [PATCH 220/866] calculator discount text modified --- .../templates/datacenterlight/includes/_calculator_form.html | 3 +-- hosting/views.py | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html index dfc0bf22..4b4aa04f 100644 --- a/datacenterlight/templates/datacenterlight/includes/_calculator_form.html +++ b/datacenterlight/templates/datacenterlight/includes/_calculator_form.html @@ -24,8 +24,7 @@

{% if vm_pricing.vat_inclusive %}{% trans "VAT included" %}
{% endif %} {% if vm_pricing.discount_amount %} - {% trans "Discount" as discount_name %} - {{ vm_pricing.discount_amount }} CHF {{ vm_pricing.discount_name|default:discount_name }} included + You save {{ vm_pricing.discount_amount }} CHF {% endif %}

diff --git a/hosting/views.py b/hosting/views.py index 7623ed90..8a4defda 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1081,6 +1081,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): 'cpu': cores, 'memory': memory, 'disk_size': storage, + 'discount': discount, 'price': price, 'vat': vat, 'vat_percent': vat_percent, From 55889499df0527fd1ae957352a5b7a782b03b0f8 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Sat, 12 May 2018 02:47:27 +0530 Subject: [PATCH 221/866] order detail style fix --- .../static/datacenterlight/css/hosting.css | 16 +++++++ .../datacenterlight/order_detail.html | 45 ++++++++++--------- hosting/static/hosting/css/order.css | 16 +++++++ hosting/templates/hosting/order_detail.html | 45 ++++++++++--------- 4 files changed, 82 insertions(+), 40 deletions(-) diff --git a/datacenterlight/static/datacenterlight/css/hosting.css b/datacenterlight/static/datacenterlight/css/hosting.css index 047f4922..0f16ab77 100644 --- a/datacenterlight/static/datacenterlight/css/hosting.css +++ b/datacenterlight/static/datacenterlight/css/hosting.css @@ -518,6 +518,22 @@ margin: 15px 0; } +.order-detail-container .thin-hr { + margin: 10px 0; +} + +.order-detail-container .subtotal-price { + font-size: 16px; +} + +.order-detail-container .subtotal-price .text-primary { + font-size: 17px; +} + +.order-detail-container .total-price { + font-size: 18px; +} + @media (max-width: 767px) { .order-detail-container { padding: 15px; diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index dfa6634e..5515435a 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -65,32 +65,37 @@ {% trans "Disk space" %}: {{vm.disk_size|intcomma}} GB

-
- {% if vm.vat > 0 %} -

- {% trans "Subtotal" %}: - {{vm.price|floatformat:2|intcomma}} CHF -

-

- {% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%): - {{vm.vat|floatformat:2|intcomma}} CHF -

+
+ {% if vm.vat > 0 or vm.discount.amount > 0 %} +
+ {% if vm.vat > 0 %} +

+ {% trans "Subtotal" %} + {{vm.price|floatformat:2|intcomma}} CHF +

+

+ {% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%) + {{vm.vat|floatformat:2|intcomma}} CHF +

+ {% endif %} + {% if vm.discount.amount > 0 %} +

+ {%trans "Discount" as discount_name %} + {{ vm.discount.name|default:discount_name }} + - {{ vm.discount.amount }} CHF +

+ {% endif %} +
+
{% endif %} - {% if vm.discount.amount > 0 %} -

- {%trans "Discount" as discount_name %} - {{ vm.discount.name|default:discount_name }}: - - {{ vm.discount.amount }} CHF -

- {% endif %} -

- {% trans "Total" %} +

+ {% trans "Total" %} {{vm.total_price|floatformat:2|intcomma}} CHF

-
+
{% csrf_token %} diff --git a/hosting/static/hosting/css/order.css b/hosting/static/hosting/css/order.css index fa932798..8aafb8a8 100644 --- a/hosting/static/hosting/css/order.css +++ b/hosting/static/hosting/css/order.css @@ -63,6 +63,22 @@ margin: 15px 0; } +.order-detail-container .thin-hr { + margin: 10px 0; +} + +.order-detail-container .subtotal-price { + font-size: 16px; +} + +.order-detail-container .subtotal-price .text-primary { + font-size: 17px; +} + +.order-detail-container .total-price { + font-size: 18px; +} + @media (max-width: 767px) { .order-confirm-btn { text-align: center; diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 9dea5359..d84ed3d3 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -127,32 +127,37 @@ {% trans "Disk space" %}: {{vm.disk_size}} GB

-
- {% if vm.vat > 0 %} -

- {% trans "Subtotal" %}: - {{vm.price|floatformat:2|intcomma}} CHF -

-

- {% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%): - {{vm.vat|floatformat:2|intcomma}} CHF -

+
+ {% if vm.vat > 0 or vm.discount.amount > 0 %} +
+ {% if vm.vat > 0 %} +

+ {% trans "Subtotal" %} + {{vm.price|floatformat:2|intcomma}} CHF +

+

+ {% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%) + {{vm.vat|floatformat:2|intcomma}} CHF +

+ {% endif %} + {% if vm.discount.amount > 0 %} +

+ {%trans "Discount" as discount_name %} + {{ vm.discount.name|default:discount_name }} + - {{ vm.discount.amount }} CHF +

+ {% endif %} +
+
{% endif %} - {% if vm.discount.amount > 0 %} -

- {%trans "Discount" as discount_name %} - {{ vm.discount.name|default:discount_name }}: - - {{ vm.discount.amount }} CHF -

- {% endif %} -

- {% trans "Total" %} +

+ {% trans "Total" %} {% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF

-
+
{% if not order %} {% block submit_btn %} From 39f7898259b2ff627103bf8f69bf9b18426a2f3c Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Sat, 12 May 2018 03:15:07 +0530 Subject: [PATCH 222/866] edit order detail footer text --- datacenterlight/locale/de/LC_MESSAGES/django.po | 10 +++++----- .../templates/datacenterlight/order_detail.html | 2 +- hosting/locale/de/LC_MESSAGES/django.po | 8 ++++---- hosting/templates/hosting/order_detail.html | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index cd92b339..9ac4f59a 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: 2018-05-07 05:15+0530\n" +"POT-Creation-Date: 2018-05-12 03:12+0530\n" "PO-Revision-Date: 2018-03-30 23:22+0000\n" "Last-Translator: b'Anonymous User '\n" "Language-Team: LANGUAGE \n" @@ -143,9 +143,6 @@ msgstr "Monat" msgid "VAT included" msgstr "MwSt. inklusive" -msgid "Discount" -msgstr "Rabatt" - msgid "Hosted in Switzerland" msgstr "Standort: Schweiz" @@ -320,6 +317,9 @@ msgstr "exkl. Mehrwertsteuer" msgid "Month" msgstr "Monat" +msgid "Discount" +msgstr "Rabatt" + msgid "Will be applied at checkout" msgstr "wird an der Kasse angewendet" @@ -398,7 +398,7 @@ msgstr "Mehrwertsteuer" #, python-format msgid "" "By clicking \"Place order\" this plan will charge your credit card account " -"with the fee of %(vm_total_price)s CHF/month" +"with %(vm_total_price)s CHF/month" msgstr "" "Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit " "%(vm_total_price)s CHF pro Monat belastet" diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 5515435a..8480e132 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -101,7 +101,7 @@ {% csrf_token %}
-
{% blocktrans with vm_total_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with the fee of {{vm_total_price}} CHF/month{% endblocktrans %}.
+
{% blocktrans with vm_total_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{vm_total_price}} CHF/month{% endblocktrans %}.
From 8fb0d9a48acdaf1b9607d1cdb5bb510171da6f1b Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Sat, 12 May 2018 21:59:06 +0530 Subject: [PATCH 227/866] order detail divider lines full width --- .../templates/datacenterlight/order_detail.html | 12 ++++++++++-- hosting/templates/hosting/order_detail.html | 12 ++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 8480e132..49347ba2 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -65,8 +65,12 @@ {% trans "Disk space" %}: {{vm.disk_size|intcomma}} GB

+
+

- {% if vm.vat > 0 or vm.discount.amount > 0 %} +
+ {% if vm.vat > 0 or vm.discount.amount > 0 %} +
{% if vm.vat > 0 %}

@@ -86,8 +90,12 @@

{% endif %}
+
+

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

{% trans "Total" %} {{vm.total_price|floatformat:2|intcomma}} CHF diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 7def5b49..e2e38c35 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -127,8 +127,12 @@ {% trans "Disk space" %}: {{vm.disk_size}} GB

+
+

- {% if vm.vat > 0 or vm.discount.amount > 0 %} +
+ {% if vm.vat > 0 or vm.discount.amount > 0 %} +
{% if vm.vat > 0 %}

@@ -148,8 +152,12 @@

{% endif %}
+
+

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

{% trans "Total" %} {% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF From cba53e0fe3966331eee18611a7363f190d3c3319 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Thu, 17 May 2018 01:01:50 +0530 Subject: [PATCH 228/866] Update Changelog --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index 2c2877ab..8146462d 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +1.9: 2018-05-16 + * #4559: [cms] enable discount on cms calculator 1.8: 2018-05-01 * #4527: [hosting] cms calculator on non-cms pages for the hosting app * bgfix: [dcl] navbar dropdown target fix From a4ca17e2edfdb30fb2bdba8d277d6ff0837119f9 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Thu, 24 May 2018 03:57:01 +0530 Subject: [PATCH 229/866] vm template prefix --- datacenterlight/admin.py | 3 ++- datacenterlight/cms_models.py | 6 ++++- datacenterlight/cms_plugins.py | 4 ++- .../migrations/0023_auto_20180524_0349.py | 25 +++++++++++++++++++ datacenterlight/models.py | 20 +++++++++++++-- opennebula_api/models.py | 10 ++++---- 6 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 datacenterlight/migrations/0023_auto_20180524_0349.py diff --git a/datacenterlight/admin.py b/datacenterlight/admin.py index d95e4f87..5a1fc8a2 100644 --- a/datacenterlight/admin.py +++ b/datacenterlight/admin.py @@ -2,7 +2,7 @@ from django.contrib import admin from cms.admin.placeholderadmin import PlaceholderAdminMixin from cms.extensions import PageExtensionAdmin from .cms_models import CMSIntegration, CMSFaviconExtension -from .models import VMPricing +from .models import VMPricing, VMTemplate class CMSIntegrationAdmin(PlaceholderAdminMixin, admin.ModelAdmin): @@ -16,3 +16,4 @@ class CMSFaviconExtensionAdmin(PageExtensionAdmin): admin.site.register(CMSIntegration, CMSIntegrationAdmin) admin.site.register(CMSFaviconExtension, CMSFaviconExtensionAdmin) admin.site.register(VMPricing) +admin.site.register(VMTemplate) diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index 5a8d7ac8..e1703aaa 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -9,7 +9,7 @@ from djangocms_text_ckeditor.fields import HTMLField from filer.fields.file import FilerFileField from filer.fields.image import FilerImageField -from datacenterlight.models import VMPricing +from datacenterlight.models import VMPricing, VMTemplate class CMSIntegration(models.Model): @@ -299,3 +299,7 @@ class DCLCalculatorPluginModel(CMSPlugin): help_text='Choose a pricing that will be associated with this ' 'Calculator' ) + vm_type = models.CharField( + max_length=50, choices=VMTemplate.VM_TYPE_CHOICES, + default=VMTemplate.PUBLIC + ) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 12de0daf..769824e0 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -88,7 +88,9 @@ class DCLCalculatorPlugin(CMSPluginBase): context = super(DCLCalculatorPlugin, self).render( context, instance, placeholder ) - context['templates'] = VMTemplate.objects.all() + context['templates'] = VMTemplate.objects.filter( + vm_type=instance.vm_type + ) return context diff --git a/datacenterlight/migrations/0023_auto_20180524_0349.py b/datacenterlight/migrations/0023_auto_20180524_0349.py new file mode 100644 index 00000000..f37d6634 --- /dev/null +++ b/datacenterlight/migrations/0023_auto_20180524_0349.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-05-23 22:19 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0022_auto_20180506_1950'), + ] + + operations = [ + migrations.AddField( + model_name='dclcalculatorpluginmodel', + name='vm_type', + field=models.CharField(choices=[('public', 'Public'), ('ipv6only', 'Ipv6Only')], default='public', max_length=50), + ), + migrations.AddField( + model_name='vmtemplate', + name='vm_type', + field=models.CharField(choices=[('public', 'Public'), ('ipv6only', 'Ipv6Only')], default='public', max_length=50), + ), + ] diff --git a/datacenterlight/models.py b/datacenterlight/models.py index ff7eeb8d..729bbdf9 100644 --- a/datacenterlight/models.py +++ b/datacenterlight/models.py @@ -6,13 +6,29 @@ logger = logging.getLogger(__name__) class VMTemplate(models.Model): + PUBLIC = 'public' + IPV6 = 'ipv6only' + VM_TYPE_CHOICES = ( + (PUBLIC, PUBLIC.title()), + (IPV6, IPV6.title()), + ) name = models.CharField(max_length=50) opennebula_vm_template_id = models.IntegerField() + vm_type = models.CharField( + max_length=50, choices=VM_TYPE_CHOICES, default=PUBLIC + ) + + def __str__(self): + return '%s - %s - %s' % ( + self.opennebula_vm_template_id, self.vm_type, self.name + ) @classmethod - def create(cls, name, opennebula_vm_template_id): + def create(cls, name, opennebula_vm_template_id, vm_type): vm_template = cls( - name=name, opennebula_vm_template_id=opennebula_vm_template_id) + name=name, opennebula_vm_template_id=opennebula_vm_template_id, + vm_type=vm_type + ) return vm_template diff --git a/opennebula_api/models.py b/opennebula_api/models.py index d9b0b6c2..35f3d8e8 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -61,7 +61,7 @@ class OpenNebulaManager(): domain=settings.OPENNEBULA_DOMAIN, port=settings.OPENNEBULA_PORT, endpoint=settings.OPENNEBULA_ENDPOINT - )) + )) def _get_opennebula_client(self, username, password): return oca.Client("{0}:{1}".format( @@ -73,7 +73,7 @@ class OpenNebulaManager(): domain=settings.OPENNEBULA_DOMAIN, port=settings.OPENNEBULA_PORT, endpoint=settings.OPENNEBULA_ENDPOINT - )) + )) def _get_user(self, user): """Get the corresponding opennebula user for a CustomUser object @@ -362,12 +362,12 @@ class OpenNebulaManager(): except: raise ConnectionRefusedError - def get_templates(self): + def get_templates(self, prefix='public-'): try: public_templates = [ template for template in self._get_template_pool() - if template.name.startswith('public-') + if template.name.startswith(prefix) ] return public_templates except ConnectionRefusedError: @@ -439,7 +439,7 @@ class OpenNebulaManager(): def delete_template(self, template_id): self.oneadmin_client.call(oca.VmTemplate.METHODS[ - 'delete'], template_id, False) + 'delete'], template_id, False) def change_user_password(self, passwd_hash): self.oneadmin_client.call( From 2d1805f11d3426858cbd8f00eb526ed2183a6794 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Thu, 24 May 2018 04:14:53 +0530 Subject: [PATCH 230/866] update fetchvmtemplates command --- .../management/commands/fetchvmtemplates.py | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/datacenterlight/management/commands/fetchvmtemplates.py b/datacenterlight/management/commands/fetchvmtemplates.py index 6a45ebad..89271dc4 100644 --- a/datacenterlight/management/commands/fetchvmtemplates.py +++ b/datacenterlight/management/commands/fetchvmtemplates.py @@ -10,16 +10,28 @@ class Command(BaseCommand): help = '''Fetches the VM templates from OpenNebula and populates the dcl VMTemplate model''' + def get_templates(self, manager, prefix): + templates = manager.get_templates('%s-' % prefix) + dcl_vm_templates = [] + for template in templates: + template_name = template.name.lstrip('%s-' % prefix) + template_id = template.id + dcl_vm_template = VMTemplate.create( + template_name, template_id, prefix + ) + dcl_vm_templates.append(dcl_vm_template) + return dcl_vm_templates + def handle(self, *args, **options): try: manager = OpenNebulaManager() - templates = manager.get_templates() dcl_vm_templates = [] - for template in templates: - template_name = template.name.lstrip('public-') - template_id = template.id - dcl_vm_template = VMTemplate.create(template_name, template_id) - dcl_vm_templates.append(dcl_vm_template) + dcl_vm_templates.extend( + self.get_templates(manager, VMTemplate.PUBLIC) + ) + dcl_vm_templates.extend( + self.get_templates(manager, VMTemplate.IPV6) + ) old_vm_templates = VMTemplate.objects.all() old_vm_templates.delete() From 57eda625864732695e47f95b5dde37e3f0c4cd7a Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 12 Jun 2018 08:28:46 +0200 Subject: [PATCH 231/866] Update UserCardDetail migration --- .../{0044_usercarddetail.py => 0045_usercarddetail.py} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename hosting/migrations/{0044_usercarddetail.py => 0045_usercarddetail.py} (89%) diff --git a/hosting/migrations/0044_usercarddetail.py b/hosting/migrations/0045_usercarddetail.py similarity index 89% rename from hosting/migrations/0044_usercarddetail.py rename to hosting/migrations/0045_usercarddetail.py index 2900a806..14785689 100644 --- a/hosting/migrations/0044_usercarddetail.py +++ b/hosting/migrations/0045_usercarddetail.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2017-10-21 10:26 +# Generated by Django 1.9.4 on 2018-06-12 06:28 from __future__ import unicode_literals from django.db import migrations, models @@ -10,8 +10,8 @@ import utils.mixins class Migration(migrations.Migration): dependencies = [ - ('membership', '0006_auto_20160526_0445'), - ('hosting', '0043_vmdetail'), + ('membership', '0007_auto_20180213_0128'), + ('hosting', '0044_hostingorder_vm_pricing'), ] operations = [ From 7494116468d63c96d1f178b80a6587bb9f96d448 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 12 Jun 2018 09:36:00 +0200 Subject: [PATCH 232/866] Add missing import --- hosting/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hosting/views.py b/hosting/views.py index 49937102..8ad15afa 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -34,6 +34,7 @@ from stored_messages.settings import stored_messages_settings from datacenterlight.models import VMTemplate, VMPricing from datacenterlight.tasks import create_vm_task from datacenterlight.utils import get_cms_integration +from hosting.models import UserCardDetail from membership.models import CustomUser, StripeCustomer from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import ( From 23630d44735bec8a50c09cc935907977f50fec0a Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 12 Jun 2018 11:11:37 +0200 Subject: [PATCH 233/866] Move csrf_token call outside the for loop --- hosting/templates/hosting/payment.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index befac44d..b473f31a 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -105,8 +105,8 @@

{%trans "Billing Address"%}


- {% for field in form %} {% csrf_token %} + {% for field in form %} {% bootstrap_field field show_label=False type='fields'%} {% endfor %} From 86f0526773ff57a93cf140759875600922ba9bf7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 12 Jun 2018 11:13:10 +0200 Subject: [PATCH 234/866] Reformat code --- hosting/views.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 8ad15afa..b63bae9f 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -14,12 +14,12 @@ from django.core.files.base import ContentFile from django.core.urlresolvers import reverse_lazy, reverse from django.http import Http404, HttpResponseRedirect, HttpResponse from django.shortcuts import redirect, render +from django.utils.decorators import method_decorator from django.utils.html import escape from django.utils.http import urlsafe_base64_decode from django.utils.safestring import mark_safe from django.utils.translation import get_language, ugettext_lazy as _ from django.utils.translation import ugettext -from django.utils.decorators import method_decorator from django.views.decorators.cache import never_cache from django.views.generic import ( View, CreateView, FormView, ListView, DetailView, DeleteView, @@ -895,8 +895,9 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): context['vm']['price'] = price context['vm']['discount'] = discount context['vm']['vat_percent'] = vat_percent - context['vm']['total_price'] = price + \ - vat - discount['amount'] + context['vm']['total_price'] = ( + price + vat - discount['amount'] + ) except WrongIdError: messages.error( self.request, From 6db38d7e292959bfef78355225d3ef39144f8bcb Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 12 Jun 2018 11:14:36 +0200 Subject: [PATCH 235/866] Check card_id also for order confirmation --- hosting/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index b63bae9f..47ed44a7 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -945,7 +945,9 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): return HttpResponseRedirect( reverse('hosting:create_virtual_machine') ) - if 'token' not in self.request.session: + + if ('token' not in self.request.session and + 'card_id' not in self.request.session): return HttpResponseRedirect(reverse('hosting:payment')) self.object = self.get_object() context = self.get_context_data(object=self.object) From fae9fce5c6be35851d1ce10617c415c7e6f1474d Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 12 Jun 2018 11:15:17 +0200 Subject: [PATCH 236/866] Remove unnecessary code --- hosting/views.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 47ed44a7..dea6447d 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -834,12 +834,6 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): ).get_context_data(**kwargs) obj = self.get_object() owner = self.request.user - stripe_api_cus_id = self.request.session.get('customer') - stripe_utils = StripeUtils() - card_details = stripe_utils.get_card_details( - stripe_api_cus_id, - self.request.session.get('token') - ) if self.request.GET.get('page') == 'payment': context['page_header_text'] = _('Confirm Order') @@ -912,10 +906,6 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): _('In order to create a VM, you need to create/upload ' 'your SSH KEY first.') ) - elif not card_details.get('response_object'): - # new order, failed to get card details - context['failed_payment'] = True - context['card_details'] = card_details else: # new order, confirm payment if 'token' in self.request.session: From 5748eecedb665675ae62b4bf5eb33dc55209c52f Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 13 Jun 2018 11:16:49 +0200 Subject: [PATCH 237/866] Create field for storing os_templates_to_show --- datacenterlight/models.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/datacenterlight/models.py b/datacenterlight/models.py index 729bbdf9..3801465a 100644 --- a/datacenterlight/models.py +++ b/datacenterlight/models.py @@ -1,5 +1,7 @@ import logging +from django import forms +from django.contrib.postgres.fields import ArrayField from django.db import models logger = logging.getLogger(__name__) @@ -32,7 +34,31 @@ class VMTemplate(models.Model): return vm_template +class MultipleChoiceArrayField(ArrayField): + """ + A field that allows us to store an array of choices. + Uses Django's Postgres ArrayField + and a MultipleChoiceField for its formfield. + """ + + def formfield(self, **kwargs): + defaults = { + 'form_class': forms.MultipleChoiceField, + 'choices': self.base_field.choices, + 'initial': [c[0] for c in self.base_field.choices], + } + defaults.update(kwargs) + # Skip our parent's formfield implementation completely as we don't + # care for it. + # pylint:disable=bad-super-call + return super(ArrayField, self).formfield(**defaults) + + class VMPricing(models.Model): + VMTemplateChoices = list( + (str(obj.opennebula_vm_template_id), obj.name) + for obj in VMTemplate.objects.all() + ) name = models.CharField(max_length=255, unique=True) vat_inclusive = models.BooleanField(default=True) vat_percentage = models.DecimalField( @@ -55,6 +81,18 @@ class VMPricing(models.Model): max_digits=6, decimal_places=2, default=0 ) + vm_templates_to_show = MultipleChoiceArrayField( + base_field=models.CharField( + blank=True, + max_length=256, + choices=VMTemplateChoices + ), + default=list, + blank=True, + help_text="Not selecting any items above will result in showing all " + "templates" + ) + def __str__(self): display_str = self.name + ' => ' + ' - '.join([ '{}/Core'.format(self.cores_unit_price.normalize()), From 94f520be355754a9164014f2569cf248441f4337 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 13 Jun 2018 11:17:18 +0200 Subject: [PATCH 238/866] Add migration --- .../0024_vmpricing_vm_templates_to_show.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py diff --git a/datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py b/datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py new file mode 100644 index 00000000..5705df5b --- /dev/null +++ b/datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-06-13 09:09 +from __future__ import unicode_literals + +import datacenterlight.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0023_auto_20180524_0349'), + ] + + operations = [ + migrations.AddField( + model_name='vmpricing', + name='vm_templates_to_show', + field=datacenterlight.models.MultipleChoiceArrayField(base_field=models.CharField(blank=True, choices=[('4', 'CentOS 7'), ('14', 'Debian 8'), ('25', 'Ubuntu 14.04'), ('26', 'Ubuntu 16.04'), ('36', 'Devuan Jessie'), ('65', 'Devuan Ascii'), ('69', 'FreeBSD 11.1')], max_length=256), blank=True, default=list, help_text='Not selecting any items above will result in showing all templates', size=None), + ), + ] From b872777bda964c36dd65073f03650db25e9a977c Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 13 Jun 2018 11:53:43 +0200 Subject: [PATCH 239/866] Filter context templates also by the ids that have been set for the calculator --- datacenterlight/cms_plugins.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 769824e0..9c6279bc 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -88,9 +88,15 @@ class DCLCalculatorPlugin(CMSPluginBase): context = super(DCLCalculatorPlugin, self).render( context, instance, placeholder ) - context['templates'] = VMTemplate.objects.filter( - vm_type=instance.vm_type - ) + ids = instance.pricing.vm_templates_to_show + if ids: + context['templates'] = VMTemplate.objects.filter( + vm_type=instance.vm_type + ).filter(opennebula_vm_template_id__in=ids) + else: + context['templates'] = VMTemplate.objects.filter( + vm_type=instance.vm_type + ) return context From dcbb0c2d64bb217e96a2921c115e8048d8f2b3c7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 19 Jun 2018 08:49:21 +0200 Subject: [PATCH 240/866] Add google analytics code for comic.ungleich.ch --- dynamicweb/settings/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index f540e998..75dfaa73 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -630,6 +630,7 @@ GOOGLE_ANALYTICS_PROPERTY_IDS = { 'ipv6onlyhosting.ch': 'UA-62285904-10', 'ipv6onlyhosting.net': 'UA-62285904-10', 'ipv6onlyhosting.com': 'UA-62285904-10', + 'comic.ungleich.ch': 'UA-62285904-13', '127.0.0.1:8000': 'localhost', 'dynamicweb-development.ungleich.ch': 'development', 'dynamicweb-staging.ungleich.ch': 'staging' From 79e83b4480e731b1ac8730227a2c691f74fe4b27 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 24 Jun 2018 09:08:22 +0200 Subject: [PATCH 241/866] Refactor show_vm_templates to DCLCalculatorPluginModel from VMPricing --- datacenterlight/cms_models.py | 45 +++++++++++++++++++ datacenterlight/cms_plugins.py | 2 +- ...culatorpluginmodel_vm_templates_to_show.py | 21 +++++++++ .../0024_vmpricing_vm_templates_to_show.py | 21 --------- datacenterlight/models.py | 38 ---------------- 5 files changed, 67 insertions(+), 60 deletions(-) create mode 100644 datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py delete mode 100644 datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index e1703aaa..deb84dc7 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -2,6 +2,8 @@ from cms.extensions import PageExtension from cms.extensions.extension_pool import extension_pool from cms.models.fields import PlaceholderField from cms.models.pluginmodel import CMSPlugin +from django import forms +from django.contrib.postgres.fields import ArrayField from django.contrib.sites.models import Site from django.db import models from django.utils.safestring import mark_safe @@ -292,7 +294,35 @@ class DCLSectionPromoPluginModel(CMSPlugin): return extra_classes +class MultipleChoiceArrayField(ArrayField): + """ + A field that allows us to store an array of choices. + Uses Django's Postgres ArrayField + and a MultipleChoiceField for its formfield. + """ + + def formfield(self, **kwargs): + defaults = { + 'form_class': forms.MultipleChoiceField, + 'choices': self.base_field.choices, + } + defaults.update(kwargs) + # Skip our parent's formfield implementation completely as we don't + # care for it. + # pylint:disable=bad-super-call + return super(ArrayField, self).formfield(**defaults) + + class DCLCalculatorPluginModel(CMSPlugin): + VMTemplateChoices = list( + ( + str(obj.opennebula_vm_template_id), + (obj.name + ' - ' + VMTemplate.IPV6.title() + if obj.vm_type == VMTemplate.IPV6 else obj.name + ) + ) + for obj in VMTemplate.objects.all() + ) pricing = models.ForeignKey( VMPricing, related_name="dcl_custom_pricing_vm_pricing", @@ -303,3 +333,18 @@ class DCLCalculatorPluginModel(CMSPlugin): max_length=50, choices=VMTemplate.VM_TYPE_CHOICES, default=VMTemplate.PUBLIC ) + vm_templates_to_show = MultipleChoiceArrayField( + base_field=models.CharField( + blank=True, + max_length=256, + choices=VMTemplateChoices + ), + default=list, + blank=True, + help_text="Recommended: If you wish to show all templates of the " + "corresponding VM Type (public/ipv6only), please do not " + "select any of the items in the above field. " + "This will allow any new template(s) added " + "in the backend to be automatically listed in this " + "calculator instance." + ) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 9c6279bc..95a496d8 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -88,7 +88,7 @@ class DCLCalculatorPlugin(CMSPluginBase): context = super(DCLCalculatorPlugin, self).render( context, instance, placeholder ) - ids = instance.pricing.vm_templates_to_show + ids = instance.vm_templates_to_show if ids: context['templates'] = VMTemplate.objects.filter( vm_type=instance.vm_type diff --git a/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py b/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py new file mode 100644 index 00000000..179dcff9 --- /dev/null +++ b/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-06-24 06:54 +from __future__ import unicode_literals + +import datacenterlight.cms_models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0023_auto_20180524_0349'), + ] + + operations = [ + migrations.AddField( + model_name='dclcalculatorpluginmodel', + name='vm_templates_to_show', + field=datacenterlight.cms_models.MultipleChoiceArrayField(base_field=models.CharField(blank=True, choices=[('4', 'CentOS 7'), ('14', 'Debian 8'), ('25', 'Ubuntu 14.04'), ('26', 'Ubuntu 16.04'), ('36', 'Devuan Jessie'), ('65', 'Devuan Ascii'), ('69', 'FreeBSD 11.1')], max_length=256), blank=True, default=list, help_text='Recommended: If you wish to show all templates of the corresponding VM Type (public/ipv6only), please do not select any of the items in the above field. This will allow any new template(s) added in the backend to be automatically listed in this calculator instance.', size=None), + ), + ] diff --git a/datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py b/datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py deleted file mode 100644 index 5705df5b..00000000 --- a/datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-06-13 09:09 -from __future__ import unicode_literals - -import datacenterlight.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('datacenterlight', '0023_auto_20180524_0349'), - ] - - operations = [ - migrations.AddField( - model_name='vmpricing', - name='vm_templates_to_show', - field=datacenterlight.models.MultipleChoiceArrayField(base_field=models.CharField(blank=True, choices=[('4', 'CentOS 7'), ('14', 'Debian 8'), ('25', 'Ubuntu 14.04'), ('26', 'Ubuntu 16.04'), ('36', 'Devuan Jessie'), ('65', 'Devuan Ascii'), ('69', 'FreeBSD 11.1')], max_length=256), blank=True, default=list, help_text='Not selecting any items above will result in showing all templates', size=None), - ), - ] diff --git a/datacenterlight/models.py b/datacenterlight/models.py index 3801465a..729bbdf9 100644 --- a/datacenterlight/models.py +++ b/datacenterlight/models.py @@ -1,7 +1,5 @@ import logging -from django import forms -from django.contrib.postgres.fields import ArrayField from django.db import models logger = logging.getLogger(__name__) @@ -34,31 +32,7 @@ class VMTemplate(models.Model): return vm_template -class MultipleChoiceArrayField(ArrayField): - """ - A field that allows us to store an array of choices. - Uses Django's Postgres ArrayField - and a MultipleChoiceField for its formfield. - """ - - def formfield(self, **kwargs): - defaults = { - 'form_class': forms.MultipleChoiceField, - 'choices': self.base_field.choices, - 'initial': [c[0] for c in self.base_field.choices], - } - defaults.update(kwargs) - # Skip our parent's formfield implementation completely as we don't - # care for it. - # pylint:disable=bad-super-call - return super(ArrayField, self).formfield(**defaults) - - class VMPricing(models.Model): - VMTemplateChoices = list( - (str(obj.opennebula_vm_template_id), obj.name) - for obj in VMTemplate.objects.all() - ) name = models.CharField(max_length=255, unique=True) vat_inclusive = models.BooleanField(default=True) vat_percentage = models.DecimalField( @@ -81,18 +55,6 @@ class VMPricing(models.Model): max_digits=6, decimal_places=2, default=0 ) - vm_templates_to_show = MultipleChoiceArrayField( - base_field=models.CharField( - blank=True, - max_length=256, - choices=VMTemplateChoices - ), - default=list, - blank=True, - help_text="Not selecting any items above will result in showing all " - "templates" - ) - def __str__(self): display_str = self.name + ' => ' + ' - '.join([ '{}/Core'.format(self.cores_unit_price.normalize()), From 70cac38f819bb67b50290e91a03afcb5bacdb4b6 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 24 Jun 2018 10:28:17 +0200 Subject: [PATCH 242/866] Move initialization of VMTemplates out of the plugin --- datacenterlight/cms_models.py | 29 +++++++++---------- ...culatorpluginmodel_vm_templates_to_show.py | 4 +-- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index deb84dc7..8c31696f 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -300,20 +300,6 @@ class MultipleChoiceArrayField(ArrayField): Uses Django's Postgres ArrayField and a MultipleChoiceField for its formfield. """ - - def formfield(self, **kwargs): - defaults = { - 'form_class': forms.MultipleChoiceField, - 'choices': self.base_field.choices, - } - defaults.update(kwargs) - # Skip our parent's formfield implementation completely as we don't - # care for it. - # pylint:disable=bad-super-call - return super(ArrayField, self).formfield(**defaults) - - -class DCLCalculatorPluginModel(CMSPlugin): VMTemplateChoices = list( ( str(obj.opennebula_vm_template_id), @@ -323,6 +309,20 @@ class DCLCalculatorPluginModel(CMSPlugin): ) for obj in VMTemplate.objects.all() ) + + def formfield(self, **kwargs): + defaults = { + 'form_class': forms.MultipleChoiceField, + 'choices': self.VMTemplateChoices, + } + defaults.update(kwargs) + # Skip our parent's formfield implementation completely as we don't + # care for it. + # pylint:disable=bad-super-call + return super(ArrayField, self).formfield(**defaults) + + +class DCLCalculatorPluginModel(CMSPlugin): pricing = models.ForeignKey( VMPricing, related_name="dcl_custom_pricing_vm_pricing", @@ -337,7 +337,6 @@ class DCLCalculatorPluginModel(CMSPlugin): base_field=models.CharField( blank=True, max_length=256, - choices=VMTemplateChoices ), default=list, blank=True, diff --git a/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py b/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py index 179dcff9..65bfce21 100644 --- a/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py +++ b/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-06-24 06:54 +# Generated by Django 1.9.4 on 2018-06-24 08:23 from __future__ import unicode_literals import datacenterlight.cms_models @@ -16,6 +16,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='dclcalculatorpluginmodel', name='vm_templates_to_show', - field=datacenterlight.cms_models.MultipleChoiceArrayField(base_field=models.CharField(blank=True, choices=[('4', 'CentOS 7'), ('14', 'Debian 8'), ('25', 'Ubuntu 14.04'), ('26', 'Ubuntu 16.04'), ('36', 'Devuan Jessie'), ('65', 'Devuan Ascii'), ('69', 'FreeBSD 11.1')], max_length=256), blank=True, default=list, help_text='Recommended: If you wish to show all templates of the corresponding VM Type (public/ipv6only), please do not select any of the items in the above field. This will allow any new template(s) added in the backend to be automatically listed in this calculator instance.', size=None), + field=datacenterlight.cms_models.MultipleChoiceArrayField(base_field=models.CharField(blank=True, max_length=256), blank=True, default=list, help_text='Recommended: If you wish to show all templates of the corresponding VM Type (public/ipv6only), please do not select any of the items in the above field. This will allow any new template(s) added in the backend to be automatically listed in this calculator instance.', size=None), ), ] From fb9000de901737eb92ec8b1114e576628501ee1b Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 24 Jun 2018 14:47:05 +0200 Subject: [PATCH 243/866] Update Changelog --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index 8146462d..045ab004 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +Next: + * feature: add vm_type option to vm_template and dcl calculator to distinguish between public and ipv6only templates (PR #635) 1.9: 2018-05-16 * #4559: [cms] enable discount on cms calculator 1.8: 2018-05-01 From 549e882ebe114880e31dbba720e3eff6d38d3ba3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 24 Jun 2018 14:54:50 +0200 Subject: [PATCH 244/866] Update Changelog --- Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog b/Changelog index 045ab004..2cf17fbe 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,5 @@ Next: + * #4847: [comic] Add google analytics code for comic.ungleich.ch * feature: add vm_type option to vm_template and dcl calculator to distinguish between public and ipv6only templates (PR #635) 1.9: 2018-05-16 * #4559: [cms] enable discount on cms calculator From e816f65114b0f7463eb7c11f33c0c968b52d4df2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 24 Jun 2018 16:00:54 +0200 Subject: [PATCH 245/866] Update Changelog --- Changelog | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Changelog b/Changelog index 2cf17fbe..df8b892f 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,6 @@ Next: - * #4847: [comic] Add google analytics code for comic.ungleich.ch + * #4799: [dcl] Show selected vm templates only in calculator (PR #638) + * #4847: [comic] Add google analytics code for comic.ungleich.ch (PR #639) * feature: add vm_type option to vm_template and dcl calculator to distinguish between public and ipv6only templates (PR #635) 1.9: 2018-05-16 * #4559: [cms] enable discount on cms calculator From a5c42b9c44f93406631945c5409be5449931652f Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 24 Jun 2018 17:05:33 +0200 Subject: [PATCH 246/866] Update Changelog for 1.9.1 --- Changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog b/Changelog index df8b892f..d175a734 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,4 @@ -Next: +1.9.1: 2018-06-24 * #4799: [dcl] Show selected vm templates only in calculator (PR #638) * #4847: [comic] Add google analytics code for comic.ungleich.ch (PR #639) * feature: add vm_type option to vm_template and dcl calculator to distinguish between public and ipv6only templates (PR #635) From 6c2eabbe6af3880c6ea4e09aa9baf33111e0d72a Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 27 Jun 2018 00:01:50 +0200 Subject: [PATCH 247/866] Use the public ipv4 if it exists; otherwise use the ipv6 to do a cdist cdist ssh configure --- datacenterlight/tasks.py | 2 +- opennebula_api/models.py | 59 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index db479b43..07d132e2 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -190,7 +190,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, # try to see if we have the IP and that if the ssh keys can # be configured - new_host = manager.get_primary_ipv4(vm_id) + new_host = manager.get_primary_ip(vm_id) 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')) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 35f3d8e8..d573d6d5 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -218,6 +218,59 @@ class OpenNebulaManager(): except: raise ConnectionRefusedError + def is_public_ipv4(self, ipv4): + """ + Checks whether the passed ipv4 is a public IP. + + :param ipv4: + :return: Returns true if it is a public IP else returns false + """ + if ipv4.startswith("10."): + return False + else: + return True + + def get_primary_ip(self, vm_id): + """ + Returns primary ipv4 if it exists and is a public ip. Otherwise returns + the IPv6 of the machine + + :param vm_id: + :return: + """ + ipv4 = self.get_primary_ipv4(vm_id) + if self.is_public_ipv4(ipv4): + return ipv4 + else: + return self.get_primary_ipv6(vm_id) + + def get_primary_ipv6(self, vm_id): + """ + Returns the primary IPv6 of the given vm. + For now, we return the first available IPv6 (to be changed later) + + :return: An IP address string, if it exists else returns None + """ + all_ipv4s = self.get_vm_ipv4_addresses(vm_id) + if len(all_ipv4s) > 0: + return all_ipv4s[0] + else: + return None + + def get_vm_ipv6_addresses(self, vm_id): + """ + Returns a list of IPv6 addresses of the given vm + + :param vm_id: The ID of the vm + :return: + """ + ipv6s = [] + vm = self.get_vm(vm_id) + for nic in vm.template.nics: + if hasattr(nic, 'ip6_global'): + ipv6s.append(nic.ip6_global) + return ipv6s + def get_primary_ipv4(self, vm_id): """ Returns the primary IPv4 of the given vm. @@ -579,7 +632,11 @@ class OpenNebulaManager(): vm = self.get_vm(order.vm_id) for nic in vm.template.nics: if hasattr(nic, 'ip'): - hosts.append(nic.ip) + if str(nic.ip).startswith("10."): + if hasattr(nic, 'ip6_global'): + hosts.append(nic.ip6_global) + else: + hosts.append(nic.ip) except WrongIdError: logger.debug( "VM with ID {} does not exist".format(order.vm_id)) From 1ec7cb876168a06a89597c3d9160b264271a4092 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 27 Jun 2018 00:44:17 +0200 Subject: [PATCH 248/866] Fix silly copy/paste bug --- opennebula_api/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index d573d6d5..0cc870c3 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -251,9 +251,9 @@ class OpenNebulaManager(): :return: An IP address string, if it exists else returns None """ - all_ipv4s = self.get_vm_ipv4_addresses(vm_id) - if len(all_ipv4s) > 0: - return all_ipv4s[0] + all_ipv6s = self.get_vm_ipv6_addresses(vm_id) + if len(all_ipv6s) > 0: + return all_ipv6s[0] else: return None From 4a5c5f79425341ede31eac3b7878a6f5212183b3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 27 Jun 2018 01:06:10 +0200 Subject: [PATCH 249/866] Refactor getting primary ip of a vm --- opennebula_api/models.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 0cc870c3..5416b6f2 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -629,14 +629,8 @@ class OpenNebulaManager(): "the ssh keys.".format(self.email)) for order in all_orders: try: - vm = self.get_vm(order.vm_id) - for nic in vm.template.nics: - if hasattr(nic, 'ip'): - if str(nic.ip).startswith("10."): - if hasattr(nic, 'ip6_global'): - hosts.append(nic.ip6_global) - else: - hosts.append(nic.ip) + ip = self.get_primary_ip(order.vm_id) + hosts.append(ip) except WrongIdError: logger.debug( "VM with ID {} does not exist".format(order.vm_id)) From 60260ccb0811299616c08e37c50ab922767cabdb Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 27 Jun 2018 09:06:28 +0200 Subject: [PATCH 250/866] Attempt to fix flake8 error --- datacenterlight/cms_models.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index 8c31696f..62a7b312 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -3,6 +3,7 @@ from cms.extensions.extension_pool import extension_pool from cms.models.fields import PlaceholderField from cms.models.pluginmodel import CMSPlugin from django import forms +from django.conf import settings from django.contrib.postgres.fields import ArrayField from django.contrib.sites.models import Site from django.db import models @@ -300,15 +301,17 @@ class MultipleChoiceArrayField(ArrayField): Uses Django's Postgres ArrayField and a MultipleChoiceField for its formfield. """ - VMTemplateChoices = list( - ( - str(obj.opennebula_vm_template_id), - (obj.name + ' - ' + VMTemplate.IPV6.title() - if obj.vm_type == VMTemplate.IPV6 else obj.name + VMTemplateChoices = [] + if settings.OPENNEBULA_DOMAIN != 'test_domain': + VMTemplateChoices = list( + ( + str(obj.opennebula_vm_template_id), + (obj.name + ' - ' + VMTemplate.IPV6.title() + if obj.vm_type == VMTemplate.IPV6 else obj.name + ) ) - ) - for obj in VMTemplate.objects.all() - ) + for obj in VMTemplate.objects.all() + ) def formfield(self, **kwargs): defaults = { From 88f0d733365f68db9ba8ec1274125251548bef73 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 27 Jun 2018 09:21:41 +0200 Subject: [PATCH 251/866] Update Changelog --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index d175a734..d616964f 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +Next: + * bugfix: Fix flake8 error that was ignored in release 1.9.1 1.9.1: 2018-06-24 * #4799: [dcl] Show selected vm templates only in calculator (PR #638) * #4847: [comic] Add google analytics code for comic.ungleich.ch (PR #639) From 88e6d9d21619e132eaa29587263fabfbad2e448a Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Wed, 27 Jun 2018 12:24:16 +0200 Subject: [PATCH 252/866] Separate two error messages with a period --- datacenterlight/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 66a91917..3554580f 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -121,7 +121,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): ) logger.error(emsg) if error_msg: - error_msg += emsg + error_msg += ". " + emsg else: error_msg = emsg From f1e021e1e9d9ded16fd32fffed5ad0c983b14ac8 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Wed, 27 Jun 2018 12:24:53 +0200 Subject: [PATCH 253/866] Improve comments --- datacenterlight/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 042eec73..8fed5dcd 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -67,12 +67,12 @@ def create_vm(billing_address_data, stripe_customer_id, specs, billing_address_user_form.is_valid() billing_address_user_form.save() - # Associate an order with a stripe subscription + # Associate the given stripe subscription with the order order.set_subscription_id( stripe_subscription_obj.id, card_details_dict ) - # If the Stripe payment succeeds, set order status approved + # Set order status approved order.set_approved() create_vm_task.delay(vm_template_id, user, specs, template, order.id) From 5eff54cffecd5616be658d4719829ac7b3aac4d5 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Wed, 27 Jun 2018 12:34:41 +0200 Subject: [PATCH 254/866] Fix a bug -- json not imported; use JsonResponse instead of HttpResponse --- hosting/views.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 07bafd97..e5383535 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1266,10 +1266,7 @@ class VirtualMachineView(LoginRequiredMixin, View): ["%s=%s" % (k, v) for (k, v) in admin_email_body.items()]), } send_plain_email_task.delay(email_to_admin_data) - return HttpResponse( - json.dumps(response), - content_type="application/json" - ) + return JsonResponse(response) class HostingBillListView(PermissionRequiredMixin, LoginRequiredMixin, From 68a65b7bc73056364673dac63c90dfd8b377f5b5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 30 Jun 2018 00:30:51 +0200 Subject: [PATCH 255/866] Detect private IP correctly --- opennebula_api/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 5416b6f2..c06d790e 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -1,3 +1,4 @@ +import ipaddress import logging import socket @@ -225,7 +226,7 @@ class OpenNebulaManager(): :param ipv4: :return: Returns true if it is a public IP else returns false """ - if ipv4.startswith("10."): + if ipaddress.ip_address(ipv4).is_private: return False else: return True From 52e53d479ecfeb729d6c68ab45e0e3d94a333169 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 07:57:15 +0200 Subject: [PATCH 256/866] Rename a variable: ipv6s -> ipv6_list --- opennebula_api/models.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index c06d790e..3b69d91d 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -252,9 +252,9 @@ class OpenNebulaManager(): :return: An IP address string, if it exists else returns None """ - all_ipv6s = self.get_vm_ipv6_addresses(vm_id) - if len(all_ipv6s) > 0: - return all_ipv6s[0] + ipv6_list = self.get_vm_ipv6_addresses(vm_id) + if len(ipv6_list) > 0: + return ipv6_list[0] else: return None @@ -265,12 +265,12 @@ class OpenNebulaManager(): :param vm_id: The ID of the vm :return: """ - ipv6s = [] + ipv6_list = [] vm = self.get_vm(vm_id) for nic in vm.template.nics: if hasattr(nic, 'ip6_global'): - ipv6s.append(nic.ip6_global) - return ipv6s + ipv6_list.append(nic.ip6_global) + return ipv6_list def get_primary_ipv4(self, vm_id): """ From 8993a7bde1a13f37648787c2488e5218331b6307 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 11:05:23 +0200 Subject: [PATCH 257/866] Reorganize imports + format code --- datacenterlight/tasks.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 07d132e2..4d0fa070 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -1,8 +1,8 @@ from datetime import datetime +from celery import current_task from celery.exceptions import MaxRetriesExceededError from celery.utils.log import get_task_logger -from celery import current_task from django.conf import settings from django.core.mail import EmailMessage from django.core.urlresolvers import reverse @@ -14,11 +14,10 @@ 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, get_or_create_vm_detail from utils.forms import UserBillingAddressForm +from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail from utils.mailer import BaseEmail from utils.models import BillingAddress - from .models import VMPricing logger = get_task_logger(__name__) @@ -173,8 +172,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, 'order_url': reverse('hosting:orders', kwargs={'pk': order.id}), 'page_header': _( - 'Your New VM %(vm_name)s at Data Center Light') % { - 'vm_name': vm.get('name')}, + 'Your New VM %(vm_name)s at Data Center Light') % + {'vm_name': vm.get('name')}, 'vm_name': vm.get('name') } email_data = { From c13af950179bef9c8fb408e9c22d1df0dcefc699 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 11:07:29 +0200 Subject: [PATCH 258/866] Refactor code: get_primary_ip -> get_ipv6 and remove unwanted code --- datacenterlight/tasks.py | 18 +++++----- opennebula_api/models.py | 74 ++++++---------------------------------- 2 files changed, 21 insertions(+), 71 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 4d0fa070..b220cc38 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -187,11 +187,11 @@ def create_vm_task(self, vm_template_id, user, specs, template, email = BaseEmail(**email_data) email.send() - # try to see if we have the IP and that if the ssh keys can - # be configured - new_host = manager.get_primary_ip(vm_id) + # try to see if we have the IPv6 of the new vm and that if the ssh + # keys can be configured + vm_ipv6 = manager.get_ipv6(vm_id) logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id)) - if new_host is not None: + if vm_ipv6 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: @@ -202,13 +202,15 @@ def create_vm_task(self, vm_template_id, user, specs, template, logger.debug( "Calling configure on {host} for " "{num_keys} keys".format( - host=new_host, num_keys=len(keys))) + host=vm_ipv6, num_keys=len(keys) + ) + ) # Let's delay the task by 75 seconds to be sure # that we run the cdist configure after the host # is up - manager.manage_public_key(keys, - hosts=[new_host], - countdown=75) + manager.manage_public_key( + keys, hosts=[vm_ipv6], countdown=75 + ) except Exception as e: logger.error(str(e)) try: diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 3b69d91d..d6811349 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -219,46 +219,19 @@ class OpenNebulaManager(): except: raise ConnectionRefusedError - def is_public_ipv4(self, ipv4): + def get_ipv6(self, vm_id): """ - Checks whether the passed ipv4 is a public IP. + Returns the first IPv6 of the given vm. - :param ipv4: - :return: Returns true if it is a public IP else returns false + :return: An IPv6 address string, if it exists else returns None """ - if ipaddress.ip_address(ipv4).is_private: - return False - else: - return True - - def get_primary_ip(self, vm_id): - """ - Returns primary ipv4 if it exists and is a public ip. Otherwise returns - the IPv6 of the machine - - :param vm_id: - :return: - """ - ipv4 = self.get_primary_ipv4(vm_id) - if self.is_public_ipv4(ipv4): - return ipv4 - else: - return self.get_primary_ipv6(vm_id) - - def get_primary_ipv6(self, vm_id): - """ - Returns the primary IPv6 of the given vm. - For now, we return the first available IPv6 (to be changed later) - - :return: An IP address string, if it exists else returns None - """ - ipv6_list = self.get_vm_ipv6_addresses(vm_id) + ipv6_list = self.get_all_ipv6_addresses(vm_id) if len(ipv6_list) > 0: return ipv6_list[0] else: return None - def get_vm_ipv6_addresses(self, vm_id): + def get_all_ipv6_addresses(self, vm_id): """ Returns a list of IPv6 addresses of the given vm @@ -272,33 +245,6 @@ class OpenNebulaManager(): ipv6_list.append(nic.ip6_global) return ipv6_list - def get_primary_ipv4(self, vm_id): - """ - Returns the primary IPv4 of the given vm. - To be changed later. - - :return: An IP address string, if it exists else returns None - """ - all_ipv4s = self.get_vm_ipv4_addresses(vm_id) - if len(all_ipv4s) > 0: - return all_ipv4s[0] - else: - return None - - def get_vm_ipv4_addresses(self, vm_id): - """ - Returns a list of IPv4 addresses of the given vm - - :param vm_id: The ID of the vm - :return: - """ - ipv4s = [] - vm = self.get_vm(vm_id) - for nic in vm.template.nics: - if hasattr(nic, 'ip'): - ipv4s.append(nic.ip) - return ipv4s - def create_vm(self, template_id, specs, ssh_key=None, vm_name=None): template = self.get_template(template_id) @@ -601,7 +547,7 @@ class OpenNebulaManager(): 'value': 'sha-.....', # public key as string 'state': True # whether key is to be added or } # removed - :param hosts: A list of hosts IP addresses + :param hosts: A list of hosts IPv6 addresses :param countdown: Parameter to be passed to celery apply_async Allows to delay a task by `countdown` number of seconds :return: @@ -614,12 +560,14 @@ class OpenNebulaManager(): link_error=save_ssh_key_error_handler.s()) else: logger.debug( - "Keys and/or hosts are empty, so not managing any keys") + "Keys and/or hosts are empty, so not managing any keys" + ) def get_all_hosts(self): """ A utility function to obtain all hosts of this owner - :return: A list of hosts IP addresses, empty if none exist + :return: A list of IPv6 addresses of all the hosts of this customer or + an empty list if none exist """ owner = CustomUser.objects.filter( email=self.email).first() @@ -630,7 +578,7 @@ class OpenNebulaManager(): "the ssh keys.".format(self.email)) for order in all_orders: try: - ip = self.get_primary_ip(order.vm_id) + ip = self.get_ipv6(order.vm_id) hosts.append(ip) except WrongIdError: logger.debug( From ae0d4c0841d10a0d459bcdfd993f0727368cdbd0 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 11:17:45 +0200 Subject: [PATCH 259/866] Refactor code --- opennebula_api/models.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index d6811349..29632009 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -1,4 +1,3 @@ -import ipaddress import logging import socket @@ -54,27 +53,18 @@ class OpenNebulaManager(): ConnectionError: If the connection to the opennebula server can't be established """ - return oca.Client("{0}:{1}".format( - user.email, - user.password), - "{protocol}://{domain}:{port}{endpoint}".format( - protocol=settings.OPENNEBULA_PROTOCOL, - domain=settings.OPENNEBULA_DOMAIN, - port=settings.OPENNEBULA_PORT, - endpoint=settings.OPENNEBULA_ENDPOINT - )) + return self._get_opennebula_client(user.email, user.password) def _get_opennebula_client(self, username, password): - return oca.Client("{0}:{1}".format( - username, - - password), + return oca.Client( + "{0}:{1}".format(username, password), "{protocol}://{domain}:{port}{endpoint}".format( protocol=settings.OPENNEBULA_PROTOCOL, domain=settings.OPENNEBULA_DOMAIN, port=settings.OPENNEBULA_PORT, endpoint=settings.OPENNEBULA_ENDPOINT - )) + ) + ) def _get_user(self, user): """Get the corresponding opennebula user for a CustomUser object @@ -438,8 +428,9 @@ class OpenNebulaManager(): return template_id def delete_template(self, template_id): - self.oneadmin_client.call(oca.VmTemplate.METHODS[ - 'delete'], template_id, False) + self.oneadmin_client.call( + oca.VmTemplate.METHODS['delete'], template_id, False + ) def change_user_password(self, passwd_hash): self.oneadmin_client.call( From ef8e380ab7a0884be9645076e134910ebd1fa5f9 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 11:22:42 +0200 Subject: [PATCH 260/866] Fix flake8 error --- datacenterlight/tasks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index b220cc38..22ee29ad 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -172,8 +172,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, 'order_url': reverse('hosting:orders', kwargs={'pk': order.id}), 'page_header': _( - 'Your New VM %(vm_name)s at Data Center Light') % - {'vm_name': vm.get('name')}, + 'Your New VM %(vm_name)s at Data Center Light') % { + 'vm_name': vm.get('name')}, 'vm_name': vm.get('name') } email_data = { From 6593983f0488b704848c244f51c0a87599d2eb78 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 16:02:35 +0200 Subject: [PATCH 261/866] Update Changelog --- Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog b/Changelog index d616964f..499fd781 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,5 @@ Next: + * #4890: [hosting] Manage SSH keys using IPv6 of the VM (PR #640) * bugfix: Fix flake8 error that was ignored in release 1.9.1 1.9.1: 2018-06-24 * #4799: [dcl] Show selected vm templates only in calculator (PR #638) From 43b3a63958fcdec044cb3f2d736168bce6aeb0ae Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 16:25:02 +0200 Subject: [PATCH 262/866] Reorganize imports --- datacenterlight/tasks.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 259a2e36..281d5f45 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -10,14 +10,12 @@ from django.utils import translation from django.utils.translation import ugettext_lazy as _ from dynamicweb.celery import app -from hosting.models import HostingOrder, HostingBill -from membership.models import StripeCustomer, CustomUser +from hosting.models import HostingOrder +from membership.models import CustomUser from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer -from utils.forms import UserBillingAddressForm from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail from utils.mailer import BaseEmail -from utils.models import BillingAddress from utils.stripe_utils import StripeUtils from .models import VMPricing From 5851277d9aa0e9b6a234be89a53c075f4e066709 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 16:36:36 +0200 Subject: [PATCH 263/866] Reorganize imports --- datacenterlight/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 8fed5dcd..5388b9d3 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -5,8 +5,8 @@ from hosting.models import HostingOrder, HostingBill from membership.models import StripeCustomer from utils.forms import UserBillingAddressForm from utils.models import BillingAddress -from .models import VMPricing from .cms_models import CMSIntegration +from .models import VMPricing def get_cms_integration(name): From 900f014d922fd73fb97da08a35e3074d1d2d74f4 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 18:33:10 +0200 Subject: [PATCH 264/866] Save order specifications in HostingOrder also --- datacenterlight/utils.py | 11 ++++-- hosting/migrations/0045_auto_20180701_1631.py | 35 +++++++++++++++++++ hosting/models.py | 16 ++++++++- 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 hosting/migrations/0045_auto_20180701_1631.py diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 5388b9d3..7b3ef73d 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -1,12 +1,12 @@ from django.contrib.sites.models import Site from datacenterlight.tasks import create_vm_task -from hosting.models import HostingOrder, HostingBill +from hosting.models import HostingOrder, HostingBill, OrderSpecifications from membership.models import StripeCustomer from utils.forms import UserBillingAddressForm from utils.models import BillingAddress from .cms_models import CMSIntegration -from .models import VMPricing +from .models import VMPricing, VMTemplate def get_cms_integration(name): @@ -53,6 +53,13 @@ def create_vm(billing_address_data, stripe_customer_id, specs, vm_pricing=vm_pricing ) + order_specs_obj, obj_created = OrderSpecifications.objects.get_or_create( + vm_template=VMTemplate.objects.get(vm_template_id), + cores=specs['cpu'], memory=specs['memory'], ssd_size=specs['disk_size'] + ) + order.order_specs = order_specs_obj + order.save() + # Create a Hosting Bill HostingBill.create(customer=customer, billing_address=billing_address) diff --git a/hosting/migrations/0045_auto_20180701_1631.py b/hosting/migrations/0045_auto_20180701_1631.py new file mode 100644 index 00000000..8af4d821 --- /dev/null +++ b/hosting/migrations/0045_auto_20180701_1631.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-07-01 16:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import utils.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0024_dclcalculatorpluginmodel_vm_templates_to_show'), + ('hosting', '0044_hostingorder_vm_pricing'), + ] + + operations = [ + migrations.CreateModel( + name='OrderSpecifications', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('cores', models.IntegerField(default=0)), + ('memory', models.IntegerField(default=0)), + ('hdd_size', models.IntegerField(default=0)), + ('ssd_size', models.IntegerField(default=0)), + ('vm_template', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='datacenterlight.VMTemplate')), + ], + bases=(utils.mixins.AssignPermissionsMixin, models.Model), + ), + migrations.AddField( + model_name='hostingorder', + name='order_specs', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='hosting.OrderSpecifications'), + ), + ] diff --git a/hosting/models.py b/hosting/models.py index de4d3aec..c30a25a8 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -7,7 +7,7 @@ from django.utils import timezone from django.utils.functional import cached_property from Crypto.PublicKey import RSA -from datacenterlight.models import VMPricing +from datacenterlight.models import VMPricing, VMTemplate from membership.models import StripeCustomer, CustomUser from utils.models import BillingAddress from utils.mixins import AssignPermissionsMixin @@ -41,6 +41,19 @@ class HostingPlan(models.Model): return price +class OrderSpecifications(AssignPermissionsMixin, models.Model): + vm_template = models.ForeignKey(VMTemplate, blank=True, null=True) + cores = models.IntegerField(default=0) + memory = models.IntegerField(default=0) + hdd_size = models.IntegerField(default=0) + ssd_size = models.IntegerField(default=0) + + def __str__(self): + return "%s - %s cores, %s GB RAM, %s GB SSD" % ( + self.vm_template.name, self.cores, self.memory, self.ssd_size + ) + + class HostingOrder(AssignPermissionsMixin, models.Model): ORDER_APPROVED_STATUS = 'Approved' ORDER_DECLINED_STATUS = 'Declined' @@ -56,6 +69,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model): price = models.FloatField() subscription_id = models.CharField(max_length=100, null=True) vm_pricing = models.ForeignKey(VMPricing) + order_specs = models.ForeignKey(OrderSpecifications, null=True, blank=True) permissions = ('view_hostingorder',) From f48005166e9d78b4b25dd02bb4225f8893fc31ac Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 18:42:11 +0200 Subject: [PATCH 265/866] Fix bug getting VMTemplate object from vm_template_id --- datacenterlight/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 7b3ef73d..3d0e4370 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -54,7 +54,9 @@ def create_vm(billing_address_data, stripe_customer_id, specs, ) order_specs_obj, obj_created = OrderSpecifications.objects.get_or_create( - vm_template=VMTemplate.objects.get(vm_template_id), + vm_template=VMTemplate.objects.get( + opennebula_vm_template_id=vm_template_id + ), cores=specs['cpu'], memory=specs['memory'], ssd_size=specs['disk_size'] ) order.order_specs = order_specs_obj From 7f57ace92d7f46bd0ae84029cbfcdade2726c784 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 19:23:05 +0200 Subject: [PATCH 266/866] Set default and on_delete attributes --- ...uto_20180701_1631.py => 0045_auto_20180701_1718.py} | 6 +++--- hosting/models.py | 10 ++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) rename hosting/migrations/{0045_auto_20180701_1631.py => 0045_auto_20180701_1718.py} (73%) diff --git a/hosting/migrations/0045_auto_20180701_1631.py b/hosting/migrations/0045_auto_20180701_1718.py similarity index 73% rename from hosting/migrations/0045_auto_20180701_1631.py rename to hosting/migrations/0045_auto_20180701_1718.py index 8af4d821..d9705a24 100644 --- a/hosting/migrations/0045_auto_20180701_1631.py +++ b/hosting/migrations/0045_auto_20180701_1718.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-07-01 16:31 +# Generated by Django 1.9.4 on 2018-07-01 17:18 from __future__ import unicode_literals from django.db import migrations, models @@ -23,13 +23,13 @@ class Migration(migrations.Migration): ('memory', models.IntegerField(default=0)), ('hdd_size', models.IntegerField(default=0)), ('ssd_size', models.IntegerField(default=0)), - ('vm_template', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='datacenterlight.VMTemplate')), + ('vm_template', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='datacenterlight.VMTemplate')), ], bases=(utils.mixins.AssignPermissionsMixin, models.Model), ), migrations.AddField( model_name='hostingorder', name='order_specs', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='hosting.OrderSpecifications'), + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hosting.OrderSpecifications'), ), ] diff --git a/hosting/models.py b/hosting/models.py index c30a25a8..b182bfc5 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -42,7 +42,10 @@ class HostingPlan(models.Model): class OrderSpecifications(AssignPermissionsMixin, models.Model): - vm_template = models.ForeignKey(VMTemplate, blank=True, null=True) + vm_template = models.ForeignKey( + VMTemplate, blank=True, null=True, default=None, + on_delete=models.SET_NULL + ) cores = models.IntegerField(default=0) memory = models.IntegerField(default=0) hdd_size = models.IntegerField(default=0) @@ -69,7 +72,10 @@ class HostingOrder(AssignPermissionsMixin, models.Model): price = models.FloatField() subscription_id = models.CharField(max_length=100, null=True) vm_pricing = models.ForeignKey(VMPricing) - order_specs = models.ForeignKey(OrderSpecifications, null=True, blank=True) + order_specs = models.ForeignKey( + OrderSpecifications, null=True, blank=True, default=None, + on_delete=models.SET_NULL + ) permissions = ('view_hostingorder',) From 00cb1de75d8998675d1e925df70806dad44ae939 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 19:46:21 +0200 Subject: [PATCH 267/866] Add type to OrderSpecification string --- hosting/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hosting/models.py b/hosting/models.py index b182bfc5..2ce604d0 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -52,8 +52,9 @@ class OrderSpecifications(AssignPermissionsMixin, models.Model): ssd_size = models.IntegerField(default=0) def __str__(self): - return "%s - %s cores, %s GB RAM, %s GB SSD" % ( - self.vm_template.name, self.cores, self.memory, self.ssd_size + return "%s - %s, %s cores, %s GB RAM, %s GB SSD" % ( + self.vm_template.name, self.vm_template.vm_type, self.cores, + self.memory, self.ssd_size ) From 44900f6a4886f4ded6b744bcebe3f4991018dad7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 22:30:23 +0200 Subject: [PATCH 268/866] Rename OrderSpecifications to OrderDetail --- datacenterlight/utils.py | 6 +++--- ...5_auto_20180701_1718.py => 0045_auto_20180701_2028.py} | 8 ++++---- hosting/models.py | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) rename hosting/migrations/{0045_auto_20180701_1718.py => 0045_auto_20180701_2028.py} (89%) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 3d0e4370..a6f760af 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -1,7 +1,7 @@ from django.contrib.sites.models import Site from datacenterlight.tasks import create_vm_task -from hosting.models import HostingOrder, HostingBill, OrderSpecifications +from hosting.models import HostingOrder, HostingBill, OrderDetail from membership.models import StripeCustomer from utils.forms import UserBillingAddressForm from utils.models import BillingAddress @@ -53,13 +53,13 @@ def create_vm(billing_address_data, stripe_customer_id, specs, vm_pricing=vm_pricing ) - order_specs_obj, obj_created = OrderSpecifications.objects.get_or_create( + order_detail_obj, obj_created = OrderDetail.objects.get_or_create( vm_template=VMTemplate.objects.get( opennebula_vm_template_id=vm_template_id ), cores=specs['cpu'], memory=specs['memory'], ssd_size=specs['disk_size'] ) - order.order_specs = order_specs_obj + order.order_detail = order_detail_obj order.save() # Create a Hosting Bill diff --git a/hosting/migrations/0045_auto_20180701_1718.py b/hosting/migrations/0045_auto_20180701_2028.py similarity index 89% rename from hosting/migrations/0045_auto_20180701_1718.py rename to hosting/migrations/0045_auto_20180701_2028.py index d9705a24..39b58aa8 100644 --- a/hosting/migrations/0045_auto_20180701_1718.py +++ b/hosting/migrations/0045_auto_20180701_2028.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-07-01 17:18 +# Generated by Django 1.9.4 on 2018-07-01 20:28 from __future__ import unicode_literals from django.db import migrations, models @@ -16,7 +16,7 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='OrderSpecifications', + name='OrderDetail', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('cores', models.IntegerField(default=0)), @@ -29,7 +29,7 @@ class Migration(migrations.Migration): ), migrations.AddField( model_name='hostingorder', - name='order_specs', - field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hosting.OrderSpecifications'), + name='order_detail', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hosting.OrderDetail'), ), ] diff --git a/hosting/models.py b/hosting/models.py index 2ce604d0..411bd267 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -41,7 +41,7 @@ class HostingPlan(models.Model): return price -class OrderSpecifications(AssignPermissionsMixin, models.Model): +class OrderDetail(AssignPermissionsMixin, models.Model): vm_template = models.ForeignKey( VMTemplate, blank=True, null=True, default=None, on_delete=models.SET_NULL @@ -73,8 +73,8 @@ class HostingOrder(AssignPermissionsMixin, models.Model): price = models.FloatField() subscription_id = models.CharField(max_length=100, null=True) vm_pricing = models.ForeignKey(VMPricing) - order_specs = models.ForeignKey( - OrderSpecifications, null=True, blank=True, default=None, + order_detail = models.ForeignKey( + OrderDetail, null=True, blank=True, default=None, on_delete=models.SET_NULL ) From cb911e05c5cac36eb52dc536ac24c2bb7f492b79 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 22:45:18 +0200 Subject: [PATCH 269/866] Update Changelog --- Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog b/Changelog index 499fd781..712bce85 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,5 @@ Next: + * #3934: [dcl,hosting] Create HostingOrder outside celery task and add and associate OrderDetail with HostingOrder * #4890: [hosting] Manage SSH keys using IPv6 of the VM (PR #640) * bugfix: Fix flake8 error that was ignored in release 1.9.1 1.9.1: 2018-06-24 From c9d01ba95f0d18b722623c5bc16575abf689040d Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 3 Jul 2018 21:52:44 +0200 Subject: [PATCH 270/866] Fix some errors --- hosting/views.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 778b3835..1a9a3c30 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -15,6 +15,7 @@ from django.http import ( Http404, HttpResponseRedirect, HttpResponse, JsonResponse ) from django.shortcuts import redirect, render +from django.utils.html import escape from django.utils.http import urlsafe_base64_decode from django.utils.safestring import mark_safe from django.utils.translation import get_language, ugettext_lazy as _ @@ -1012,9 +1013,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): ' back to the payment page.') ) } - return HttpResponse( - json.dumps(response), content_type="application/json" - ) + return JsonResponse(response) else: card_id = request.session.get('card_id') user_card_detail = UserCardDetail.objects.get(id=card_id) From 9904a71d384f8a31d9a3bc0de42455c8c7e9d533 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 3 Jul 2018 22:34:59 +0200 Subject: [PATCH 271/866] Rename usercarddetail migration --- .../{0045_usercarddetail.py => 0046_usercarddetail.py} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename hosting/migrations/{0045_usercarddetail.py => 0046_usercarddetail.py} (92%) diff --git a/hosting/migrations/0045_usercarddetail.py b/hosting/migrations/0046_usercarddetail.py similarity index 92% rename from hosting/migrations/0045_usercarddetail.py rename to hosting/migrations/0046_usercarddetail.py index 14785689..97c1e94a 100644 --- a/hosting/migrations/0045_usercarddetail.py +++ b/hosting/migrations/0046_usercarddetail.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-06-12 06:28 +# Generated by Django 1.9.4 on 2018-07-03 20:32 from __future__ import unicode_literals from django.db import migrations, models @@ -11,7 +11,7 @@ class Migration(migrations.Migration): dependencies = [ ('membership', '0007_auto_20180213_0128'), - ('hosting', '0044_hostingorder_vm_pricing'), + ('hosting', '0045_auto_20180701_2028'), ] operations = [ From 1c5ff1f9dd4da604e3b57fd4eaa3d4e0e74fdda7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 3 Jul 2018 23:22:10 +0200 Subject: [PATCH 272/866] Add UserCardDetail in the landing non-logged in flow --- datacenterlight/views.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index db36d23a..3ea3cdc8 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -13,7 +13,7 @@ from django.views.decorators.cache import cache_control from django.views.generic import FormView, CreateView, DetailView from hosting.forms import HostingUserLoginForm -from hosting.models import HostingOrder +from hosting.models import HostingOrder, UserCardDetail from membership.models import CustomUser, StripeCustomer from opennebula_api.serializers import VMTemplateSerializer from utils.forms import BillingAddressForm, BillingAddressFormSignup @@ -397,9 +397,9 @@ class OrderConfirmationView(DetailView): stripe_api_cus_id = request.session.get('customer') vm_template_id = template.get('id', 1) stripe_utils = StripeUtils() - card_details = stripe_utils.get_card_details(stripe_api_cus_id, - request.session.get( - 'token')) + card_details = stripe_utils.get_cards_details_from_token( + request.session.get('token') + ) if not card_details.get('response_object'): msg = card_details.get('error') messages.add_message(self.request, messages.ERROR, msg, @@ -493,6 +493,7 @@ class OrderConfirmationView(DetailView): else: # We assume that if the user is here, his/her StripeCustomer # object already exists + stripe_customer = request.user.stripecustomer stripe_customer_id = request.user.stripecustomer.id custom_user = request.user @@ -502,6 +503,16 @@ class OrderConfirmationView(DetailView): billing_address_data.update({ 'user': custom_user.id }) + + if 'token' in request.session: + ucd = UserCardDetail.get_or_create_user_card_detail( + stripe_customer=stripe_customer, + card_details=card_details_dict + ) + UserCardDetail.save_default_card_local( + stripe_customer.stripe_id, + ucd.card_id + ) user = { 'name': custom_user.name, 'email': custom_user.email, From d98a683b2a389f05df2e83150e84e3b668f739ae Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 3 Jul 2018 23:23:02 +0200 Subject: [PATCH 273/866] Do a get on UserCardDetail only when fingerprint, exp_month and exp_year are known --- hosting/models.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/hosting/models.py b/hosting/models.py index e1577bd8..b5fc9023 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -272,12 +272,16 @@ class UserCardDetail(AssignPermissionsMixin, models.Model): :return: UserCardDetail object """ try: - card_detail = UserCardDetail.objects.get( - stripe_customer=stripe_customer, - fingerprint=card_details['fingerprint'], - exp_month=card_details['exp_month'], - exp_year=card_details['exp_year'] - ) + if ('fingerprint' in card_details and 'exp_month' in card_details + and 'exp_year' in card_details): + card_detail = UserCardDetail.objects.get( + stripe_customer=stripe_customer, + fingerprint=card_details['fingerprint'], + exp_month=card_details['exp_month'], + exp_year=card_details['exp_year'] + ) + else: + raise UserCardDetail.DoesNotExist() except UserCardDetail.DoesNotExist: preferred = False if 'preferred' in card_details: From c118e86230e1905f9c64a4aa2a796b4c0607d586 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 3 Jul 2018 23:56:42 +0200 Subject: [PATCH 274/866] Remove unused context parameters -- introduced during merge --- hosting/views.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 1a9a3c30..a28815f2 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -930,10 +930,6 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): context['cc_last4'] = card_detail.last4 context['cc_brand'] = card_detail.brand context['site_url'] = reverse('hosting:create_virtual_machine') - context['cc_last4'] = card_details.get('response_object').get( - 'last4') - context['cc_brand'] = card_details.get('response_object').get( - 'cc_brand') context['vm'] = self.request.session.get('specs') return context From 3d8237a34a5a91346456a6ee99d89f169fd89686 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 4 Jul 2018 00:50:44 +0200 Subject: [PATCH 275/866] Get and show all card details in the landing flow, if the user is logged in --- datacenterlight/views.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 3ea3cdc8..616dde30 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -222,19 +222,15 @@ class PaymentOrderView(FormView): billing_address_form = BillingAddressForm( instance=self.request.user.billing_addresses.first() ) - # Get user last order - last_hosting_order = HostingOrder.objects.filter( - customer__user=self.request.user - ).last() - - # If user has already an hosting order, get the credit card - # data from it - if last_hosting_order: - credit_card_data = last_hosting_order.get_cc_data() - if credit_card_data: - context['credit_card_data'] = credit_card_data - else: - context['credit_card_data'] = None + user = self.request.user + if hasattr(user, 'stripecustomer'): + stripe_customer = user.stripecustomer + else: + stripe_customer = None + cards_list = UserCardDetail.get_all_cards_list( + stripe_customer=stripe_customer + ) + context.update({'cards_list': cards_list}) else: billing_address_form = BillingAddressFormSignup( initial=billing_address_data From 8e742852a58302cb0291430abe94afcc37de2d21 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 4 Jul 2018 02:03:08 +0200 Subject: [PATCH 276/866] Show all cards in landing and hosting payment page --- .../datacenterlight/landing_payment.html | 24 +++++++++++++++++-- hosting/templates/hosting/payment.html | 24 +++++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html index 256b0931..b774f4c2 100644 --- a/datacenterlight/templates/datacenterlight/landing_payment.html +++ b/datacenterlight/templates/datacenterlight/landing_payment.html @@ -125,9 +125,29 @@
{% endfor %} {% if card_list_len > 0 %} -
{% trans "Use another card" %}
+
+
+
+

{% trans "Add a new credit card" %}

+
+
+ +
+
+
+
+
+
+

{%trans "New Credit Card" %}

+
+ {% include "hosting/includes/_card_input.html" %} +
+
+ {% else%} + {% include "hosting/includes/_card_input.html" %} {% endif %} - {% include "hosting/includes/_card_input.html" %}
{% endwith %}
diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index b473f31a..e09775cf 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -138,9 +138,29 @@
{% endfor %} {% if card_list_len > 0 %} -
{% trans "Use another card" %}
+
+
+
+

{% trans "Add a new credit card" %}

+
+
+ +
+
+
+
+
+
+

{%trans "New Credit Card" %}

+
+ {% include "hosting/includes/_card_input.html" %} +
+
+ {% else%} + {% include "hosting/includes/_card_input.html" %} {% endif %} - {% include "hosting/includes/_card_input.html" %}
{% endwith %} From dc28186fe9e7cb6d69c9d4d2d87431088f611a2b Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 4 Jul 2018 02:03:50 +0200 Subject: [PATCH 277/866] Add styles for landing payment choices --- .../static/datacenterlight/css/common.css | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/datacenterlight/static/datacenterlight/css/common.css b/datacenterlight/static/datacenterlight/css/common.css index b6eabd75..4b3b7b41 100644 --- a/datacenterlight/static/datacenterlight/css/common.css +++ b/datacenterlight/static/datacenterlight/css/common.css @@ -158,4 +158,17 @@ footer .dcl-link-separator::before { .thin-hr { margin-top: 10px; margin-bottom: 10px; +} + +.payment-container .credit-card-info { + padding-bottom: 15px; + border-bottom: 1px solid #eee; +} +.credit-card-info { + display: flex; +} + +.credit-card-info .align-bottom { + align-self: flex-end; + padding-right: 0 !important; } \ No newline at end of file From 9bf8992ff4f0142fbf178cd95592e8b7d08b36e5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 4 Jul 2018 02:53:43 +0200 Subject: [PATCH 278/866] Landing flow: set/get token/card_id --- datacenterlight/views.py | 82 ++++++++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 20 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 616dde30..26afd574 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -282,14 +282,55 @@ class PaymentOrderView(FormView): ) if address_form.is_valid(): token = address_form.cleaned_data.get('token') + owner = self.request.user + if token is '': + card_id = address_form.cleaned_data.get('card') + customer = owner.stripecustomer + try: + user_card_detail = UserCardDetail.objects.get(id=card_id) + if not request.user.has_perm( + 'view_usercarddetail', user_card_detail + ): + raise UserCardDetail.DoesNotExist( + _("{user} does not have permission to access the " + "card").format(user=request.user.email) + ) + except UserCardDetail.DoesNotExist as e: + ex = str(e) + logger.error("Card Id: {card_id}, Exception: {ex}".format( + card_id=card_id, ex=ex + ) + ) + msg = _("An error occurred. Details: {}".format(ex)) + messages.add_message( + self.request, messages.ERROR, msg, + extra_tags='make_charge_error' + ) + return HttpResponseRedirect( + reverse('datacenterlight:payment') + '#payment_error' + ) + request.session['card_id'] = user_card_detail.id + else: + # Get or create stripe customer + customer = StripeCustomer.get_or_create( + email=owner.email, token=token + ) + if not customer: + msg = _("Invalid credit card") + messages.add_message( + self.request, messages.ERROR, msg, + extra_tags='make_charge_error') + return HttpResponseRedirect( + reverse('datacenterlight:payment') + '#payment_error') + request.session['token'] = token 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) + email=this_user.get('email'), token=token + ) else: user_email = address_form.cleaned_data.get('email') user_name = address_form.cleaned_data.get('name') @@ -337,7 +378,6 @@ class PaymentOrderView(FormView): billing_address_form=address_form ) ) - request.session['token'] = token if type(customer) is StripeCustomer: request.session['customer'] = customer.stripe_id else: @@ -358,32 +398,34 @@ class OrderConfirmationView(DetailView): @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): + context = {} 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_api_cus_id = request.session.get('customer') - stripe_utils = StripeUtils() - 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') - context = { + if 'token' in self.request.session: + token = self.request.session['token'] + stripe_utils = StripeUtils() + card_details = stripe_utils.get_cards_details_from_token( + token + ) + if not card_details.get('response_object'): + return HttpResponseRedirect(reverse('hosting:payment')) + card_details_response = card_details['response_object'] + context['cc_last4'] = card_details_response['last4'] + context['cc_brand'] = card_details_response['brand'] + else: + card_id = self.request.session.get('card_id') + card_detail = UserCardDetail.objects.get(id=card_id) + context['cc_last4'] = card_detail.last4 + context['cc_brand'] = card_detail.brand + context.update({ 'site_url': reverse('datacenterlight:index'), - 'cc_last4': card_details.get('response_object').get('last4'), - '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') ), 'cms_integration': get_cms_integration('default'), - } + }) return render(request, self.template_name, context) def post(self, request, *args, **kwargs): From ec0216790fb08f483a06fd5e4846f52ec88b3576 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 5 Jul 2018 09:55:45 +0200 Subject: [PATCH 279/866] Remove redundant code --- datacenterlight/views.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 26afd574..02e91470 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -311,17 +311,6 @@ class PaymentOrderView(FormView): ) request.session['card_id'] = user_card_detail.id else: - # Get or create stripe customer - customer = StripeCustomer.get_or_create( - email=owner.email, token=token - ) - if not customer: - msg = _("Invalid credit card") - messages.add_message( - self.request, messages.ERROR, msg, - extra_tags='make_charge_error') - return HttpResponseRedirect( - reverse('datacenterlight:payment') + '#payment_error') request.session['token'] = token if request.user.is_authenticated(): this_user = { From e9ac699be8b69d75098712b427e1dc7e16c0fc13 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 5 Jul 2018 09:57:18 +0200 Subject: [PATCH 280/866] Add card detail creation code in various landing cases --- datacenterlight/views.py | 149 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 141 insertions(+), 8 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 02e91470..cd69c3ad 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -424,13 +424,75 @@ class OrderConfirmationView(DetailView): stripe_api_cus_id = request.session.get('customer') vm_template_id = template.get('id', 1) stripe_utils = StripeUtils() - card_details = stripe_utils.get_cards_details_from_token( - 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') + + if 'token' in request.session: + card_details = stripe_utils.get_cards_details_from_token( + 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') + 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 JsonResponse(response) + card_details_response = card_details['response_object'] + card_details_dict = { + 'last4': card_details_response['last4'], + 'brand': card_details_response['brand'], + 'card_id': card_details_response['card_id'] + } + stripe_customer_obj = StripeCustomer.objects.filter(stripe_id=stripe_api_cus_id).first() + if stripe_customer_obj: + ucd = UserCardDetail.get_user_card_details( + stripe_customer_obj, card_details_response + ) + if not ucd: + acc_result = stripe_utils.associate_customer_card( + stripe_api_cus_id, request.session['token'], + set_as_default=True + ) + if acc_result['response_object'] is None: + msg = _( + 'An error occurred while associating the card.' + ' Details: {details}'.format( + details=acc_result['error'] + ) + ) + messages.add_message(self.request, messages.ERROR, msg, + extra_tags='failed_payment') + response = { + 'status': False, + 'redirect': "{url}#{section}".format( + url=reverse('hosting: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 JsonResponse(response) + elif 'card_id' in request.session: + card_id = request.session.get('card_id') + user_card_detail = UserCardDetail.objects.get(id=card_id) + card_details_dict = { + 'last4': user_card_detail.last4, + 'brand': user_card_detail.brand, + 'card_id': user_card_detail.card_id + } + else: response = { 'status': False, 'redirect': "{url}#{section}".format( @@ -444,7 +506,6 @@ class OrderConfirmationView(DetailView): } return JsonResponse(response) - card_details_dict = card_details.get('response_object') cpu = specs.get('cpu') memory = specs.get('memory') disk_size = specs.get('disk_size') @@ -498,6 +559,30 @@ class OrderConfirmationView(DetailView): user=custom_user, stripe_id=stripe_api_cus_id ) stripe_customer_id = stripe_customer.id + + if 'token' in request.session: + ucd = UserCardDetail.get_or_create_user_card_detail( + stripe_customer=self.request.user.stripecustomer, + card_details=card_details_response + ) + UserCardDetail.save_default_card_local( + self.request.user.stripecustomer.stripe_id, + ucd.card_id + ) + + else: + card_id = request.session.get('card_id') + user_card_detail = UserCardDetail.objects.get(id=card_id) + card_details_dict = { + 'last4': user_card_detail.last4, + 'brand': user_card_detail.brand, + 'card_id': user_card_detail.card_id + } + if not user_card_detail.preferred: + UserCardDetail.set_default_card( + stripe_api_cus_id=stripe_api_cus_id, + stripe_source_id=user_card_detail.card_id + ) except CustomUser.DoesNotExist: logger.debug( "Customer {} does not exist.".format(user.get('email'))) @@ -517,6 +602,31 @@ class OrderConfirmationView(DetailView): new_user = authenticate(username=custom_user.email, password=password) login(request, new_user) + + if 'token' in request.session: + ucd = UserCardDetail.get_or_create_user_card_detail( + stripe_customer=self.request.user.stripecustomer, + card_details=card_details_response + ) + UserCardDetail.save_default_card_local( + self.request.user.stripecustomer.stripe_id, + ucd.card_id + ) + + else: + card_id = request.session.get('card_id') + user_card_detail = UserCardDetail.objects.get(id=card_id) + card_details_dict = { + 'last4': user_card_detail.last4, + 'brand': user_card_detail.brand, + 'card_id': user_card_detail.card_id + } + if not user_card_detail.preferred: + UserCardDetail.set_default_card( + stripe_api_cus_id=stripe_api_cus_id, + stripe_source_id=user_card_detail.card_id + ) + else: # We assume that if the user is here, his/her StripeCustomer # object already exists @@ -524,6 +634,29 @@ class OrderConfirmationView(DetailView): stripe_customer_id = request.user.stripecustomer.id custom_user = request.user + if 'token' in request.session: + ucd = UserCardDetail.get_or_create_user_card_detail( + stripe_customer=self.request.user.stripecustomer, + card_details=card_details_response + ) + UserCardDetail.save_default_card_local( + self.request.user.stripecustomer.stripe_id, + ucd.card_id + ) + else: + card_id = request.session.get('card_id') + user_card_detail = UserCardDetail.objects.get(id=card_id) + card_details_dict = { + 'last4': user_card_detail.last4, + 'brand': user_card_detail.brand, + 'card_id': user_card_detail.card_id + } + if not user_card_detail.preferred: + UserCardDetail.set_default_card( + stripe_api_cus_id=stripe_api_cus_id, + stripe_source_id=user_card_detail.card_id + ) + # Save billing address billing_address_data = request.session.get('billing_address_data') logger.debug('billing_address_data is {}'.format(billing_address_data)) From 8d7b01d7e273ac48a9aaa1ea9ba2b85ed084a6f4 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 5 Jul 2018 09:57:47 +0200 Subject: [PATCH 281/866] Disable dissociating card on subscription failure --- datacenterlight/views.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index cd69c3ad..b93df4d8 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -530,6 +530,13 @@ class OrderConfirmationView(DetailView): # Check if the subscription was approved and is active if (stripe_subscription_obj is None or stripe_subscription_obj.status != 'active'): + # At this point, we have created a Stripe API card and + # associated it with the customer; but the transaction failed + # due to some reason. + # stripe_utils.dissociate_customer_card( + # stripe_api_cus_id, + # card_details_dict['card_id'] + # ) msg = subscription_result.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') From faa0604faef9caa4934924aebce0851e9464a7f3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 5 Jul 2018 09:58:07 +0200 Subject: [PATCH 282/866] Remove redundant code --- datacenterlight/views.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index b93df4d8..c0d509cf 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -671,15 +671,6 @@ class OrderConfirmationView(DetailView): 'user': custom_user.id }) - if 'token' in request.session: - ucd = UserCardDetail.get_or_create_user_card_detail( - stripe_customer=stripe_customer, - card_details=card_details_dict - ) - UserCardDetail.save_default_card_local( - stripe_customer.stripe_id, - ucd.card_id - ) user = { 'name': custom_user.name, 'email': custom_user.email, From 9035f9806040ec6dbe52180daf3b18cdf07b9e41 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 5 Jul 2018 09:58:57 +0200 Subject: [PATCH 283/866] Update UserCardDetail get method --- hosting/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hosting/models.py b/hosting/models.py index b5fc9023..3ae3b0a5 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -347,7 +347,7 @@ class UserCardDetail(AssignPermissionsMixin, models.Model): :param stripe_customer: :param card_details: - :return: The UserCardDetails object if it exists, False otherwise + :return: The UserCardDetails object if it exists, None otherwise """ try: ucd = UserCardDetail.objects.get( @@ -358,4 +358,4 @@ class UserCardDetail(AssignPermissionsMixin, models.Model): ) return ucd except UserCardDetail.DoesNotExist: - return False + return None From baa2817f573a4c437dee9c5319588feca6a50b30 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 5 Jul 2018 10:00:12 +0200 Subject: [PATCH 284/866] Update BillingAddressFormSignup: add email validation --- utils/forms.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/utils/forms.py b/utils/forms.py index bd00b42d..fe5dc282 100644 --- a/utils/forms.py +++ b/utils/forms.py @@ -137,6 +137,31 @@ class BillingAddressFormSignup(BillingAddressForm): email = forms.EmailField(label=_('Email Address')) field_order = ['name', 'email'] + class Meta: + model = BillingAddress + fields = ['name', 'email', 'cardholder_name', 'street_address', + 'city', 'postal_code', 'country'] + labels = { + 'name': 'Name', + 'email': _('Email'), + 'cardholder_name': _('Cardholder Name'), + 'street_address': _('Street Address'), + 'city': _('City'), + 'postal_code': _('Postal Code'), + 'Country': _('Country'), + } + + def clean_email(self): + email = self.cleaned_data.get('email') + try: + CustomUser.objects.get(email=email) + raise forms.ValidationError( + _("The email {} is already registered with us. Please reset " + "your password and access your account.".format(email)) + ) + except CustomUser.DoesNotExist: + return email + class UserBillingAddressForm(forms.ModelForm): user = forms.ModelChoiceField(queryset=CustomUser.objects.all(), From 6b663d82a1f8246d67d1c4f6806c833d08240fef Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 5 Jul 2018 20:34:57 +0200 Subject: [PATCH 285/866] Remove unused variable --- datacenterlight/views.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index c0d509cf..6d129ebd 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -282,10 +282,8 @@ class PaymentOrderView(FormView): ) if address_form.is_valid(): token = address_form.cleaned_data.get('token') - owner = self.request.user if token is '': card_id = address_form.cleaned_data.get('card') - customer = owner.stripecustomer try: user_card_detail = UserCardDetail.objects.get(id=card_id) if not request.user.has_perm( From 1e214f7b210becdecc3d25b4707eccff0de9e50a Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 5 Jul 2018 20:35:34 +0200 Subject: [PATCH 286/866] Improve comment --- datacenterlight/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 6d129ebd..127a272e 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -530,7 +530,7 @@ class OrderConfirmationView(DetailView): or stripe_subscription_obj.status != 'active'): # At this point, we have created a Stripe API card and # associated it with the customer; but the transaction failed - # due to some reason. + # due to some reason. We may want to dissociate this card. # stripe_utils.dissociate_customer_card( # stripe_api_cus_id, # card_details_dict['card_id'] From 5cd62abc70453b215e925a98f4478de73a50b434 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 5 Jul 2018 20:36:43 +0200 Subject: [PATCH 287/866] Refactor code --- datacenterlight/views.py | 92 +++++++++------------------------------- 1 file changed, 21 insertions(+), 71 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 127a272e..649810d0 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -564,30 +564,6 @@ class OrderConfirmationView(DetailView): user=custom_user, stripe_id=stripe_api_cus_id ) stripe_customer_id = stripe_customer.id - - if 'token' in request.session: - ucd = UserCardDetail.get_or_create_user_card_detail( - stripe_customer=self.request.user.stripecustomer, - card_details=card_details_response - ) - UserCardDetail.save_default_card_local( - self.request.user.stripecustomer.stripe_id, - ucd.card_id - ) - - else: - card_id = request.session.get('card_id') - user_card_detail = UserCardDetail.objects.get(id=card_id) - card_details_dict = { - 'last4': user_card_detail.last4, - 'brand': user_card_detail.brand, - 'card_id': user_card_detail.card_id - } - if not user_card_detail.preferred: - UserCardDetail.set_default_card( - stripe_api_cus_id=stripe_api_cus_id, - stripe_source_id=user_card_detail.card_id - ) except CustomUser.DoesNotExist: logger.debug( "Customer {} does not exist.".format(user.get('email'))) @@ -607,60 +583,34 @@ class OrderConfirmationView(DetailView): new_user = authenticate(username=custom_user.email, password=password) login(request, new_user) - - if 'token' in request.session: - ucd = UserCardDetail.get_or_create_user_card_detail( - stripe_customer=self.request.user.stripecustomer, - card_details=card_details_response - ) - UserCardDetail.save_default_card_local( - self.request.user.stripecustomer.stripe_id, - ucd.card_id - ) - - else: - card_id = request.session.get('card_id') - user_card_detail = UserCardDetail.objects.get(id=card_id) - card_details_dict = { - 'last4': user_card_detail.last4, - 'brand': user_card_detail.brand, - 'card_id': user_card_detail.card_id - } - if not user_card_detail.preferred: - UserCardDetail.set_default_card( - stripe_api_cus_id=stripe_api_cus_id, - stripe_source_id=user_card_detail.card_id - ) - else: # We assume that if the user is here, his/her StripeCustomer # object already exists - stripe_customer = request.user.stripecustomer stripe_customer_id = request.user.stripecustomer.id custom_user = request.user - if 'token' in request.session: - ucd = UserCardDetail.get_or_create_user_card_detail( - stripe_customer=self.request.user.stripecustomer, - card_details=card_details_response + if 'token' in request.session: + ucd = UserCardDetail.get_or_create_user_card_detail( + stripe_customer=self.request.user.stripecustomer, + card_details=card_details_response + ) + UserCardDetail.save_default_card_local( + self.request.user.stripecustomer.stripe_id, + ucd.card_id + ) + else: + card_id = request.session.get('card_id') + user_card_detail = UserCardDetail.objects.get(id=card_id) + card_details_dict = { + 'last4': user_card_detail.last4, + 'brand': user_card_detail.brand, + 'card_id': user_card_detail.card_id + } + if not user_card_detail.preferred: + UserCardDetail.set_default_card( + stripe_api_cus_id=stripe_api_cus_id, + stripe_source_id=user_card_detail.card_id ) - UserCardDetail.save_default_card_local( - self.request.user.stripecustomer.stripe_id, - ucd.card_id - ) - else: - card_id = request.session.get('card_id') - user_card_detail = UserCardDetail.objects.get(id=card_id) - card_details_dict = { - 'last4': user_card_detail.last4, - 'brand': user_card_detail.brand, - 'card_id': user_card_detail.card_id - } - if not user_card_detail.preferred: - UserCardDetail.set_default_card( - stripe_api_cus_id=stripe_api_cus_id, - stripe_source_id=user_card_detail.card_id - ) # Save billing address billing_address_data = request.session.get('billing_address_data') From 0db4a113e6d1fa281ec0368297240dc254e81bb2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 5 Jul 2018 23:06:47 +0200 Subject: [PATCH 288/866] Add some styles and do not let user remove card if he has only one --- datacenterlight/static/datacenterlight/css/common.css | 8 ++++++++ .../templates/datacenterlight/landing_payment.html | 2 +- hosting/static/hosting/css/commons.css | 9 +++++++++ hosting/templates/hosting/settings.html | 4 ++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/datacenterlight/static/datacenterlight/css/common.css b/datacenterlight/static/datacenterlight/css/common.css index 4b3b7b41..28674b30 100644 --- a/datacenterlight/static/datacenterlight/css/common.css +++ b/datacenterlight/static/datacenterlight/css/common.css @@ -171,4 +171,12 @@ footer .dcl-link-separator::before { .credit-card-info .align-bottom { align-self: flex-end; padding-right: 0 !important; +} + +.new-card-head { + margin-top: 10px; +} +.new-card-button-margin button{ + margin-top: 5px; + margin-bottom: 5px; } \ No newline at end of file diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html index b774f4c2..4c43f41c 100644 --- a/datacenterlight/templates/datacenterlight/landing_payment.html +++ b/datacenterlight/templates/datacenterlight/landing_payment.html @@ -130,7 +130,7 @@

{% trans "Add a new credit card" %}

-
+
diff --git a/hosting/static/hosting/css/commons.css b/hosting/static/hosting/css/commons.css index 5de13353..292aeda9 100644 --- a/hosting/static/hosting/css/commons.css +++ b/hosting/static/hosting/css/commons.css @@ -431,3 +431,12 @@ margin-top: 10px; margin-bottom: 10px; } + + +.new-card-head { + margin-top: 10px; +} +.new-card-button-margin button{ + margin-top: 5px; + margin-bottom: 5px; +} \ No newline at end of file diff --git a/hosting/templates/hosting/settings.html b/hosting/templates/hosting/settings.html index 64057747..62dcc947 100644 --- a/hosting/templates/hosting/settings.html +++ b/hosting/templates/hosting/settings.html @@ -31,6 +31,7 @@

{%trans "Credit Card" %}


+ {% with card_list_len=cards_list|length %} {% for card in cards_list %}
{% trans "Credit Card" %}
@@ -38,6 +39,7 @@
{% trans "Type" %}: {{card.brand}}
+ {% if card_list_len > 1 %}
{% trans "REMOVE CARD" %}
+ {% endif %}
{% if card.preferred %} {% trans "DEFAULT" %} @@ -83,6 +86,7 @@

{% blocktrans %}We are using Stripe for payment and do not store your information in our database.{% endblocktrans %}

{% endfor %} + {% endwith %}
From 1f2743a65d4843ee7537da2d72593e316397354c Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 6 Jul 2018 01:19:44 +0200 Subject: [PATCH 289/866] Makemessages for datacenterlight, hosting and utils --- .../locale/de/LC_MESSAGES/django.po | 79 ++++++++---- hosting/locale/de/LC_MESSAGES/django.po | 119 +++++++++++++----- utils/locale/de/LC_MESSAGES/django.po | 11 +- 3 files changed, 149 insertions(+), 60 deletions(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index 4a95c2fc..42eab589 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: 2018-05-12 21:43+0530\n" +"POT-Creation-Date: 2018-07-05 23:11+0000\n" "PO-Revision-Date: 2018-03-30 23:22+0000\n" "Last-Translator: b'Anonymous User '\n" "Language-Team: LANGUAGE \n" @@ -329,6 +329,14 @@ msgstr "wird an der Kasse angewendet" msgid "Credit Card" msgstr "Kreditkarte" +msgid "" +"Please select one of the cards that you used before or fill in your credit " +"card information below. We are using Stripe for payment and do not store your information in our " +"database." +msgstr "" +"" + msgid "" "Please fill in your credit card information below. We are using Stripe for payment and do not " @@ -338,31 +346,23 @@ msgstr "" "\"https://stripe.com\" target=\"_blank\">Stripe für die Bezahlung und " "speichern keine Informationen in unserer Datenbank." -msgid "" -"You are not making any payment yet. After submitting your card information, " -"you will be taken to the Confirm Order 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 "Card Number" -msgstr "Kreditkartennummer" - -msgid "Expiry Date" -msgstr "Ablaufdatum" - -msgid "CVC" +msgid "Last" 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." +msgid "Type" +msgstr "" + +msgid "SELECT" +msgstr "" + +msgid "Add a new credit card" +msgstr "" + +msgid "NEW CARD" +msgstr "" + +msgid "New Credit Card" 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" @@ -516,6 +516,13 @@ msgstr "Ungültige Speicher-Grösse" msgid "Incorrect pricing name. Please contact support{support_email}" msgstr "" +#, python-brace-format +msgid "{user} does not have permission to access the card" +msgstr "" + +msgid "An error occurred. Details: {}" +msgstr "" + msgid "Confirm Order" msgstr "Bestellung Bestätigen" @@ -529,6 +536,10 @@ msgstr "" "Es ist ein Fehler bei der Zahlung betreten. Du wirst nach dem Schliessen vom " "Popup zur Bezahlseite weitergeleitet." +#, python-brace-format +msgid "An error occurred while associating the card. Details: {details}" +msgstr "" + msgid "Thank you for the order." msgstr "Danke für Deine Bestellung." @@ -539,6 +550,28 @@ msgstr "" "Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du " "auf sie zugreifen kannst." +#~ msgid "" +#~ "You are not making any payment yet. After submitting your card " +#~ "information, you will be taken to the Confirm Order 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 "Card Number" +#~ msgstr "Kreditkartennummer" + +#~ msgid "Expiry Date" +#~ msgstr "Ablaufdatum" + +#~ 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 "Pricing" #~ msgstr "Preise" diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index d61d09c0..8d55d1b7 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: 2018-05-12 03:53+0530\n" +"POT-Creation-Date: 2018-07-05 23:15+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -274,6 +274,27 @@ msgstr "" msgid "You can always order a new VM by following the link below." msgstr "" +msgid "Card Number" +msgstr "Kreditkartennummer" + +msgid "Expiry Date" +msgstr "Ablaufdatum" + +msgid "CVC" +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 "" +"" + +msgid "SUBMIT" +msgstr "ABSENDEN" + msgid "Toggle navigation" msgstr "Umschalten" @@ -439,6 +460,14 @@ msgstr "wird an der Kasse angewendet" msgid "Billing Address" msgstr "Rechnungsadresse" +msgid "" +"Please select one of the cards that you used before or fill in your credit " +"card information below. We are using Stripe for payment and do not store your information in our " +"database." +msgstr "" +"" + msgid "" "Please fill in your credit card information below. We are using Stripe for payment and do not " @@ -448,28 +477,24 @@ msgstr "" "\"https://stripe.com\" target=\"_blank\">Stripe für die Bezahlung und " "speichern keine Informationen in unserer Datenbank." -msgid "" -"You are not making any payment yet. After submitting your card information, " -"you will be taken to the Confirm Order 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 "SUBMIT" -msgstr "ABSENDEN" - -msgid "Card Number" -msgstr "Kreditkartennummer" - -msgid "Expiry Date" -msgstr "Ablaufdatum" - -msgid "CVC" +msgid "Last" msgstr "" -msgid "Card Type" +msgid "Type" msgstr "Kartentyp" +msgid "SELECT" +msgstr "" + +msgid "Add a new credit card" +msgstr "Neue Kreditkarte hinzufügen." + +msgid "NEW CARD" +msgstr "BEARBEITEN" + +msgid "New Credit Card" +msgstr "" + msgid "Processing" msgstr "Weiter" @@ -485,11 +510,20 @@ msgstr "Passwort zurücksetzen" msgid "UPDATE" msgstr "" -msgid "Last" +msgid "REMOVE CARD" +msgstr "KARTE ENTFERNEN" + +msgid "Remove Card" msgstr "" -msgid "Type" -msgstr "Kartentyp" +msgid "Do you want to remove this associated card?" +msgstr "Möchtest Du den Schlüssel löschen?" + +msgid "Delete" +msgstr "Löschen" + +msgid "DEFAULT" +msgstr "" msgid "No Credit Cards Added" msgstr "Es wurde keine Kreditkarte hinzugefügt" @@ -536,9 +570,6 @@ msgstr "" msgid "Private Key" msgstr "" -msgid "Delete" -msgstr "Löschen" - msgid "Delete SSH Key" msgstr "SSH Key löschen" @@ -670,6 +701,35 @@ msgstr "Dein Passwort konnte nicht zurückgesetzt werden." msgid "The reset password link is no longer valid." msgstr "Der Link zum Zurücksetzen Deines Passwortes ist nicht mehr gültig." +msgid "Card deassociation successful" +msgstr "" + +msgid "You are not permitted to do this operation" +msgstr "" + +msgid "The selected card does not exist" +msgstr "" + +msgid "Billing address updated successfully" +msgstr "" + +msgid "You seem to have already added this card" +msgstr "" + +#, python-brace-format +msgid "An error occurred while associating the card. Details: {details}" +msgstr "" + +msgid "Successfully associated the card with your account" +msgstr "" + +#, python-brace-format +msgid "{user} does not have permission to access the card" +msgstr "" + +msgid "An error occurred. Details: {}" +msgstr "" + msgid "Invalid credit card" msgstr "Ungültige Kreditkarte" @@ -807,15 +867,6 @@ msgstr "" #~ msgid "Notifications " #~ msgstr "Benachrichtigungen" -#~ msgid "REMOVE CARD" -#~ msgstr "KARTE ENTFERNEN" - -#~ msgid "EDIT CARD" -#~ msgstr "BEARBEITEN" - -#~ msgid "Add a new Card." -#~ msgstr "Neue Kreditkarte hinzufügen." - #~ msgid "You are not making any payment here." #~ msgstr "Es wird noch keine Bezahlung vorgenommen" diff --git a/utils/locale/de/LC_MESSAGES/django.po b/utils/locale/de/LC_MESSAGES/django.po index f18fc9c2..a9aaf8a0 100644 --- a/utils/locale/de/LC_MESSAGES/django.po +++ b/utils/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-10-10 21:35+0530\n" +"POT-Creation-Date: 2018-07-05 23:18+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -777,10 +777,15 @@ msgstr "" msgid "Email Address" msgstr "" -msgid "Street Building" +msgid "Email" msgstr "" -msgid "Email" +msgid "" +"The email {} is already registered with us. Please reset your password and " +"access your account." +msgstr "" + +msgid "Street Building" msgstr "" msgid "Phone number" From 4c06a9e730bfc671a2709740bb45f45676093d65 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 6 Jul 2018 19:33:26 +0200 Subject: [PATCH 290/866] Add de translations by MalcolmA --- .../locale/de/LC_MESSAGES/django.po | 24 +++++++----- hosting/locale/de/LC_MESSAGES/django.po | 37 ++++++++++--------- utils/locale/de/LC_MESSAGES/django.po | 6 ++- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index 42eab589..1b66b640 100644 --- a/datacenterlight/locale/de/LC_MESSAGES/django.po +++ b/datacenterlight/locale/de/LC_MESSAGES/django.po @@ -335,7 +335,10 @@ msgid "" "\"_blank\">Stripe for payment and do not store your information in our " "database." msgstr "" -"" +"Bitte wähle eine der zuvor genutzten Kreditkarten oder gib Deine " +"Kreditkartendetails unten an. Die Bezahlung wird über " +"Stripe abgewickelt. " +"Wir speichern Deine Kreditkartendetails nicht in unserer Datenbank." msgid "" "Please fill in your credit card information below. We are using Date: Sat, 7 Jul 2018 01:08:45 +0200 Subject: [PATCH 291/866] Remove token parameter from stripe_utils.get_card_details and add additional paramters to uniquely identify a card --- datacenterlight/tests.py | 3 +-- utils/stripe_utils.py | 8 ++++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/datacenterlight/tests.py b/datacenterlight/tests.py index d6cd6adf..c755cc6f 100644 --- a/datacenterlight/tests.py +++ b/datacenterlight/tests.py @@ -86,8 +86,7 @@ class CeleryTaskTestCase(TestCase): token=self.token ) card_details = self.stripe_utils.get_card_details( - stripe_customer.stripe_id, - self.token + stripe_customer.stripe_id ) card_details_dict = card_details.get('error') self.assertEquals(card_details_dict, None) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index e4b9b02e..2045df8e 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -109,12 +109,16 @@ class StripeUtils(object): return new_card_data @handleStripeError - def get_card_details(self, customer_id, token): + def get_card_details(self, customer_id): 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 + 'brand': credit_card_raw_data.brand, + 'exp_month': credit_card_raw_data.exp_month, + 'exp_year': credit_card_raw_data.exp_year, + 'fingerprint': credit_card_raw_data.fingerprint, + 'card_id': credit_card_raw_data.id } return card_details From e60b93d126dfe81a9f0360144136d08c91beb05a Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 7 Jul 2018 01:28:25 +0200 Subject: [PATCH 292/866] Add management command import_usercarddetails --- .../commands/import_usercarddetails.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 hosting/management/commands/import_usercarddetails.py diff --git a/hosting/management/commands/import_usercarddetails.py b/hosting/management/commands/import_usercarddetails.py new file mode 100644 index 00000000..fa5b975e --- /dev/null +++ b/hosting/management/commands/import_usercarddetails.py @@ -0,0 +1,39 @@ +from django.core.management.base import BaseCommand + +from hosting.models import UserCardDetail +from membership.models import CustomUser +from utils.stripe_utils import StripeUtils + + +class Command(BaseCommand): + help = '''Imports the usercard details of all customers. Created just for + multiple card support.''' + + def handle(self, *args, **options): + try: + stripe_utils = StripeUtils() + for user in CustomUser.objects.filter(id__gte=114): + if user.stripecustomer: + card_details_resp = stripe_utils.get_card_details( + user.stripecustomer.stripe_id + ) + card_details = card_details_resp['response_object'] + if card_details: + ucd = UserCardDetail.get_or_create_user_card_detail( + stripe_customer=user.stripecustomer, + card_details=card_details + ) + UserCardDetail.save_default_card_local( + user.stripecustomer.stripe_id, + ucd.card_id + ) + print("Saved user card details for {}".format( + user.email + )) + else: + print(" --- Could not get card details for {}".format( + user.email + )) + print("Error: {}".format(card_details_resp['error'])) + except Exception as e: + print("Error occurred. Details {}".format(str(e))) From e18b8a527a3d19d219613fc90a800a5df786d65a Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 7 Jul 2018 01:35:27 +0200 Subject: [PATCH 293/866] Add missing DE translation --- hosting/locale/de/LC_MESSAGES/django.po | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index d924fd4e..64dde71e 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -468,7 +468,10 @@ msgid "" "\"_blank\">Stripe for payment and do not store your information in our " "database." msgstr "" -"" +"Bitte wähle eine der zuvor genutzten Kreditkarten oder gib Deine " +"Kreditkartendetails unten an. Die Bezahlung wird über " +"Stripe abgewickelt. " +"Wir speichern Deine Kreditkartendetails nicht in unserer Datenbank." msgid "" "Please fill in your credit card information below. We are using Date: Sat, 7 Jul 2018 01:50:46 +0200 Subject: [PATCH 295/866] Add check to forbid user from deleting the last card --- hosting/views.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index a28815f2..7e23a682 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -15,12 +15,12 @@ from django.http import ( Http404, HttpResponseRedirect, HttpResponse, JsonResponse ) from django.shortcuts import redirect, render +from django.utils.decorators import method_decorator from django.utils.html import escape from django.utils.http import urlsafe_base64_decode from django.utils.safestring import mark_safe from django.utils.translation import get_language, ugettext_lazy as _ from django.utils.translation import ugettext -from django.utils.decorators import method_decorator from django.views.decorators.cache import never_cache from django.views.generic import ( View, CreateView, FormView, ListView, DetailView, DeleteView, @@ -615,7 +615,13 @@ class SettingsView(LoginRequiredMixin, FormView): if 'delete_card' in request.POST: try: card = UserCardDetail.objects.get(pk=self.kwargs.get('pk')) - if request.user.has_perm(self.permission_required[0], card): + if (request.user.has_perm(self.permission_required[0], card) + and + request.user + .stripecustomer + .usercarddetail_set + .count() > 1 + ): if card.card_id is not None: stripe_utils = StripeUtils() stripe_utils.dissociate_customer_card( From 0f26917f35fc6ba13f181763f0d1e980498b7429 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 7 Jul 2018 01:54:45 +0200 Subject: [PATCH 296/866] Fix PEP8 warning --- hosting/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 7e23a682..f8bcbe14 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -620,8 +620,7 @@ class SettingsView(LoginRequiredMixin, FormView): request.user .stripecustomer .usercarddetail_set - .count() > 1 - ): + .count() > 1): if card.card_id is not None: stripe_utils = StripeUtils() stripe_utils.dissociate_customer_card( From 34ed51a643a95b794f632768cd7a44a2946fabf6 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 7 Jul 2018 02:03:42 +0200 Subject: [PATCH 297/866] Don't dissociate card if transaction fails --- datacenterlight/views.py | 9 ++++----- hosting/views.py | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 649810d0..bf87d9b9 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -530,11 +530,10 @@ class OrderConfirmationView(DetailView): or stripe_subscription_obj.status != 'active'): # At this point, we have created a Stripe API card and # associated it with the customer; but the transaction failed - # due to some reason. We may want to dissociate this card. - # stripe_utils.dissociate_customer_card( - # stripe_api_cus_id, - # card_details_dict['card_id'] - # ) + # due to some reason. So, we would want to dissociate this card + # here. + # ... + msg = subscription_result.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') diff --git a/hosting/views.py b/hosting/views.py index f8bcbe14..6af1885b 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1054,11 +1054,10 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): stripe_subscription_obj.status != 'active'): # At this point, we have created a Stripe API card and # associated it with the customer; but the transaction failed - # due to some reason. So, we dissociate this card. - stripe_utils.dissociate_customer_card( - request.user.stripecustomer.stripe_id, - card_details_dict['card_id'] - ) + # due to some reason. So, we would want to dissociate this card + # here. + # ... + msg = subscription_result.get('error') messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment') From 33bd2e1760fb33112b33c7a3d31efcd2881efd60 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 7 Jul 2018 02:15:38 +0200 Subject: [PATCH 298/866] Improve import_usercarddetails management command --- .../commands/import_usercarddetails.py | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/hosting/management/commands/import_usercarddetails.py b/hosting/management/commands/import_usercarddetails.py index fa5b975e..de5a91f9 100644 --- a/hosting/management/commands/import_usercarddetails.py +++ b/hosting/management/commands/import_usercarddetails.py @@ -12,28 +12,34 @@ class Command(BaseCommand): def handle(self, *args, **options): try: stripe_utils = StripeUtils() - for user in CustomUser.objects.filter(id__gte=114): - if user.stripecustomer: - card_details_resp = stripe_utils.get_card_details( - user.stripecustomer.stripe_id - ) - card_details = card_details_resp['response_object'] - if card_details: - ucd = UserCardDetail.get_or_create_user_card_detail( - stripe_customer=user.stripecustomer, - card_details=card_details + for user in CustomUser.objects.all(): + if hasattr(user, 'stripecustomer'): + if user.stripecustomer: + card_details_resp = stripe_utils.get_card_details( + user.stripecustomer.stripe_id ) - UserCardDetail.save_default_card_local( - user.stripecustomer.stripe_id, - ucd.card_id - ) - print("Saved user card details for {}".format( - user.email - )) - else: - print(" --- Could not get card details for {}".format( - user.email - )) - print("Error: {}".format(card_details_resp['error'])) + card_details = card_details_resp['response_object'] + if card_details: + ucd = UserCardDetail.get_or_create_user_card_detail( + stripe_customer=user.stripecustomer, + card_details=card_details + ) + UserCardDetail.save_default_card_local( + user.stripecustomer.stripe_id, + ucd.card_id + ) + print("Saved user card details for {}".format( + user.email + )) + else: + print(" --- Could not get card details for " + "{}".format(user.email)) + print(" --- Error: {}".format( + card_details_resp['error'] + )) + else: + print(" === {} does not have a StripeCustomer object".format( + user.email + )) except Exception as e: - print("Error occurred. Details {}".format(str(e))) + print(" *** Error occurred. Details {}".format(str(e))) From 2ada3ccc6fc7c7546ab9d25086c0cf5097e53ebc Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 7 Jul 2018 02:35:22 +0200 Subject: [PATCH 299/866] Update Changelog --- Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog b/Changelog index 712bce85..e0fbf7f9 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,5 @@ Next: + * #3747: [dcl,hosting] Add multiple cards support * #3934: [dcl,hosting] Create HostingOrder outside celery task and add and associate OrderDetail with HostingOrder * #4890: [hosting] Manage SSH keys using IPv6 of the VM (PR #640) * bugfix: Fix flake8 error that was ignored in release 1.9.1 From d9bcdf22b70ef588375c76889ad70926fecec762 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 7 Jul 2018 03:01:37 +0200 Subject: [PATCH 300/866] Add missing param in DE translation --- utils/locale/de/LC_MESSAGES/django.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/locale/de/LC_MESSAGES/django.po b/utils/locale/de/LC_MESSAGES/django.po index ae679d16..6313dfb8 100644 --- a/utils/locale/de/LC_MESSAGES/django.po +++ b/utils/locale/de/LC_MESSAGES/django.po @@ -784,7 +784,7 @@ msgid "" "The email {} is already registered with us. Please reset your password and " "access your account." msgstr "" -"Diese E-Mail-Adresse existiert bereits. Bitte setze dein Passwort zurück um " +"Diese E-Mail-Adresse {} existiert bereits. Bitte setze dein Passwort zurück " "auf dein Konto zuzugreifen." msgid "Street Building" From 3ccefbdb744cc692adad82de4941d6faa6b29aef Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 7 Jul 2018 21:29:44 +0200 Subject: [PATCH 301/866] Reorganize imports --- utils/forms.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/utils/forms.py b/utils/forms.py index fe5dc282..6713f410 100644 --- a/utils/forms.py +++ b/utils/forms.py @@ -1,10 +1,11 @@ from django import forms -from .models import ContactMessage, BillingAddress, UserBillingAddress -from django.template.loader import render_to_string -from django.core.mail import EmailMultiAlternatives -from django.utils.translation import ugettext_lazy as _ from django.contrib.auth import authenticate +from django.core.mail import EmailMultiAlternatives +from django.template.loader import render_to_string +from django.utils.translation import ugettext_lazy as _ + from membership.models import CustomUser +from .models import ContactMessage, BillingAddress, UserBillingAddress # from utils.fields import CountryField From b580ac24f6a62f5bf25098ef4aa3c135454ce5e1 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 7 Jul 2018 21:30:40 +0200 Subject: [PATCH 302/866] Change string formatting --- utils/forms.py | 8 +++++--- utils/locale/de/LC_MESSAGES/django.po | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/utils/forms.py b/utils/forms.py index 6713f410..fdc67d26 100644 --- a/utils/forms.py +++ b/utils/forms.py @@ -67,7 +67,8 @@ class ResendActivationEmailForm(forms.Form): try: c = CustomUser.objects.get(email=email) if c.validated == 1: - raise forms.ValidationError(_("The account is already active.")) + raise forms.ValidationError( + _("The account is already active.")) return email except CustomUser.DoesNotExist: raise forms.ValidationError(_("User does not exist")) @@ -157,8 +158,9 @@ class BillingAddressFormSignup(BillingAddressForm): try: CustomUser.objects.get(email=email) raise forms.ValidationError( - _("The email {} is already registered with us. Please reset " - "your password and access your account.".format(email)) + _("The email %(email)s is already registered with us. " + "Please reset your password and access your account.") % + {'email': email} ) except CustomUser.DoesNotExist: return email diff --git a/utils/locale/de/LC_MESSAGES/django.po b/utils/locale/de/LC_MESSAGES/django.po index 6313dfb8..670e15b9 100644 --- a/utils/locale/de/LC_MESSAGES/django.po +++ b/utils/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: 2018-07-05 23:18+0000\n" +"POT-Creation-Date: 2018-07-07 19:27+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -781,10 +781,10 @@ msgid "Email" msgstr "E-Mail" msgid "" -"The email {} is already registered with us. Please reset your password and " -"access your account." +"The email %(email)s is already registered with us. Please reset your " +"password and access your account." msgstr "" -"Diese E-Mail-Adresse {} existiert bereits. Bitte setze dein Passwort zurück " +"Diese E-Mail-Adresse %(email)s existiert bereits. Bitte setze dein Passwort zurück " "auf dein Konto zuzugreifen." msgid "Street Building" From 28cac31a93bf8225ede59af111c5cad9dd667f6c Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 7 Jul 2018 21:45:32 +0200 Subject: [PATCH 303/866] Update Changelog for 2.0 --- Changelog | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Changelog b/Changelog index e0fbf7f9..e2bb96cd 100644 --- a/Changelog +++ b/Changelog @@ -1,6 +1,6 @@ -Next: - * #3747: [dcl,hosting] Add multiple cards support - * #3934: [dcl,hosting] Create HostingOrder outside celery task and add and associate OrderDetail with HostingOrder +2.0: 2018-07-07 + * #3747: [dcl,hosting] Add multiple cards support (PR #530) + * #3934: [dcl,hosting] Create HostingOrder outside celery task and add and associate OrderDetail with HostingOrder (PR #624) * #4890: [hosting] Manage SSH keys using IPv6 of the VM (PR #640) * bugfix: Fix flake8 error that was ignored in release 1.9.1 1.9.1: 2018-06-24 From b3cdbbcae2ac6bca8496a164ef767e3b32666141 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Wed, 11 Jul 2018 18:50:56 +0200 Subject: [PATCH 304/866] Move placeholder to blog_ungleich This was the cause why the structure/content switch was not available on the blog page --- ungleich/templates/cms/ungleichch/base_ungleich.html | 1 - ungleich/templates/cms/ungleichch/blog_ungleich.html | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ungleich/templates/cms/ungleichch/base_ungleich.html b/ungleich/templates/cms/ungleichch/base_ungleich.html index e19e7421..b82402a4 100644 --- a/ungleich/templates/cms/ungleichch/base_ungleich.html +++ b/ungleich/templates/cms/ungleichch/base_ungleich.html @@ -55,7 +55,6 @@
{% block base_content %} - {% placeholder "default" %} {% endblock %}
diff --git a/ungleich/templates/cms/ungleichch/blog_ungleich.html b/ungleich/templates/cms/ungleichch/blog_ungleich.html index d5475867..ebd4b102 100644 --- a/ungleich/templates/cms/ungleichch/blog_ungleich.html +++ b/ungleich/templates/cms/ungleichch/blog_ungleich.html @@ -1,5 +1,5 @@ {% extends "base_ungleich.html" %} +{% load cms_tags %} {% block base_content %} -{% block content %} -{% endblock %} +{% placeholder "default" %} {% endblock %} From a5929b3e863a8156a17be8ae4e5b88b728366174 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 14 Jul 2018 18:41:28 +0200 Subject: [PATCH 305/866] Update Changelog --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index e2bb96cd..f0d5e09d 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +2.0.1: 2018-07-14 + * bugfix: [blog] Enable content/structure mode in blog page 2.0: 2018-07-07 * #3747: [dcl,hosting] Add multiple cards support (PR #530) * #3934: [dcl,hosting] Create HostingOrder outside celery task and add and associate OrderDetail with HostingOrder (PR #624) From c97e2c55f3892c0f3002ff9d0a324d905356b08e Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 14 Jul 2018 19:03:04 +0200 Subject: [PATCH 306/866] Add missing content block in the blog_ungleich.html template file --- ungleich/templates/cms/ungleichch/blog_ungleich.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ungleich/templates/cms/ungleichch/blog_ungleich.html b/ungleich/templates/cms/ungleichch/blog_ungleich.html index ebd4b102..e7ed3f25 100644 --- a/ungleich/templates/cms/ungleichch/blog_ungleich.html +++ b/ungleich/templates/cms/ungleichch/blog_ungleich.html @@ -2,4 +2,6 @@ {% load cms_tags %} {% block base_content %} {% placeholder "default" %} +{% block content %} +{% endblock %} {% endblock %} From 098a9065f14a30785e3e170d523fd704c04dbe14 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 14 Jul 2018 19:05:14 +0200 Subject: [PATCH 307/866] Update Changelog for 2.0.2 --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index f0d5e09d..2436f1a3 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +2.0.2: 2018-07-14 + * bugfix: [blog] Add missing content block in the blog_ungleich.html template file 2.0.1: 2018-07-14 * bugfix: [blog] Enable content/structure mode in blog page 2.0: 2018-07-07 From 15e435d220dbbcea537fa215bb6ea57cb2bf78fd Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 14 Jul 2018 20:05:18 +0200 Subject: [PATCH 308/866] Remove unused ^comic/$ url --- dynamicweb/urls.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/dynamicweb/urls.py b/dynamicweb/urls.py index c8961971..bee1398d 100644 --- a/dynamicweb/urls.py +++ b/dynamicweb/urls.py @@ -57,9 +57,6 @@ urlpatterns += i18n_patterns( url(r'^blog/$', RedirectView.as_view(url=reverse_lazy('ungleich:post-list')), name='blog_list_view'), - url(r'^comic/$', - ungleich_views.PostListViewUngleich.as_view(category='comic'), - name='comic_post_list_view'), url(r'^cms/', include('cms.urls')), url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')), url(r'^$', RedirectView.as_view(url='/cms') if REDIRECT_TO_CMS From 13e33cbb7ad0c687dd580a2baad35c30826efeeb Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 16 Jul 2018 20:44:09 +0200 Subject: [PATCH 309/866] Remove unused import --- dynamicweb/urls.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dynamicweb/urls.py b/dynamicweb/urls.py index bee1398d..7e2d58a1 100644 --- a/dynamicweb/urls.py +++ b/dynamicweb/urls.py @@ -11,7 +11,6 @@ from hosting.views import ( RailsHostingView, DjangoHostingView, NodeJSHostingView ) from membership import urls as membership_urls -from ungleich import views as ungleich_views from ungleich_page.views import LandingView from django.views.generic import RedirectView from django.core.urlresolvers import reverse_lazy From d6a404d49d25d86e4af0bbdc2c3cd4c508dd4b2c Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 16 Jul 2018 22:38:29 +0200 Subject: [PATCH 310/866] Add missing placeholder --- ungleich/templates/cms/ungleichch/base_ungleich.html | 1 + 1 file changed, 1 insertion(+) diff --git a/ungleich/templates/cms/ungleichch/base_ungleich.html b/ungleich/templates/cms/ungleichch/base_ungleich.html index b82402a4..ba314b25 100644 --- a/ungleich/templates/cms/ungleichch/base_ungleich.html +++ b/ungleich/templates/cms/ungleichch/base_ungleich.html @@ -55,6 +55,7 @@
{% block base_content %} + {% placeholder "base_ungleich_content" %} {% endblock %}
From 4a19bd1971bf0491e2d73bf857edb7d1b85dcf42 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 18 Jul 2018 21:48:39 +0200 Subject: [PATCH 311/866] Set X_FRAME_OPTIONS from env --- dynamicweb/settings/base.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 75dfaa73..d526881f 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -702,6 +702,12 @@ if ENABLE_LOGGING: TEST_MANAGE_SSH_KEY_PUBKEY = env('TEST_MANAGE_SSH_KEY_PUBKEY') TEST_MANAGE_SSH_KEY_HOST = env('TEST_MANAGE_SSH_KEY_HOST') +X_FRAME_OPTIONS_ALLOW_FROM_URI = env('X_FRAME_OPTIONS_ALLOW_FROM_URI') +X_FRAME_OPTIONS = ('SAMEORIGIN' if X_FRAME_OPTIONS_ALLOW_FROM_URI is None else + 'ALLOW-FROM {}'.format( + X_FRAME_OPTIONS_ALLOW_FROM_URI.strip() + )) + DEBUG = bool_env('DEBUG') if DEBUG: From 1291b49ec34524086a84bac29a890d78d113659c Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 18 Jul 2018 21:49:11 +0200 Subject: [PATCH 312/866] Reformat base.py --- dynamicweb/settings/base.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index d526881f..7d333a2f 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -2,16 +2,15 @@ Copyright 2015 ungleich. """ +import json +import logging # -*- coding: utf-8 -*- # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os -import json - -from django.utils.translation import ugettext_lazy as _ # dotenv import dotenv -import logging +from django.utils.translation import ugettext_lazy as _ logger = logging.getLogger(__name__) @@ -56,6 +55,7 @@ PROJECT_DIR = os.path.abspath( dotenv.read_dotenv("{0}/.env".format(PROJECT_DIR)) from multisite import SiteID + SITE_ID = SiteID(default=1) APP_ROOT_ENDPOINT = "/" @@ -580,7 +580,6 @@ MULTISITE_FALLBACK_KWARGS = { FILER_ENABLE_PERMISSIONS = True - ############################################# # configurations for opennebula-integration # ############################################# From 62d23b8a5ce7c871879fbe5bd0691a500d658bee Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 18 Jul 2018 22:02:21 +0200 Subject: [PATCH 313/866] Update Changelog --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index 2436f1a3..381fe562 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +Next: + * 5126: Allow dynamicweb sites to be iframed on other by setting `X_FRAME_OPTIONS_ALLOW_FROM_URI` 2.0.2: 2018-07-14 * bugfix: [blog] Add missing content block in the blog_ungleich.html template file 2.0.1: 2018-07-14 From be2831818d4647cfddc6c2fdd2d8da877f709399 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 18 Jul 2018 22:08:47 +0200 Subject: [PATCH 314/866] Update Changelog for 2.0.3 --- Changelog | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Changelog b/Changelog index 381fe562..bc127879 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,6 @@ -Next: - * 5126: Allow dynamicweb sites to be iframed on other by setting `X_FRAME_OPTIONS_ALLOW_FROM_URI` +2.0.3: + * Remove unused /comic url (PR #644) + * 5126: Allow dynamicweb sites to be iframed on other by setting `X_FRAME_OPTIONS_ALLOW_FROM_URI` (PR #645) 2.0.2: 2018-07-14 * bugfix: [blog] Add missing content block in the blog_ungleich.html template file 2.0.1: 2018-07-14 From 15db1c88d7827c84a99eff52d9bdda48cfa7c88f Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 18 Jul 2018 22:14:35 +0200 Subject: [PATCH 315/866] Add release date for 2.0.3 --- Changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog b/Changelog index bc127879..6c48e4b5 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,4 @@ -2.0.3: +2.0.3: 2018-07-18 * Remove unused /comic url (PR #644) * 5126: Allow dynamicweb sites to be iframed on other by setting `X_FRAME_OPTIONS_ALLOW_FROM_URI` (PR #645) 2.0.2: 2018-07-14 From 831bf9a8be06b42de0f42b2dd5fefeef654cf560 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 21 Jul 2018 10:46:53 +0200 Subject: [PATCH 316/866] Add gdpr_banner.html template --- templates/gdpr/gdpr_banner.html | 70 +++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 templates/gdpr/gdpr_banner.html diff --git a/templates/gdpr/gdpr_banner.html b/templates/gdpr/gdpr_banner.html new file mode 100644 index 00000000..1e132865 --- /dev/null +++ b/templates/gdpr/gdpr_banner.html @@ -0,0 +1,70 @@ +{% if request.COOKIES.gdpr_accepted %} +{% else %} + +
+ + + +{% endif %} \ No newline at end of file From 44fb184436e74f11fa3c8b218cb292b8c03ce7b7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 21 Jul 2018 10:49:58 +0200 Subject: [PATCH 317/866] Add templates/gdpr to the template path --- dynamicweb/settings/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 7d333a2f..5a1cd2e4 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -192,6 +192,7 @@ TEMPLATES = [ os.path.join(PROJECT_DIR, 'ungleich_page/templates/ungleich_page'), os.path.join(PROJECT_DIR, 'templates/analytics'), + os.path.join(PROJECT_DIR, 'templates/gdpr'), ], 'APP_DIRS': True, 'OPTIONS': { From 4dce0ee5bde98e96654adcebc19ac21b1bf72bcc Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 21 Jul 2018 10:51:11 +0200 Subject: [PATCH 318/866] Add gdpr banner to dcl template --- datacenterlight/templates/datacenterlight/base.html | 2 +- datacenterlight/templates/datacenterlight/cms/base.html | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/datacenterlight/templates/datacenterlight/base.html b/datacenterlight/templates/datacenterlight/base.html index 75cb8de2..4bff9f59 100644 --- a/datacenterlight/templates/datacenterlight/base.html +++ b/datacenterlight/templates/datacenterlight/base.html @@ -43,7 +43,7 @@ - + {% include "gdpr_banner.html" %} {% block navbar %} {% include "datacenterlight/includes/_navbar.html" %} {% endblock navbar %} diff --git a/datacenterlight/templates/datacenterlight/cms/base.html b/datacenterlight/templates/datacenterlight/cms/base.html index a614db67..c155a55d 100644 --- a/datacenterlight/templates/datacenterlight/cms/base.html +++ b/datacenterlight/templates/datacenterlight/cms/base.html @@ -49,6 +49,7 @@ + {% include "gdpr_banner.html" %} {% cms_toolbar %} {% placeholder 'datacenterlight_navbar' %} From 069cd18268395d75ca96f4af99583a991c2df353 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 21 Jul 2018 13:27:15 +0200 Subject: [PATCH 319/866] Add gdpr banner to all base templates --- datacenterlight/templates/datacenterlight/base_hosting.html | 1 + hosting/templates/hosting/base.html | 1 + hosting/templates/hosting/base_short.html | 1 + ungleich/templates/cms/ungleichch/base_ungleich.html | 1 + ungleich_page/templates/ungleich_page/glasfaser.html | 1 + ungleich_page/templates/ungleich_page/glasfaser_cms_page.html | 1 + ungleich_page/templates/ungleich_page/landing.html | 2 +- ungleich_page/templates/ungleich_page/ungleich_cms_page.html | 4 +++- 8 files changed, 10 insertions(+), 2 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/base_hosting.html b/datacenterlight/templates/datacenterlight/base_hosting.html index e863d46b..e50c4975 100644 --- a/datacenterlight/templates/datacenterlight/base_hosting.html +++ b/datacenterlight/templates/datacenterlight/base_hosting.html @@ -49,6 +49,7 @@ + {% include "gdpr_banner.html" %} {% cms_toolbar %} {% render_placeholder cms_integration.navbar_placeholder %} diff --git a/hosting/templates/hosting/base.html b/hosting/templates/hosting/base.html index cbf0874f..aca42e0f 100644 --- a/hosting/templates/hosting/base.html +++ b/hosting/templates/hosting/base.html @@ -48,6 +48,7 @@ + {% include "gdpr_banner.html" %} {% include "hosting/includes/_navbar.html" %} diff --git a/hosting/templates/hosting/base_short.html b/hosting/templates/hosting/base_short.html index 63f2b499..18ed3e26 100644 --- a/hosting/templates/hosting/base_short.html +++ b/hosting/templates/hosting/base_short.html @@ -51,6 +51,7 @@ + {% include "gdpr_banner.html" %} {% cms_toolbar %} {% block navbar %} diff --git a/ungleich/templates/cms/ungleichch/base_ungleich.html b/ungleich/templates/cms/ungleichch/base_ungleich.html index ba314b25..74a0b985 100644 --- a/ungleich/templates/cms/ungleichch/base_ungleich.html +++ b/ungleich/templates/cms/ungleichch/base_ungleich.html @@ -43,6 +43,7 @@ + {% include "gdpr_banner.html" %} {% cms_toolbar %} {% show_menu 0 0 0 1 "cms/ungleichch/_menu.html" %} diff --git a/ungleich_page/templates/ungleich_page/glasfaser.html b/ungleich_page/templates/ungleich_page/glasfaser.html index e1c350aa..a2e2c33e 100644 --- a/ungleich_page/templates/ungleich_page/glasfaser.html +++ b/ungleich_page/templates/ungleich_page/glasfaser.html @@ -40,6 +40,7 @@ + {% include "gdpr_banner.html" %}
- logo + logo
- Your virtual machine {{vm.name}} subscription has been charged,
you can view your invoice clicking on the button below. + Your virtual machine {{vm.name}} subscription has been charged,
you can view your invoice clicking on the button below.
{{ order.id }} {{ order.created_at | date:"M d, Y H:i" }}{{ order.price|intcomma }}{{ order.price|floatformat:2|intcomma }} {% trans 'See Invoice' %}
+ + + + + + + + + + {% for order in orders %} + + + + + + + {% endfor %} + +
{% trans "Order Nr." %}{% trans "Date" %}{% trans "Amount" %}
{{ order.id }}{{ order.created_at | date:'Y-m-d h:i a' }}{{ order.price|floatformat:2|intcomma }} + {% trans 'See Invoice' %} +
+ + {% if is_paginated %} + + {% endif %} + +{% endblock %} From de3734bf2000ba0c2db170342cb055f0a960b48b Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 20:29:33 +0200 Subject: [PATCH 581/866] Add total_in_chf utility method --- hosting/models.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hosting/models.py b/hosting/models.py index fb2a805b..b735bb8f 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -325,6 +325,15 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model): instance.assign_permissions(instance.customer.user) return instance + def total_in_chf(self): + """ + Returns amount in chf. The total amount in this model is in cents. + Hence we multiply it by 0.01 to obtain the result + + :return: + """ + return self.total * 0.01 + class VMDetail(models.Model): user = models.ForeignKey(CustomUser) From 71832f8afc277cb1ebf1ea4d710d353d3d6651f3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 20:31:24 +0200 Subject: [PATCH 582/866] invoices.html: Replace all order instances by invoice --- hosting/templates/hosting/invoices.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hosting/templates/hosting/invoices.html b/hosting/templates/hosting/invoices.html index 96d9e9e3..2fa2e3f4 100644 --- a/hosting/templates/hosting/invoices.html +++ b/hosting/templates/hosting/invoices.html @@ -18,20 +18,20 @@ - + - {% for order in orders %} + {% for invoice in invoices %} - - - + + + {% endfor %} From dbe3b2558cd132270fabd46ff89aef7d5f32f487 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 20:31:54 +0200 Subject: [PATCH 583/866] Create an InvoiceListView --- hosting/views.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 32de4e54..043bad99 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -61,7 +61,7 @@ from .forms import ( from .mixins import ProcessVMSelectionMixin, HostingContextMixin from .models import ( HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail, - GenericProduct + GenericProduct, MonthlyHostingBill ) logger = logging.getLogger(__name__) @@ -1146,6 +1146,22 @@ class OrdersHostingListView(LoginRequiredMixin, ListView): return super(OrdersHostingListView, self).get(request, *args, **kwargs) +class InvoiceListView(OrdersHostingListView): + template_name = "hosting/invoices.html" + context_object_name = "invoices" + model = MonthlyHostingBill + ordering = '-created' + + def get_queryset(self): + user = self.request.user + self.queryset = MonthlyHostingBill.objects.filter(customer__user=user) + return super(InvoiceListView, self).get_queryset() + + @method_decorator(decorators) + def get(self, request, *args, **kwargs): + return super(InvoiceListView, self).get(request, *args, **kwargs) + + class OrdersHostingDeleteView(LoginRequiredMixin, DeleteView): login_url = reverse_lazy('hosting:login') success_url = reverse_lazy('hosting:orders') From def5a3a0115c70bbbdca5daef51a4fbaffe8462b Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 20:34:04 +0200 Subject: [PATCH 584/866] Add invoice urls --- hosting/urls.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hosting/urls.py b/hosting/urls.py index 32ef8400..3a0dd72f 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -8,7 +8,8 @@ from .views import ( MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, HostingPricingView, CreateVirtualMachinesView, HostingBillListView, HostingBillDetailView, SSHKeyDeleteView, SSHKeyCreateView, SSHKeyListView, - SSHKeyChoiceView, DashboardView, SettingsView, ResendActivationEmailView + SSHKeyChoiceView, DashboardView, SettingsView, ResendActivationEmailView, + InvoiceListView ) @@ -22,10 +23,13 @@ urlpatterns = [ url(r'payment/?$', PaymentVMView.as_view(), name='payment'), url(r'settings/?$', SettingsView.as_view(), name='settings'), url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'), + url(r'invoices/?$', InvoiceListView.as_view(), name='invoices'), url(r'order-confirmation/?$', OrdersHostingDetailView.as_view(), name='order-confirmation'), url(r'orders/(?P\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'), + url(r'invoices/(?P\d+)/?$', OrdersHostingDetailView.as_view(), + name='invoices'), url(r'bills/?$', HostingBillListView.as_view(), name='bills'), url(r'bills/(?P\d+)/?$', HostingBillDetailView.as_view(), name='bills'), From e843a6f85753292e803dc6d373e7ca36490094e5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 21:16:19 +0200 Subject: [PATCH 585/866] Make invoicelistview not inherit OrderHostingListView --- hosting/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 043bad99..47456574 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1146,8 +1146,9 @@ class OrdersHostingListView(LoginRequiredMixin, ListView): return super(OrdersHostingListView, self).get(request, *args, **kwargs) -class InvoiceListView(OrdersHostingListView): +class InvoiceListView(LoginRequiredMixin, ListView): template_name = "hosting/invoices.html" + login_url = reverse_lazy('hosting:login') context_object_name = "invoices" model = MonthlyHostingBill ordering = '-created' From 247bbe622fb47352e457faa5f6a370199fb941b4 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 21:29:49 +0200 Subject: [PATCH 586/866] Add missing invoice_number argument to MHB create --- hosting/models.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hosting/models.py b/hosting/models.py index b735bb8f..5b48abbf 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -305,6 +305,10 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model): args['receipt_number'] if args['receipt_number'] is not None else '' ), + invoice_number=( + args['invoice_number'] + if args['invoice_number'] is not None else '' + ), paid_at=datetime.utcfromtimestamp( args['paid_at']).replace(tzinfo=pytz.utc), period_start=datetime.utcfromtimestamp( From ba9e5548811bb1e4160ac777b294b5268c9e9fd4 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 21:52:07 +0200 Subject: [PATCH 587/866] Implement get_object for invoice detail + url fix --- hosting/urls.py | 4 ++-- hosting/views.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/hosting/urls.py b/hosting/urls.py index 3a0dd72f..3f5a6f50 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -9,7 +9,7 @@ from .views import ( HostingPricingView, CreateVirtualMachinesView, HostingBillListView, HostingBillDetailView, SSHKeyDeleteView, SSHKeyCreateView, SSHKeyListView, SSHKeyChoiceView, DashboardView, SettingsView, ResendActivationEmailView, - InvoiceListView + InvoiceListView, InvoiceDetailView ) @@ -28,7 +28,7 @@ urlpatterns = [ name='order-confirmation'), url(r'orders/(?P\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'), - url(r'invoices/(?P\d+)/?$', OrdersHostingDetailView.as_view(), + url(r'invoices/(?P[-\w]+)/?$', InvoiceDetailView.as_view(), name='invoices'), url(r'bills/?$', HostingBillListView.as_view(), name='bills'), url(r'bills/(?P\d+)/?$', HostingBillDetailView.as_view(), diff --git a/hosting/views.py b/hosting/views.py index 47456574..17f63039 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1163,6 +1163,35 @@ class InvoiceListView(LoginRequiredMixin, ListView): return super(InvoiceListView, self).get(request, *args, **kwargs) +class InvoiceDetailView(LoginRequiredMixin, DetailView): + template_name = "hosting/invoice-detail.html" + context_object_name = "invoice" + login_url = reverse_lazy('hosting:login') + permission_required = ['view_monthlyhostingbill'] + model = MonthlyHostingBill + + def get_object(self, queryset=None): + invoice_id = self.kwargs.get('invoice_id') + try: + invoice_obj = MonthlyHostingBill.objects.get(invoice_number=invoice_id) + logger.debug("Found MHB for id {invoice_id}".format( + invoice_id=invoice_id + )) + if self.request.user.has_perm( + self.permission_required[0], invoice_obj + ) or self.request.user.email == settings.ADMIN_EMAIL: + logger.debug("User has permission to invoice_obj") + else: + logger.error("User does not have permission to access") + invoice_obj = None + except HostingOrder.DoesNotExist: + logger.debug("MHB not found for id {invoice_id}".format( + invoice_id=invoice_id + )) + invoice_obj = None + return invoice_obj + + class OrdersHostingDeleteView(LoginRequiredMixin, DeleteView): login_url = reverse_lazy('hosting:login') success_url = reverse_lazy('hosting:orders') From 94586c854a47f7569f3ef35371e50fec9d589414 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 22:48:23 +0200 Subject: [PATCH 588/866] Add invoice detail --- hosting/templates/hosting/invoice_detail.html | 235 ++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 hosting/templates/hosting/invoice_detail.html diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html new file mode 100644 index 00000000..ff6ec31d --- /dev/null +++ b/hosting/templates/hosting/invoice_detail.html @@ -0,0 +1,235 @@ +{% extends "hosting/base_short.html" %} +{% load staticfiles bootstrap3 humanize i18n custom_tags %} + + +{% block content %} +
+ {% if messages %} +
+ {% for message in messages %} + {{ message }} + {% endfor %} +
+ {% endif %} + {% if not error %} +
+

+ {% + blocktrans with page_header_text=page_header_text|default:"Invoice" + %}{{page_header_text}}{% endblocktrans %} +

+ {% if invoice %} +
+ + +
+ {% endif %} +
+
+ {% if invoice %} +

+ {% trans "Invoice #" %} {{invoice.invoice_number}} +

+ {% endif %} +

+ {% trans "Date" %}: + + {% if invoice %} + {{invoice.paid_at|date:'Y-m-d h:i a'}} + {% else %} + {% now "Y-m-d h:i a" %} + {% endif %} + +

+ {% if invoice and vm %} +

+ {% trans "Status" %}: + + {% if vm.terminated_at %} + {% trans "Terminated" %} + {% elif invoice.order.status == 'Approved' %} + {% trans "Approved" %} + {% else %} + {% trans "Declined" %} + {% endif %} + +

+ {% endif %} +
+
+
+

{% trans "Billed to" %}:

+

+ {% if invoice.order %} + {{user.name}}
+ {{invoice.order.billing_address.street_address}}, + {{invoice.order.billing_address.postal_code}}
+ {{invoice.order.billing_address.city}}, + {{invoice.order.billing_address.country}} + {% endif %} +

+
+
+
+
+

{% trans "Payment method" %}:

+

+ {% if invoice.order %} + {{invoice.order.cc_brand}} {% trans "ending in" %} **** + {{invoice.order.last4}}
+ {{user.email}} + {% endif %} +

+
+
+
+

{% trans "Invoice summary" %}

+ {% if vm %} +

+ {% trans "Product" %}:  + {% if vm.name %} + {{ vm.name }} + {% endif %} +

+
+
+ {% if period_start %} +

+ {% trans "Period" %}: + + {{ period_start|date:'Y-m-d h:i a' }} - {{ period_end|date:'Y-m-d h:i a' }} + +

+ {% endif %} +

+ {% trans "Cores" %}: + {% if vm.cores %} + {{vm.cores|floatformat}} + {% else %} + {{vm.cpu|floatformat}} + {% endif %} +

+

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

+

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

+
+
+
+
+ {% if vm.vat > 0 or vm.discount.amount > 0 %} +
+
+ {% if vm.vat > 0 %} +

+ {% trans "Subtotal" %} + {{vm.price|floatformat:2|intcomma}} + CHF +

+

+ {% trans "VAT" %} ({{ + vm.vat_percent|floatformat:2|intcomma }}%) + + {{vm.vat|floatformat:2|intcomma}} + CHF +

+ {% endif %} + {% if vm.discount.amount > 0 %} +

+ {%trans "Discount" as discount_name %} + {{ vm.discount.name|default:discount_name + }} + - {{ vm.discount.amount + }} CHF +

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

+ {% trans "Total" %} + {% if vm.total_price + %}{{vm.total_price|floatformat:2|intcomma}}{% else + %}{{vm.price|floatformat:2|intcomma}}{% endif %} + CHF +

+
+
+ {% else %} +

+ {% trans "Product" %}:  + {{ product_name }} +

+
+
+

+ {% trans "Amount" %}: + {{total_in_chf|floatformat:2|intcomma}} + CHF +

+ {% if invoice.order.generic_payment_description %} +

+ {% trans "Description" %}: + {{invoice.order.generic_payment_description}} +

+ {% endif %} + {% if invoice.order.subscription_id %} +

+ {% trans "Recurring" %}: + {{invoice.order.created_at|date:'d'|ordinal}} + {% trans "of every month" %} +

+ {% endif %} +
+
+ {% endif %} +
+
+
+ + {% endif %} +
+ + + + +{%endblock%} + +{% block js_extra %} +{% if invoice.order %} + + + + +{% endif %} +{% endblock js_extra %} From d37a2de6eb5dec8c935b7b4692d87875cf60775a Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 22:48:56 +0200 Subject: [PATCH 589/866] Add utility functions --- hosting/models.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/hosting/models.py b/hosting/models.py index 5b48abbf..d58e2fce 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -338,6 +338,33 @@ class MonthlyHostingBill(AssignPermissionsMixin, models.Model): """ return self.total * 0.01 + def discount_in_chf(self): + """ + Returns discount in chf. + + :return: + """ + return self.discount * 0.01 + + def get_vm_id(self): + """ + Returns the VM_ID metadata if set in this MHB else returns None + :return: + """ + return_value = None + if len(self.lines_meta_data_csv) > 0: + vm_ids = [vm_id.strip() for vm_id in + self.lines_meta_data_csv.split(",")] + if len(vm_ids) == 1: + return vm_ids[0] + else: + logger.debug( + "More than one VM_ID" + "for MonthlyHostingBill {}".format(self.invoice_id) + ) + logger.debug("VM_IDS=".format(','.join(vm_ids))) + return return_value + class VMDetail(models.Model): user = models.ForeignKey(CustomUser) From ba6fa531db76bb7a8683f45cf9b76fd973f4a393 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 22:49:25 +0200 Subject: [PATCH 590/866] Correct the name of the layout --- hosting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 17f63039..5a8f45c7 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1164,7 +1164,7 @@ class InvoiceListView(LoginRequiredMixin, ListView): class InvoiceDetailView(LoginRequiredMixin, DetailView): - template_name = "hosting/invoice-detail.html" + template_name = "hosting/invoice_detail.html" context_object_name = "invoice" login_url = reverse_lazy('hosting:login') permission_required = ['view_monthlyhostingbill'] From 7de2129a0053fd799e568a7508b791a2345d2ee4 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 22:49:45 +0200 Subject: [PATCH 591/866] Implement get invoice --- hosting/views.py | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/hosting/views.py b/hosting/views.py index 5a8f45c7..49c78b7e 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1191,6 +1191,82 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): invoice_obj = None return invoice_obj + def get_context_data(self, **kwargs): + # Get context + context = super(InvoiceDetailView, self).get_context_data(**kwargs) + obj = self.get_object() + + if obj is not None: + vm_id = obj.get_vm_id() + try: + # Try to get vm details from database + vm_detail = VMDetail.objects.get(vm_id=vm_id) + context['vm'] = vm_detail.__dict__ + context['vm']['name'] = '{}-{}'.format( + context['vm']['configuration'], context['vm']['vm_id']) + price, vat, vat_percent, discount = get_vm_price_with_vat( + cpu=context['vm']['cores'], + ssd_size=context['vm']['disk_size'], + memory=context['vm']['memory'], + pricing_name=(obj.vm_pricing.name + if obj.vm_pricing else 'default') + ) + context['vm']['vat'] = vat + context['vm']['price'] = price + context['vm']['discount'] = discount + context['vm']['vat_percent'] = vat_percent + context['vm']['total_price'] = price + vat - discount['amount'] + except VMDetail.DoesNotExist: + # fallback to get it from the infrastructure + try: + manager = OpenNebulaManager( + email=self.request.email, + password=self.request.password + ) + vm = manager.get_vm(vm_id) + context['vm'] = VirtualMachineSerializer(vm).data + price, vat, vat_percent, discount = get_vm_price_with_vat( + cpu=context['vm']['cores'], + ssd_size=context['vm']['disk_size'], + memory=context['vm']['memory'], + pricing_name=(obj.vm_pricing.name + if obj.vm_pricing else 'default') + ) + context['vm']['vat'] = vat + context['vm']['price'] = price + context['vm']['discount'] = discount + context['vm']['vat_percent'] = vat_percent + context['vm']['total_price'] = ( + price + vat - discount['amount'] + ) + except WrongIdError: + logger.error("WrongIdError while accessing " + "invoice {}".format(obj.invoice_id)) + 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' + return context + + # add context params from monthly hosting bill + context['period_start'] = obj.period_start + context['period_end'] = obj.period_end + context['paid_at'] = obj.paid_at + context['total_in_chf'] = obj.total_in_chf() + context['invoice_number'] = obj.invoice_number + context['discount_on_stripe'] = obj.discount_in_chf() + return context + else: + raise Http404 + + @method_decorator(decorators) + def get(self, request, *args, **kwargs): + context = self.get_context_data() + return self.render_to_response(context) + class OrdersHostingDeleteView(LoginRequiredMixin, DeleteView): login_url = reverse_lazy('hosting:login') From d07f3d7eba1d54c552bfaea0d56d94b2220bec62 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 22:59:01 +0200 Subject: [PATCH 592/866] Add missing object param --- hosting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 49c78b7e..39e2a7a9 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1264,7 +1264,7 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): @method_decorator(decorators) def get(self, request, *args, **kwargs): - context = self.get_context_data() + context = self.get_context_data(object=self.get_object()) return self.render_to_response(context) From 76e3d951354f4d2278f322792be90d221566e1f3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 22:59:39 +0200 Subject: [PATCH 593/866] Use invoice_number of invoice pk --- hosting/templates/hosting/invoices.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/templates/hosting/invoices.html b/hosting/templates/hosting/invoices.html index 2fa2e3f4..6a0aeb41 100644 --- a/hosting/templates/hosting/invoices.html +++ b/hosting/templates/hosting/invoices.html @@ -31,7 +31,7 @@
{% endfor %} From 3ed5823c93651e25bf1e0749d5b0045b2b0459fe Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:04:35 +0200 Subject: [PATCH 594/866] Add missing self.object initializer --- hosting/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hosting/views.py b/hosting/views.py index 39e2a7a9..3a8997a9 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1264,6 +1264,7 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): @method_decorator(decorators) def get(self, request, *args, **kwargs): + self.object = self.get_object() context = self.get_context_data(object=self.get_object()) return self.render_to_response(context) From ef09ae4dab1b355630af25edd4aeefa5a0f6062a Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:07:37 +0200 Subject: [PATCH 595/866] Obtaing pricing from order --- hosting/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 3a8997a9..12b0027f 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1208,8 +1208,8 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): cpu=context['vm']['cores'], ssd_size=context['vm']['disk_size'], memory=context['vm']['memory'], - pricing_name=(obj.vm_pricing.name - if obj.vm_pricing else 'default') + pricing_name=(obj.order.vm_pricing.name + if obj.order.vm_pricing else 'default') ) context['vm']['vat'] = vat context['vm']['price'] = price @@ -1229,8 +1229,8 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): cpu=context['vm']['cores'], ssd_size=context['vm']['disk_size'], memory=context['vm']['memory'], - pricing_name=(obj.vm_pricing.name - if obj.vm_pricing else 'default') + pricing_name=(obj.order.vm_pricing.name + if obj.order.vm_pricing else 'default') ) context['vm']['vat'] = vat context['vm']['price'] = price From ddd3cebc39f49aca62973921be9f7d716001332b Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:09:57 +0200 Subject: [PATCH 596/866] Fix blocktrans reformatted mistakenly --- hosting/templates/hosting/invoice_detail.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html index ff6ec31d..8a094519 100644 --- a/hosting/templates/hosting/invoice_detail.html +++ b/hosting/templates/hosting/invoice_detail.html @@ -14,9 +14,8 @@ {% if not error %}

- {% - blocktrans with page_header_text=page_header_text|default:"Invoice" - %}{{page_header_text}}{% endblocktrans %} + + {% blocktrans with page_header_text=page_header_text|default:"Invoice" %}{{page_header_text}}{% endblocktrans %}

{% if invoice %}
From 47422a99afa1b6cccd0533915d2fefe565c1bbf7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:11:59 +0200 Subject: [PATCH 597/866] Fix more autoformatting related errors --- hosting/templates/hosting/invoice_detail.html | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html index 8a094519..22d1d87a 100644 --- a/hosting/templates/hosting/invoice_detail.html +++ b/hosting/templates/hosting/invoice_detail.html @@ -207,7 +207,6 @@ CHE-156.970.649 MWST
- {% endif %}
@@ -216,10 +215,7 @@
{%endblock%} From d00e84a4b69508904225ce69934a671c5cf05db5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:24:56 +0200 Subject: [PATCH 598/866] Fix bug related to proper alignment --- hosting/views.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 12b0027f..ce73ad3c 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1251,14 +1251,14 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): context['error'] = 'WrongIdError' return context - # add context params from monthly hosting bill - context['period_start'] = obj.period_start - context['period_end'] = obj.period_end - context['paid_at'] = obj.paid_at - context['total_in_chf'] = obj.total_in_chf() - context['invoice_number'] = obj.invoice_number - context['discount_on_stripe'] = obj.discount_in_chf() - return context + # add context params from monthly hosting bill + context['period_start'] = obj.period_start + context['period_end'] = obj.period_end + context['paid_at'] = obj.paid_at + context['total_in_chf'] = obj.total_in_chf() + context['invoice_number'] = obj.invoice_number + context['discount_on_stripe'] = obj.discount_in_chf() + return context else: raise Http404 From 903fee4db198be5a4e35ba5c068a9ea97cf558f2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:31:52 +0200 Subject: [PATCH 599/866] Fix more autoformatting issues --- hosting/templates/hosting/invoice_detail.html | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html index 22d1d87a..9c300b3f 100644 --- a/hosting/templates/hosting/invoice_detail.html +++ b/hosting/templates/hosting/invoice_detail.html @@ -137,8 +137,7 @@ CHF

- {% trans "VAT" %} ({{ - vm.vat_percent|floatformat:2|intcomma }}%) + {% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%) {{vm.vat|floatformat:2|intcomma}} CHF @@ -147,10 +146,8 @@ {% if vm.discount.amount > 0 %}

{%trans "Discount" as discount_name %} - {{ vm.discount.name|default:discount_name - }} - - {{ vm.discount.amount - }} CHF + {{ vm.discount.name|default:discount_name }} + - {{ vm.discount.amount }} CHF

{% endif %} @@ -162,9 +159,7 @@

{% trans "Total" %} - {% if vm.total_price - %}{{vm.total_price|floatformat:2|intcomma}}{% else - %}{{vm.price|floatformat:2|intcomma}}{% endif %} + {% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} CHF

From f1a7958f03e3c61eef6e4b3dd4028066c273c353 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:34:26 +0200 Subject: [PATCH 600/866] Use correct class --- hosting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index ce73ad3c..34e720f6 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1184,7 +1184,7 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): else: logger.error("User does not have permission to access") invoice_obj = None - except HostingOrder.DoesNotExist: + except MonthlyHostingBill.DoesNotExist: logger.debug("MHB not found for id {invoice_id}".format( invoice_id=invoice_id )) From baf62f1924c4079bb01bbb8fb0d629223fdd4661 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Apr 2019 23:54:52 +0200 Subject: [PATCH 601/866] Simplify showing total price --- hosting/templates/hosting/invoice_detail.html | 6 ++---- hosting/views.py | 6 ++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html index 9c300b3f..80226123 100644 --- a/hosting/templates/hosting/invoice_detail.html +++ b/hosting/templates/hosting/invoice_detail.html @@ -139,8 +139,7 @@

{% trans "VAT" %} ({{ vm.vat_percent|floatformat:2|intcomma }}%) - {{vm.vat|floatformat:2|intcomma}} - CHF + {{vm.vat|floatformat:2|intcomma}} CHF

{% endif %} {% if vm.discount.amount > 0 %} @@ -159,8 +158,7 @@

{% trans "Total" %} - {% if vm.total_price %}{{vm.total_price|floatformat:2|intcomma}}{% else %}{{vm.price|floatformat:2|intcomma}}{% endif %} - CHF + {{total_in_chf}} CHF

diff --git a/hosting/views.py b/hosting/views.py index 34e720f6..af01ae86 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1173,7 +1173,9 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): def get_object(self, queryset=None): invoice_id = self.kwargs.get('invoice_id') try: - invoice_obj = MonthlyHostingBill.objects.get(invoice_number=invoice_id) + invoice_obj = MonthlyHostingBill.objects.get( + invoice_number=invoice_id + ) logger.debug("Found MHB for id {invoice_id}".format( invoice_id=invoice_id )) @@ -1184,7 +1186,7 @@ class InvoiceDetailView(LoginRequiredMixin, DetailView): else: logger.error("User does not have permission to access") invoice_obj = None - except MonthlyHostingBill.DoesNotExist: + except MonthlyHostingBill.DoesNotExist as dne: logger.debug("MHB not found for id {invoice_id}".format( invoice_id=invoice_id )) From 13f84a8580cda21cf55675fb1cf1e6d3a41ee248 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 4 Apr 2019 00:05:20 +0200 Subject: [PATCH 602/866] Add missing endif --- hosting/templates/hosting/invoice_detail.html | 1 + 1 file changed, 1 insertion(+) diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html index 80226123..0a2473e3 100644 --- a/hosting/templates/hosting/invoice_detail.html +++ b/hosting/templates/hosting/invoice_detail.html @@ -200,6 +200,7 @@ CHE-156.970.649 MWST
+ {% endif %}
From ef1bdee9a7da7e341f6e44d7046f7b544cc0f5cd Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 4 Apr 2019 00:05:45 +0200 Subject: [PATCH 603/866] Remove more autoformatting --- hosting/templates/hosting/invoice_detail.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hosting/templates/hosting/invoice_detail.html b/hosting/templates/hosting/invoice_detail.html index 0a2473e3..675962fa 100644 --- a/hosting/templates/hosting/invoice_detail.html +++ b/hosting/templates/hosting/invoice_detail.html @@ -204,8 +204,7 @@
{% trans "Order Nr." %}{% trans "Invoice Nr." %} {% trans "Date" %} {% trans "Amount" %}
{{ order.id }}{{ order.created_at | date:'Y-m-d h:i a' }}{{ order.price|floatformat:2|intcomma }}{{ invoice.invoice_number }}{{ invoice.paid_at | date:'Y-m-d h:i a' }}{{ invoice.total_in_chf|floatformat:2|intcomma }} - {% trans 'See Invoice' %} + {% trans 'See Invoice' %}
{{ invoice.paid_at | date:'Y-m-d h:i a' }} {{ invoice.total_in_chf|floatformat:2|intcomma }} - {% trans 'See Invoice' %} + {% trans 'See Invoice' %}