Merged master into task/3622/decouple_opennebula_dcl_flow
This commit is contained in:
commit
6b7ae88f74
19 changed files with 296 additions and 142 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -35,3 +35,4 @@ secret-key
|
|||
|
||||
.env
|
||||
*.mo
|
||||
*.log
|
||||
|
|
23
Changelog
23
Changelog
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 ""
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
font-size: 14px;
|
||||
padding-left: 0;
|
||||
margin-bottom: 30px;
|
||||
font-family: 'Lato';
|
||||
font-family: Lato, sans-serif;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
25
utils/migrations/0006_auto_20170810_1742.py
Normal file
25
utils/migrations/0006_auto_20170810_1742.py
Normal 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),
|
||||
),
|
||||
]
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue