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

View File

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

View File

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

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-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" "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"
@ -393,14 +393,14 @@ msgstr ""
msgid "Delete SSH Key" msgid "Delete SSH Key"
msgstr "SSH Key löschen" 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?" msgstr "Möchtest Du den Schlüssel löschen?"
msgid "Show" msgid "Show"
msgstr "Anzeigen" msgstr "Anzeigen"
msgid "Public ssh key" msgid "Public SSH Key"
msgstr "" msgstr "Public SSH Key"
msgid "Download" msgid "Download"
msgstr "" msgstr ""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -50,7 +50,7 @@
<div class="modal-body"> <div class="modal-body">
<div class="modal-icon"><i class="fa fa-trash" aria-hidden="true"></i></div> <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> <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 %}"> <form method="post" action="{% url 'hosting:delete_ssh_key' user_key.id %}">
{% csrf_token %} {% csrf_token %}
<div class="modal-footer"> <div class="modal-footer">
@ -77,8 +77,8 @@
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<h4 class="modal-title" id="ModalLabel_Public_Key">{% trans "Public ssh key" %}</h4> <h4 class="modal-title" id="ModalLabel_Public_Key">{% trans "Public SSH key" %}</h4>
<p style="margin-top: 10px;">{{ user_key.public_key }}</p> <p class="key_contain" style="margin-top: 10px;">{{ user_key.public_key }}</p>
<div class="modal-footer"> <div class="modal-footer">
</div> </div>
</div> </div>

View File

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

View File

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

View File

@ -19,7 +19,7 @@ REGISTRATION_MESSAGE = {'subject': "Validation mail",
'from': 'test@test.com'} 'from': 'test@test.com'}
def get_anonymous_user_instance(): def get_anonymous_user_instance(CustomUser):
return CustomUser(name='Anonymous', email='anonymous@ungleich.ch', return CustomUser(name='Anonymous', email='anonymous@ungleich.ch',
validation_slug=make_password(None)) validation_slug=make_password(None))
@ -173,7 +173,6 @@ class StripeCustomer(models.Model):
Check if there is a registered stripe customer with that email Check if there is a registered stripe customer with that email
or create a new one or create a new one
""" """
stripe_customer = None
try: try:
stripe_utils = StripeUtils() stripe_utils = StripeUtils()
stripe_customer = cls.objects.get(user__email=email) stripe_customer = cls.objects.get(user__email=email)
@ -189,7 +188,7 @@ class StripeCustomer(models.Model):
user = CustomUser.objects.get(email=email) user = CustomUser.objects.get(email=email)
stripe_utils = StripeUtils() 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'): if stripe_data.get('response_object'):
stripe_cus_id = stripe_data.get('response_object').get('id') 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 'error': None
} }
common_message = "Currently its not possible to make payments." common_message = "Currently it's not possible to make payments."
try: try:
response_object = f(*args, **kwargs) response_object = f(*args, **kwargs)
response = { response = {
@ -90,12 +90,12 @@ class StripeUtils(object):
def check_customer(self, id, user, token): def check_customer(self, id, user, token):
customers = self.stripe.Customer.all() customers = self.stripe.Customer.all()
if not customers.get('data'): if not customers.get('data'):
customer = self.create_customer(token, user.email) customer = self.create_customer(token, user.email, user.name)
else: else:
try: try:
customer = stripe.Customer.retrieve(id) customer = stripe.Customer.retrieve(id)
except stripe.InvalidRequestError: 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.stripe_id = customer.get('response_object').get('id')
user.stripecustomer.save() user.stripecustomer.save()
return customer return customer
@ -107,11 +107,12 @@ class StripeUtils(object):
return customer return customer
@handleStripeError @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( customer = self.stripe.Customer.create(
source=token, source=token,
description='description for testing', description=name,
email=email email=email
) )
return customer return customer

View File

@ -3,6 +3,9 @@ from django.test import Client
from django.http.request import HttpRequest from django.http.request import HttpRequest
from model_mommy import mommy from model_mommy import mommy
from utils.stripe_utils import StripeUtils
import stripe
from django.conf import settings
class BaseTestCase(TestCase): class BaseTestCase(TestCase):
@ -11,7 +14,6 @@ class BaseTestCase(TestCase):
""" """
def setUp(self): def setUp(self):
# Password # Password
self.dummy_password = 'test_password' self.dummy_password = 'test_password'
@ -83,3 +85,35 @@ class BaseTestCase(TestCase):
view.kwargs = kwargs view.kwargs = kwargs
view.config = None view.config = None
return view 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)