Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
		
				commit
				
					
						5e51fa6dbf
					
				
			
		
					 21 changed files with 661 additions and 170 deletions
				
			
		| 
						 | 
				
			
			@ -446,8 +446,8 @@ AUTH_USER_MODEL = 'membership.CustomUser'
 | 
			
		|||
 | 
			
		||||
# PAYMENT
 | 
			
		||||
 | 
			
		||||
STRIPE_API_PUBLIC_KEY = 'pk_test_ZRg6P8g5ybiHE6l2RW5pSaYV'  # used in frontend to call from user browser
 | 
			
		||||
STRIPE_API_PRIVATE_KEY = 'sk_test_uIPMdgXoRGydrcD7fkwcn7dj'  # used in backend payment
 | 
			
		||||
STRIPE_API_PUBLIC_KEY = 'pk_test_QqBZ50Am8KOxaAlOxbcm9Psl'  # used in frontend to call from user browser
 | 
			
		||||
STRIPE_API_PRIVATE_KEY = 'sk_test_dqAmbKAij12QCGfkYZ3poGt2'  # used in backend payment
 | 
			
		||||
STRIPE_DESCRIPTION_ON_PAYMENT = "Payment for ungleich GmbH services"
 | 
			
		||||
 | 
			
		||||
# EMAIL MESSAGES
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										9
									
								
								hosting/managers.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								hosting/managers.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
