merged master

This commit is contained in:
Arvind Tiwari 2017-10-03 18:42:00 +05:30
commit 734a41a371
25 changed files with 330 additions and 370 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-23 21:22+0000\n" "POT-Creation-Date: 2017-10-01 22:13+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"
@ -388,24 +388,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 "
@ -417,36 +399,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 "Some problem encountered. Please try again later."
msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
msgid "We are cutting down the costs significantly!"
msgstr "Wir sorgen dafür, dass die Kosten für Dich signifikant abnehmen"
msgid "Order Now!"
msgstr "Bestelle jetzt!"
msgid ""
"Our VMs are hosted in Glarus, Switzerland, and our website is currently "
"running in BETA mode. If you want more information that you did not find on "
"our website, or if your order is more detailed, or if you encounter any "
"technical hiccups, please contact us at support@datacenterlight.ch, our team "
"will get in touch with you asap."
msgstr ""
"Unsere VMs werden in der Schweiz im Kanton Glarus gehostet und befinden sich "
"zur Zeit noch in der BETA-Phase. Möchtest du mehr über uns erfahren und hast "
"auf unserer Website nicht genügend Informationen gefunden? Möchtest eine "
"detailliertere Bestellung aufgeben? Bist du auf technische Probleme "
"gestossen, die du uns mitteilen möchtest? Dann zögere nicht und kontaktiere "
"uns unter support@datacenterlight.ch. Unser Team wird sich umgehend um dein "
"Anliegen kümmern!"
msgid "Thank you for order! Our team will contact you via email" 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 "
@ -540,6 +492,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 ""
@ -547,6 +502,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."
@ -558,6 +515,51 @@ 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 "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 "Processing..."
#~ msgstr "Abarbeitung..."
#~ msgid "Hold tight, we are processing your request"
#~ msgstr "Bitte warten - wir verbeiten Deine Anfrage gerade"
#~ msgid "Some problem encountered. Please try again later."
#~ msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
#~ msgid "We are cutting down the costs significantly!"
#~ msgstr "Wir sorgen dafür, dass die Kosten für Dich signifikant abnehmen"
#~ msgid "Order Now!"
#~ msgstr "Bestelle jetzt!"
#~ msgid ""
#~ "Our VMs are hosted in Glarus, Switzerland, and our website is currently "
#~ "running in BETA mode. If you want more information that you did not find "
#~ "on our website, or if your order is more detailed, or if you encounter "
#~ "any technical hiccups, please contact us at support@datacenterlight.ch, "
#~ "our team will get in touch with you asap."
#~ msgstr ""
#~ "Unsere VMs werden in der Schweiz im Kanton Glarus gehostet und befinden "
#~ "sich zur Zeit noch in der BETA-Phase. Möchtest du mehr über uns erfahren "
#~ "und hast auf unserer Website nicht genügend Informationen gefunden? "
#~ "Möchtest eine detailliertere Bestellung aufgeben? Bist du auf technische "
#~ "Probleme gestossen, die du uns mitteilen möchtest? Dann zögere nicht und "
#~ "kontaktiere uns unter support@datacenterlight.ch. Unser Team wird sich "
#~ "umgehend um dein Anliegen kümmern!"
#~ msgid "Email Address" #~ msgid "Email Address"
#~ msgstr "E-Mail-Adresse" #~ msgstr "E-Mail-Adresse"

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

