merged master

This commit is contained in:
Arvind Tiwari 2017-10-03 18:54:47 +05:30
commit 6802e2fdbc
26 changed files with 294 additions and 395 deletions

View file

@ -1,10 +1,15 @@
Next: 1.2.4: 2017-10-02
* #3780: [hosting] Store VM details locally
* #3764: [hosting] Show cancelled VMs' invoices * #3764: [hosting] Show cancelled VMs' invoices
* #3736: [dcl] Refactor the place where we compute the VM price * #3736: [dcl] Refactor the place where we compute the VM price
* #3730: [dcl] Refactor price parameter passed in the DCL flow * #3730: [dcl] Refactor price parameter passed in the DCL flow
* #3807: [dcl] Remove PricingView as it is no more used * #3807: [dcl] Remove PricingView as it is no more used
* #3813: [hosting] JS error in create ssh key page * #3813: [hosting] JS error in create ssh key page
* #3756: [dcl] Update landing calculator and billing info page * #3756: [dcl] Update landing calculator and billing info page
* Bugfix: Fix PR 493 bug that creates a new StripeCustomer for each buying of VM with the same email id
* #3835: [all] Forbidden (403) CSRF verification failed issue.
* Bugfix: [hosting] Dashboard strictly available after login
* #3808: [dcl] Order confirmation page redesign
1.2.3: 2017-09-25 1.2.3: 2017-09-25
* #3484: [dcl, hosting] Refactored account activation, password reset, VM order and cancellation email * #3484: [dcl, hosting] Refactored account activation, password reset, VM order and cancellation email
* #3731: [dcl, hosting] Added cdist ssh key handler * #3731: [dcl, hosting] Added cdist ssh key handler

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-28 18:36+0530\n" "POT-Creation-Date: 2017-10-03 18:54+0530\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -374,24 +374,6 @@ msgstr "Weiter"
msgid "Enter your credit card number" msgid "Enter your credit card number"
msgstr "Deine Kreditkartennummer" msgstr "Deine Kreditkartennummer"
msgid "Confirm Order"
msgstr "Bestellung Bestätigen"
msgid "Date"
msgstr "Datum"
msgid "Billed To:"
msgstr "Rechnungsadresse"
msgid "Payment Method:"
msgstr "Bezahlmethode"
msgid "ending in"
msgstr "endend in"
msgid "Order summary"
msgstr "Bestellungsübersicht"
#, python-format #, python-format
msgid "" msgid ""
"By clicking \"Place order\" this plan will charge your credit card account " "By clicking \"Place order\" this plan will charge your credit card account "
@ -403,21 +385,6 @@ msgstr ""
msgid "Place order" msgid "Place order"
msgstr "Bestellen" msgstr "Bestellen"
msgid "Processing..."
msgstr "Abarbeitung..."
msgid "Hold tight, we are processing your request"
msgstr "Bitte warten - wir verbeiten Deine Anfrage gerade"
msgid "OK"
msgstr ""
msgid "Close"
msgstr ""
msgid "Some problem encountered. Please try again later."
msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
msgid "Thank you for order! Our team will contact you via email" msgid "Thank you for order! Our team will contact you via email"
msgstr "" msgstr ""
"Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich mit " "Vielen Dank für die Bestellung. Unser Team setzt sich sobald wie möglich mit "
@ -511,6 +478,9 @@ msgstr "Ungültige RAM-Grösse"
msgid "Invalid storage size" msgid "Invalid storage size"
msgstr "Ungültige Speicher-Grösse" msgstr "Ungültige Speicher-Grösse"
msgid "Confirm Order"
msgstr "Bestellung Bestätigen"
msgid "Error." msgid "Error."
msgstr "" msgstr ""
@ -518,6 +488,8 @@ msgid ""
"There was a payment related error. On close of this popup, you will be " "There was a payment related error. On close of this popup, you will be "
"redirected back to the payment page." "redirected back to the payment page."
msgstr "" msgstr ""
"Es ist ein Fehler bei der Zahlung betreten. Du wirst nach dem Schliessen vom "
"Popup zur Bezahlseite weitergeleitet."
msgid "Thank you for the order." msgid "Thank you for the order."
msgstr "Danke für Deine Bestellung." msgstr "Danke für Deine Bestellung."
@ -529,6 +501,30 @@ msgstr ""
"Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du " "Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du "
"auf sie zugreifen kannst." "auf sie zugreifen kannst."
#~ msgid "Processing..."
#~ msgstr "Abarbeitung..."
#~ msgid "Hold tight, we are processing your request"
#~ msgstr "Bitte warten - wir verbeiten Deine Anfrage gerade"
#~ msgid "Some problem encountered. Please try again later."
#~ msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
#~ msgid "Date"
#~ msgstr "Datum"
#~ msgid "Billed To:"
#~ msgstr "Rechnungsadresse"
#~ msgid "Payment Method:"
#~ msgstr "Bezahlmethode"
#~ msgid "ending in"
#~ msgstr "endend in"
#~ msgid "Order summary"
#~ msgstr "Bestellungsübersicht"
#~ msgid "We are cutting down the costs significantly!" #~ msgid "We are cutting down the costs significantly!"
#~ msgstr "Wir sorgen dafür, dass die Kosten für Dich signifikant abnehmen" #~ msgstr "Wir sorgen dafür, dass die Kosten für Dich signifikant abnehmen"

View file

@ -1501,6 +1501,8 @@ tech-sub-sec h2 {
font-size: 14px; font-size: 14px;
font-weight: 300; font-weight: 300;
letter-spacing: 2px; letter-spacing: 2px;
line-height: 24px;
display: block;
} }
.content-section-a { .content-section-a {

View file

@ -51,15 +51,20 @@ def retry_task(task, exception=None):
@app.task(bind=True, max_retries=settings.CELERY_MAX_RETRIES) @app.task(bind=True, max_retries=settings.CELERY_MAX_RETRIES)
def create_vm_task(self, vm_template_id, user, specs, template, def create_vm_task(self, vm_template_id, user, specs, template,
stripe_customer_id, billing_address_data, stripe_customer_id, billing_address_data,
billing_address_id, stripe_subscription_id, cc_details):
charge, cc_details):
logger.debug( logger.debug(
"Running create_vm_task on {}".format(current_task.request.hostname)) "Running create_vm_task on {}".format(current_task.request.hostname))
vm_id = None vm_id = None
try: try:
final_price = specs.get('price') final_price = specs.get('price')
billing_address = BillingAddress.objects.filter( billing_address = BillingAddress(
id=billing_address_id).first() 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() customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
if 'pass' in user: if 'pass' in user:
@ -112,8 +117,7 @@ def create_vm_task(self, vm_template_id, user, specs, template,
billing_address_user_form.save() billing_address_user_form.save()
# Associate an order with a stripe subscription # Associate an order with a stripe subscription
charge_object = DictDotLookup(charge) order.set_subscription_id(stripe_subscription_id, cc_details)
order.set_subscription_id(charge_object, cc_details)
# If the Stripe payment succeeds, set order status approved # If the Stripe payment succeeds, set order status approved
order.set_approved() order.set_approved()
@ -185,7 +189,8 @@ def create_vm_task(self, vm_template_id, user, specs, template,
public_keys] public_keys]
if len(keys) > 0: if len(keys) > 0:
logger.debug( logger.debug(
"Calling configure on {host} for {num_keys} keys".format( "Calling configure on {host} for "
"{num_keys} keys".format(
host=new_host, num_keys=len(keys))) host=new_host, num_keys=len(keys)))
# Let's delay the task by 75 seconds to be sure # Let's delay the task by 75 seconds to be sure
# that we run the cdist configure after the host # that we run the cdist configure after the host
@ -214,32 +219,3 @@ def create_vm_task(self, vm_template_id, user, specs, template,
return return
return vm_id return vm_id
class DictDotLookup(object):
"""
Creates objects that behave much like a dictionaries, but allow nested
key access using object '.' (dot) lookups.
"""
def __init__(self, d):
for k in d:
if isinstance(d[k], dict):
self.__dict__[k] = DictDotLookup(d[k])
elif isinstance(d[k], (list, tuple)):
l = []
for v in d[k]:
if isinstance(v, dict):
l.append(DictDotLookup(v))
else:
l.append(v)
self.__dict__[k] = l
else:
self.__dict__[k] = d[k]
def __getitem__(self, name):
if name in self.__dict__:
return self.__dict__[name]
def __iter__(self):
return iter(self.__dict__.keys())

View file

@ -16,7 +16,7 @@
<hr class="intro-divider"> <hr class="intro-divider">
<ul class="list-inline intro-social-buttons"> <ul class="list-inline intro-social-buttons">
<li> <li>
<a class="btn btn-default btn-lg btn-transparent url" href="#how"><i class="#Services"></i> <span class="network-name">{% trans "Highlights" %}</span></a> <a class="btn btn-default btn-lg btn-transparent url" href="#how"><span class="network-name">{% trans "Highlights" %}</span></a>
</li> </li>
<li> <li>
<a class="btn btn-primary btn-lg page-scroll url" href="#price"><span class="network-name">{% trans "I want it!" %}</span></a> <a class="btn btn-primary btn-lg page-scroll url" href="#price"><span class="network-name">{% trans "I want it!" %}</span></a>

View file

@ -1,143 +1,24 @@
{% extends "hosting/base_short.html" %} {% extends "hosting/order_detail.html" %}
{% load staticfiles bootstrap3 %}
{% load i18n %} {% load i18n %}
{% load custom_tags %}
{% block navbar %} {% block navbar %}
{% include "datacenterlight/includes/_navbar.html" %} {% include "datacenterlight/includes/_navbar.html" %}
{% endblock navbar %} {% endblock navbar %}
{% block content %} {% block submit_btn %}
<form id="virtual_machine_create_form" action="" method="POST">
{% csrf_token %}
<div class="row">
<div class="col-sm-8">
<div class="dcl-place-order-text">{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price }}CHF/month{% endblocktrans %}.</div>
</div>
<div class="col-sm-4 order-confirm-btn text-right">
<button class="btn choice-btn" id="btn-create-vm"
data-toggle="modal" data-target="#createvm-modal">
{% trans "Place order" %}
</button>
</div>
</div>
</form>
{% endblock submit_btn %}
<div class="order-detail-container">
{% if messages %}
<div class="row">
<div class="col-xs-12 col-md-8 col-md-offset-2">
<br/>
<div class="alert alert-warning">
{% for message in messages %}
<span>{{ message }}</span>
{% endfor %}
</div>
</div>
</div>
{% endif %}
{% if not error %}
<div class="row">
<div class="col-xs-12 col-md-8 col-md-offset-2">
<div class="invoice-title">
<h2>{% trans "Confirm Order"%}</h2>
</div>
<hr>
<div class="row">
<div class="col-xs-12 col-sm-6 pull-right order-confirm-date">
<address>
<strong>{% trans "Date"%}:</strong><br>
<span id="order-created_at">{% now "Y-m-d H:i" %}</span><br><br>
</address>
</div>
<div class="col-xs-12 col-sm-6">
<address>
<h3><b>{% trans "Billed To:"%}</b></h3>
{% with billing_address_data as billing_address %}
{{billing_address.cardholder_name}}<br> {{billing_address.street_address}}, {{billing_address.postal_code}}<br>
{{billing_address.city}}, {{billing_address.country}}.
{% endwith %}
</address>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<address>
<strong>{% trans "Payment Method:"%}</strong><br>
{{cc_brand}} {% trans "ending in" %} **** {{cc_last4}}<br>
{{request.session.user.email}}
</address>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h3><b>{% trans "Order summary"%}</b></h3>
<hr>
<div class="content">
{% with request.session.specs as vm %}
<p><b>{% trans "Cores"%}</b> <span class="pull-right">{{vm.cpu}}</span></p>
<hr>
<p><b>{% trans "Memory"%}</b> <span class="pull-right">{{vm.memory}} GB</span></p>
<hr>
<p><b>{% trans "Disk space"%}</b> <span class="pull-right">{{vm.disk_size}} GB</span></p>
<hr>
<p><b>{% trans "Configuration"%}</b> <span class="pull-right">{{request.session.template.name}}</span></p>
<hr>
<h4>{% trans "Total"%}<p class="pull-right"><b>{{vm.price}} CHF</b><span class="dcl-price-month"> /{% trans "Month" %}</span></p></h4>
{% endwith %}
</div>
<br/>
<form method="post" id="virtual_machine_create_form">
{% csrf_token %}
<div class="row">
<div class="col-sm-8">
<p class="dcl-place-order-text">{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price }}CHF/month{% endblocktrans %}.</p>
</div>
<div class="col-sm-4 content">
<button class="btn btn-info pull-right"
id="btn-create-vm"
data-href="{% url 'hosting:order-confirmation' %}"
data-toggle="modal"
data-target="#createvm-modal">
{% trans "Place order"%}
</button>
</div>
</div>
</form>
</div>
</div>
{% endif %}
</div>
<!-- Create VM Modal -->
<div class="modal fade" id="createvm-modal" tabindex="-1" role="dialog"
aria-hidden="true" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close hidden" data-dismiss="modal"
aria-label="create-vm-close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="modal-icon">
<i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
<span class="sr-only">{% trans "Processing..." %}</span>
</div>
<h4 class="modal-title" id="createvm-modal-title">
</h4>
<div class="modal-text" id="createvm-modal-body">
{% trans "Hold tight, we are processing your request" %}
</div>
<div class="modal-footer">
<a id="createvm-modal-done-btn" class="btn btn-success btn-ok btn-wide hide" href="">{% trans "OK" %}</a>
<button id="createvm-modal-close-btn" type="button" class="btn btn-danger btn-ok btn-wide hide" data-dismiss="modal" aria-label="create-vm-close">{% trans "Close" %}</button>
</div>
</div>
</div>
</div>
</div>
<!-- / Create VM Modal -->
<script type="text/javascript">
{% trans "Some problem encountered. Please try again later." as err_msg %}
var create_vm_error_message = '{{err_msg|safe}}.';
window.onload = function () {
var locale_date = moment.utc(document.getElementById("order-created_at").textContent,'YYYY-MM-DD HH:mm').toDate();
locale_date = moment(locale_date).format("YYYY-MM-DD h:mm:ss a");
document.getElementById('order-created_at').innerHTML = locale_date;
};
</script>
{%endblock%}

View file

@ -13,7 +13,6 @@ from datacenterlight.tasks import create_vm_task
from membership.models import StripeCustomer from membership.models import StripeCustomer
from opennebula_api.serializers import VMTemplateSerializer from opennebula_api.serializers import VMTemplateSerializer
from utils.hosting_utils import get_vm_price from utils.hosting_utils import get_vm_price
from utils.models import BillingAddress
from utils.stripe_utils import StripeUtils from utils.stripe_utils import StripeUtils
@ -75,21 +74,12 @@ class CeleryTaskTestCase(TestCase):
stripe_customer.stripe_id, stripe_customer.stripe_id,
self.token) self.token)
card_details_dict = card_details.get('response_object') card_details_dict = card_details.get('response_object')
billing_address = BillingAddress(
cardholder_name=self.customer_name,
postal_code='1232',
country='CH',
street_address='Monty\'s Street',
city='Hollywood')
billing_address.save()
billing_address_data = {'cardholder_name': self.customer_name, billing_address_data = {'cardholder_name': self.customer_name,
'postal_code': '1231', 'postal_code': '1231',
'country': 'CH', 'country': 'CH',
'token': self.token, 'token': self.token,
'street_address': 'Monty\'s Street', 'street_address': 'Monty\'s Street',
'city': 'Hollywood'} 'city': 'Hollywood'}
billing_address_id = billing_address.id
vm_template_id = template_data.get('id', 1) vm_template_id = template_data.get('id', 1)
cpu = specs.get('cpu') cpu = specs.get('cpu')
@ -125,8 +115,7 @@ class CeleryTaskTestCase(TestCase):
template_data, template_data,
stripe_customer.id, stripe_customer.id,
billing_address_data, billing_address_data,
billing_address_id, stripe_subscription_obj.id,
stripe_subscription_obj,
card_details_dict) card_details_dict)
new_vm_id = 0 new_vm_id = 0
res = None res = None

