From 6536991209fc25af113a91dfa93988ede3a00952 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 6 Oct 2017 01:17:35 +0530 Subject: [PATCH 001/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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/727] 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 bd875ffe7deb21c8d99eaa0501a43ef75285141d Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 18 Apr 2018 23:50:52 +0200 Subject: [PATCH 092/727] 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 093/727] 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 094/727] 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 095/727] 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 096/727] 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 097/727] 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 098/727] 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 3debf3411858df959c96362ecf9a073649464583 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 20 Apr 2018 20:25:24 +0530 Subject: [PATCH 099/727] 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 100/727] 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 3b6c2b9d4e578b60c8e9264ce591034d3737569c Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Sat, 21 Apr 2018 22:27:43 +0530 Subject: [PATCH 101/727] 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 57eda625864732695e47f95b5dde37e3f0c4cd7a Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 12 Jun 2018 08:28:46 +0200 Subject: [PATCH 102/727] 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 103/727] 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 104/727] 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 105/727] 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 106/727] 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 107/727] 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 6c2eabbe6af3880c6ea4e09aa9baf33111e0d72a Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 27 Jun 2018 00:01:50 +0200 Subject: [PATCH 108/727] 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 109/727] 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 110/727] 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 111/727] 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 112/727] 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 113/727] 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 114/727] 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 115/727] 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 116/727] 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 117/727] 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 118/727] 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 119/727] 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 120/727] 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 121/727] 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 122/727] 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 123/727] 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 124/727] 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 125/727] 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 126/727] 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 127/727] 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 128/727] 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 129/727] 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 130/727] 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 131/727] 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 132/727] 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 133/727] 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 134/727] 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 135/727] 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 136/727] 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 137/727] 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 138/727] 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 139/727] 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 140/727] 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 141/727] 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 142/727] 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 143/727] 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 144/727] 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 145/727] 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 146/727] 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 147/727] 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 148/727] 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 149/727] 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 150/727] 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 151/727] 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 152/727] 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 153/727] 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 154/727] 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 156/727] 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 157/727] 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 158/727] 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 159/727] 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 160/727] 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 161/727] 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 162/727] 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 163/727] 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 164/727] 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 165/727] 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 166/727] 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 167/727] 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 168/727] 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 169/727] 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 170/727] 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 171/727] 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 172/727] 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 173/727] 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 174/727] 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 175/727] 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 176/727] 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 177/727] 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 178/727] 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 179/727] 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 180/727] 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" %}