fix merge conflict

This commit is contained in:
Tomislav R 2016-04-28 19:47:44 +02:00
commit 69f9f4a416
28 changed files with 1244 additions and 438 deletions

View file

@ -6,8 +6,8 @@ from .views import ContactView, IndexView, AboutView
urlpatterns = [ urlpatterns = [
# url(r'^$', IndexView.as_view(), name='home'), # url(r'^$', IndexView.as_view(), name='home'),
# url(_(r'home/?$'), IndexView.as_view(), name='home'), url(_(r'home/?$'), IndexView.as_view(), name='home'),
# url(_(r'about/?$'), AboutView.as_view(), name='about'), url(_(r'about/?$'), AboutView.as_view(), name='about'),
url(_(r'contact/?$'), ContactView.as_view(), name='contact'), url(_(r'contact/?$'), ContactView.as_view(), name='contact'),
url(_(r'supporters/?$'), views.supporters, name='supporters'), url(_(r'supporters/?$'), views.supporters, name='supporters'),
url(r'calendar_api/(?P<month>\d+)/(?P<year>\d+)?$',views.CalendarApi.as_view()), url(r'calendar_api/(?P<month>\d+)/(?P<year>\d+)?$',views.CalendarApi.as_view()),

View file

@ -30,6 +30,7 @@ dotenv.read_dotenv("{0}/.env".format(PROJECT_DIR))
SITE_ID = 1 SITE_ID = 1
APP_ROOT_ENDPOINT = "/" APP_ROOT_ENDPOINT = "/"
APPEND_SLASH=True
LOGIN_URL = None LOGIN_URL = None
LOGOUT_URL = None LOGOUT_URL = None
@ -445,8 +446,8 @@ AUTH_USER_MODEL = 'membership.CustomUser'
# PAYMENT # PAYMENT
STRIPE_API_PUBLIC_KEY = 'pk_test_uvWyHNJgVL2IB8kjfgJkGjg4' # used in frontend to call from user browser STRIPE_API_PUBLIC_KEY = 'pk_test_QqBZ50Am8KOxaAlOxbcm9Psl' # used in frontend to call from user browser
STRIPE_API_PRIVATE_KEY = 'sk_test_uIPMdgXoRGydrcD7fkwcn7dj' # used in backend payment STRIPE_API_PRIVATE_KEY = 'sk_test_dqAmbKAij12QCGfkYZ3poGt2' # used in backend payment
STRIPE_DESCRIPTION_ON_PAYMENT = "Payment for ungleich GmbH services" STRIPE_DESCRIPTION_ON_PAYMENT = "Payment for ungleich GmbH services"
# EMAIL MESSAGES # EMAIL MESSAGES

View file

@ -5,12 +5,14 @@ from django.conf.urls.i18n import i18n_patterns
from django.conf.urls.static import static from django.conf.urls.static import static
from django.conf import settings from django.conf import settings
from hosting.views import RailsHostingView from hosting.views import RailsHostingView, DjangoHostingView, NodeJSHostingView
from membership import urls as membership_urls from membership import urls as membership_urls
urlpatterns = [ urlpatterns = [
url(r'^hosting/', include('hosting.urls', namespace="hosting")), url(r'^hosting/', include('hosting.urls', namespace="hosting")),
url(r'^railshosting/', RailsHostingView.as_view(), name="rails.hosting"), url(r'^railshosting/', RailsHostingView.as_view(), name="rails.hosting"),
url(r'^nodehosting/', NodeJSHostingView.as_view(), name="node.hosting"),
url(r'^djangohosting/', DjangoHostingView.as_view(), name="django.hosting"),
url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')), url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')),
url(r'^jsi18n/(?P<packages>\S+?)/$', url(r'^jsi18n/(?P<packages>\S+?)/$',
'django.views.i18n.javascript_catalog'), 'django.views.i18n.javascript_catalog'),
@ -18,16 +20,15 @@ urlpatterns = [
# note the django CMS URLs included via i18n_patterns # note the django CMS URLs included via i18n_patterns
urlpatterns += i18n_patterns('', urlpatterns += i18n_patterns('',
# url(r'^$',include('ungleich.urls')),
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
url(r'^digitalglarus/login/', include(membership_urls)), url(r'^digitalglarus/login/', include(membership_urls)),
url(r'^digitalglarus/', include('digitalglarus.urls', url(r'^digitalglarus/', include('digitalglarus.urls',
namespace="digitalglarus"), namespace="digitalglarus"),
name='digitalglarus'),
url(r'^blog/', include('ungleich.urls', namespace='ungleich')), url(r'^blog/', include('ungleich.urls', namespace='ungleich')),
url(r'^ungleich_page/', url(r'^ungleich_page/',
include('ungleich_page.urls', namespace='ungleich_page'), include('ungleich_page.urls', namespace='ungleich_page'),
name='ungleich_page'), name='ungleich_page'),
url(r'^blog/',include('ungleich.urls',namespace='ungleich')),
url(r'^', include('cms.urls')), url(r'^', include('cms.urls')),
) )

View file

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-04-23 07:10
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('hosting', '0007_auto_20160418_0103'),
]
operations = [
migrations.CreateModel(
name='VirtualMachinePlan',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('cores', models.IntegerField()),
('memory', models.IntegerField()),
('disk_size', models.IntegerField()),
('price', models.FloatField()),
('client', models.ManyToManyField(to=settings.AUTH_USER_MODEL)),
('vm_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hosting.VirtualMachineType')),
],
),
]

View file

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-04-26 04:44
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('utils', '0002_billingaddress'),
('membership', '0004_stripecustomer'),
('hosting', '0008_virtualmachineplan'),
]
operations = [
migrations.CreateModel(
name='HostingOrder',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
],
),
migrations.RemoveField(
model_name='virtualmachineplan',
name='client',
),
migrations.AddField(
model_name='hostingorder',
name='VMPlan',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hosting.VirtualMachinePlan'),
),
migrations.AddField(
model_name='hostingorder',
name='billing_address',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='utils.BillingAddress'),
),
migrations.AddField(
model_name='hostingorder',
name='customer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='membership.StripeCustomer'),
),
]

View file

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-04-26 05:30
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hosting', '0009_auto_20160426_0444'),
]
operations = [
migrations.AddField(
model_name='hostingorder',
name='approved',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='hostingorder',
name='stripe_charge_id',
field=models.CharField(max_length=100, null=True),
),
]

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-04-26 05:55
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('hosting', '0010_auto_20160426_0530'),
]
operations = [
migrations.AlterField(
model_name='hostingorder',
name='VMPlan',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='hosting.VirtualMachinePlan'),
),
]

20
hosting/mixins.py Normal file
View file

@ -0,0 +1,20 @@
from django.shortcuts import redirect
from django.core.urlresolvers import reverse
class ProcessVMSelectionMixin(object):
def post(self, request, *args, **kwargs):
vm_specs = {
'cores': request.POST.get('cores'),
'memory': request.POST.get('memory'),
'disk_size': request.POST.get('disk_space'),
'hosting_company': request.POST.get('hosting_company'),
'hosting_company_name': request.POST.get('hosting_company_name'),
'final_price': request.POST.get('final_price')
}
request.session['vm_specs'] = vm_specs
if not request.user.is_authenticated():
request.session['vm_specs'] = vm_specs
return redirect(reverse('hosting:login'))
return redirect(reverse('hosting:payment'))