from django.db import models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VMPlansManager(models.Manager):
 | 
			
		||||
 | 
			
		||||
    def active(self, user, **kwargs):
 | 
			
		||||
        return self.prefetch_related('hosting_orders__customer__user').\
 | 
			
		||||
            filter(hosting_orders__customer__user=user, hosting_orders__approved=True, **kwargs)\
 | 
			
		||||
            .distinct()
 | 
			
		||||
							
								
								
									
										27
									
								
								hosting/migrations/0012_auto_20160501_1850.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								hosting/migrations/0012_auto_20160501_1850.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Generated by Django 1.9.4 on 2016-05-01 18:50
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('hosting', '0011_auto_20160426_0555'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='hostingorder',
 | 
			
		||||
            name='cc_brand',
 | 
			
		||||
            field=models.CharField(default='Visa', max_length=10),
 | 
			
		||||
            preserve_default=False,
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='hostingorder',
 | 
			
		||||
            name='last4',
 | 
			
		||||
            field=models.CharField(default=1111, max_length=4),
 | 
			
		||||
            preserve_default=False,
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										21
									
								
								hosting/migrations/0013_auto_20160505_0302.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								hosting/migrations/0013_auto_20160505_0302.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Generated by Django 1.9.4 on 2016-05-05 03:02
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('hosting', '0012_auto_20160501_1850'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='hostingorder',
 | 
			
		||||
            name='VMPlan',
 | 
			
		||||
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hosting.VirtualMachinePlan'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										21
									
								
								hosting/migrations/0014_auto_20160505_0541.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								hosting/migrations/0014_auto_20160505_0541.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Generated by Django 1.9.4 on 2016-05-05 05:41
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('hosting', '0013_auto_20160505_0302'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name='hostingorder',
 | 
			
		||||
            name='VMPlan',
 | 
			
		||||
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='hosting_orders', to='hosting.VirtualMachinePlan'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -2,10 +2,13 @@ import json
 | 
			
		|||
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from django.utils.functional import cached_property
 | 
			
		||||
from django.core import serializers
 | 
			
		||||
from membership.models import StripeCustomer
 | 
			
		||||
from utils.models import BillingAddress
 | 
			
		||||
 | 
			
		||||
from .managers import VMPlansManager
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RailsBetaUser(models.Model):
 | 
			
		||||
    email = models.EmailField(unique=True)
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +84,17 @@ class VirtualMachinePlan(models.Model):
 | 
			
		|||
    vm_type = models.ForeignKey(VirtualMachineType)
 | 
			
		||||
    price = models.FloatField()
 | 
			
		||||
 | 
			
		||||
    objects = VMPlansManager()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def hosting_company_name(self):
 | 
			
		||||
        return self.vm_type.get_hosting_company_display()
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def name(self):
 | 
			
		||||
        name = 'vm-%s' % self.id
 | 
			
		||||
        return name
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def create(cls, data, user):
 | 
			
		||||
        instance = cls.objects.create(**data)
 | 
			
		||||
| 
						 | 
				
			
			@ -88,13 +102,23 @@ class VirtualMachinePlan(models.Model):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class HostingOrder(models.Model):
 | 
			
		||||
    VMPlan = models.OneToOneField(VirtualMachinePlan)
 | 
			
		||||
 | 
			
		||||
    ORDER_APPROVED_STATUS = 'Approved'
 | 
			
		||||
    ORDER_DECLINED_STATUS = 'Declined'
 | 
			
		||||
 | 
			
		||||
    VMPlan = models.ForeignKey(VirtualMachinePlan, related_name='hosting_orders')
 | 
			
		||||
    customer = models.ForeignKey(StripeCustomer)
 | 
			
		||||
    billing_address = models.ForeignKey(BillingAddress)
 | 
			
		||||
    created_at = models.DateTimeField(auto_now_add=True)
 | 
			
		||||
    approved = models.BooleanField(default=False)
 | 
			
		||||
    last4 = models.CharField(max_length=4)
 | 
			
		||||
    cc_brand = models.CharField(max_length=10)
 | 
			
		||||
    stripe_charge_id = models.CharField(max_length=100, null=True)
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def status(self):
 | 
			
		||||
        return self.ORDER_APPROVED_STATUS if self.approved else self.ORDER_DECLINED_STATUS
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def create(cls, VMPlan=None, customer=None, billing_address=None):
 | 
			
		||||
        instance = cls.objects.create(VMPlan=VMPlan, customer=customer,
 | 
			
		||||
| 
						 | 
				
			
			@ -107,6 +131,8 @@ class HostingOrder(models.Model):
 | 
			
		|||
 | 
			
		||||
    def set_stripe_charge(self, stripe_charge):
 | 
			
		||||
        self.stripe_charge_id = stripe_charge.id
 | 
			
		||||
        self.last4 = stripe_charge.source.last4
 | 
			
		||||
        self.cc_brand = stripe_charge.source.brand
 | 
			
		||||
        self.save()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										20
									
								
								hosting/static/hosting/css/commons.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								hosting/static/hosting/css/commons.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
.dashboard-container {
 | 
			
		||||
	padding-top:5%; padding-bottom: 11%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.borderless td {
 | 
			
		||||
    border: none !important;
 | 
			
		||||
}
 | 
			
		||||
.borderless thead {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.borderless tbody:before {
 | 
			
		||||
    content: "-";
 | 
			
		||||
    display: block;
 | 
			
		||||
    color: transparent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.inline-headers h3, .inline-headers h4 {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  vertical-align: baseline;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								hosting/static/hosting/css/order.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								hosting/static/hosting/css/order.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
.order-detail-container {padding-top:5%; padding-bottom: 11%;}
 | 
			
		||||
 | 
			
		||||
.order-detail-container .invoice-title h2, .invoice-title h3 {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.order-detail-container .table > tbody > tr > .no-line {
 | 
			
		||||
    border-top: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.order-detail-container .table > thead > tr > .no-line {
 | 
			
		||||
    border-bottom: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.order-detail-container .table > tbody > tr > .thick-line {
 | 
			
		||||
    border-top: 2px solid;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								hosting/static/hosting/css/orders.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								hosting/static/hosting/css/orders.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
.orders-container {padding-top:5%; padding-bottom: 11%;}
 | 
			
		||||
 | 
			
		||||
.orders-container .table > tbody > tr > td {
 | 
			
		||||
     vertical-align: middle;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								hosting/static/hosting/css/virtual-machine.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								hosting/static/hosting/css/virtual-machine.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
.virtual-machine-container .tabs-left, .virtual-machine-container .tabs-right {
 | 
			
		||||
  border-bottom: none;
 | 
			
		||||
  padding-top: 2px;
 | 
			
		||||
}
 | 
			
		||||
.virtual-machine-container .tabs-left {
 | 
			
		||||
  border-right: 1px solid #ddd;
 | 
			
		||||
}
 | 
			
		||||
.virtual-machine-container .tabs-right {
 | 
			
		||||
  border-left: 1px solid #ddd;
 | 
			
		||||
}
 | 
			
		||||
.virtual-machine-container .tabs-left>li, .virtual-machine-container .tabs-right>li {
 | 
			
		||||
  float: none;
 | 
			
		||||
  margin-bottom: 2px;
 | 
			
		||||
}
 | 
			
		||||
.virtual-machine-container .tabs-left>li {
 | 
			
		||||
  margin-right: -1px;
 | 
			
		||||
}
 | 
			
		||||
.virtual-machine-container .tabs-right>li {
 | 
			
		||||
  margin-left: -1px;
 | 
			
		||||
}
 | 
			
		||||
.virtual-machine-container .tabs-left>li.active>a,
 | 
			
		||||
.virtual-machine-container .tabs-left>li.active>a:hover,
 | 
			
		||||
.virtual-machine-container .tabs-left>li.active>a:focus {
 | 
			
		||||
  border-bottom-color: #ddd;
 | 
			
		||||
  border-right-color: transparent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.virtual-machine-container .tabs-right>li.active>a,
 | 
			
		||||
.virtual-machine-container .tabs-right>li.active>a:hover,
 | 
			
		||||
.virtual-machine-container .tabs-right>li.active>a:focus {
 | 
			
		||||
  border-bottom: 1px solid #ddd;
 | 
			
		||||
  border-left-color: transparent;
 | 
			
		||||
}
 | 
			
		||||
.virtual-machine-container .tabs-left>li>a {
 | 
			
		||||
  border-radius: 4px 0 0 4px;
 | 
			
		||||
  margin-right: 0;
 | 
			
		||||
  display:block;
 | 
			
		||||
}
 | 
			
		||||
.virtual-machine-container .tabs-right>li>a {
 | 
			
		||||
  border-radius: 0 4px 4px 0;
 | 
			
		||||
  margin-right: 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -18,6 +18,10 @@
 | 
			
		|||
    <!-- Custom CSS -->
 | 
			
		||||
    <link href="{% static 'hosting/css/landing-page.css' %}" rel="stylesheet">
 | 
			
		||||
    <link href="{% static 'hosting/css/payment.css' %}" rel="stylesheet">
 | 
			
		||||
    <link href="{% static 'hosting/css/order.css' %}" rel="stylesheet">
 | 
			
		||||
    <link href="{% static 'hosting/css/orders.css' %}" rel="stylesheet">
 | 
			
		||||
    <link href="{% static 'hosting/css/commons.css' %}" rel="stylesheet">
 | 
			
		||||
    <link href="{% static 'hosting/css/virtual-machine.css' %}" rel="stylesheet">
 | 
			
		||||
 | 
			
		||||
    <!-- Custom Fonts -->
 | 
			
		||||
    <link href='http://fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'>
 | 
			
		||||
| 
						 | 
				
			
			@ -49,30 +53,51 @@
 | 
			
		|||
                    <span class="icon-bar"></span>
 | 
			
		||||
                    <span class="icon-bar"></span>
 | 
			
		||||
                </button>
 | 
			
		||||
                <a class="navbar-brand topnav" href="#"><img src="img/logo_black.svg"></a>
 | 
			
		||||
                <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="{{ request.META.HTTP_REFERER }}#how">How it works</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li>
 | 
			
		||||
                        <a href="{{ request.META.HTTP_REFERER }}#your">Your infrastructure</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                   <li>
 | 
			
		||||
                        <a href="{{ request.META.HTTP_REFERER }}#our">Our inftrastructure</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li>
 | 
			
		||||
                        <a href="{{ request.META.HTTP_REFERER }}#price">Pricing</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li>
 | 
			
		||||
                        <a href="{{ request.META.HTTP_REFERER }}#contact">Contact</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    {% if request.user.is_authenticated %}
 | 
			
		||||
                    <li>
 | 
			
		||||
                        <a href="{% url 'hosting:logout' %}"> Logout</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                        <li>
 | 
			
		||||
                            <a href="{% url 'hosting:virtual_machines' %}">
 | 
			
		||||
                                <i class="fa fa-server" aria-hidden="true"></i> My Virtual Machines
 | 
			
		||||
                            </a>
 | 
			
		||||
                        </li>
 | 
			
		||||
                        <li>
 | 
			
		||||
                            <a href="{% url 'hosting:orders' %}">
 | 
			
		||||
                                <i class="fa fa-credit-card"></i> My Orders
 | 
			
		||||
                            </a>
 | 
			
		||||
                        </li>
 | 
			
		||||
 | 
			
		||||
                        <li class="dropdown">
 | 
			
		||||
                          <a class="dropdown-toggle" role="button" data-toggle="dropdown" href="#">
 | 
			
		||||
                            <i class="glyphicon glyphicon-user"></i> {{request.user.name}} <span class="caret"></span></a>
 | 
			
		||||
                          <ul id="g-account-menu" class="dropdown-menu" role="menu">
 | 
			
		||||
                            <li><a href="{% url 'hosting:logout' %}"><i class="glyphicon glyphicon-lock"></i> Logout</a></li>
 | 
			
		||||
                          </ul>
 | 
			
		||||
                        </li>
 | 
			
		||||
                    {% else %}
 | 
			
		||||
                        <li>
 | 
			
		||||
                            <a href="{{ request.META.HTTP_REFERER }}#how">How it works</a>
 | 
			
		||||
                        </li>
 | 
			
		||||
                        <li>
 | 
			
		||||
                            <a href="{{ request.META.HTTP_REFERER }}#your">Your infrastructure</a>
 | 
			
		||||
                        </li>
 | 
			
		||||
                       <li>
 | 
			
		||||
                            <a href="{{ request.META.HTTP_REFERER }}#our">Our inftrastructure</a>
 | 
			
		||||
                        </li>
 | 
			
		||||
                        <li>
 | 
			
		||||
                            <a href="{{ request.META.HTTP_REFERER }}#price">Pricing</a>
 | 
			
		||||
                        </li>
 | 
			
		||||
                        <li>
 | 
			
		||||
                            <a href="{{ request.META.HTTP_REFERER }}#contact">Contact</a>
 | 
			
		||||
                        </li>  
 | 
			
		||||
                        <li>
 | 
			
		||||
                            <a href="{% url 'hosting:login' %}?next={{request.current_path}}">Login</a>
 | 
			
		||||
                        </li>   
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </ul>
 | 
			
		||||
            </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,79 +0,0 @@
 | 
			
		|||
{% extends "hosting/base_short.html" %}
 | 
			
		||||
{% load staticfiles bootstrap3 %}
 | 
			
		||||
{% block content %} 
 | 
			
		||||
 | 
			
		||||
<style type="text/css">
 | 
			
		||||
.invoice-title h2, .invoice-title h3 {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.table > tbody > tr > .no-line {
 | 
			
		||||
    border-top: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.table > thead > tr > .no-line {
 | 
			
		||||
    border-bottom: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.table > tbody > tr > .thick-line {
 | 
			
		||||
    border-top: 2px solid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
<div class="container  payment-container">
 | 
			
		||||
    <div class="row">
 | 
			
		||||
        <div class="col-xs-8 col-xs-offset-2">
 | 
			
		||||
    		<div class="invoice-title">
 | 
			
		||||
    			<h2>Invoice</h2><h3 class="pull-right">Order # {{order.id}}</h3>
 | 
			
		||||
    		</div>
 | 
			
		||||
    		<hr>
 | 
			
		||||
    		<div class="row">
 | 
			
		||||
    			<div class="col-xs-6">
 | 
			
		||||
    				<address>
 | 
			
		||||
                    <h3><b>Billed To:</b></h3>
 | 
			
		||||
    					John Smith<br>
 | 
			
		||||
    					1234 Main<br>
 | 
			
		||||
    					Apt. 4B<br>
 | 
			
		||||
    					Springfield, ST 54321
 | 
			
		||||
    				</address>
 | 
			
		||||
    			</div>
 | 
			
		||||
                <div class="col-xs-6 text-right">
 | 
			
		||||
                    <address>
 | 
			
		||||
                        <strong>Order Date:</strong><br>
 | 
			
		||||
                        {{order.created_at}}<br><br>
 | 
			
		||||
                    </address>
 | 
			
		||||
                </div>
 | 
			
		||||
    		</div>
 | 
			
		||||
    		<div class="row">
 | 
			
		||||
    			<div class="col-xs-6">
 | 
			
		||||
    				<address>
 | 
			
		||||
    					<strong>Payment Method:</strong><br>
 | 
			
		||||
    					{{brand}} ending **** {{last4}}<br>
 | 
			
		||||
    					{{user.email}}
 | 
			
		||||
    				</address>
 | 
			
		||||
    			</div>
 | 
			
		||||
    		</div>
 | 
			
		||||
    	</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <div class="row">
 | 
			
		||||
        <div class="col-md-8 col-md-offset-2">
 | 
			
		||||
            <h3><b>Order summary</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>
 | 
			
		||||
</div>
 | 
			
		||||
{%endblock%}
 | 
			
		||||
							
								
								
									
										64
									
								
								hosting/templates/hosting/order_detail.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								hosting/templates/hosting/order_detail.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,64 @@
 | 
			
		|||
{% extends "hosting/base_short.html" %}
 | 
			
		||||
{% load staticfiles bootstrap3 %}
 | 
			
		||||
{% block content %} 
 | 
			
		||||
 | 
			
		||||
<div class="container  order-detail-container">
 | 
			
		||||
    <div class="row">
 | 
			
		||||
        <div class="col-xs-8 col-xs-offset-2">
 | 
			
		||||
    		<div class="invoice-title">
 | 
			
		||||
    			<h2>Invoice</h2><h3 class="pull-right">Order # {{object.id}}</h3>
 | 
			
		||||
    		</div>
 | 
			
		||||
    		<hr>
 | 
			
		||||
    		<div class="row">
 | 
			
		||||
    			<div class="col-xs-6">
 | 
			
		||||
    				<address>
 | 
			
		||||
                    <h3><b>Billed To:</b></h3>
 | 
			
		||||
    					{{user.name}}<br>
 | 
			
		||||
                        {{object.billing_address.street_address}},{{order.billing_address.postal_code}}<br>
 | 
			
		||||
                        {{object.billing_address.city}}, {{object.billing_address.country}}.
 | 
			
		||||
    				</address>
 | 
			
		||||
    			</div>
 | 
			
		||||
                <div class="col-xs-6 text-right">
 | 
			
		||||
                    <address>
 | 
			
		||||
                        <strong>Order Date:</strong><br>
 | 
			
		||||
                        {{object.created_at}}<br><br>
 | 
			
		||||
                        <strong>Status:</strong><br>
 | 
			
		||||
                        <strong class="{% if object.status == 'Approved' %}text-success
 | 
			
		||||
                                       {%else%} text-danger
 | 
			
		||||
                                       {% endif %}">{{object.status}}</strong>
 | 
			
		||||
                        <br><br>
 | 
			
		||||
                    </address>
 | 
			
		||||
 | 
			
		||||
                </div>
 | 
			
		||||
    		</div>
 | 
			
		||||
    		<div class="row">
 | 
			
		||||
    			<div class="col-xs-6">
 | 
			
		||||
    				<address>
 | 
			
		||||
    					<strong>Payment Method:</strong><br>
 | 
			
		||||
    					{{object.cc_brand}} ending **** {{object.last4}}<br>
 | 
			
		||||
    					{{user.email}}
 | 
			
		||||
    				</address>
 | 
			
		||||
    			</div>
 | 
			
		||||
    		</div>
 | 
			
		||||
    	</div>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <div class="row">
 | 
			
		||||
        <div class="col-md-8 col-md-offset-2">
 | 
			
		||||
            <h3><b>Order summary</b></h3>
 | 
			
		||||
            <hr>
 | 
			
		||||
            <div class="content">
 | 
			
		||||
                <p><b>Type</b> <span class="pull-right">{{object.VMPlan.hosting_company_name}}</span></p>
 | 
			
		||||
                <hr>
 | 
			
		||||
                <p><b>Cores</b> <span class="pull-right">{{object.VMPlan.cores}}</span></p>
 | 
			
		||||
                <hr>
 | 
			
		||||
                <p><b>Memory</b> <span class="pull-right">{{object.VMPlan.memory}} GiB</span></p>
 | 
			
		||||
                <hr>
 | 
			
		||||
                <p><b>Disk space</b> <span class="pull-right">{{object.VMPlan.disk_size}} GiB</span></p>
 | 
			
		||||
                <hr>
 | 
			
		||||
                <h4>Total<p class="pull-right"><b>{{object.VMPlan.price}} CHF</b></p></h4>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
{%endblock%}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,31 +1,59 @@
 | 
			
		|||
{% extends "hosting/base_short.html" %}
 | 
			
		||||
{% load staticfiles bootstrap3 %}
 | 
			
		||||
{% block content %} 
 | 
			
		||||
 | 
			
		||||
<div>
 | 
			
		||||
	<div class="container payment-container">
 | 
			
		||||
	<div class="container orders-container">
 | 
			
		||||
		<div class="row">
 | 
			
		||||
			<div class="col-md-8 col-md-offset-2">
 | 
			
		||||
				<table class="table"> 
 | 
			
		||||
				<caption>My orders.</caption> 
 | 
			
		||||
				<table class="table borderless table-hover"> 
 | 
			
		||||
				<h3><i class="fa fa-credit-card"></i> My Orders</h3> 
 | 
			
		||||
				<br/>
 | 
			
		||||
				<thead> 
 | 
			
		||||
				<tr> 
 | 
			
		||||
					<th>#</th>
 | 
			
		||||
					<th>Date</th>
 | 
			
		||||
					<th>Amount</th>
 | 
			
		||||
					<th>Status</th>
 | 
			
		||||
					<th></th>
 | 
			
		||||
				</tr> 
 | 
			
		||||
				</thead> 
 | 
			
		||||
				<tbody> 
 | 
			
		||||
					{% for order in orders %}
 | 
			
		||||
					<tr> 
 | 
			
		||||
						<th scope="row">{{order.id}}</th> 
 | 
			
		||||
						<td scope="row">{{order.id}}</td> 
 | 
			
		||||
						<td>{{order.created_at}}</td> 
 | 
			
		||||
						<td>{{order.VMPlan.price}}</td> 
 | 
			
		||||
						<td>{{order.approved}}</td> 
 | 
			
		||||
						<td>{{order.VMPlan.price}} CHF</td> 
 | 
			
		||||
						<td>{% if order.approved %}
 | 
			
		||||
								<span class="text-success strong">Approved</span>
 | 
			
		||||
							{% else%} 
 | 
			
		||||
								<span class="text-danger strong">Declined</span>
 | 
			
		||||
							{% endif%}
 | 
			
		||||
						</td> 
 | 
			
		||||
						<td>
 | 
			
		||||
							<button type="button" class="btn btn-default"><a href="{% url 'hosting:orders' order.id %}">View Detail</a></button>
 | 
			
		||||
						</td>
 | 
			
		||||
					</tr>
 | 
			
		||||
					{% endfor %}
 | 
			
		||||
				</tbody> 
 | 
			
		||||
				</table>
 | 
			
		||||
 | 
			
		||||
			    {% if is_paginated %}
 | 
			
		||||
			        <div class="pagination">
 | 
			
		||||
			            <span class="page-links">
 | 
			
		||||
			                {% if page_obj.has_previous %}
 | 
			
		||||
			                    <a href="{{request.path}}?page={{ page_obj.previous_page_number }}">previous</a>
 | 
			
		||||
			                {% endif %}
 | 
			
		||||
			                <span class="page-current">
 | 
			
		||||
			                    Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
 | 
			
		||||
			                </span>
 | 
			
		||||
			                {% if page_obj.has_next %}
 | 
			
		||||
			                    <a href="{{request.path}}?page={{ page_obj.next_page_number }}">next</a>
 | 
			
		||||
			                {% endif %}
 | 
			
		||||
			            </span>
 | 
			
		||||
			        </div>
 | 
			
		||||
			    {% endif %}
 | 
			
		||||
 | 
			
		||||
			</div>
 | 
			
		||||
 | 
			
		||||
	    </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										155
									
								
								hosting/templates/hosting/virtual_machine_detail.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								hosting/templates/hosting/virtual_machine_detail.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,155 @@
 | 
			
		|||
{% extends "hosting/base_short.html" %}
 | 
			
		||||
{% load staticfiles bootstrap3 %}
 | 
			
		||||
{% block content %} 
 | 
			
		||||
<div>
 | 
			
		||||
	<div class="container virtual-machine-container dashboard-container ">
 | 
			
		||||
		<div class="row">
 | 
			
		||||
			<div class="col-md-9 col-md-offset-2">
 | 
			
		||||
				 <div  class="col-sm-12">
 | 
			
		||||
				        <h3><i class="fa fa-cloud" aria-hidden="true"></i> {{virtual_machine.name}}</h3>
 | 
			
		||||
				        <hr/>
 | 
			
		||||
				        <div class="col-md-3"> <!-- required for floating -->
 | 
			
		||||
				          <!-- Nav tabs -->
 | 
			
		||||
				          <ul class="nav nav-tabs tabs-left sideways">
 | 
			
		||||
				            <li class="active">
 | 
			
		||||
				            	<a href="#settings-v" data-toggle="tab">
 | 
			
		||||
				            		<i class="fa fa-cogs" aria-hidden="true"></i>
 | 
			
		||||
				            		Settings
 | 
			
		||||
				            	</a>
 | 
			
		||||
				            </li>
 | 
			
		||||
				            <li>
 | 
			
		||||
				            	<a href="#billing-v" data-toggle="tab">
 | 
			
		||||
				            		<i class="fa fa-money" aria-hidden="true"></i>
 | 
			
		||||
				            		Billing
 | 
			
		||||
				            	</a>
 | 
			
		||||
				            </li>
 | 
			
		||||
				            <li>
 | 
			
		||||
				            	<a href="#orders-v" data-toggle="tab">
 | 
			
		||||
				            		<i class="fa fa-credit-card"></i> 
 | 
			
		||||
				            		Orders
 | 
			
		||||
				            	</a>
 | 
			
		||||
				            </li>
 | 
			
		||||
				            <li>
 | 
			
		||||
				            	<a href="#status-v" data-toggle="tab">
 | 
			
		||||
				            		<i class="fa fa-signal" aria-hidden="true"></i> Status
 | 
			
		||||
				            	</a>
 | 
			
		||||
				            </li>
 | 
			
		||||
				          </ul>
 | 
			
		||||
				        </div>
 | 
			
		||||
 | 
			
		||||
				        <div class="col-md-9">
 | 
			
		||||
				          <!-- Tab panes -->
 | 
			
		||||
				          <div class="tab-content">
 | 
			
		||||
				            <div class="tab-pane active" id="settings-v">
 | 
			
		||||
				            	<div class="row">
 | 
			
		||||
									<div class="col-md-12">
 | 
			
		||||
									<h3>{{virtual_machine.hosting_company_name}}</h3>
 | 
			
		||||
									<hr>
 | 
			
		||||
									</div>
 | 
			
		||||
				            	</div>
 | 
			
		||||
								<div class="row">
 | 
			
		||||
								  <div class="col-md-12">
 | 
			
		||||
								    <div class="row">
 | 
			
		||||
								      <div class="col-md-3">
 | 
			
		||||
								        <div class="well text-center">
 | 
			
		||||
								        	<i class="fa fa-cubes" aria-hidden="true"></i> Cores <br/>
 | 
			
		||||
								        	<span class="label label-success">{{virtual_machine.cores}}</span>
 | 
			
		||||
								        </div>
 | 
			
		||||
								      </div>
 | 
			
		||||
								      <div class="col-md-3">
 | 
			
		||||
								        <div class="well text-center">
 | 
			
		||||
								        	<i class="fa fa-tachometer" aria-hidden="true"></i> Memory <br/>
 | 
			
		||||
								        	<span class="label label-success">{{virtual_machine.memory}} GiB</span>
 | 
			
		||||
								        </div>
 | 
			
		||||
								      </div>
 | 
			
		||||
								      <div class="col-md-3">
 | 
			
		||||
								        <div class="well text-center">
 | 
			
		||||
								        	<i class="fa fa-hdd-o" aria-hidden="true"></i> Disk <br/>
 | 
			
		||||
								        	<span class="label label-success">{{virtual_machine.disk_size}} GiB</span>
 | 
			
		||||
								        </div>
 | 
			
		||||
								      </div>
 | 
			
		||||
								    </div><!--/row-->    
 | 
			
		||||
								  </div><!--/col-12-->
 | 
			
		||||
								</div><!--/row-->
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
				            </div>
 | 
			
		||||
				            <div class="tab-pane" id="billing-v">
 | 
			
		||||
 | 
			
		||||
				            	<div class="row ">
 | 
			
		||||
									<div class="col-md-12 inline-headers">
 | 
			
		||||
										<h3>Current pricing</h3>
 | 
			
		||||
										<span class="h3 pull-right"><strong>{{virtual_machine.price|floatformat}} CHF</strong>/mo</span> 
 | 
			
		||||
										<hr>
 | 
			
		||||
									</div>
 | 
			
		||||
				            	</div>
 | 
			
		||||
				            </div>
 | 
			
		||||
				            <div class="tab-pane" id="orders-v">
 | 
			
		||||
								<div class="row">
 | 
			
		||||
								  <div class="col-md-12">
 | 
			
		||||
									<table class="table borderless table-hover"> 
 | 
			
		||||
										<h3>Orders</h3> 
 | 
			
		||||
										<br/>
 | 
			
		||||
										<thead> 
 | 
			
		||||
										<tr> 
 | 
			
		||||
											<th>#</th>
 | 
			
		||||
											<th>Date</th>
 | 
			
		||||
											<th>Amount</th>
 | 
			
		||||
											<th>Status</th>
 | 
			
		||||
											<th></th>
 | 
			
		||||
										</tr> 
 | 
			
		||||
										</thead> 
 | 
			
		||||
										<tbody> 
 | 
			
		||||
											{% for order in virtual_machine.hosting_orders.all %}
 | 
			
		||||
											<tr> 
 | 
			
		||||
												<td scope="row">{{order.id}}</td> 
 | 
			
		||||
												<td>{{order.created_at}}</td> 
 | 
			
		||||
												<td>{{order.VMPlan.price}} CHF</td> 
 | 
			
		||||
												<td>{% if order.approved %}
 | 
			
		||||
														<span class="text-success strong">Approved</span>
 | 
			
		||||
													{% else%} 
 | 
			
		||||
														<span class="text-danger strong">Declined</span>
 | 
			
		||||
													{% endif%}
 | 
			
		||||
												</td> 
 | 
			
		||||
												<td>
 | 
			
		||||
													<button type="button" class="btn btn-default"><a href="{% url 'hosting:orders' order.id %}">View Detail</a></button>
 | 
			
		||||
												</td>
 | 
			
		||||
											</tr>
 | 
			
		||||
											{% endfor %}
 | 
			
		||||
										</tbody> 
 | 
			
		||||
									</table>
 | 
			
		||||
								  </div><!--/col-12-->
 | 
			
		||||
								</div><!--/row-->
 | 
			
		||||
				            </div>
 | 
			
		||||
				            <div class="tab-pane" id="status-v">
 | 
			
		||||
 | 
			
		||||
				            	<div class="row ">
 | 
			
		||||
									<div class="col-md-12 inline-headers">
 | 
			
		||||
										<h3>Current status</h3>
 | 
			
		||||
										<span class="h3 pull-right label label-success"><strong>Online</strong></span> 
 | 
			
		||||
										<hr>
 | 
			
		||||
									</div>
 | 
			
		||||
				            	</div>
 | 
			
		||||
				            </div>
 | 
			
		||||
				          </div>
 | 
			
		||||
				        </div>
 | 
			
		||||
 | 
			
		||||
				        <div class="clearfix"></div>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
 | 
			
		||||
	    </div>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
{%endblock%}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										56
									
								
								hosting/templates/hosting/virtual_machines.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								hosting/templates/hosting/virtual_machines.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
{% extends "hosting/base_short.html" %}
 | 
			
		||||
{% load staticfiles bootstrap3 %}
 | 
			
		||||
{% block content %} 
 | 
			
		||||
<div>
 | 
			
		||||
	<div class="container dashboard-container">
 | 
			
		||||
		<div class="row">
 | 
			
		||||
			<div class="col-md-8 col-md-offset-2">
 | 
			
		||||
				<table class="table borderless table-hover"> 
 | 
			
		||||
				<h3><i class="fa fa-server" aria-hidden="true"></i> Virtual Machines</h3> 
 | 
			
		||||
				<br/>
 | 
			
		||||
				<thead> 
 | 
			
		||||
				<tr> 
 | 
			
		||||
					<th>ID</th>
 | 
			
		||||
					<th>Type</th>
 | 
			
		||||
					<th>Amount</th>
 | 
			
		||||
					<th></th>
 | 
			
		||||
				</tr>
 | 
			
		||||
				</thead>
 | 
			
		||||
				<tbody> 
 | 
			
		||||
					{% for vm in vms %}
 | 
			
		||||
					<tr> 
 | 
			
		||||
						<td scope="row">{{vm.name}}</td> 
 | 
			
		||||
						<td>{{vm.hosting_company_name}}</td> 
 | 
			
		||||
						<td>{{vm.price}} CHF</td> 
 | 
			
		||||
						<td>
 | 
			
		||||
							<button type="button" class="btn btn-default"><a href="{% url 'hosting:virtual_machines' vm.id %}">View Detail</a></button>
 | 
			
		||||
						</td>
 | 
			
		||||
					</tr>
 | 
			
		||||
					{% endfor %}
 | 
			
		||||
				</tbody> 
 | 
			
		||||
				</table>
 | 
			
		||||
 | 
			
		||||
			    {% if is_paginated %}
 | 
			
		||||
			        <div class="pagination">
 | 
			
		||||
			            <span class="page-links">
 | 
			
		||||
			                {% if page_obj.has_previous %}
 | 
			
		||||
			                    <a href="{{request.path}}?page={{ page_obj.previous_page_number }}">previous</a>
 | 
			
		||||
			                {% endif %}
 | 
			
		||||
			                <span class="page-current">
 | 
			
		||||
			                    Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
 | 
			
		||||
			                </span>
 | 
			
		||||
			                {% if page_obj.has_next %}
 | 
			
		||||
			                    <a href="{{request.path}}?page={{ page_obj.next_page_number }}">next</a>
 | 
			
		||||
			                {% endif %}
 | 
			
		||||
			            </span>
 | 
			
		||||
			        </div>
 | 
			
		||||
			    {% endif %}
 | 
			
		||||
				
 | 
			
		||||
			</div>
 | 
			
		||||
 | 
			
		||||
	    </div>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
{%endblock%}
 | 
			
		||||
							
								
								
									
										72
									
								
								hosting/test_views.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								hosting/test_views.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
from django.test import TestCase
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
from django.core.urlresolvers import resolve
 | 
			
		||||
from .models import VirtualMachineType
 | 
			
		||||
from .views import DjangoHostingView, RailsHostingView, NodeJSHostingView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProcessVMSelectionTestMixin(object):
 | 
			
		||||
 | 
			
		||||
    def url_resolve_to_view_correctly(self):
 | 
			
		||||
        found = resolve(self.url)
 | 
			
		||||
        self.assertEqual(found.func.__name__, self.view.__name__)
 | 
			
		||||
 | 
			
		||||
    def test_get(self):
 | 
			
		||||
        response = self.client.get(self.url)
 | 
			
		||||
        self.assertEqual(response.status_code, 200)
 | 
			
		||||
        self.assertEqual(self.view.get_context_data(), self.expected_context)
 | 
			
		||||
        self.assertEqual(response.context['hosting'], self.expected_context['hosting'])
 | 
			
		||||
        self.assertTemplateUsed(response, self.expected_template)
 | 
			
		||||
 | 
			
		||||
    def test_anonymous_post(self):
 | 
			
		||||
        response = self.client.post(self.url)
 | 
			
		||||
        self.assertRedirects(response, expected_url=reverse('hosting:login'),
 | 
			
		||||
                             status_code=302, target_status_code=200)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DjangoHostingViewTest(TestCase, ProcessVMSelectionTestMixin):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.url = reverse('django.hosting')
 | 
			
		||||
        self.view = DjangoHostingView()
 | 
			
		||||
        self.expected_template = 'hosting/django.html'
 | 
			
		||||
        self.expected_context = {
 | 
			
		||||
            'hosting': "django",
 | 
			
		||||
            'hosting_long': "Django",
 | 
			
		||||
            'domain': "django-hosting.ch",
 | 
			
		||||
            'google_analytics': "UA-62285904-6",
 | 
			
		||||
            'email': "info@django-hosting.ch",
 | 
			
		||||
            'vm_types': VirtualMachineType.get_serialized_vm_types(),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RailsHostingViewTest(TestCase, ProcessVMSelectionTestMixin):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.url = reverse('rails.hosting')
 | 
			
		||||
        self.view = RailsHostingView()
 | 
			
		||||
        self.expected_template = 'hosting/rails.html'
 | 
			
		||||
        self.expected_context = {
 | 
			
		||||
            'hosting': "rails",
 | 
			
		||||
            'hosting_long': "Ruby On Rails",
 | 
			
		||||
            'domain': "rails-hosting.ch",
 | 
			
		||||
            'google_analytics': "UA-62285904-5",
 | 
			
		||||
            'email': "info@rails-hosting.ch",
 | 
			
		||||
            'vm_types': VirtualMachineType.get_serialized_vm_types(),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NodeJSHostingViewTest(TestCase, ProcessVMSelectionTestMixin):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.url = reverse('node.hosting')
 | 
			
		||||
        self.view = NodeJSHostingView()
 | 
			
		||||
        self.expected_template = 'hosting/nodejs.html'
 | 
			
		||||
        self.expected_context = {
 | 
			
		||||
            'hosting': "nodejs",
 | 
			
		||||
            'hosting_long': "NodeJS",
 | 
			
		||||
            'domain': "node-hosting.ch",
 | 
			
		||||
            'google_analytics': "UA-62285904-7",
 | 
			
		||||
            'email': "info@node-hosting.ch",
 | 
			
		||||
            'vm_types': VirtualMachineType.get_serialized_vm_types(),
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,20 +1,24 @@
 | 
			
		|||
from django.conf.urls import url
 | 
			
		||||
 | 
			
		||||
from .views import DjangoHostingView, RailsHostingView, PaymentVMView, \
 | 
			
		||||
                    NodeJSHostingView, LoginView, SignupView, IndexView, \
 | 
			
		||||
                    InvoiceVMView, OrdersHostingView
 | 
			
		||||
    NodeJSHostingView, LoginView, SignupView, IndexView, \
 | 
			
		||||
    OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\
 | 
			
		||||
    VirtualMachineDetailListView
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    # 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'nodejs/?$', NodeJSHostingView.as_view(), name='nodejshosting'),
 | 
			
		||||
    url(r'rails/?$', RailsHostingView.as_view(),   name='railshosting'),
 | 
			
		||||
    url(r'rails/?$', RailsHostingView.as_view(), name='railshosting'),
 | 
			
		||||
    url(r'payment/?$', PaymentVMView.as_view(), name='payment'),
 | 
			
		||||
    url(r'invoice/?$', InvoiceVMView.as_view(), name='invoice'),
 | 
			
		||||
    url(r'orders/?$', OrdersHostingView.as_view(), name='orders'),
 | 
			
		||||
    url(r'login/?$', LoginView.as_view(),  name='login'),
 | 
			
		||||
    url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'),
 | 
			
		||||
    url(r'orders/(?P<pk>\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'),
 | 
			
		||||
    url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'),
 | 
			
		||||
    url(r'my-virtual-machines/(?P<pk>\d+)/?$', VirtualMachineDetailListView.as_view(),
 | 
			
		||||
        name='virtual_machines'),
 | 
			
		||||
    url(r'login/?$', LoginView.as_view(), name='login'),
 | 
			
		||||
    url(r'signup/?$', SignupView.as_view(), name='signup'),
 | 
			
		||||
    url(r'^logout/?$', 'django.contrib.auth.views.logout',
 | 
			
		||||
                          {'next_page': '/ungleich_page'}, name='logout')
 | 
			
		||||
        {'next_page': '/ungleich_page'}, name='logout')
 | 
			
		||||
]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,16 +2,12 @@
 | 
			
		|||
from django.shortcuts import get_object_or_404, render
 | 
			
		||||
from django.core.urlresolvers import reverse_lazy, reverse
 | 
			
		||||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
			
		||||
from django.contrib.auth.decorators import login_required
 | 
			
		||||
from django.utils.decorators import method_decorator
 | 
			
		||||
 | 
			
		||||
from django.views.generic import View, CreateView, FormView
 | 
			
		||||
from django.shortcuts import redirect
 | 
			
		||||
from django.views.generic import View, CreateView, FormView, ListView, DetailView
 | 
			
		||||
from django.http import HttpResponseRedirect
 | 
			
		||||
from django.contrib.auth import authenticate, login
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
 | 
			
		||||
from membership.forms import PaymentForm
 | 
			
		||||
from membership.models import CustomUser, StripeCustomer
 | 
			
		||||
from utils.stripe_utils import StripeUtils
 | 
			
		||||
from utils.forms import BillingAddressForm
 | 
			
		||||
| 
						 | 
				
			
			@ -21,7 +17,6 @@ from .forms import HostingUserSignupForm, HostingUserLoginForm
 | 
			
		|||
from .mixins import ProcessVMSelectionMixin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DjangoHostingView(ProcessVMSelectionMixin, View):
 | 
			
		||||
    template_name = "hosting/django.html"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +101,7 @@ class IndexView(View):
 | 
			
		|||
 | 
			
		||||
class LoginView(FormView):
 | 
			
		||||
    template_name = 'hosting/login.html'
 | 
			
		||||
    success_url = reverse_lazy('hosting:login')
 | 
			
		||||
    success_url = reverse_lazy('hosting:orders')
 | 
			
		||||
    form_class = HostingUserLoginForm
 | 
			
		||||
    moodel = CustomUser
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -217,60 +212,45 @@ class PaymentVMView(FormView):
 | 
			
		|||
                'order': order.id,
 | 
			
		||||
                'billing_address': billing_address.id
 | 
			
		||||
            })
 | 
			
		||||
            return HttpResponseRedirect(reverse('hosting:invoice'))
 | 
			
		||||
            return HttpResponseRedirect(reverse('hosting:orders', kwargs={'pk': order.id}))
 | 
			
		||||
        else:
 | 
			
		||||
            return self.form_invalid(form)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InvoiceVMView(LoginRequiredMixin, View):
 | 
			
		||||
    template_name = "hosting/invoice.html"
 | 
			
		||||
class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
 | 
			
		||||
    template_name = "hosting/order_detail.html"
 | 
			
		||||
    login_url = reverse_lazy('hosting:login')
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        charge = self.request.session.get('charge')
 | 
			
		||||
        order_id = self.request.session.get('order')
 | 
			
		||||
        billing_address_id = self.request.session.get('billing_address')
 | 
			
		||||
        last4 = charge.get('source').get('last4')
 | 
			
		||||
        brand = charge.get('source').get('brand')
 | 
			
		||||
 | 
			
		||||
        order = get_object_or_404(HostingOrder, pk=order_id)
 | 
			
		||||
        billing_address = get_object_or_404(BillingAddress, pk=billing_address_id)
 | 
			
		||||
 | 
			
		||||
        if not charge:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        context = {
 | 
			
		||||
            'last4': last4,
 | 
			
		||||
            'brand': brand,
 | 
			
		||||
            'order': order,
 | 
			
		||||
            'billing_address': billing_address,
 | 
			
		||||
        }
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
    def get(self, request, *args, **kwargs):
 | 
			
		||||
 | 
			
		||||
        context = self.get_context_data()
 | 
			
		||||
 | 
			
		||||
        return render(request, self.template_name, context)
 | 
			
		||||
    model = HostingOrder
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OrdersHostingView(LoginRequiredMixin, View):
 | 
			
		||||
class OrdersHostingListView(LoginRequiredMixin, ListView):
 | 
			
		||||
    template_name = "hosting/orders.html"
 | 
			
		||||
    login_url = reverse_lazy('hosting:login')
 | 
			
		||||
    context_object_name = "orders"
 | 
			
		||||
    model = HostingOrder
 | 
			
		||||
    paginate_by = 10
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        user = self.request.user
 | 
			
		||||
        orders = HostingOrder.objects.filter(customer__user=user)
 | 
			
		||||
        context = {
 | 
			
		||||
            'orders':orders
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
    def get(self, request, *args, **kwargs):
 | 
			
		||||
 | 
			
		||||
        context = self.get_context_data()
 | 
			
		||||
 | 
			
		||||
        return render(request, self.template_name, context)
 | 
			
		||||
        self.queryset = HostingOrder.objects.filter(customer__user=user)
 | 
			
		||||
        return super(OrdersHostingListView, self).get_queryset()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
 | 
			
		||||
    template_name = "hosting/virtual_machines.html"
 | 
			
		||||
    login_url = reverse_lazy('hosting:login')
 | 
			
		||||
    context_object_name = "vms"
 | 
			
		||||
    model = VirtualMachinePlan
 | 
			
		||||
    paginate_by = 10
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        user = self.request.user
 | 
			
		||||
        self.queryset = VirtualMachinePlan.objects.active(user)
 | 
			
		||||
        return super(VirtualMachinesPlanListView, self).get_queryset()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VirtualMachineDetailListView(LoginRequiredMixin, DetailView):
 | 
			
		||||
    template_name = "hosting/virtual_machine_detail.html"
 | 
			
		||||
    login_url = reverse_lazy('hosting:login')
 | 
			
		||||
    model = VirtualMachinePlan
 | 
			
		||||
    context_object_name = "virtual_machine"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -131,7 +131,7 @@ 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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,8 +52,6 @@ def handleStripeError(f):
 | 
			
		|||
    return handleProblems
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StripeUtils(object):
 | 
			
		||||
    CURRENCY = 'chf'
 | 
			
		||||
    INTERVAL = 'month'
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +69,7 @@ class StripeUtils(object):
 | 
			
		|||
                customer = stripe.Customer.retrieve(id)
 | 
			
		||||
            except stripe.InvalidRequestError:
 | 
			
		||||
                customer = self.create_customer(token, user.email)
 | 
			
		||||
                user.stripecustomer.stripe_id = customer.get('id')
 | 
			
		||||
                user.stripecustomer.stripe_id = customer.get('response_object').get('id')
 | 
			
		||||
                user.stripecustomer.save()
 | 
			
		||||
        return customer
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue