Merged master into task/3622/decouple_opennebula_dcl_flow

This commit is contained in:
PCoder 2017-08-19 03:24:22 +05:30
commit 6b7ae88f74
19 changed files with 296 additions and 142 deletions

1
.gitignore vendored
View File

@ -35,3 +35,4 @@ secret-key
.env
*.mo
*.log

View File

@ -1,3 +1,26 @@
1.0.24: 2017-08-15
* #3699: [datacenterlight] Added oneadmin ssh key by default to the created VM via DCL landing
* #3687: [datacenterlight] Added the name of the customer as description field of the stripe metadata
[all] Added CustomUser as a parameter in get_anonymous_user function to resolve issues with tests
1.0.23: 2017-08-11
* #3629: [datacentlight] Fixed navbar changing language from DE to EN between menus bug
* #3623: [hosting] Fixed “Confirm Order” text appearing in “Invoice” place
* #3633: [datacenterlight, hosting] Translated “All Rights Reserved” for German pages
* #3627: [datacenterlight, hosting] Added border for payment warning message when the user has already submitted card information
* #3620: [hosting] Updated SSH Key page with new style: new key choice page, upload key page, added icons for downloading and deleting key on mobile
* [hosting] bug fix: added modal icon and translation back for delete SSH Key
* #3660: [datacenterlight] Rearranged desktop and mobile view for “Why Data Centre Light?” IPv6/SSD section
* #3646: Added file with VM Template hosting migration
* #3617: [hosting] Fixed Password reset confirmation page style bug
* #3408: [hosting] Changed background image of signup/login background into smaller size
* #3621: [hosting] Fixed signup/login/password reset page navbar logo overlapping with form
* #3354: [hosting] Restyled modal
* #3638: [hosting] Added “download” btn on generated key list for generated keys from upload your key page
* #3655: [hosting] Disabled deleting SSH keys from other users
* #3619: [datacenterlight, hosting] Replaced 'Lato-Light' and 'Lato-Regular' with only Lato with appropriate font-weights
* #3677: [hosting] Added wrapping for show SSH key modal text
* #3683: [hosting] Fixed footer floating bug on VM creating page
* #3676: [datacenterlight, hosting] Added missing card holder's name field migration
1.0.22: 2017-07-30
* #3593: [datacenterlight] Removed underbars between social icons in index
* #3509: [datacenterlight, hosting] Made navbar transparent and removed mobile navbar bug in login/signup/reset-password

View File

@ -4,10 +4,10 @@
* For details, see http://www.apache.org/licenses/LICENSE-2.0.
*/
@font-face {
/*@font-face {
font-family: 'Lato-Light';
src: url('../fonts/Lato/Lato-Light.ttf');
}
}*/
body,
html {
@ -22,7 +22,12 @@ h3,
h4,
h5,
h6 {
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-family: 'Lato', sans-serif;
font-weight: 300;
}
button, input, optgroup, select, textarea {
font-weight: 300;
}
@ -143,13 +148,15 @@ h6 {
.navbar-default .navbar-nav>li>a {
cursor: pointer;
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
}
.navbar-transparent .navbar-nav>li>a {
color: #fff;
cursor: pointer;
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
}
.navbar-transparent .navbar-nav>li>a:hover {
@ -202,13 +209,15 @@ h6 {
.navbar-transparent .nav-language .select-language {
color: #fff;
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
}
.nav-language .select-language span {
margin-left: 5px;
margin-right: 5px;
font-family: 'Lato', sans-serif;
/*font-family: 'Lato', sans-serif;*/
font-weight: normal;
}
.nav-language .drop-language{
/*position: absolute;*/
@ -237,7 +246,8 @@ h6 {
.nav-language .drop-language a{
cursor: pointer;
padding: 5px 10px !important;
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
}
/* Show the dropdown menu on hover */
@ -260,7 +270,8 @@ h6 {
.navbar-transparent .nav-language .drop-language a {
color: #fff;
padding: 5px 10px !important;
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
}
/* .nav-language:hover .drop-language{
display: block;
@ -343,7 +354,7 @@ h6 {
.intro-message>h1 {
margin: 0;
font-weight: 400;
font-weight: 300;
font-size: 6em;
}
@ -792,7 +803,8 @@ tech-sub-sec h2 {
}
.percent-text {
font-family: 'Lato', sans-serif;
/*font-family: 'Lato', sans-serif;*/
/* font-weight: normal; */
font-size: 50px;
color: #999;
}
@ -879,7 +891,7 @@ tech-sub-sec h2 {
.dropdown-menu>li>a {
font-size: 13px;
font-weight: 300;
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
}
.navbar-default .navbar-nav>.active>a,
@ -898,7 +910,8 @@ tech-sub-sec h2 {
background: -webkit-linear-gradient(top, #f0f4f7, #fff) no-repeat;
background: linear-gradient(to bottom, #f0f4f7, #fff) no-repeat;
display: flex;
font-family: 'Lato', sans-serif;
/*font-family: 'Lato', sans-serif;*/
/* font-weight: normal; */
}
.price-calc-section .text {
@ -963,7 +976,8 @@ tech-sub-sec h2 {
}
.price-calc-section .card .title h3 {
font-family: 'Lato', sans-serif;
/*font-family: 'Lato', sans-serif;*/
font-weight: normal;
}
.price-calc-section .card .price {
@ -1050,8 +1064,9 @@ tech-sub-sec h2 {
.price-calc-section .card .description.input label {
font-size: 15px;
font-weight: 800;
font-family: 'Lato';
font-weight: 700;
/*font-weight: 800;*/
/*font-family: 'Lato';*/
margin-bottom: 0;
width: 40px;
}
@ -1364,7 +1379,8 @@ tech-sub-sec h2 {
padding: 30px;
}
.percent-text {
font-family: 'Lato';
/*font-family: 'Lato';*/
font-weight: normal;
font-size: 37px;
/* text-align: center; */
}
@ -1402,7 +1418,7 @@ tech-sub-sec h2 {
.network-name {
text-transform: uppercase;
font-size: 14px;
font-weight: 400;
font-weight: 300;
letter-spacing: 2px;
}

View File

@ -4,6 +4,7 @@ from .forms import BetaAccessForm
from .models import BetaAccess, BetaAccessVMType, BetaAccessVM, VMTemplate
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.core.mail import EmailMessage
from utils.mailer import BaseEmail
from django.shortcuts import render
from django.shortcuts import redirect
@ -12,12 +13,14 @@ from django.core.exceptions import ValidationError
from django.views.decorators.cache import cache_control
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from utils.forms import BillingAddressForm
from hosting.models import HostingOrder
from utils.forms import BillingAddressForm, UserBillingAddressForm
from utils.models import BillingAddress
from hosting.models import HostingOrder, HostingBill
from utils.stripe_utils import StripeUtils
from datetime import datetime
from membership.models import CustomUser, StripeCustomer
from opennebula_api.models import OpenNebulaManager
from opennebula_api.serializers import VirtualMachineTemplateSerializer, VMTemplateSerializer
from opennebula_api.serializers import VirtualMachineTemplateSerializer, VirtualMachineSerializer, VMTemplateSerializer
from datacenterlight.tasks import create_vm_task
@ -423,6 +426,10 @@ class OrderConfirmationView(DetailView):
customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
stripe_utils = StripeUtils()
card_details = stripe_utils.get_card_details(customer.stripe_id, request.session.get('token'))
if not card_details.get('response_object') and not card_details.get('paid'):
msg = card_details.get('error')
messages.add_message(self.request, messages.ERROR, msg, extra_tags='failed_payment')
return HttpResponseRedirect(reverse('datacenterlight:payment') + '#payment_error')
context = {
'site_url': reverse('datacenterlight:index'),
'cc_last4': card_details.get('response_object').get('last4'),
@ -438,6 +445,8 @@ class OrderConfirmationView(DetailView):
customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
billing_address_data = request.session.get('billing_address_data')
billing_address_id = request.session.get('billing_address')
billing_address = BillingAddress.objects.filter(
id=billing_address_id).first()
vm_template_id = template.get('id', 1)
final_price = specs.get('price')
@ -445,15 +454,12 @@ class OrderConfirmationView(DetailView):
stripe_utils = StripeUtils()
charge_response = stripe_utils.make_charge(amount=final_price,
customer=customer.stripe_id)
charge = charge_response.get('response_object')
# Check if the payment was approved
if not charge:
context = {}
context.update({
'paymentError': charge_response.get('error')
})
return render(request, self.payment_template_name, context)
if not charge_response.get('response_object') and not charge_response.get('paid'):
msg = charge_response.get('error')
messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error')
return HttpResponseRedirect(reverse('datacenterlight:payment') + '#payment_error')
charge = charge_response.get('response_object')
create_vm_task.delay(vm_template_id, user, specs, template, stripe_customer_id, billing_address_data,

View File

@ -523,6 +523,9 @@ OPENNEBULA_PORT = env('OPENNEBULA_PORT')
# default value is /RPC2
OPENNEBULA_ENDPOINT = env('OPENNEBULA_ENDPOINT')
# The public ssh key of the oneadmin user
ONEADMIN_USER_SSH_PUBLIC_KEY = env('ONEADMIN_USER_SSH_PUBLIC_KEY')
# dcl email configurations
DCL_TEXT = env('DCL_TEXT')
DCL_SUPPORT_FROM_ADDRESS = env('DCL_SUPPORT_FROM_ADDRESS')

View File

@ -14,7 +14,6 @@ def generate_ssh_key_name():
class HostingUserLoginForm(forms.Form):
email = forms.CharField(widget=forms.EmailInput())
password = forms.CharField(widget=forms.PasswordInput())
@ -45,7 +44,6 @@ class HostingUserLoginForm(forms.Form):
class HostingUserSignupForm(forms.ModelForm):
confirm_password = forms.CharField(widget=forms.PasswordInput())
password = forms.CharField(widget=forms.PasswordInput())
@ -88,9 +86,8 @@ class UserHostingKeyForm(forms.ModelForm):
def clean(self):
cleaned_data = self.cleaned_data
if not self.cleaned_data.get('name', ''):
if 'generate' in self.request.POST:
self.cleaned_data['name'] = generate_ssh_key_name()
if not cleaned_data.get('public_key'):
private_key, public_key = UserHostingKey.generate_keys()
cleaned_data.update({
'private_key': private_key,

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-08-04 18:25+0000\n"
"POT-Creation-Date: 2017-08-11 01:16+0530\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -393,14 +393,14 @@ msgstr ""
msgid "Delete SSH Key"
msgstr "SSH Key löschen"
msgid "Do You want to delete this key?"
msgid "Do you want to delete this key?"
msgstr "Möchtest Du den Schlüssel löschen?"
msgid "Show"
msgstr "Anzeigen"
msgid "Public ssh key"
msgstr ""
msgid "Public SSH Key"
msgstr "Public SSH Key"
msgid "Download"
msgstr ""

View File

@ -7,7 +7,7 @@
}
.content-dashboard{
min-height: calc(100vh - 120px);
min-height: calc(100vh - 70px);
width: 80%;
margin: 0 auto;
max-width: 1120px;
@ -110,12 +110,16 @@
font-weight: 100;
color: #999;
}
.modal-body .modal-icon {
margin-bottom: 10px;
}
.modal-title {
margin: 0;
line-height: 1.42857143;
font-size: 25px;
padding: 0;
font-family: 'Lato', sans-serif;
/*font-family: 'Lato', sans-serif;*/
font-weight: 300;
}
.modal-text {
padding-top: 15px;

View File

@ -4,7 +4,7 @@
* For details, see http://www.apache.org/licenses/LICENSE-2.0.
*/
@font-face {
/*@font-face {
font-family: 'Lato-Regular';
src: url('../fonts/Lato/Lato-Regular.ttf');
}
@ -16,7 +16,7 @@
@font-face {
font-family: 'Lato-Light';
src: url('../fonts/Lato/Lato-Light.ttf');
}
}*/
body,
html {
@ -31,8 +31,9 @@ h3,
h4,
h5,
h6 {
font-family: 'Lato-Regular', sans-serif;
font-weight: 300;
/*font-family: 'Lato-Regular', sans-serif;*/
font-family: 'Lato', sans-serif;
/*font-weight: 300;*/
}
.topnav {
@ -53,7 +54,8 @@ h6 {
.navbar-transparent .navbar-nav>li>a {
color: #fff;
cursor: pointer;
font-family: 'Lato-Regular', sans-serif;
/*font-family: 'Lato-Regular', sans-serif;*/
font-weight: normal;
}
.navbar-transparent .navbar-nav>li>a:hover {
color: #fff;
@ -376,7 +378,8 @@ h6 {
text-align: center;
font-size: 18px;
line-height: 30px;
font-family: 'Lato' !important;
/*font-family: 'Lato' !important;*/
font-weight: 300 !important;
}
.sign-up-message a {
@ -454,16 +457,16 @@ h6 {
}
footer {
padding: 2%;
padding: 20px;
background-color: #f8f8f8;
#position: absolute;
/* position: absolute */
right: 0;
bottom: 0;
left: 0;
}
p.copyright {
margin: 15px 0 0;
margin: 14px 0 0;
}
a#forgotpassword {
@ -488,7 +491,8 @@ a.unlink:hover {
/***** DCL payment page **********/
.dcl-order-container {
font-family: Lato;
/*font-family: Lato;*/
font-weight: 300;
}
.dcl-order-table-header {
@ -547,11 +551,16 @@ a.unlink:hover {
}
.card-warning-content {
font-family: Lato;
/*font-family: Lato;*/
font-weight: 300;
border: 1px solid #a1a1a1;
border-radius: 3px;
padding: 5px;
}
.card-warning-error {
border: 1px solid #EB4D5C;
color: #EB4D5C;
}
.card-warning-addtional-margin {
margin-top: 15px;

View File

@ -9,7 +9,7 @@
font-size: 14px;
padding-left: 0;
margin-bottom: 30px;
font-family: 'Lato';
font-family: Lato, sans-serif;
}

View File

@ -1,6 +1,6 @@
/* ssh_keys_choice */
.h1-thin {
font-family: Lato, sans-serif;
/*font-family: Lato, sans-serif;*/
font-weight: 300;
font-size: 32px;
}
@ -10,12 +10,12 @@
}
.dashboard-choice-container .page-header p {
font-size: 16px;
font-family: Lato, sans-serif;
/*font-family: Lato, sans-serif;*/
font-weight: 300;
}
.dashboard-choice-container h2 {
font-family: Lato, sans-serif;
font-weight: 400;
/*font-family: Lato, sans-serif;
font-weight: 400;*/
font-size: 22px;
margin-top: 0;
}
@ -26,7 +26,7 @@
}
.choice-container p{
font-size: 18px;
font-family: Lato, sans-serif;
/*font-family: Lato, sans-serif;*/
font-weight: 300;
}
.choice-container-top {
@ -119,7 +119,7 @@
color: #717274;
font-size: 16px;
font-weight: 300;
font-family: 'Lato';
/*font-family: 'Lato';*/
}
.borderless tbody:before {
@ -195,7 +195,8 @@
border-bottom: 1px solid grey;
box-shadow: none;
border-radius: 0;
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
font-size: 20px;
padding-left: 0;
}
@ -203,57 +204,58 @@
.form_key_name::-webkit-input-placeholder{
font-size: 20px;
font-weight:100;
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
}
.form_key_name::-moz-input-placeholder{
font-size: 20px;
font-weight:200;
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
}
.form_key_name:-moz-input-placeholder{
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
font-size: 20px;
font-weight:200;
}
.form_key_name:-ms-input-placeholder {
font-size: 20px;
font-family: 'Lato-Light', sans-serif;
font-weight:200;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
}
.form_public_key::-webkit-input-placeholder{
position: relative;
top: 110px;
font-size: 20px;
font-weight: 200;
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
}
.form_public_key::-moz-input-placeholder{
position: relative;
top: 110px;
font-size: 20px;
font-family: 'Lato-Light', sans-serif;
font-weight:200;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
}
.form_public_key:-moz-input-placeholder{
position: relative;
top: 110px;
font-size: 20px;
font-weight:200;
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
}
.form_public_key:-ms-input-placeholder {
position: relative;
top: 110px;
font-size: 20px;
font-weight:200;
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
}
.underform-contaner{
margin-bottom: 20px;
@ -273,7 +275,8 @@
}
}
.underform-contaner h4{
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
}
.underform-contaner button{
/* font-family: Lato; */
@ -287,13 +290,16 @@
color: #fff;
}
.control-label{
font-family: 'Lato-Light', sans-serif;
/*font-family: 'Lato-Light', sans-serif;*/
font-weight: 300;
font-size: 20px;
font-weight:200;
}
.form-ssh h3{
margin-bottom: 40px;
}
.key_contain {
word-break: break-all;
}
.custom_form_button{
border-radius: 0;
}

View File

@ -59,7 +59,6 @@
{% csrf_token %}
{% bootstrap_field field show_label=False type='fields'%}
{% endfor %}
{% bootstrap_form_errors form type='non_fields'%}
</form>
</div>
<div class="col-xs-12 col-sm-7 col-md-6 creditcard-box dcl-creditcard">
@ -86,13 +85,29 @@
</form>
<div class="row">
<div class="col-xs-12">
<p class="card-warning-content card-warning-addtional-margin">
{% blocktrans %}
You are not making any payment yet. After submitting your card
information, you will be taken to the Confirm Order Page.
{% endblocktrans %}
</p>
</div>
{% if not messages and not form.non_field_errors %}
<p class="card-warning-content card-warning-addtional-margin">
{% blocktrans %}
You are not making any payment yet. After submitting your card
information, you will be taken to the Confirm Order Page.
{% endblocktrans %}
</p>
{% endif %}
<div id='payment_error'>
{% for message in messages %}
{% if 'failed_payment' or 'make_charge_error' in message.tags %}
<ul class="list-unstyled"><li>
<p class="card-warning-content card-warning-error">{{ message|safe }}</p>
</li></ul>
{% endif %}
{% endfor %}
{% for error in form.non_field_errors %}
<p class="card-warning-content card-warning-error">
{{ error|escape }}
</p>
{% endfor %}
</div>
</div>
<div class="col-xs-12">
<div class="col-xs-6 pull-right">
<button id="payment_button_with_creditcard" class="btn btn-success stripe-payment-btn"
@ -130,12 +145,29 @@
<div id="card-errors" role="alert"></div>
<div class="row">
<div class="col-xs-12">
<p class="card-warning-content">
{% blocktrans %}
You are not making any payment yet. After submitting your card
information, you will be taken to the Confirm Order Page.
{% endblocktrans %}
</p>
{% if not messages and not form.non_field_errors %}
<p class="card-warning-content">
{% blocktrans %}
You are not making any payment yet. After submitting your card
information, you will be taken to the Confirm Order Page.
{% endblocktrans %}
</p>
{% endif %}
<div id='payment_error'>
{% for message in messages %}
{% if 'failed_payment' or 'make_charge_error' in message.tags %}
<ul class="list-unstyled"><li>
<p class="card-warning-content card-warning-error">{{ message|safe }}</p>
</li></ul>
{% endif %}
{% endfor %}
{% for error in form.non_field_errors %}
<p class="card-warning-content card-warning-error">
{{ error|escape }}
</p>
{% endfor %}
</div>
</div>
<div class="col-xs-12">
<div class="col-xs-6 pull-right">
@ -150,15 +182,6 @@
<p class="payment-errors"></p>
</div>
</div>
{% if paymentError %}
<div class="row">
<div class="col-xs-12">
<p>
{% bootstrap_alert paymentError alert_type='danger' %}
</p>
</div>
</div>
{% endif %}
</form>
{% endif %}

View File

@ -50,7 +50,7 @@
<div class="modal-body">
<div class="modal-icon"><i class="fa fa-trash" aria-hidden="true"></i></div>
<h4 class="modal-title" id="ModalLabel">{% trans "Delete SSH Key"%}</h4>
<p class="modal-text">{% trans "Do You want to delete this key?"%}</p>
<p class="modal-text">{% trans "Do you want to delete this key?"%}</p>
<form method="post" action="{% url 'hosting:delete_ssh_key' user_key.id %}">
{% csrf_token %}
<div class="modal-footer">
@ -77,8 +77,8 @@
</button>
</div>
<div class="modal-body">
<h4 class="modal-title" id="ModalLabel_Public_Key">{% trans "Public ssh key" %}</h4>
<p style="margin-top: 10px;">{{ user_key.public_key }}</p>
<h4 class="modal-title" id="ModalLabel_Public_Key">{% trans "Public SSH key" %}</h4>
<p class="key_contain" style="margin-top: 10px;">{{ user_key.public_key }}</p>
<div class="modal-footer">
</div>
</div>

View File

@ -1,12 +1,12 @@
{% extends "hosting/base_short.html" %}
{% load staticfiles bootstrap3 i18n %}
{% block content %}
{% block content %}
<div>
<div class="dashboard-container">
<div class="row">
<div class="col-xs-12 container-table">
<table class="table borderless table-hover">
<h3 class="pull-left"><i class="fa fa-server fa-separate" aria-hidden="true"></i> {% trans "Virtual Machines"%} </h3>
<table class="table borderless table-hover">
<h3 class="pull-left"><i class="fa fa-server fa-separate" aria-hidden="true"></i> {% trans "Virtual Machines"%}</h3>
<div class="col-md-12">
<br/>
{% if messages %}
@ -19,12 +19,12 @@
</div>
{% if not error %}
<p class="pull-right btn-create-vm">
<a class="btn btn-success" href="{% url 'hosting:create_virtual_machine' %}" >{% trans "Create VM"%} </a>
<a class="btn btn-success" href="{% url 'hosting:create_virtual_machine' %}" >{% trans "Create VM"%} </a>
</p>
<br/>
<thead>
<tr>
<thead>
<tr>
<th>{% trans "ID"%}</th>
<th>{% trans "Ipv4"%}</th>
<th>{% trans "Ipv6"%}</th>
@ -32,36 +32,36 @@
<th></th>
</tr>
</thead>
<tbody>
<tbody>
{% for vm in vms %}
<tr>
<td scope="row">{{vm.vm_id}}</td>
<td scope="row">{{vm.vm_id}}</td>
{% if vm.ipv6 %}
<td>{{vm.ipv4}}</td>
<td>{{vm.ipv6}}</td>
<td>{{vm.ipv4}}</td>
<td>{{vm.ipv6}}</td>
{% endif %}
<td>
{% if vm.state == 'ACTIVE' %}
<span class="h3 label label-success"><strong> {{vm.state}}</strong></span>
{% elif vm.state == 'FAILED' %}
<span class="h3 label label-danger"><strong>{{vm.state}}</strong></span>
{% else %}
<span class="h3 label label-warning"><strong>{{vm.state}}</strong></span>
{% endif %}
{% endif %}
</td>
</td>
<td>
<button type="button" class="btn btn-default"><a
href="{% url 'hosting:virtual_machines' vm.vm_id %}">{% trans "View Detail"%}</a></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</tbody>
{% endif %}
</table>
{% if is_paginated %}
<div class="pagination">
@ -78,7 +78,7 @@
</span>
</div>
{% endif %}
</div>
</div>

View File

@ -210,9 +210,9 @@ class SignupValidateView(TemplateView):
def get_context_data(self, **kwargs):
context = super(SignupValidateView, self).get_context_data(**kwargs)
login_url = '<a href="' + \
reverse('hosting:login') + '">' + str(_('login')) + '</a>'
reverse('hosting:login') + '">' + str(_('login')) + '</a>'
home_url = '<a href="' + \
reverse('datacenterlight:index') + '">Data Center Light</a>'
reverse('datacenterlight:index') + '">Data Center Light</a>'
message = '{signup_success_message} {lurl}</a> \
<br />{go_back} {hurl}.'.format(
signup_success_message=_(
@ -234,7 +234,7 @@ class SignupValidatedView(SignupValidateView):
context = super(SignupValidateView, self).get_context_data(**kwargs)
validated = CustomUser.validate_url(self.kwargs['validate_slug'])
login_url = '<a href="' + \
reverse('hosting:login') + '">' + str(_('login')) + '</a>'
reverse('hosting:login') + '">' + str(_('login')) + '</a>'
section_title = _('Account activation')
if validated:
message = '{account_activation_string} <br /> {login_string} {lurl}.'.format(
@ -244,7 +244,7 @@ class SignupValidatedView(SignupValidateView):
lurl=login_url)
else:
home_url = '<a href="' + \
reverse('datacenterlight:index') + '">Data Center Light</a>'
reverse('datacenterlight:index') + '">Data Center Light</a>'
message = '{sorry_message} <br />{go_back_to} {hurl}'.format(
sorry_message=_("Sorry. Your request is invalid."),
go_back_to=_('Go back to'),
@ -342,6 +342,15 @@ class SSHKeyDeleteView(LoginRequiredMixin, DeleteView):
success_url = reverse_lazy('hosting:ssh_keys')
model = UserHostingKey
def get_object(self, queryset=None):
""" Hook to ensure UserHostingKey object is owned by request.user.
We reply with a Http404 if the user is not the owner of the key.
"""
obj = super(SSHKeyDeleteView, self).get_object()
if not obj.user == self.request.user:
raise Http404
return obj
def delete(self, request, *args, **kwargs):
owner = self.request.user
manager = OpenNebulaManager()
@ -547,8 +556,9 @@ class PaymentVMView(LoginRequiredMixin, FormView):
customer = StripeCustomer.get_or_create(email=owner.email,
token=token)
if not customer:
form.add_error("__all__", "Invalid credit card")
return self.render_to_response(self.get_context_data(form=form))
msg = _("Invalid credit card")
messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error')
return HttpResponseRedirect(reverse('hosting:payment') + '#payment_error')
# Create Billing Address
billing_address = form.save()
@ -557,15 +567,12 @@ class PaymentVMView(LoginRequiredMixin, FormView):
stripe_utils = StripeUtils()
charge_response = stripe_utils.make_charge(amount=final_price,
customer=customer.stripe_id)
charge = charge_response.get('response_object')
# Check if the payment was approved
if not charge:
context.update({
'paymentError': charge_response.get('error'),
'form': form
})
return render(request, self.template_name, context)
if not charge_response.get('response_object') and not charge_response.get('paid'):
msg = charge_response.get('error')
messages.add_message(self.request, messages.ERROR, msg, extra_tags='make_charge_error')
return HttpResponseRedirect(reverse('hosting:payment') + '#payment_error')
charge = charge_response.get('response_object')

View File

@ -19,7 +19,7 @@ REGISTRATION_MESSAGE = {'subject': "Validation mail",
'from': 'test@test.com'}
def get_anonymous_user_instance():
def get_anonymous_user_instance(CustomUser):
return CustomUser(name='Anonymous', email='anonymous@ungleich.ch',
validation_slug=make_password(None))
@ -173,7 +173,6 @@ class StripeCustomer(models.Model):
Check if there is a registered stripe customer with that email
or create a new one
"""
stripe_customer = None
try:
stripe_utils = StripeUtils()
stripe_customer = cls.objects.get(user__email=email)
@ -189,7 +188,7 @@ class StripeCustomer(models.Model):
user = CustomUser.objects.get(email=email)
stripe_utils = StripeUtils()
stripe_data = stripe_utils.create_customer(token, email)
stripe_data = stripe_utils.create_customer(token, email, user.name)
if stripe_data.get('response_object'):
stripe_cus_id = stripe_data.get('response_object').get('id')

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-08-10 17:42
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('utils', '0005_auto_20170322_1443'),
]
operations = [
migrations.AddField(
model_name='billingaddress',
name='cardholder_name',
field=models.CharField(default='', max_length=100),
),
migrations.AddField(
model_name='userbillingaddress',
name='cardholder_name',
field=models.CharField(default='', max_length=100),
),
]

View File

@ -11,7 +11,7 @@ def handleStripeError(f):
'error': None
}
common_message = "Currently its not possible to make payments."
common_message = "Currently it's not possible to make payments."
try:
response_object = f(*args, **kwargs)
response = {
@ -90,12 +90,12 @@ class StripeUtils(object):
def check_customer(self, id, user, token):
customers = self.stripe.Customer.all()
if not customers.get('data'):
customer = self.create_customer(token, user.email)
customer = self.create_customer(token, user.email, user.name)
else:
try:
customer = stripe.Customer.retrieve(id)
except stripe.InvalidRequestError:
customer = self.create_customer(token, user.email)
customer = self.create_customer(token, user.email, user.name)
user.stripecustomer.stripe_id = customer.get('response_object').get('id')
user.stripecustomer.save()
return customer
@ -107,11 +107,12 @@ class StripeUtils(object):
return customer
@handleStripeError
def create_customer(self, token, email):
def create_customer(self, token, email, name=None):
if name is None or name.strip() == "":
name = email
customer = self.stripe.Customer.create(
source=token,
description='description for testing',
description=name,
email=email
)
return customer

View File

@ -3,6 +3,9 @@ from django.test import Client
from django.http.request import HttpRequest
from model_mommy import mommy
from utils.stripe_utils import StripeUtils
import stripe
from django.conf import settings
class BaseTestCase(TestCase):
@ -11,7 +14,6 @@ class BaseTestCase(TestCase):
"""
def setUp(self):
# Password
self.dummy_password = 'test_password'
@ -83,3 +85,35 @@ class BaseTestCase(TestCase):
view.kwargs = kwargs
view.config = None
return view
class TestStripeCustomerDescription(TestCase):
"""
A class to test setting the description field of the stripe customer
https://stripe.com/docs/api#metadata
"""
def setUp(self):
self.dummy_password = 'test_password'
self.dummy_email = 'test@ungleich.ch'
self.customer = mommy.make('membership.CustomUser')
self.customer.set_password(self.dummy_password)
self.customer.email = self.dummy_email
self.customer.save()
stripe.api_key = settings.STRIPE_API_PRIVATE_KEY
def test_creating_stripe_customer(self):
test_name = "Monty Python"
token = stripe.Token.create(
card={
"number": '4111111111111111',
"exp_month": 12,
"exp_year": 2022,
"cvc": '123'
},
)
stripe_utils = StripeUtils()
stripe_data = stripe_utils.create_customer(token.id, self.customer.email, test_name)
self.assertEqual(stripe_data.get('error'), None)
customer_data = stripe_data.get('response_object')
self.assertEqual(customer_data.description, test_name)