View file

@ -1,7 +1,10 @@
import json
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core import serializers from django.core import serializers
import json from membership.models import StripeCustomer
from utils.models import BillingAddress
class RailsBetaUser(models.Model): class RailsBetaUser(models.Model):
@ -42,7 +45,13 @@ class VirtualMachineType(models.Model):
def get_serialized_vm_types(cls): def get_serialized_vm_types(cls):
return [vm.get_serialized_data() return [vm.get_serialized_data()
for vm in cls.objects.all()] for vm in cls.objects.all()]
# return serializers.serialize("json",)
def calculate_price(self, specifications):
price = float(specifications['cores']) * self.core_price
price += float(specifications['memory']) * self.memory_price
price += float(specifications['disk_size']) * self.disk_size_price
price += self.base_price
return price
def defeault_price(self): def defeault_price(self):
price = self.base_price price = self.base_price
@ -63,3 +72,51 @@ class VirtualMachineType(models.Model):
'default_price': self.defeault_price(), 'default_price': self.defeault_price(),
'id': self.id, 'id': self.id,
} }
class VirtualMachinePlan(models.Model):
cores = models.IntegerField()
memory = models.IntegerField()
disk_size = models.IntegerField()
vm_type = models.ForeignKey(VirtualMachineType)
price = models.FloatField()
@classmethod
def create(cls, data, user):
instance = cls.objects.create(**data)
return instance
class HostingOrder(models.Model):
VMPlan = models.OneToOneField(VirtualMachinePlan)
customer = models.ForeignKey(StripeCustomer)
billing_address = models.ForeignKey(BillingAddress)
created_at = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=False)
stripe_charge_id = models.CharField(max_length=100, null=True)
@classmethod
def create(cls, VMPlan=None, customer=None, billing_address=None):
instance = cls.objects.create(VMPlan=VMPlan, customer=customer,
billing_address=billing_address)
return instance
def set_approved(self):
self.approved = True
self.save()
def set_stripe_charge(self, stripe_charge):
self.stripe_charge_id = stripe_charge.id
self.save()

View file

@ -31,8 +31,7 @@ h6 {
} }
.intro-header { .intro-header {
height: 85%; padding-top: 50px; /* If you're making other pages, make sure there is 50px of padding to make sure the navbar doesn't overlap content! */
padding-top: 10%; /* If you're making other pages, make sure there is 50px of padding to make sure the navbar doesn't overlap content! */
padding-bottom: 50px; padding-bottom: 50px;
text-align: center; text-align: center;
color: #f8f8f8; color: #f8f8f8;
@ -48,8 +47,7 @@ h6 {
background-size: cover; background-size: cover;
} }
.intro-header-2 { .intro-header-2 {
height: 85%; padding-top: 50px; /* If you're making other pages, make sure there is 50px of padding to make sure the navbar doesn't overlap content! */
padding-top: 100px; /* If you're making other pages, make sure there is 50px of padding to make sure the navbar doesn't overlap content! */
padding-bottom: 50px; padding-bottom: 50px;
text-align: center; text-align: center;
color: #f8f8f8; color: #f8f8f8;
@ -61,10 +59,23 @@ h6 {
padding-top: 20%; padding-top: 20%;
padding-bottom: 20%; padding-bottom: 20%;
} }
.intro-signup {
.intro-auth {
text-align: center;
color: #f8f8f8;
position: relative; position: relative;
padding-top: 20%; padding-bottom: 25%;
padding-bottom: 20%; padding-top: 10%;
}
.intro-login {
background: url(../img/intro-bg.jpg) no-repeat center center;
background-size: cover;
}
.intro-signup {
background: url(../img/configure.jpg) no-repeat center center;
background-size: cover;
} }
.intro-message > h1 { .intro-message > h1 {
@ -203,3 +214,11 @@ a#forgotpassword {
font-weight: 700; font-weight: 700;
color: #6db97c; color: #6db97c;
} }
a.unlink {
color: inherit;
}
a.unlink:hover {
color: inherit;
}

View file

@ -0,0 +1,28 @@
.payment-container {padding-top:5%; padding-bottom: 11%;}
.creditcard-box .panel-title {display: inline;font-weight: bold; font-size:17px;}
.creditcard-box .checkbox.pull-right { margin: 0; }
.creditcard-box .pl-ziro { padding-left: 0px; }
.creditcard-box .form-control.error {
border-color: red;
outline: 0;
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(255,0,0,0.6);
}
.creditcard-box label.error {
font-weight: bold;
color: red;
padding: 2px 8px;
margin-top: 2px;
}
.creditcard-box .payment-errors {
font-weight: bold;
color: red;
padding: 2px 8px;
margin-top: 2px;
}
.summary-box .content {
padding-top: 15px;
}

View file

@ -0,0 +1,124 @@
$( document ).ready(function() {
$.ajaxSetup({
beforeSend: function(xhr, settings) {
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
var $form = $('#payment-form');
$form.submit(payWithStripe);
/* If you're using Stripe for payments */
function payWithStripe(e) {
e.preventDefault();
/* Visual feedback */
$form.find('[type=submit]').html('Validating <i class="fa fa-spinner fa-pulse"></i>');
var PublishableKey = window.stripeKey;
Stripe.setPublishableKey(PublishableKey);
Stripe.card.createToken($form, function stripeResponseHandler(status, response) {
if (response.error) {
/* Visual feedback */
$form.find('[type=submit]').html('Try again');
/* Show Stripe errors on the form */
$form.find('.payment-errors').text(response.error.message);
$form.find('.payment-errors').closest('.row').show();
} else {
/* Visual feedback */
$form.find('[type=submit]').html('Processing <i class="fa fa-spinner fa-pulse"></i>');
/* Hide Stripe errors on the form */
$form.find('.payment-errors').closest('.row').hide();
$form.find('.payment-errors').text("");
// response contains id and card, which contains additional card details
var token = response.id;
// AJAX
//set token on a hidden input
$('#id_token').val(token);
$('#billing-form').submit();
}
});
}
/* Form validation */
$.validator.addMethod("month", function(value, element) {
return this.optional(element) || /^(01|02|03|04|05|06|07|08|09|10|11|12)$/.test(value);
}, "Please specify a valid 2-digit month.");
$.validator.addMethod("year", function(value, element) {
return this.optional(element) || /^[0-9]{2}$/.test(value);
}, "Please specify a valid 2-digit year.");
validator = $form.validate({
rules: {
cardNumber: {
required: true,
creditcard: true,
digits: true
},
expMonth: {
required: true,
month: true
},
expYear: {
required: true,
year: true
},
cvCode: {
required: true,
digits: true
}
},
highlight: function(element) {
$(element).closest('.form-control').removeClass('success').addClass('error');
},
unhighlight: function(element) {
$(element).closest('.form-control').removeClass('error').addClass('success');
},
errorPlacement: function(error, element) {
$(element).closest('.form-group').append(error);
}
});
paymentFormReady = function() {
if ($form.find('[name=cardNumber]').hasClass("success") &&
$form.find('[name=expMonth]').hasClass("success") &&
$form.find('[name=expYear]').hasClass("success") &&
$form.find('[name=cvCode]').val().length > 1) {
return true;
} else {
return false;
}
}
$form.find('[type=submit]').prop('disabled', true);
var readyInterval = setInterval(function() {
if (paymentFormReady()) {
$form.find('[type=submit]').prop('disabled', false);
clearInterval(readyInterval);
}
}, 250);
});

View file

@ -8,8 +8,9 @@ $( document ).ready(function() {
function calculate_price(vm_type){ function calculate_price(vm_type){
var ID_SELECTOR = "#"; var ID_SELECTOR = "#";
var CURRENCY = "$"; var CURRENCY = "CHF";
var final_price_selector = ID_SELECTOR.concat(vm_type.concat('-final-price')); var final_price_selector = ID_SELECTOR.concat(vm_type.concat('-final-price'));
var final_price_input_selector = final_price_selector.concat('-input');
var core_selector = ID_SELECTOR.concat(vm_type.concat('-cores')); var core_selector = ID_SELECTOR.concat(vm_type.concat('-cores'));
var memory_selector = ID_SELECTOR.concat(vm_type.concat('-memory')); var memory_selector = ID_SELECTOR.concat(vm_type.concat('-memory'));
var disk_size_selector = ID_SELECTOR.concat(vm_type.concat('-disk_space')); var disk_size_selector = ID_SELECTOR.concat(vm_type.concat('-disk_space'));
@ -27,8 +28,9 @@ $( document ).ready(function() {
price += company_prices.memory_price*memory; price += company_prices.memory_price*memory;
price += company_prices.disk_size_price*disk_size; price += company_prices.disk_size_price*disk_size;
console.log(final_price_selector); console.log(final_price_input_selector);
$(final_price_selector).text(price.toString().concat(CURRENCY)); $(final_price_selector).text(price.toString().concat(CURRENCY));
$(final_price_input_selector).attr('value', price);
} }
@ -47,7 +49,5 @@ $( document ).ready(function() {
$('.disk-space-selector').on('change',change_attribute); $('.disk-space-selector').on('change',change_attribute);
console.log("mirame",window.VMTypesData);
}); });

View file

@ -0,0 +1,134 @@
{% load staticfiles bootstrap3%}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Payment</title>
<!-- Bootstrap Core CSS -->
<link href="{% static 'hosting/css/bootstrap.min.css' %}" rel="stylesheet">
<!-- Custom CSS -->
<link href="{% static 'hosting/css/landing-page.css' %}" rel="stylesheet">
<link href="{% static 'hosting/css/payment.css' %}" rel="stylesheet">
<!-- Custom Fonts -->
<link href='http://fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<link href="http://fonts.googleapis.com/css?family=Lato:300,400,700,300italic,400italic,700italic" rel="stylesheet" type="text/css">
<link rel="shortcut icon" href="img/favicon.ico" type="image/x-icon" />
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-default navbar-fixed-top topnav" role="navigation">
<div class="container topnav">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand topnav" href="#"><img src="img/logo_black.svg"></a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li>
<a href="#how">How it works</a>
</li>
<li>
<a href="#your">Your infrastructure</a>
</li>
<li>
<a href="#our">Our inftrastructure</a>
</li>
<li>
<a href="#price">Pricing</a>
</li>
<li>
<a href="#contact">Contact</a>
</li>
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container -->
</nav>
<!-- Header -->
<a name="about"></a>
{% block content %}
{% endblock %}
<!-- Footer -->
<footer>
<div class="container">
<div class="row">
<div class="col-lg-12">
<ul class="list-inline">
<li>
<a href="#">Home</a>
</li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a href="#about">How it works</a></li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a href="#about">Your infrastructure</a></li>
<li>&sdot;</li>
<li>
<a href="#about">Our infrastructure</a></li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a href="#services">Pricing</a>
</li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a href="#contact">Contact</a>
</li>
</ul>
<p class="copyright text-muted small">Copyright &copy; ungleich GmbH {% now "Y" %}. All Rights Reserved</p>
</div>
</div>
</div>
</footer>
<!-- jQuery -->
<script src="{% static 'hosting/js/jquery.js' %}"></script>
<script type="text/javascript" src="http://cdn.jsdelivr.net/jquery.validation/1.13.1/jquery.validate.min.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="{% static 'hosting/js/bootstrap.min.js' %}"></script>
<!-- Stripe Lib -->
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
<!-- Proccess payment lib -->
<script type="text/javascript" src="{% static 'hosting/js/payment.js' %}"></script>
</body>
</html>

View file

@ -1,5 +1,4 @@
{% load staticfiles %} {% load staticfiles %}
<a name="about"></a> <a name="about"></a>
<div class="intro-header"> <div class="intro-header">
<div class="container"> <div class="container">

View file

@ -17,70 +17,12 @@
<div class="row text-center"> <div class="row text-center">
<div class="block"> <div class="block">
{% for vm in vm_types %}
<div class="row well pricing">
<form class="form-inline p-green" role="form">
<div class="btn-group col-md-3">
<div class="form-group">
<big>
{{vm.hosting_company_name}}
</big>
<p>
{{vm.description}}
</p>
</div>
</div>
<div class="btn-group col-md-2">
<div class="form-group">
<label for="cores">Cores:</label>
<select class="form-control" id="cores">
{% with ''|center:10 as range %}
{% for _ in range %}
<option>{{ forloop.counter }}</option>
{% endfor %}
{% endwith %}
</select>
</div>
</div>
<div class="btn-group col-md-2">
<label for="memory">Memory: </label>
<div class="form-group">
<select class="form-control short-input" id="memory">
{% with ''|center:50 as range %}
{% for _ in range %}
<option>{{ forloop.counter }}</option>
{% endfor %}
{% endwith %}
</select>
<span>GiB</span>
</div>
</div>
<div class="form-group col-md-2">
<label for="Disk Size">Disk Size: </label>
<input class="form-control short-input" type="number" id="disk_space" min="0" value="0"/>
<span>GiB</span>
</div>
<div class="col-md-2">
<h3>$199</h3>
</div>
<div class="col-md-1">
<button type="submit" class="btn btn-default">Buy it</button>
</div>
<!-- <button type="submit" class="btn btn-default">Submit</button> -->
</form>
</div>
{% endfor %}
{% for vm in vm_types %} {% for vm in vm_types %}
<div class="col-xs-12 col-sm-6 col-md-3"> <div class="col-xs-12 col-sm-6 col-md-3">
<form class="form-inline"> <form class="form-inline" method="POST" action="{{request.path}}">
{% csrf_token %}
<input type="hidden" name="hosting_company" value="{{vm.hosting_company}}">
<input type="hidden" name="hosting_company_name" value="{{vm.hosting_company_name}}">
<ul class="pricing {% cycle 'p-green' 'p-yel' 'p-red' 'p-blue' %}"> <ul class="pricing {% cycle 'p-green' 'p-yel' 'p-red' 'p-blue' %}">
<li style="height:200px;"> <li style="height:200px;">
<!-- <img src="http://bread.pp.ua/n/settings_g.svg" alt=""> --> <!-- <img src="http://bread.pp.ua/n/settings_g.svg" alt=""> -->
@ -91,7 +33,7 @@
<div class="btn-group"> <div class="btn-group">
<div class="form-group"> <div class="form-group">
<label for="cores">Cores: </label> <label for="cores">Cores: </label>
<select class="form-control cores-selector" id="{{vm.hosting_company}}-cores" data-vm-type="{{vm.hosting_company}}"> <select class="form-control cores-selector" name="cores" id="{{vm.hosting_company}}-cores" data-vm-type="{{vm.hosting_company}}">
{% with ''|center:10 as range %} {% with ''|center:10 as range %}
{% for _ in range %} {% for _ in range %}
<option>{{ forloop.counter }}</option> <option>{{ forloop.counter }}</option>
@ -106,7 +48,7 @@
<div class="btn-group"> <div class="btn-group">
<div class="form-group"> <div class="form-group">
<label for="memory">Memory: </label> <label for="memory">Memory: </label>
<select class="form-control memory-selector" id="{{vm.hosting_company}}-memory" data-vm-type="{{vm.hosting_company}}"> <select class="form-control memory-selector" name="memory" id="{{vm.hosting_company}}-memory" data-vm-type="{{vm.hosting_company}}">
{% with ''|center:50 as range %} {% with ''|center:50 as range %}
{% for _ in range %} {% for _ in range %}
<option>{{ forloop.counter }}</option> <option>{{ forloop.counter }}</option>
@ -120,12 +62,13 @@
<li class="row"> <li class="row">
<div class="form-group"> <div class="form-group">
<label for="Disk Size">Disk Size: </label> <label for="Disk Size">Disk Size: </label>
<input class="form-control short-input disk-space-selector" type="number" id="{{vm.hosting_company}}-disk_space" min="10" value="10" data-vm-type="{{vm.hosting_company}}"/> <input class="form-control short-input disk-space-selector" name="disk_space" type="number" id="{{vm.hosting_company}}-disk_space" min="10" value="10" data-vm-type="{{vm.hosting_company}}"/>
<span>GiB</span> <span>GiB</span>
</div> </div>
</li> </li>
<li> <li>
<h3 id="{{vm.hosting_company}}-final-price">{{vm.default_price|floatformat}}$</h3> <input id="{{vm.hosting_company}}-final-price-input" type="hidden" name="final_price" value="{{vm.default_price|floatformat}}">
<h3 id="{{vm.hosting_company}}-final-price">{{vm.default_price|floatformat}}CHF</h3>
<span>per month</span> <span>per month</span>
</li> </li>
<li> <li>

View file

@ -1,86 +1,12 @@
{% extends "hosting/base_short.html" %}
{% load staticfiles bootstrap3%} {% load staticfiles bootstrap3%}
<!DOCTYPE html> {% block content %}
<html lang="en">
<head> <div class="intro-auth intro-login">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Rails Hosting.ch - Ruby on Rails as easy as possible</title>
<!-- Bootstrap Core CSS -->
<link href="{% static 'hosting/css/bootstrap.min.css' %}" rel="stylesheet">
<!-- Custom CSS -->
<link href="{% static 'hosting/css/landing-page.css' %}" rel="stylesheet">
<!-- Custom Fonts -->
<link href='http://fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'>
<link href="font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href="http://fonts.googleapis.com/css?family=Lato:300,400,700,300italic,400italic,700italic" rel="stylesheet" type="text/css">
<link rel="shortcut icon" href="img/favicon.ico" type="image/x-icon" />
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-default navbar-fixed-top topnav" role="navigation">
<div class="container topnav">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand topnav" href="#"><img src="img/logo_black.svg"></a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li>
<a href="#how">How it works</a>
</li>
<li>
<a href="#your">Your infrastructure</a>
</li>
<li>
<a href="#our">Our inftrastructure</a>
</li>
<li>
<a href="#price">Pricing</a>
</li>
<li>
<a href="#contact">Contact</a>
</li>
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container -->
</nav>
<!-- Header -->
<a name="about"></a>
<div class="intro-header">
<div class="container"> <div class="container">
<div class="col-md-4">&nbsp;</div><div class="col-md-4"> <div class="col-md-4 col-md-offset-4">
<div class="intro-message"> <div class="intro-message">
<h2>Login</h2> <h2 class="section-heading">Login</h2>
<form action="{% url 'hosting:login' %}" method="post" class="form" novalidate> <form action="{% url 'hosting:login' %}" method="post" class="form" novalidate>
{% csrf_token %} {% csrf_token %}
{% for field in form %} {% for field in form %}
@ -93,6 +19,8 @@
</button> </button>
{% endbuttons %} {% endbuttons %}
</form> </form>
<span>Doesn't have an account ? <a class="unlink" href="{% url 'hosting:signup' %}">Sign up</a></span>
<ul class="list-inline intro-social-buttons"> <ul class="list-inline intro-social-buttons">
</ul> </ul>
@ -102,47 +30,4 @@
<!-- /.container --> <!-- /.container -->
</div> </div>
<!-- /.intro-header --> {% endblock %}
<!-- Footer -->
<footer>
<div class="container">
<div class="row">
<div class="col-lg-12">
<ul class="list-inline">
<li>
<a href="#">Home</a>
</li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a href="#about">How it works</a></li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a href="#about">Your infrastructure</a></li>
<li>&sdot;</li>
<li>
<a href="#about">Our infrastructure</a></li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a href="#services">Pricing</a>
</li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a href="#contact">Contact</a>
</li>
</ul>
<p class="copyright text-muted small">Copyright &copy; ungleich GmbH {% now "Y" %}. All Rights Reserved</p>
</div>
</div>
</div>
</footer>
<!-- jQuery -->
<script src="js/jquery.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>

View file

@ -0,0 +1,110 @@
{% extends "hosting/base_short.html" %}
{% load staticfiles bootstrap3 %}
{%block content %}
<!-- Credit card form -->
<div>
<div class="container payment-container">
<div class="row">
<div class="col-xs-12 col-md-4 col-md-offset-2 billing">
<h3><b>Billing Address</b></h3>
<hr>
<form role="form" id="billing-form" method="post" action="{% url 'hosting:payment' %}" novalidate>
{% for field in form %}
{% csrf_token %}
{% bootstrap_field field show_label=False type='fields'%}
{% endfor %}
{% bootstrap_form_errors form type='non_fields'%}
</form>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-md-4 col-md-offset-2 creditcard-box">
<h3><b>Payment Details</b></h3>
<hr>
<div>
<div>
<form role="form" id="payment-form" novalidate>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label" for="cardNumber">CARD NUMBER</label>
<div class="input-group">
<input type="text" class="form-control" name="cardNumber" placeholder="Valid Card Number" required autofocus data-stripe="number" />
<span class="input-group-addon"><i class="fa fa-credit-card"></i></span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-7 col-md-7">
<div class="form-group">
<label for="expMonth">EXPIRATION DATE</label>
<div class="col-xs-6 col-lg-6 pl-ziro">
<input type="text" class="form-control" name="expMonth" placeholder="MM" required data-stripe="exp_month" />
</div>
<div class="col-xs-6 col-lg-6 pl-ziro">
<input type="text" class="form-control" name="expYear" placeholder="YY" required data-stripe="exp_year" />
</div>
</div>
</div>
<div class="col-xs-5 col-md-5 pull-right">
<div class="form-group">
<label for="cvCode">CV CODE</label>
<input type="password" class="form-control" name="cvCode" placeholder="CV" required data-stripe="cvc" />
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<button class="btn btn-success btn-lg btn-block" type="submit">Submit Payment</button>
</div>
</div>
<div class="row" style="display:none;">
<div class="col-xs-12">
<p class="payment-errors"></p>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="col-xs-12 col-md-3 col-md-offset-1 summary-box">
<form role="form" novalidate>
<div class="row">
<div class="col-xs-12">
<h3><b>Billing Amount</b></h3>
<hr>
<div class="content">
<p><b>Type</b> <span class="pull-right">{{request.session.vm_specs.hosting_company_name}}</span></p>
<hr>
<p><b>Cores</b> <span class="pull-right">{{request.session.vm_specs.cores}}</span></p>
<hr>
<p><b>Memory</b> <span class="pull-right">{{request.session.vm_specs.memory}} GiB</span></p>
<hr>
<p><b>Disk space</b> <span class="pull-right">{{request.session.vm_specs.disk_size}} GiB</span></p>
<hr>
<h4>Total<p class="pull-right"><b>{{request.session.vm_specs.final_price}} CHF</b></p></h4>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- stripe key data -->
{% if stripe_key %}
<script type="text/javascript">
(function () {window.stripeKey = "{{stripe_key}}";})();
</script>
{%endif%}
{%endblock%}

View file

@ -1,87 +1,13 @@
{% extends "hosting/base_short.html" %}
{% load staticfiles bootstrap3%} {% load staticfiles bootstrap3%}
{% block content %}
<!DOCTYPE html> <div class="intro-auth intro-signup">
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Signup</title>
<!-- Bootstrap Core CSS -->
<link href="{% static 'hosting/css/bootstrap.min.css' %}" rel="stylesheet">
<!-- Custom CSS -->
<link href="{% static 'hosting/css/landing-page.css' %}" rel="stylesheet">
<!-- Custom Fonts -->
<link href='http://fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'>
<link href="font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href="http://fonts.googleapis.com/css?family=Lato:300,400,700,300italic,400italic,700italic" rel="stylesheet" type="text/css">
<link rel="shortcut icon" href="img/favicon.ico" type="image/x-icon" />
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-default navbar-fixed-top topnav" role="navigation">
<div class="container topnav">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand topnav" href="#"><img src="{% static 'hosting/img/logo_black.svg' %}"></a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li>
<a href="#how">How it works</a>
</li>
<li>
<a href="#your">Your infrastructure</a>
</li>
<li>
<a href="#our">Our inftrastructure</a>
</li>
<li>
<a href="#price">Pricing</a>
</li>
<li>
<a href="#contact">Contact</a>
</li>
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container -->
</nav>
<!-- Header -->
<a name="about"></a>
<div class="intro-header-2">
<div class="container"> <div class="container">
<div class="col-md-4">&nbsp;</div><div class="col-md-4"> <div class="col-md-4">&nbsp;</div>
<div class="col-md-4">
<div class="intro-message"> <div class="intro-message">
<h2>Sign up</h2> <h2 class="section-heading">Sign up</h2>
<form action="{% url 'hosting:signup' %}" method="post" class="form" novalidate> <form action="{% url 'hosting:signup' %}" method="post" class="form" novalidate>
{% csrf_token %} {% csrf_token %}
{% for field in form %} {% for field in form %}
@ -93,57 +19,11 @@
</button> </button>
{% endbuttons %} {% endbuttons %}
</form> </form>
<span>Already have an account ? <a class="unlink" href="{% url 'hosting:login' %}">Log in</a></span>
<ul class="list-inline intro-social-buttons"> <ul class="list-inline intro-social-buttons">
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- /.container --> {% endblock %}
</div>
<!-- /.intro-header -->
<!-- Footer -->
<footer>
<div class="container">
<div class="row">
<div class="col-lg-12">
<ul class="list-inline">
<li>
<a href="#">Home</a>
</li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a href="#about">How it works</a></li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a href="#about">Your infrastructure</a></li>
<li>&sdot;</li>
<li>
<a href="#about">Our infrastructure</a></li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a href="#services">Pricing</a>
</li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a href="#contact">Contact</a>
</li>
</ul>
<p class="copyright text-muted small">Copyright &copy; ungleich GmbH {% now "Y" %}. All Rights Reserved</p>
</div>
</div>
</div>
</footer>
<!-- jQuery -->
<script src="js/jquery.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>

View file

@ -1,14 +1,15 @@
from django.conf.urls import url from django.conf.urls import url
from .views import VMPricingView, DjangoHostingView, RailsHostingView, \ from .views import DjangoHostingView, RailsHostingView, PaymentVMView, \
NodeJSHostingView, LoginView, SignupView, IndexView NodeJSHostingView, LoginView, SignupView, IndexView
urlpatterns = [ urlpatterns = [
url(r'index/$', IndexView.as_view(), name='index'),
url(r'pricing/?$', VMPricingView.as_view(), name='pricing'), url(r'pricing/?$', VMPricingView.as_view(), name='pricing'),
url(r'index/?$', IndexView.as_view(), name='index'),
url(r'django/?$', DjangoHostingView.as_view(), name='djangohosting'), url(r'django/?$', DjangoHostingView.as_view(), name='djangohosting'),
url(r'nodejs/?$', NodeJSHostingView.as_view(), name='nodejshosting'), url(r'nodejs/?$', NodeJSHostingView.as_view(), name='nodejshosting'),
url(r'rails/?$', RailsHostingView.as_view(), name='railshosting'), url(r'rails/?$', RailsHostingView.as_view(), name='railshosting'),
url(r'login/?$', LoginView.as_view(), name='login'), url(r'login/?$', LoginView.as_view(), name='login'),
url(r'signup/?$', SignupView.as_view(), name='signup'), url(r'signup/?$', SignupView.as_view(), name='signup'),
url(r'payment/?$', PaymentVMView.as_view(), name='payment'),
] ]

View file

@ -3,50 +3,54 @@ from django.shortcuts import get_object_or_404, render
from django.core.urlresolvers import reverse_lazy, reverse from django.core.urlresolvers import reverse_lazy, reverse
from django.views.generic import View, CreateView, FormView from django.views.generic import View, CreateView, FormView
from django.shortcuts import redirect
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
from django.conf import settings
from membership.models import CustomUser from membership.forms import PaymentForm
from .models import RailsBetaUser, VirtualMachineType from membership.models import CustomUser, StripeCustomer
from utils.stripe_utils import StripeUtils
from utils.forms import BillingAddressForm
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder
from .forms import HostingUserSignupForm, HostingUserLoginForm from .forms import HostingUserSignupForm, HostingUserLoginForm
from .mixins import ProcessVMSelectionMixin
class VMPricingView(View):
template_name = "hosting/pricing.html"
def get(self, request, *args, **kwargs): class DjangoHostingView(ProcessVMSelectionMixin, View):
return render(request, self.template_name, request)
class DjangoHostingView(View):
template_name = "hosting/django.html" template_name = "hosting/django.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = {} context = {
context["hosting"] = "django" 'hosting': "django",
context["hosting_long"] = "Django" 'hosting_long': "Django",
context["domain"] = "django-hosting.ch" 'domain': "django-hosting.ch",
context["google_analytics"] = "UA-62285904-6" 'google_analytics': "UA-62285904-6",
context["email"] = "info@django-hosting.ch" 'email': "info@django-hosting.ch",
context["vm_types"] = VirtualMachineType.get_serialized_vm_types() 'vm_types': VirtualMachineType.get_serialized_vm_types(),
}
return context return context
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
context = self.get_context_data() context = self.get_context_data()
return render(request, self.template_name, context) return render(request, self.template_name, context)
class RailsHostingView(View): class RailsHostingView(ProcessVMSelectionMixin, View):
template_name = "hosting/rails.html" template_name = "hosting/rails.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = {} context = {
context["hosting"] = "rails" 'hosting': "rails",
context["hosting_long"] = "Ruby On Rails" 'hosting_long': "Ruby On Rails",
context["domain"] = "rails-hosting.ch" 'domain': "rails-hosting.ch",
context["google_analytics"] = "UA-62285904-5" 'google_analytics': "UA-62285904-5",
context["email"] = "info@rails-hosting.ch" 'email': "info@rails-hosting.ch",
context["vm_types"] = VirtualMachineType.get_serialized_vm_types() 'vm_types': VirtualMachineType.get_serialized_vm_types(),
}
return context return context
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
@ -54,21 +58,24 @@ class RailsHostingView(View):
return render(request, self.template_name, context) return render(request, self.template_name, context)
class NodeJSHostingView(View): class NodeJSHostingView(ProcessVMSelectionMixin, View):
template_name = "hosting/nodejs.html" template_name = "hosting/nodejs.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = {} context = {
context["hosting"] = "nodejs" 'hosting': "nodejs",
context["hosting_long"] = "NodeJS" 'hosting_long': "NodeJS",
context["domain"] = "node-hosting.ch" 'domain': "node-hosting.ch",
context["google_analytics"] = "UA-62285904-7" 'google_analytics': "UA-62285904-7",
context["email"] = "info@node-hosting.ch" 'email': "info@node-hosting.ch",
context["vm_types"] = VirtualMachineType.get_serialized_vm_types() 'vm_types': VirtualMachineType.get_serialized_vm_types(),
}
return context return context
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
context = self.get_context_data() context = self.get_context_data()
return render(request, self.template_name, context) return render(request, self.template_name, context)
@ -76,33 +83,39 @@ class IndexView(View):
template_name = "hosting/index.html" template_name = "hosting/index.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = {} context = {
context["hosting"] = "nodejs" 'hosting': "nodejs",
context["hosting_long"] = "NodeJS" 'hosting_long': "NodeJS",
context["domain"] = "node-hosting.ch" 'domain': "node-hosting.ch",
context["google_analytics"] = "UA-62285904-7" 'google_analytics': "UA-62285904-7",
context["email"] = "info@node-hosting.ch" 'email': "info@node-hosting.ch",
context["vm_types"] = VirtualMachineType.get_serialized_vm_types() 'vm_types': VirtualMachineType.get_serialized_vm_types(),
}
return context return context
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
context = self.get_context_data() context = self.get_context_data()
return render(request, self.template_name, context) return render(request, self.template_name, context)
class LoginView(FormView): class LoginView(FormView):
template_name = 'hosting/login.html' template_name = 'hosting/login.html'
success_url = reverse_lazy('hosting:login')
form_class = HostingUserLoginForm form_class = HostingUserLoginForm
moodel = CustomUser moodel = CustomUser
success_url = reverse_lazy('hosting:login')
def form_valid(self, form): def form_valid(self, form):
email = form.cleaned_data.get('email') email = form.cleaned_data.get('email')
password = form.cleaned_data.get('password') password = form.cleaned_data.get('password')
auth_user = authenticate(email=email, password=password) auth_user = authenticate(email=email, password=password)
if auth_user: if auth_user:
login(self.request, auth_user) login(self.request, auth_user)
return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url())
return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url())
@ -115,43 +128,77 @@ class SignupView(CreateView):
return reverse_lazy('hosting:signup') return reverse_lazy('hosting:signup')
def form_valid(self, form): def form_valid(self, form):
name = form.cleaned_data.get('name') name = form.cleaned_data.get('name')
email = form.cleaned_data.get('email') email = form.cleaned_data.get('email')
password = form.cleaned_data.get('password') password = form.cleaned_data.get('password')
CustomUser.register(name, password, email) CustomUser.register(name, password, email)
auth_user = authenticate(email=email, password=password) auth_user = authenticate(email=email, password=password)
login(self.request, auth_user) login(self.request, auth_user)
return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url())
class PaymentVMView(FormView):
template_name = 'hosting/payment.html'
form_class = BillingAddressForm
# class RailsBetaUserForm(ModelForm): def get_context_data(self, **kwargs):
# required_css_class = 'form-control' context = super(PaymentVMView, self).get_context_data(**kwargs)
# class Meta: context.update({
# model = RailsBetaUser 'stripe_key': settings.STRIPE_API_PUBLIC_KEY
# fields = [ 'email' ] })
return context
# def hosting(request, context): def post(self, request, *args, **kwargs):
# email = RailsBetaUser(received_date=datetime.datetime.now()) form = self.get_form()
# if request.method == 'POST': if form.is_valid():
# context['form'] = RailsBetaUserForm(request.POST, instance=email)
# if context['form'].is_valid():
# context['form'].save()
# email = context['form'].cleaned_data['email']
# subject = "%shosting request" % context['hosting']
# message = "Request for beta by: %s" % email
# mail_managers(subject, message) specifications = request.session.get('vm_specs')
vm_type = specifications.get('hosting_company')
vm = VirtualMachineType.objects.get(hosting_company=vm_type)
final_price = vm.calculate_price(specifications)
# return HttpResponseRedirect(reverse("hosting:beta")) plan_data = {
# else: 'vm_type': vm,
# context['form'] = RailsBetaUserForm() 'cores': specifications.get('cores'),
# context['error_message'] = "a problem" 'memory': specifications.get('memory'),
'disk_size': specifications.get('disk_size'),
'price': final_price
}
token = form.cleaned_data.get('token')
# page = "hosting/%s.html" % context['hosting'] # Get or create stripe customer
customer = StripeCustomer.get_or_create(email=self.request.user.email,
token=token)
# Create Virtual Machine Plan
plan = VirtualMachinePlan.create(plan_data, request.user)
# return render(request, page, context) # Create Billing Address
billing_address = form.save()
# def beta(request): # Create a Hosting Order
# return render(request, 'hosting/beta.html') order = HostingOrder.create(VMPlan=plan, customer=customer,
billing_address=billing_address)
# Make stripe charge to a customer
stripe_utils = StripeUtils()
charge = stripe_utils.make_charge(amount=final_price,
customer=customer.stripe_id)
order.set_stripe_charge(charge)
if not charge.paid:
# raise an error
pass
# If the Stripe payment was successed, set order status approved
order.set_approved()
# order.charge =
# Billing Address should be store here
return HttpResponseRedirect(reverse('hosting:payment'))
else:
return self.form_invalid(form)