@ -50,15 +50,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:
@ -111,8 +116,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()
@ -183,7 +187,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
@ -212,32 +217,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">
<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 %} {% csrf_token %}
<div class="row"> <div class="row">
<div class="col-sm-8"> <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 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>
<div class="col-sm-4 content"> <div class="col-sm-4 order-confirm-btn text-right">
<button class="btn btn-info pull-right" <button class="btn choice-btn" id="btn-create-vm"
id="btn-create-vm" data-toggle="modal" data-target="#createvm-modal">
data-href="{% url 'hosting:order-confirmation' %}"
data-toggle="modal"
data-target="#createvm-modal">
{% trans "Place order" %} {% trans "Place order" %}
</button> </button>
</div> </div>
</div> </div>
</form> </form>
</div> {% endblock submit_btn %}
</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
} }
try:
custom_user = CustomUser.objects.get(email=user_email)
customer = StripeCustomer.objects.filter(
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( customer = StripeCustomer.create_stripe_api_customer(
email=this_user.get('email'), email=user_email,
token=token, token=token,
customer_name=form.cleaned_data.get('name')) 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-24 12:34+0000\n" "POT-Creation-Date: 2017-10-01 22:12+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"
@ -331,12 +331,15 @@ 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 ""
msgid "Terminated"
msgstr "Beendet"
msgid "Approved" msgid "Approved"
msgstr "Akzeptiert" msgstr "Akzeptiert"
@ -352,12 +355,18 @@ 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"
msgid "Product" msgid "Product"
msgstr "Produkt" msgstr "Produkt"
msgid "Period"
msgstr "Periode"
msgid "Cores" msgid "Cores"
msgstr "Prozessorkerne" msgstr "Prozessorkerne"
@ -390,15 +399,18 @@ msgstr "Abarbeitung..."
msgid "Hold tight, we are processing your request" msgid "Hold tight, we are processing your request"
msgstr "Bitte warten - wir bearbeiten Deine Anfrage gerade" msgstr "Bitte warten - wir bearbeiten Deine Anfrage gerade"
msgid "OK"
msgstr ""
msgid "Close"
msgstr "Schliessen"
msgid "Some problem encountered. Please try again later." msgid "Some problem encountered. Please try again later."
msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal." 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"
@ -423,9 +435,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 "
@ -599,16 +608,13 @@ msgstr "Deine Virtuelle Maschine beenden"
msgid "Do you want to cancel your Virtual Machine" msgid "Do you want to cancel your Virtual Machine"
msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst" msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst"
msgid "OK"
msgstr ""
#, python-format #, python-format
msgid "" msgid ""
"Your Virtual Machine <strong>%(machine_name)s</strong> is successfully " "Your Virtual Machine <strong>%(machine_name)s</strong> is successfully "
"terminated!" "terminated!"
msgstr "" msgstr ""
"Deine Virtuelle Machine (VM) <strong>%(machine_name)s</strong> wurde erfolgreich " "Deine Virtuelle Machine (VM) <strong>%(machine_name)s</strong> wurde "
"beendet!" "erfolgreich beendet!"
msgid "Virtual Machines" msgid "Virtual Machines"
msgstr "Virtuelle Maschinen" msgstr "Virtuelle Maschinen"
@ -616,6 +622,13 @@ msgstr "Virtuelle Maschinen"
msgid "To create a new virtual machine, click \"Create VM\"" msgid "To create a new virtual machine, click \"Create VM\""
msgstr "Um eine neue VM zu erzeugen, klicke \"Neue VM erzeugen\"" msgstr "Um eine neue VM zu erzeugen, klicke \"Neue VM erzeugen\""
#, python-format
msgid ""
"To access your VM, <a href=\"%(create_ssh_url)s\">add your SSH key here</a>"
msgstr ""
"Um auf Deine VM zuzugreifen, <a href=\"%(create_ssh_url)s\">füge Deinen SSH-"
"Key hinzu</a>"
msgid "CREATE VM" msgid "CREATE VM"
msgstr "NEUE VM" msgstr "NEUE VM"
@ -673,6 +686,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."
@ -697,8 +720,11 @@ 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 "Terminated" msgid ""
msgstr "Beendet" "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"
@ -706,8 +732,13 @@ msgstr "Fehler beenden VM"
msgid "Virtual Machine Cancellation" msgid "Virtual Machine Cancellation"
msgstr "VM Kündigung" msgstr "VM Kündigung"
#~ msgid "Close" msgid "There was an error processing your request. Please try again."
#~ msgstr "Schliessen" msgstr ""
"Es gab einen Fehler bei der Bearbeitung Deine Anfrage. Bitte versuche es "
"noch einmal."
#~ msgid "Invoice Date"
#~ msgstr "Rechnung Datum"
#~ 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"
@ -842,5 +873,5 @@ msgstr "VM Kündigung"
#~ "Your SSH private key was already generated and downloaded, if you lost " #~ "Your SSH private key was already generated and downloaded, if you lost "
#~ "it, contact us. " #~ "it, contact us. "
#~ msgstr "" #~ msgstr ""
#~ "Dein privater SSH Key wurde bereits generiert und heruntergeladen. " #~ "Dein privater SSH Key wurde bereits generiert und heruntergeladen. Falls "
#~ "Falls Du ihn verloren hast, kontaktiere uns." #~ "Du ihn verloren hast, kontaktiere uns."

View file

@ -90,19 +90,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

@ -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) {
if (data.status === true) {
fa_icon = $('.modal-icon > .fa'); fa_icon = $('.modal-icon > .fa');
fa_icon.attr('class', 'checkmark'); modal_btn = $('#createvm-modal-done-btn');
// $('.modal-header > .close').removeClass('hidden');
$('#createvm-modal-title').text(data.msg_title); $('#createvm-modal-title').text(data.msg_title);
$('#createvm-modal-body').text(data.msg_body); $('#createvm-modal-body').html(data.msg_body);
$('#createvm-modal-done-btn') modal_btn.attr('href', data.redirect)
.attr('href', data.redirect)
.removeClass('hide'); .removeClass('hide');
if (data.status === true) {
fa_icon.attr('class', 'checkmark');
} else {
fa_icon.attr('class', 'fa fa-close');
modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide');
} }
}, },
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>
{% if order %}
<div class="dashboard-container-options"> <div class="dashboard-container-options">
<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> <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>
<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-print"><img src="{% static 'hosting/img/icon-print.svg' %}" class="svg-img"></button>
</div> </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,6 +138,7 @@
{% endif %} {% endif %}
</div> </div>
{% if not order %} {% if not order %}
{% block submit_btn %}
<form method="post" id="virtual_machine_create_form"> <form method="post" id="virtual_machine_create_form">
{% csrf_token %} {% csrf_token %}
<div class="row"> <div class="row">
@ -145,6 +152,7 @@
</div> </div>
</div> </div>
</form> </form>
{% endblock submit_btn %}
{% endif %} {% endif %}
{% endif %} {% endif %}
</div> </div>
@ -197,8 +205,10 @@
{%endblock%} {%endblock%}
{% block js_extra %} {% block js_extra %}
{% if order %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.5/jspdf.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.5/jspdf.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<script src="{% static 'hosting/js/html2pdf.js' %}"></script> <script src="{% static 'hosting/js/html2pdf.js' %}"></script>
<script src="{% static 'hosting/js/order.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
@ -269,7 +268,8 @@ class SignupValidatedView(SignupValidateView):
reverse('hosting:login') + '">' + str(_('login')) + '</a>' reverse('hosting:login') + '">' + str(_('login')) + '</a>'
section_title = _('Account activation') section_title = _('Account activation')
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"),
@ -636,10 +636,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(
@ -687,10 +684,12 @@ 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'] = ( context['vm']['name'] = '{}-{}'.format(
'{}-{}'.format( context['vm']['configuration'], context['vm']['vm_id'])
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']
) )
context['subscription_end_date'] = vm_detail.end_date() context['subscription_end_date'] = vm_detail.end_date()
except VMDetail.DoesNotExist: except VMDetail.DoesNotExist:
@ -755,7 +754,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
@ -773,8 +771,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)
@ -793,12 +790,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,
@ -809,8 +818,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',
@ -1013,6 +1021,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 = {
@ -1022,7 +1031,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)
@ -1130,3 +1143,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

@ -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 = {