View file

@ -19,7 +19,7 @@ from hosting.forms import HostingUserLoginForm
from membership.models import CustomUser, StripeCustomer from membership.models import CustomUser, StripeCustomer
from opennebula_api.serializers import VMTemplateSerializer from opennebula_api.serializers import VMTemplateSerializer
from utils.forms import ( from utils.forms import (
BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm BillingAddressForm, BillingAddressFormSignup
) )
from utils.hosting_utils import get_vm_price from utils.hosting_utils import get_vm_price
from utils.mailer import BaseEmail from utils.mailer import BaseEmail
@ -348,6 +348,11 @@ class PaymentOrderView(FormView):
form_kwargs.update({ form_kwargs.update({
'instance': self.request.user.billing_addresses.first() 'instance': self.request.user.billing_addresses.first()
}) })
if 'billing_address_data' in self.request.session:
billing_address_data = self.request.session['billing_address_data']
form_kwargs.update({
'initial': billing_address_data
})
return form_kwargs return form_kwargs
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -379,14 +384,40 @@ class PaymentOrderView(FormView):
email=this_user.get('email'), email=this_user.get('email'),
token=token) token=token)
else: else:
user_email = form.cleaned_data.get('email')
user_name = form.cleaned_data.get('name')
this_user = { this_user = {
'email': form.cleaned_data.get('email'), 'email': user_email,
'name': form.cleaned_data.get('name') 'name': user_name
} }
customer = StripeCustomer.create_stripe_api_customer( try:
email=this_user.get('email'), custom_user = CustomUser.objects.get(email=user_email)
token=token, customer = StripeCustomer.objects.filter(
customer_name=form.cleaned_data.get('name')) user_id=custom_user.id).first()
if customer is None:
logger.debug(
("User {email} is already registered with us."
"But, StripeCustomer does not exist for {email}."
"Hence, creating a new StripeCustomer.").format(
email=user_email
)
)
customer = StripeCustomer.create_stripe_api_customer(
email=user_email,
token=token,
customer_name=user_name)
except CustomUser.DoesNotExist:
logger.debug(
("StripeCustomer does not exist for {email}."
"Hence, creating a new StripeCustomer.").format(
email=user_email
)
)
customer = StripeCustomer.create_stripe_api_customer(
email=user_email,
token=token,
customer_name=user_name)
request.session['billing_address_data'] = form.cleaned_data request.session['billing_address_data'] = form.cleaned_data
request.session['user'] = this_user request.session['user'] = this_user
# Get or create stripe customer # Get or create stripe customer
@ -395,8 +426,10 @@ class PaymentOrderView(FormView):
return self.render_to_response( return self.render_to_response(
self.get_context_data(form=form)) self.get_context_data(form=form))
request.session['token'] = token request.session['token'] = token
request.session[ if type(customer) is StripeCustomer:
'customer'] = customer.id if request.user.is_authenticated() else customer request.session['customer'] = customer.stripe_id
else:
request.session['customer'] = customer
return HttpResponseRedirect( return HttpResponseRedirect(
reverse('datacenterlight:order_confirmation')) reverse('datacenterlight:order_confirmation'))
else: else:
@ -415,14 +448,7 @@ class OrderConfirmationView(DetailView):
return HttpResponseRedirect(reverse('datacenterlight:index')) return HttpResponseRedirect(reverse('datacenterlight:index'))
if 'token' not in request.session: if 'token' not in request.session:
return HttpResponseRedirect(reverse('datacenterlight:payment')) return HttpResponseRedirect(reverse('datacenterlight:payment'))
stripe_customer_id = request.session.get('customer') stripe_api_cus_id = request.session.get('customer')
if request.user.is_authenticated():
customer = StripeCustomer.objects.filter(
id=stripe_customer_id).first()
stripe_api_cus_id = customer.stripe_id
else:
stripe_api_cus_id = stripe_customer_id
stripe_utils = StripeUtils() stripe_utils = StripeUtils()
card_details = stripe_utils.get_card_details(stripe_api_cus_id, card_details = stripe_utils.get_card_details(stripe_api_cus_id,
request.session.get( request.session.get(
@ -437,6 +463,8 @@ class OrderConfirmationView(DetailView):
'site_url': reverse('datacenterlight:index'), 'site_url': reverse('datacenterlight:index'),
'cc_last4': card_details.get('response_object').get('last4'), 'cc_last4': card_details.get('response_object').get('last4'),
'cc_brand': card_details.get('response_object').get('brand'), 'cc_brand': card_details.get('response_object').get('brand'),
'vm': request.session.get('specs'),
'page_header_text': _('Confirm Order'),
'billing_address_data': request.session.get('billing_address_data') 'billing_address_data': request.session.get('billing_address_data')
} }
return render(request, self.template_name, context) return render(request, self.template_name, context)
@ -445,15 +473,8 @@ class OrderConfirmationView(DetailView):
template = request.session.get('template') template = request.session.get('template')
specs = request.session.get('specs') specs = request.session.get('specs')
user = request.session.get('user') user = request.session.get('user')
stripe_customer_id = request.session.get('customer') stripe_api_cus_id = request.session.get('customer')
if request.user.is_authenticated():
customer = StripeCustomer.objects.filter(
id=stripe_customer_id).first()
stripe_api_cus_id = customer.stripe_id
else:
stripe_api_cus_id = stripe_customer_id
vm_template_id = template.get('id', 1) vm_template_id = template.get('id', 1)
stripe_utils = StripeUtils() stripe_utils = StripeUtils()
card_details = stripe_utils.get_card_details(stripe_api_cus_id, card_details = stripe_utils.get_card_details(stripe_api_cus_id,
request.session.get( request.session.get(
@ -498,8 +519,8 @@ class OrderConfirmationView(DetailView):
'response_object').stripe_plan_id}]) 'response_object').stripe_plan_id}])
stripe_subscription_obj = subscription_result.get('response_object') stripe_subscription_obj = subscription_result.get('response_object')
# Check if the subscription was approved and is active # Check if the subscription was approved and is active
if stripe_subscription_obj is None or \ if (stripe_subscription_obj is None
stripe_subscription_obj.status != 'active': or stripe_subscription_obj.status != 'active'):
msg = subscription_result.get('error') msg = subscription_result.get('error')
messages.add_message(self.request, messages.ERROR, msg, messages.add_message(self.request, messages.ERROR, msg,
extra_tags='failed_payment') extra_tags='failed_payment')
@ -546,10 +567,10 @@ class OrderConfirmationView(DetailView):
password=password) password=password)
login(request, new_user) login(request, new_user)
else: else:
customer = StripeCustomer.objects.filter( # We assume that if the user is here, his/her StripeCustomer
id=stripe_customer_id).first() # object already exists
custom_user = customer.user stripe_customer_id = request.user.stripecustomer.id
stripe_customer_id = customer.id custom_user = request.user
# Save billing address # Save billing address
billing_address_data = request.session.get('billing_address_data') billing_address_data = request.session.get('billing_address_data')
@ -557,12 +578,6 @@ class OrderConfirmationView(DetailView):
billing_address_data.update({ billing_address_data.update({
'user': custom_user.id 'user': custom_user.id
}) })
billing_address_user_form = UserBillingAddressForm(
instance=custom_user.billing_addresses.first(),
data=billing_address_data)
billing_address = billing_address_user_form.save()
billing_address_id = billing_address.id
logger.debug("billing address id = {}".format(billing_address_id))
user = { user = {
'name': custom_user.name, 'name': custom_user.name,
'email': custom_user.email, 'email': custom_user.email,
@ -574,8 +589,7 @@ class OrderConfirmationView(DetailView):
create_vm_task.delay(vm_template_id, user, specs, template, create_vm_task.delay(vm_template_id, user, specs, template,
stripe_customer_id, billing_address_data, stripe_customer_id, billing_address_data,
billing_address_id, stripe_subscription_obj.id, card_details_dict)
stripe_subscription_obj, card_details_dict)
for session_var in ['specs', 'template', 'billing_address', for session_var in ['specs', 'template', 'billing_address',
'billing_address_data', 'billing_address_data',
'token', 'customer']: 'token', 'customer']:

View file

@ -161,6 +161,8 @@ MIDDLEWARE_CLASSES = (
'cms.middleware.language.LanguageCookieMiddleware', 'cms.middleware.language.LanguageCookieMiddleware',
) )
CSRF_FAILURE_VIEW = 'hosting.views.forbidden_view'
ROOT_URLCONF = 'dynamicweb.urls' ROOT_URLCONF = 'dynamicweb.urls'
TEMPLATES = [ TEMPLATES = [

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-28 18:36+0530\n" "POT-Creation-Date: 2017-10-03 18:54+0530\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -308,8 +308,8 @@ msgstr "Alle Benachrichtigungen"
msgid "%(page_header_text)s" msgid "%(page_header_text)s"
msgstr "" msgstr ""
msgid "Invoice Date" msgid "Date"
msgstr "Rechnung Datum" msgstr "Datum"
msgid "Status" msgid "Status"
msgstr "" msgstr ""
@ -332,6 +332,9 @@ msgstr "Bezahlmethode"
msgid "ending in" msgid "ending in"
msgstr "endend in" msgstr "endend in"
msgid "Credit Card"
msgstr "Kreditkarte"
msgid "Order summary" msgid "Order summary"
msgstr "Bestellungsübersicht" msgstr "Bestellungsübersicht"
@ -339,7 +342,7 @@ msgid "Product"
msgstr "Produkt" msgstr "Produkt"
msgid "Period" msgid "Period"
msgstr "" msgstr "Periode"
msgid "Cores" msgid "Cores"
msgstr "Prozessorkerne" msgstr "Prozessorkerne"
@ -385,9 +388,6 @@ msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
msgid "Order Nr." msgid "Order Nr."
msgstr "Bestellung Nr." msgstr "Bestellung Nr."
msgid "Date"
msgstr "Datum"
msgid "Amount" msgid "Amount"
msgstr "Betrag" msgstr "Betrag"
@ -412,9 +412,6 @@ msgstr "inkl. Mehrwertsteuer"
msgid "Billing Address" msgid "Billing Address"
msgstr "Rechnungsadresse" msgstr "Rechnungsadresse"
msgid "Credit Card"
msgstr "Kreditkarte"
msgid "" msgid ""
"\n" "\n"
" Please fill in your credit card information " " Please fill in your credit card information "
@ -604,8 +601,10 @@ msgstr "Um eine neue VM zu erzeugen, klicke \"Neue VM erzeugen\""
#, python-format #, python-format
msgid "" msgid ""
"To access your VM, add your SSH key <a href=\"%(create_ssh_url)s\">here</a>" "To access your VM, <a href=\"%(create_ssh_url)s\">add your SSH key here</a>"
msgstr "" msgstr ""
"Um auf Deine VM zuzugreifen, <a href=\"%(create_ssh_url)s\">füge Deinen SSH-"
"Key hinzu</a>"
msgid "login" msgid "login"
msgstr "anmelden" msgstr "anmelden"
@ -661,6 +660,16 @@ msgid "In order to create a VM, you need to create/upload your SSH KEY first."
msgstr "" msgstr ""
"Um eine VM zu erstellen musst du zuerst einen SSH-Key erstellen / hochladen." "Um eine VM zu erstellen musst du zuerst einen SSH-Key erstellen / hochladen."
msgid "Error."
msgstr "Fehler"
msgid ""
"There was a payment related error. On close of this popup, you will be "
"redirected back to the payment page."
msgstr ""
"Es ist ein Fehler bei der Zahlung betreten. Du wirst nach dem Schliessen vom "
"Popup zur Bezahlseite weitergeleitet"
msgid "Thank you for the order." msgid "Thank you for the order."
msgstr "Danke für Deine Bestellung." msgstr "Danke für Deine Bestellung."
@ -685,65 +694,29 @@ msgid ""
"contact Data Center Light Support." "contact Data Center Light Support."
msgstr "Kontaktiere den Data Center Light Support." msgstr "Kontaktiere den Data Center Light Support."
msgid ""
"We could not find the requested VM. Please contact Data Center Light Support."
msgstr ""
"Wir konnten die gesucht VM nicht finden. Kontaktiere den Data Center Light "
"Support."
msgid "Error terminating VM" msgid "Error terminating VM"
msgstr "Fehler beenden VM" msgstr "Fehler beenden VM"
#, fuzzy, python-format #, python-format
#| msgid "Virtual Machine Cancellation"
msgid "Virtual Machine %(vm_name)s Cancelled" msgid "Virtual Machine %(vm_name)s Cancelled"
msgstr "VM Kündigung" msgstr "Virtuelle Maschine %(vm_name)s Kündigung"
msgid "There was an error processing your request. Please try again."
msgstr ""
"Es gab einen Fehler bei der Bearbeitung Deine Anfrage. Bitte versuche es "
"noch einmal."
#~ msgid "My VM page" #~ msgid "My VM page"
#~ msgstr "Meine VM page" #~ msgstr "Meine VM page"
#~ msgid "" #~ msgid "Invoice Date"
#~ "\n" #~ msgstr "Rechnung Datum"
#~ "You're receiving this email because you requested a password reset for "
#~ "your user account at %(site_name)s.<br/>\n"
#~ "Please go to the following page and choose a new password: %(base_url)s"
#~ "%(password_reset_url)s<br/>\n"
#~ "If you didn't request a new password, ignore this e-mail.<br/>\n"
#~ "Thank you!\n"
#~ msgstr ""
#~ "\n"
#~ "Du erhälst diese E-Mail da Du Dein Passwort für Deinen Account bei "
#~ "%(site_name)s zurücksetzen möchtest.<br/>\n"
#~ "Bitte folge diesem Link und wähle ein neues Passwort: %(base_url)s"
#~ "%(password_reset_url)s Solltest Du kein neues Passwort angefordert haben, "
#~ "dann ignoriere diese E-Mail.<br/>\n"
#~ "Dankeschön!\n"
#~ msgid ""
#~ "You're receiving this email because you requested a password reset for "
#~ "your user account at %(site_name)s.\n"
#~ "Please go to the following page and choose a new password: %(base_url)s"
#~ "%(password_reset_url)s\n"
#~ "If you didn't request a new password, ignore this e-mail.\n"
#~ "Thank you!\n"
#~ msgstr ""
#~ "Du erhälst diese E-Mail da Du Dein Passwort für Deinen Account bei "
#~ "%(site_name)s zurücksetzen möchtest.\n"
#~ "Bitte folge diesem Link und wähle ein neues Passwort: %(base_url)s"
#~ "%(password_reset_url)s Solltest Du kein neues Passwort angefordert haben, "
#~ "dann ignoriere diese E-Mail.\n"
#~ "Dankeschön!\n"
#~ msgid ""
#~ "You're receiving this mail because your virtual machine [%(vm_name)s] has "
#~ "been cancelled.\n"
#~ "You can see your order status by clicking here\n"
#~ "%(base_url)s%(vm_order_url)s\n"
#~ "If you want to order a new virtual machine, you can do it by clicking "
#~ "this link.\n"
#~ "%(base_url)s%(my_virtual_machines_url)s\n"
#~ msgstr ""
#~ "Du erhälst diese E-Mail, da Deine virtuelle Maschine [%(vm_name)s] "
#~ "gekündigt wurde.\n"
#~ "Um Deinen Auftragsstatus zu sehen, klicke hier.\n"
#~ "%(base_url)s%(vm_order_url)s\n"
#~ "Falls Du eine neue virtuelle Maschine bestellen möchtest, kannst Du dies "
#~ "tun, indem Du diesen Link klickst.\n"
#~ "%(base_url)s%(my_virtual_machines_url)s\n"
#~ msgid "VM %(VM_ID)s terminated successfully" #~ msgid "VM %(VM_ID)s terminated successfully"
#~ msgstr "VM %(VM_ID)s erfolgreich beendet" #~ msgstr "VM %(VM_ID)s erfolgreich beendet"

View file

@ -88,19 +88,19 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
self.cc_brand = stripe_charge.source.brand self.cc_brand = stripe_charge.source.brand
self.save() self.save()
def set_subscription_id(self, subscription_object, cc_details): def set_subscription_id(self, subscription_id, cc_details):
""" """
When creating a Stripe subscription, we have subscription id. When creating a Stripe subscription, we have subscription id.
We store this in the subscription_id field. We store this in the subscription_id field.
This method sets the subscription id from subscription_object This method sets the subscription id
and also the last4 and credit card brands used for this order. and the last4 and credit card brands used for this order.
:param subscription_object: Stripe's subscription object :param subscription_id: Stripe's subscription id
:param cc_details: A dict containing card details :param cc_details: A dict containing card details
{last4, brand} {last4, brand}
:return: :return:
""" """
self.subscription_id = subscription_object.id self.subscription_id = subscription_id
self.last4 = cc_details.get('last4') self.last4 = cc_details.get('last4')
self.cc_brand = cc_details.get('brand') self.cc_brand = cc_details.get('brand')
self.save() self.save()

View file

@ -656,7 +656,6 @@ a.unlink:hover {
.card-element { .card-element {
margin-bottom: 10px; margin-bottom: 10px;
padding: 0;
} }
.card-element label{ .card-element label{

View file

@ -86,23 +86,31 @@ $(document).ready(function() {
url: create_vm_form.attr('action'), url: create_vm_form.attr('action'),
type: 'POST', type: 'POST',
data: create_vm_form.serialize(), data: create_vm_form.serialize(),
init: function(){
ok_btn = $('#createvm-modal-done-btn');
close_btn = $('#createvm-modal-close-btn');
ok_btn.addClass('btn btn-success btn-ok btn-wide hide');
close_btn.addClass('btn btn-danger btn-ok btn-wide hide');
},
success: function (data) { success: function (data) {
fa_icon = $('.modal-icon > .fa');
modal_btn = $('#createvm-modal-done-btn');
$('#createvm-modal-title').text(data.msg_title);
$('#createvm-modal-body').html(data.msg_body);
modal_btn.attr('href', data.redirect)
.removeClass('hide');
if (data.status === true) { if (data.status === true) {
fa_icon = $('.modal-icon > .fa');
fa_icon.attr('class', 'checkmark'); fa_icon.attr('class', 'checkmark');
// $('.modal-header > .close').removeClass('hidden'); } else {
$('#createvm-modal-title').text(data.msg_title); fa_icon.attr('class', 'fa fa-close');
$('#createvm-modal-body').text(data.msg_body); modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide');
$('#createvm-modal-done-btn')
.attr('href', data.redirect)
.removeClass('hide');
} }
}, },
error: function (xmlhttprequest, textstatus, message) { error: function (xmlhttprequest, textstatus, message) {
fa_icon = $('.modal-icon > .fa'); fa_icon = $('.modal-icon > .fa');
fa_icon.attr('class', 'fa fa-close'); fa_icon.attr('class', 'fa fa-close');
if (typeof(create_vm_error_message) !== 'undefined') { if (typeof(create_vm_error_message) !== 'undefined') {
$('#createvm-modal-text').text(create_vm_error_message); $('#createvm-modal-body').text(create_vm_error_message);
} }
$('#btn-create-vm').prop('disabled', false); $('#btn-create-vm').prop('disabled', false);
$('#createvm-modal-close-btn').removeClass('hide'); $('#createvm-modal-close-btn').removeClass('hide');

View file

@ -0,0 +1,8 @@
{% if messages %}
<ul class="list-unstyled msg-list">
{% for message in messages %}
<div
class="alert {% if message.tags and message.tags == 'error' %} alert-danger {% else %} alert-{{message.tags}} {% endif %}">{{ message|safe }}</div>
{% endfor %}
</ul>
{% endif %}

View file

@ -16,13 +16,7 @@
<div class="auth-content"> <div class="auth-content">
<div class="intro-message auth-box"> <div class="intro-message auth-box">
<h2 class="section-heading">{% trans "Login"%}</h2> <h2 class="section-heading">{% trans "Login"%}</h2>
{% if messages %} {% include 'hosting/includes/_messages.html' %}
<ul class="list-unstyled msg-list">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
<form action="{% url 'hosting:login' %}" method="post" class="form" novalidated> <form action="{% url 'hosting:login' %}" method="post" class="form" novalidated>
{% csrf_token %} {% csrf_token %}
{% for field in form %} {% for field in form %}

View file

@ -17,10 +17,12 @@
<h1 class="dashboard-title-thin"> <h1 class="dashboard-title-thin">
<img src="{% static 'hosting/img/billing.svg' %}" class="un-icon">{% blocktrans with page_header_text=page_header_text|default:"Invoice" %}{{page_header_text}}{% endblocktrans %} <img src="{% static 'hosting/img/billing.svg' %}" class="un-icon">{% blocktrans with page_header_text=page_header_text|default:"Invoice" %}{{page_header_text}}{% endblocktrans %}
</h1> </h1>
<div class="dashboard-container-options"> {% if order %}
<button type="button" class="btn-plain btn-pdf" data-target="#order-detail{{order.pk}}"><img src="{% static 'hosting/img/icon-pdf.svg' %}" class="svg-img"></button> <div class="dashboard-container-options">
<button type="button" class="btn-plain btn-print"><img src="{% static 'hosting/img/icon-print.svg' %}" class="svg-img"></button> <button type="button" class="btn-plain btn-pdf" data-target="#order-detail{{order.pk}}"><img src="{% static 'hosting/img/icon-pdf.svg' %}" class="svg-img"></button>
</div> <button type="button" class="btn-plain btn-print"><img src="{% static 'hosting/img/icon-print.svg' %}" class="svg-img"></button>
</div>
{% endif %}
</div> </div>
<div class="order-details"> <div class="order-details">
{% if order %} {% if order %}
@ -29,7 +31,7 @@
</p> </p>
{% endif %} {% endif %}
<p> <p>
<strong>{% trans "Invoice Date" %}:</strong> <strong>{% trans "Date" %}:</strong>
<span id="order-created_at"> <span id="order-created_at">
{% if order %} {% if order %}
{{order.created_at|date:'Y-m-d H:i'}} {{order.created_at|date:'Y-m-d H:i'}}
@ -80,8 +82,7 @@
{{order.last4}}<br> {{order.last4}}<br>
{{user.email}} {{user.email}}
{% else %} {% else %}
{{cc_brand|default:'Card'}} {% trans "ending in" %} **** {{cc_brand|default:_('Credit Card')}} {% trans "ending in" %} ****{{cc_last4}}<br>
{{cc_last4}}<br>
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
{{request.user.email}} {{request.user.email}}
{% else %} {% else %}
@ -94,7 +95,12 @@
<div> <div>
<h4>{% trans "Order summary" %}</h4> <h4>{% trans "Order summary" %}</h4>
<p> <p>
<strong>{% trans "Product" %}:</strong> {{vm.name}} <strong>{% trans "Product" %}:</strong>&nbsp;
{% if vm.name %}
{{ vm.name }}
{% else %}
{{ request.session.template.name }}
{% endif %}
</p> </p>
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
@ -132,19 +138,21 @@
{% endif %} {% endif %}
</div> </div>
{% if not order %} {% if not order %}
<form method="post" id="virtual_machine_create_form"> {% block submit_btn %}
{% csrf_token %} <form method="post" id="virtual_machine_create_form">
<div class="row"> {% csrf_token %}
<div class="col-sm-8"> <div class="row">
<div class="dcl-place-order-text">{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price }}CHF/month{% endblocktrans %}.</div> <div class="col-sm-8">
<div class="dcl-place-order-text">{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price }}CHF/month{% endblocktrans %}.</div>
</div>
<div class="col-sm-4 order-confirm-btn text-right">
<button class="btn choice-btn" id="btn-create-vm" data-href="{% url 'hosting:order-confirmation' %}" data-toggle="modal" data-target="#createvm-modal">
{% trans "Place order" %}
</button>
</div>
</div> </div>
<div class="col-sm-4 order-confirm-btn text-right"> </form>
<button class="btn choice-btn" id="btn-create-vm" data-href="{% url 'hosting:order-confirmation' %}" data-toggle="modal" data-target="#createvm-modal"> {% endblock submit_btn %}
{% trans "Place order" %}
</button>
</div>
</div>
</form>
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>
@ -197,8 +205,10 @@
{%endblock%} {%endblock%}
{% block js_extra %} {% block js_extra %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.5/jspdf.min.js"></script> {% if order %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.5/jspdf.min.js"></script>
<script src="{% static 'hosting/js/html2pdf.js' %}"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<script src="{% static 'hosting/js/order.js' %}"></script> <script src="{% static 'hosting/js/html2pdf.js' %}"></script>
<script src="{% static 'hosting/js/order.js' %}"></script>
{% endif %}
{% endblock js_extra %} {% endblock js_extra %}

View file

@ -17,6 +17,7 @@
<div class="auth-content"> <div class="auth-content">
<div class="intro-message auth-box sign-up"> <div class="intro-message auth-box sign-up">
<h2 class="section-heading">{% trans "Resend activation link"%}</h2> <h2 class="section-heading">{% trans "Resend activation link"%}</h2>
{% include 'hosting/includes/_messages.html' %}
<form action="{% url 'hosting:resend_activation_link' %}" method="post" class="form" novalidate> <form action="{% url 'hosting:resend_activation_link' %}" method="post" class="form" novalidate>
{% csrf_token %} {% csrf_token %}
{% for field in form %} {% for field in form %}

View file

@ -17,6 +17,7 @@
<div class="auth-content"> <div class="auth-content">
<div class="intro-message auth-box sign-up"> <div class="intro-message auth-box sign-up">
<h2 class="section-heading">{% trans "Reset your password"%}</h2> <h2 class="section-heading">{% trans "Reset your password"%}</h2>
{% include 'hosting/includes/_messages.html' %}
<form action="{% url 'hosting:reset_password' %}" method="post" class="form" novalidate> <form action="{% url 'hosting:reset_password' %}" method="post" class="form" novalidate>
{% csrf_token %} {% csrf_token %}
{% for field in form %} {% for field in form %}

View file

@ -15,6 +15,7 @@
<div class="auth-content"> <div class="auth-content">
<div class="intro-message auth-box sign-up"> <div class="intro-message auth-box sign-up">
<h2 class="section-heading">{% trans "Sign up"%}</h2> <h2 class="section-heading">{% trans "Sign up"%}</h2>
{% include 'hosting/includes/_messages.html' %}
<form action="{% url 'hosting:signup' %}" method="post" class="form" novalidate> <form action="{% url 'hosting:signup' %}" method="post" class="form" novalidate>
{% csrf_token %} {% csrf_token %}
{% for field in form %} {% for field in form %}

View file

@ -11,6 +11,7 @@
<div class="auth-center"> <div class="auth-center">
<div class="auth-title"> <div class="auth-title">
<h2>{% trans "Your VM hosted in Switzerland"%}</h2> <h2>{% trans "Your VM hosted in Switzerland"%}</h2>
{% include 'hosting/includes/_messages.html' %}
</div> </div>
<div class="auth-content"> <div class="auth-content">
<div class="intro-message auth-box sign-up"> <div class="intro-message auth-box sign-up">

View file

@ -16,7 +16,7 @@
<p>{% trans 'To create a new virtual machine, click "Create VM"' %} <p>{% trans 'To create a new virtual machine, click "Create VM"' %}
{% if show_create_ssh_key_msg %} {% if show_create_ssh_key_msg %}
{% url 'hosting:create_ssh_key' as create_ssh_url %} {% url 'hosting:create_ssh_key' as create_ssh_url %}
<br/>{% blocktrans %}To access your VM, add your SSH key <a href="{{create_ssh_url}}">here</a>{% endblocktrans %} <br/>{% blocktrans %}To access your VM, <a href="{{create_ssh_url}}">add your SSH key here</a>{% endblocktrans %}
{% endif %} {% endif %}
</p> </p>
<div class="text-right"> <div class="text-right">

View file

@ -60,8 +60,9 @@ CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a \
minutes." minutes."
class DashboardView(View): class DashboardView(LoginRequiredMixin, View):
template_name = "hosting/dashboard.html" template_name = "hosting/dashboard.html"
login_url = reverse_lazy('hosting:login')
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = {} context = {}
@ -80,8 +81,6 @@ class DjangoHostingView(ProcessVMSelectionMixin, View):
templates = OpenNebulaManager().get_templates() templates = OpenNebulaManager().get_templates()
data = VirtualMachineTemplateSerializer(templates, many=True).data data = VirtualMachineTemplateSerializer(templates, many=True).data
configuration_options = HostingPlan.get_serialized_configs() configuration_options = HostingPlan.get_serialized_configs()
# configuration_detail = dict(VirtualMachinePlan.VM_CONFIGURATION).get(HOSTING)
context = { context = {
'hosting': HOSTING, 'hosting': HOSTING,
'hosting_long': "Django", 'hosting_long': "Django",
@ -134,7 +133,6 @@ class NodeJSHostingView(ProcessVMSelectionMixin, View):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
HOSTING = 'nodejs' HOSTING = 'nodejs'
# configuration_detail = dict(VirtualMachinePlan.VM_CONFIGURATION).get(HOSTING)
templates = OpenNebulaManager().get_templates() templates = OpenNebulaManager().get_templates()
configuration_options = HostingPlan.get_serialized_configs() configuration_options = HostingPlan.get_serialized_configs()
@ -249,7 +247,8 @@ class SignupValidateView(TemplateView):
<br />{go_back} {hurl}.'.format( <br />{go_back} {hurl}.'.format(
signup_success_message=_( signup_success_message=_(
'Thank you for signing up. We have sent an email to you. ' 'Thank you for signing up. We have sent an email to you. '
'Please follow the instructions in it to activate your account. Once activated, you can login using'), 'Please follow the instructions in it to activate your '
'account. Once activated, you can login using'),
go_back=_('Go back to'), go_back=_('Go back to'),
lurl=login_url, lurl=login_url,
hurl=home_url hurl=home_url
@ -271,7 +270,8 @@ class SignupValidatedView(SignupValidateView):
user = CustomUser.objects.filter( user = CustomUser.objects.filter(
validation_slug=self.kwargs['validate_slug']).first() validation_slug=self.kwargs['validate_slug']).first()
if validated: if validated:
message = '{account_activation_string} <br /> {login_string} {lurl}.'.format( message = ('{account_activation_string} <br />'
' {login_string} {lurl}.').format(
account_activation_string=_( account_activation_string=_(
"Your account has been activated."), "Your account has been activated."),
login_string=_("You can now"), login_string=_("You can now"),
@ -653,10 +653,7 @@ class PaymentVMView(LoginRequiredMixin, FormView):
return HttpResponseRedirect( return HttpResponseRedirect(
reverse('hosting:payment') + '#payment_error') reverse('hosting:payment') + '#payment_error')
# Create Billing Address
billing_address = form.save()
request.session['billing_address_data'] = billing_address_data request.session['billing_address_data'] = billing_address_data
request.session['billing_address'] = billing_address.id
request.session['token'] = token request.session['token'] = token
request.session['customer'] = customer.id request.session['customer'] = customer.id
return HttpResponseRedirect("{url}?{query_params}".format( return HttpResponseRedirect("{url}?{query_params}".format(
@ -704,7 +701,13 @@ class OrdersHostingDetailView(LoginRequiredMixin,
try: try:
vm_detail = VMDetail.objects.get(vm_id=obj.vm_id) vm_detail = VMDetail.objects.get(vm_id=obj.vm_id)
context['vm'] = vm_detail.__dict__ context['vm'] = vm_detail.__dict__
context['vm']['name'] = '{}-{}'.format(context['vm']['configuration'], context['vm']['vm_id']) context['vm']['name'] = '{}-{}'.format(
context['vm']['configuration'], context['vm']['vm_id'])
context['vm']['price'] = get_vm_price(
cpu=context['vm']['cores'],
disk_size=context['vm']['disk_size'],
memory=context['vm']['memory']
)
except VMDetail.DoesNotExist: except VMDetail.DoesNotExist:
try: try:
manager = OpenNebulaManager( manager = OpenNebulaManager(
@ -767,7 +770,6 @@ class OrdersHostingDetailView(LoginRequiredMixin,
stripe_customer_id = request.session.get('customer') stripe_customer_id = request.session.get('customer')
customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
billing_address_data = request.session.get('billing_address_data') billing_address_data = request.session.get('billing_address_data')
billing_address_id = request.session.get('billing_address')
vm_template_id = template.get('id', 1) vm_template_id = template.get('id', 1)
# Make stripe charge to a customer # Make stripe charge to a customer
@ -785,8 +787,7 @@ class OrdersHostingDetailView(LoginRequiredMixin,
cpu = specs.get('cpu') cpu = specs.get('cpu')
memory = specs.get('memory') memory = specs.get('memory')
disk_size = specs.get('disk_size') disk_size = specs.get('disk_size')
amount_to_be_charged = get_vm_price(cpu=cpu, memory=memory, amount_to_be_charged = specs.get('price')
disk_size=disk_size)
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu, plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
memory=memory, memory=memory,
disk_size=disk_size) disk_size=disk_size)
@ -805,12 +806,24 @@ class OrdersHostingDetailView(LoginRequiredMixin,
'response_object').stripe_plan_id}]) 'response_object').stripe_plan_id}])
stripe_subscription_obj = subscription_result.get('response_object') stripe_subscription_obj = subscription_result.get('response_object')
# Check if the subscription was approved and is active # Check if the subscription was approved and is active
if stripe_subscription_obj is None or stripe_subscription_obj.status != 'active': if (stripe_subscription_obj is None or
stripe_subscription_obj.status != 'active'):
msg = subscription_result.get('error') msg = subscription_result.get('error')
messages.add_message(self.request, messages.ERROR, msg, messages.add_message(self.request, messages.ERROR, msg,
extra_tags='failed_payment') extra_tags='failed_payment')
return HttpResponseRedirect( response = {
reverse('hosting:payment') + '#payment_error') '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")
user = { user = {
'name': self.request.user.name, 'name': self.request.user.name,
'email': self.request.user.email, 'email': self.request.user.email,
@ -821,8 +834,7 @@ class OrdersHostingDetailView(LoginRequiredMixin,
} }
create_vm_task.delay(vm_template_id, user, specs, template, create_vm_task.delay(vm_template_id, user, specs, template,
stripe_customer_id, billing_address_data, stripe_customer_id, billing_address_data,
billing_address_id, stripe_subscription_obj.id, card_details_dict)
stripe_subscription_obj, card_details_dict)
for session_var in ['specs', 'template', 'billing_address', for session_var in ['specs', 'template', 'billing_address',
'billing_address_data', 'billing_address_data',
@ -1025,6 +1037,7 @@ class VirtualMachineView(LoginRequiredMixin, View):
return redirect(reverse('hosting:virtual_machines')) return redirect(reverse('hosting:virtual_machines'))
elif self.request.is_ajax(): elif self.request.is_ajax():
return HttpResponse() return HttpResponse()
context = None
try: try:
serializer = VirtualMachineSerializer(vm) serializer = VirtualMachineSerializer(vm)
context = { context = {
@ -1034,7 +1047,11 @@ class VirtualMachineView(LoginRequiredMixin, View):
} }
except Exception as ex: except Exception as ex:
logger.debug("Exception generated {}".format(str(ex))) logger.debug("Exception generated {}".format(str(ex)))
pass messages.error(self.request,
_('We could not find the requested VM. Please '
'contact Data Center Light Support.')
)
return redirect(reverse('hosting:virtual_machines'))
return render(request, self.template_name, context) return render(request, self.template_name, context)
@ -1144,3 +1161,15 @@ class HostingBillDetailView(PermissionRequiredMixin, LoginRequiredMixin,
bill.total_price += vm['price'] bill.total_price += vm['price']
context['vms'] = vms context['vms'] = vms
return context return context
def forbidden_view(request, exception=None, reason=''):
"""
Handle 403 error
"""
logger.error(str(exception) if exception else None)
logger.error('Reason = {reason}'.format(reason=reason))
err_msg = _('There was an error processing your request. Please try '
'again.')
messages.add_message(request, messages.ERROR, err_msg)
return HttpResponseRedirect(request.get_full_path())

View file

@ -184,9 +184,9 @@ class StripeCustomer(models.Model):
customer_name=None): customer_name=None):
""" """
This method creates a Stripe API customer with the given This method creates a Stripe API customer with the given
email, token and customer_name. This is different from email, token and customer_name. This is different from
get_or_create method below in that it does not create a get_or_create method below in that it does not create a
CustomUser and associate the customer created in stripe CustomUser and associate the customer created in stripe
with it, while get_or_create does that before creating the with it, while get_or_create does that before creating the
stripe user. stripe user.
""" """

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-25 20:11+0000\n" "POT-Creation-Date: 2017-09-29 20:33+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -771,10 +771,13 @@ msgstr ""
msgid "Country" msgid "Country"
msgstr "" msgstr ""
msgid "Street Building" msgid "Name"
msgstr "" msgstr ""
msgid "Name" msgid "Email Address"
msgstr ""
msgid "Street Building"
msgstr "" msgstr ""
msgid "Email" msgid "Email"
@ -786,9 +789,9 @@ msgstr "Telefon"
msgid "Message" msgid "Message"
msgstr "Nachricht" msgstr "Nachricht"
msgid "An email with the activation link has been sent to your email" msgid "An email with the activation link has been sent to you"
msgstr "" msgstr ""
"Der Link zum Zurücksetzen deines Passwortes wurde an deine E-Mail gesendet" "Es wurde eine E-Mail mit dem Aktivierungslink an Dich gesendet."
msgid "Account Activation" msgid "Account Activation"
msgstr "Accountaktivierung" msgstr "Accountaktivierung"

View file

@ -28,28 +28,34 @@ def handleStripeError(f):
body = e.json_body body = e.json_body
err = body['error'] err = body['error']
response.update({'error': err['message']}) response.update({'error': err['message']})
logger.error(str(e))
return response return response
except stripe.error.RateLimitError as e: except stripe.error.RateLimitError as e:
response.update( response.update(
{'error': "Too many requests made to the API too quickly"}) {'error': "Too many requests made to the API too quickly"})
return response return response
except stripe.error.InvalidRequestError as e: except stripe.error.InvalidRequestError as e:
logger.error(str(e))
response.update({'error': "Invalid parameters"}) response.update({'error': "Invalid parameters"})
return response return response
except stripe.error.AuthenticationError as e: except stripe.error.AuthenticationError as e:
# Authentication with Stripe's API failed # Authentication with Stripe's API failed
# (maybe you changed API keys recently) # (maybe you changed API keys recently)
logger.error(str(e))
response.update({'error': common_message}) response.update({'error': common_message})
return response return response
except stripe.error.APIConnectionError as e: except stripe.error.APIConnectionError as e:
logger.error(str(e))
response.update({'error': common_message}) response.update({'error': common_message})
return response return response
except stripe.error.StripeError as e: except stripe.error.StripeError as e:
# maybe send email # maybe send email
logger.error(str(e))
response.update({'error': common_message}) response.update({'error': common_message})
return response return response
except Exception as e: except Exception as e:
# maybe send email # maybe send email
logger.error(str(e))
response.update({'error': common_message}) response.update({'error': common_message})
return response return response

View file

@ -66,7 +66,7 @@ class LoginViewMixin(FormView):
class ResendActivationLinkViewMixin(FormView): class ResendActivationLinkViewMixin(FormView):
success_message = _( success_message = _(
"An email with the activation link has been sent to your email") "An email with the activation link has been sent to you")
def generate_email_context(self, user): def generate_email_context(self, user):
context = { context = {