View file

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-04-26 04:44
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('membership', '0003_auto_20160422_1002'),
]
operations = [
migrations.CreateModel(
name='StripeCustomer',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stripe_id', models.CharField(max_length=100, unique=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View file

@ -8,6 +8,7 @@ from django.core.mail import send_mail
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from utils.stripe_utils import StripeUtils
REGISTRATION_MESSAGE = {'subject': "Validation mail", REGISTRATION_MESSAGE = {'subject': "Validation mail",
'message': 'Please validate Your account under this link http://localhost:8000/en-us/digitalglarus/login/validate/{}', 'message': 'Please validate Your account under this link http://localhost:8000/en-us/digitalglarus/login/validate/{}',
@ -121,6 +122,32 @@ class CustomUser(AbstractBaseUser):
return self.is_admin return self.is_admin
class StripeCustomer(models.Model):
user = models.OneToOneField(CustomUser)
stripe_id = models.CharField(unique=True, max_length=100)
@classmethod
def get_or_create(cls, email=None, token=None):
"""
Check if there is a registered stripe customer with that email
or create a new one
"""
try:
stripe_customer = cls.objects.get(user__email=email)
return stripe_customer
except StripeCustomer.DoesNotExist:
user = CustomUser.objects.get(email=email)
stripe_utils = StripeUtils()
stripe_data = stripe_utils.create_customer(token, email)
stripe_customer = StripeCustomer.objects.\
create(user=user, stripe_id=stripe_data.get('id'))
return stripe_customer
class CreditCards(models.Model): class CreditCards(models.Model):
name = models.CharField(max_length=50) name = models.CharField(max_length=50)
user_id = models.ForeignKey(CustomUser, on_delete=models.CASCADE) user_id = models.ForeignKey(CustomUser, on_delete=models.CASCADE)

258
utils/fields.py Normal file
View file

@ -0,0 +1,258 @@
from django.utils.translation import ugettext as _
from django.db import models
from django import forms
# http://xml.coverpages.org/country3166.html
COUNTRIES = (
('AD', _('Andorra')),
('AE', _('United Arab Emirates')),
('AF', _('Afghanistan')),
('AG', _('Antigua & Barbuda')),
('AI', _('Anguilla')),
('AL', _('Albania')),
('AM', _('Armenia')),
('AN', _('Netherlands Antilles')),
('AO', _('Angola')),
('AQ', _('Antarctica')),
('AR', _('Argentina')),
('AS', _('American Samoa')),
('AT', _('Austria')),
('AU', _('Australia')),
('AW', _('Aruba')),
('AZ', _('Azerbaijan')),
('BA', _('Bosnia and Herzegovina')),
('BB', _('Barbados')),
('BD', _('Bangladesh')),
('BE', _('Belgium')),
('BF', _('Burkina Faso')),
('BG', _('Bulgaria')),
('BH', _('Bahrain')),
('BI', _('Burundi')),
('BJ', _('Benin')),
('BM', _('Bermuda')),
('BN', _('Brunei Darussalam')),
('BO', _('Bolivia')),
('BR', _('Brazil')),
('BS', _('Bahama')),
('BT', _('Bhutan')),
('BV', _('Bouvet Island')),
('BW', _('Botswana')),
('BY', _('Belarus')),
('BZ', _('Belize')),
('CA', _('Canada')),
('CC', _('Cocos (Keeling) Islands')),
('CF', _('Central African Republic')),
('CG', _('Congo')),
('CH', _('Switzerland')),
('CI', _('Ivory Coast')),
('CK', _('Cook Iislands')),
('CL', _('Chile')),
('CM', _('Cameroon')),
('CN', _('China')),
('CO', _('Colombia')),
('CR', _('Costa Rica')),
('CU', _('Cuba')),
('CV', _('Cape Verde')),
('CX', _('Christmas Island')),
('CY', _('Cyprus')),
('CZ', _('Czech Republic')),
('DE', _('Germany')),
('DJ', _('Djibouti')),
('DK', _('Denmark')),
('DM', _('Dominica')),
('DO', _('Dominican Republic')),
('DZ', _('Algeria')),
('EC', _('Ecuador')),
('EE', _('Estonia')),
('EG', _('Egypt')),
('EH', _('Western Sahara')),
('ER', _('Eritrea')),
('ES', _('Spain')),
('ET', _('Ethiopia')),
('FI', _('Finland')),
('FJ', _('Fiji')),
('FK', _('Falkland Islands (Malvinas)')),
('FM', _('Micronesia')),
('FO', _('Faroe Islands')),
('FR', _('France')),
('FX', _('France, Metropolitan')),
('GA', _('Gabon')),
('GB', _('United Kingdom (Great Britain)')),
('GD', _('Grenada')),
('GE', _('Georgia')),
('GF', _('French Guiana')),
('GH', _('Ghana')),
('GI', _('Gibraltar')),
('GL', _('Greenland')),
('GM', _('Gambia')),
('GN', _('Guinea')),
('GP', _('Guadeloupe')),
('GQ', _('Equatorial Guinea')),
('GR', _('Greece')),
('GS', _('South Georgia and the South Sandwich Islands')),
('GT', _('Guatemala')),
('GU', _('Guam')),
('GW', _('Guinea-Bissau')),
('GY', _('Guyana')),
('HK', _('Hong Kong')),
('HM', _('Heard & McDonald Islands')),
('HN', _('Honduras')),
('HR', _('Croatia')),
('HT', _('Haiti')),
('HU', _('Hungary')),
('ID', _('Indonesia')),
('IE', _('Ireland')),
('IL', _('Israel')),
('IN', _('India')),
('IO', _('British Indian Ocean Territory')),
('IQ', _('Iraq')),
('IR', _('Islamic Republic of Iran')),
('IS', _('Iceland')),
('IT', _('Italy')),
('JM', _('Jamaica')),
('JO', _('Jordan')),
('JP', _('Japan')),
('KE', _('Kenya')),
('KG', _('Kyrgyzstan')),
('KH', _('Cambodia')),
('KI', _('Kiribati')),
('KM', _('Comoros')),
('KN', _('St. Kitts and Nevis')),
('KP', _('Korea, Democratic People\'s Republic of')),
('KR', _('Korea, Republic of')),
('KW', _('Kuwait')),
('KY', _('Cayman Islands')),
('KZ', _('Kazakhstan')),
('LA', _('Lao People\'s Democratic Republic')),
('LB', _('Lebanon')),
('LC', _('Saint Lucia')),
('LI', _('Liechtenstein')),
('LK', _('Sri Lanka')),
('LR', _('Liberia')),
('LS', _('Lesotho')),
('LT', _('Lithuania')),
('LU', _('Luxembourg')),
('LV', _('Latvia')),
('LY', _('Libyan Arab Jamahiriya')),
('MA', _('Morocco')),
('MC', _('Monaco')),
('MD', _('Moldova, Republic of')),
('MG', _('Madagascar')),
('MH', _('Marshall Islands')),
('ML', _('Mali')),
('MN', _('Mongolia')),
('MM', _('Myanmar')),
('MO', _('Macau')),
('MP', _('Northern Mariana Islands')),
('MQ', _('Martinique')),
('MR', _('Mauritania')),
('MS', _('Monserrat')),
('MT', _('Malta')),
('MU', _('Mauritius')),
('MV', _('Maldives')),
('MW', _('Malawi')),
('MX', _('Mexico')),
('MY', _('Malaysia')),
('MZ', _('Mozambique')),
('NA', _('Namibia')),
('NC', _('New Caledonia')),
('NE', _('Niger')),
('NF', _('Norfolk Island')),
('NG', _('Nigeria')),
('NI', _('Nicaragua')),
('NL', _('Netherlands')),
('NO', _('Norway')),
('NP', _('Nepal')),
('NR', _('Nauru')),
('NU', _('Niue')),
('NZ', _('New Zealand')),
('OM', _('Oman')),
('PA', _('Panama')),
('PE', _('Peru')),
('PF', _('French Polynesia')),
('PG', _('Papua New Guinea')),
('PH', _('Philippines')),
('PK', _('Pakistan')),
('PL', _('Poland')),
('PM', _('St. Pierre & Miquelon')),
('PN', _('Pitcairn')),
('PR', _('Puerto Rico')),
('PT', _('Portugal')),
('PW', _('Palau')),
('PY', _('Paraguay')),
('QA', _('Qatar')),
('RE', _('Reunion')),
('RO', _('Romania')),
('RU', _('Russian Federation')),
('RW', _('Rwanda')),
('SA', _('Saudi Arabia')),
('SB', _('Solomon Islands')),
('SC', _('Seychelles')),
('SD', _('Sudan')),
('SE', _('Sweden')),
('SG', _('Singapore')),
('SH', _('St. Helena')),
('SI', _('Slovenia')),
('SJ', _('Svalbard & Jan Mayen Islands')),
('SK', _('Slovakia')),
('SL', _('Sierra Leone')),
('SM', _('San Marino')),
('SN', _('Senegal')),
('SO', _('Somalia')),
('SR', _('Suriname')),
('ST', _('Sao Tome & Principe')),
('SV', _('El Salvador')),
('SY', _('Syrian Arab Republic')),
('SZ', _('Swaziland')),
('TC', _('Turks & Caicos Islands')),
('TD', _('Chad')),
('TF', _('French Southern Territories')),
('TG', _('Togo')),
('TH', _('Thailand')),
('TJ', _('Tajikistan')),
('TK', _('Tokelau')),
('TM', _('Turkmenistan')),
('TN', _('Tunisia')),
('TO', _('Tonga')),
('TP', _('East Timor')),
('TR', _('Turkey')),
('TT', _('Trinidad & Tobago')),
('TV', _('Tuvalu')),
('TW', _('Taiwan, Province of China')),
('TZ', _('Tanzania, United Republic of')),
('UA', _('Ukraine')),
('UG', _('Uganda')),
('UM', _('United States Minor Outlying Islands')),
('US', _('United States of America')),
('UY', _('Uruguay')),
('UZ', _('Uzbekistan')),
('VA', _('Vatican City State (Holy See)')),
('VC', _('St. Vincent & the Grenadines')),
('VE', _('Venezuela')),
('VG', _('British Virgin Islands')),
('VI', _('United States Virgin Islands')),
('VN', _('Viet Nam')),
('VU', _('Vanuatu')),
('WF', _('Wallis & Futuna Islands')),
('WS', _('Samoa')),
('YE', _('Yemen')),
('YT', _('Mayotte')),
('YU', _('Yugoslavia')),
('ZA', _('South Africa')),
('ZM', _('Zambia')),
('ZR', _('Zaire')),
('ZW', _('Zimbabwe')),
('ZZ', _('Unknown or unspecified country')),
)
class CountryField(models.CharField):
def __init__(self, *args, **kwargs):
kwargs.setdefault('choices', COUNTRIES)
kwargs.setdefault('default', 'CH')
kwargs.setdefault('max_length', 2)
super(CountryField, self).__init__(*args, **kwargs)
def get_internal_type(self):
return "CharField"

View file

@ -1,8 +1,23 @@
from django import forms from django import forms
from .models import ContactMessage from .models import ContactMessage, BillingAddress
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.core.mail import EmailMultiAlternatives from django.core.mail import EmailMultiAlternatives
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
# from utils.fields import CountryField
class BillingAddressForm(forms.ModelForm):
token = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = BillingAddress
fields = ['street_address', 'city', 'postal_code', 'country']
labels = {
'street_address': _('Street Address'),
'city': _('City'),
'postal_code': _('Postal Code'),
'Country': _('Country'),
}
class ContactUsForm(forms.ModelForm): class ContactUsForm(forms.ModelForm):

File diff suppressed because one or more lines are too long

View file

@ -1,8 +1,18 @@
from django.db import models from django.db import models
from .fields import CountryField
# Create your models here. # Create your models here.
class BillingAddress(models.Model):
street_address = models.CharField(max_length=100)
city = models.CharField(max_length=50)
postal_code = models.CharField(max_length=50)
country = CountryField()
class ContactMessage(models.Model): class ContactMessage(models.Model):
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
email = models.EmailField() email = models.EmailField()

76
utils/stripe_utils.py Normal file
View file

@ -0,0 +1,76 @@
import stripe
from django.conf import settings
class StripeUtils(object):
CURRENCY = 'chf'
INTERVAL = 'month'
SUCCEEDED_STATUS = 'succeeded'
def __init__(self):
self.stripe = stripe
self.stripe.api_key = settings.STRIPE_API_PRIVATE_KEY
def create_customer(self, token, email):
stripe.api_key = settings.STRIPE_API_PRIVATE_KEY
customer = stripe.Customer.create(
source=token,
description='description for testing',
email=email
)
return customer
def make_charge(self, amount=None, customer=None):
amount = int(amount * 100) # stripe amount unit, in cents
charge = self.stripe.Charge.create(
amount=amount, # in cents
currency=self.CURRENCY,
customer=customer
)
return charge
def create_plan(self, amount, name, id):
self.stripe.Plan.create(
amount=amount,
interval=self.INTERVAL,
name=name,
currency=self.CURRENCY,
id=id)
def make_payment(self, user, amount, token):
try:
# Use Stripe's library to make requests...
charge = self.stripe.Charge.create(
amount=amount,
currency=self.CURRENCY,
source=token,
description=settings.STRIPE_DESCRIPTION_ON_PAYMENT
)
if charge.get('status') == self.SUCCEEDED_STATUS:
# do something
pass
return charge['status']
except self.stripe.error.CardError as e:
# Since it's a decline, stripe.error.CardError will be caught
body = e.json_body
err = body['error']
return err['message']
except self.stripe.error.RateLimitError as e:
return "Too many requests made to the API too quickly"
except self.stripe.error.InvalidRequestError as e:
return "Invalid parameters"
except self.stripe.error.AuthenticationError as e:
# Authentication with Stripe's API failed
# (maybe you changed API keys recently)
pass
except self.stripe.error.APIConnectionError as e:
return "Currently its not possible to make payments."
except self.stripe.error.StripeError as e:
# maybe send email
return "Currently its not possible to make payments."
except Exception as e:
# maybe send email
return "Currently its not possible to make payments."