Merge pull request #81 from levivm/develop
SSH Key, notifications and VM status.
This commit is contained in:
commit
350d09f339
22 changed files with 392 additions and 22 deletions
|
@ -54,6 +54,7 @@ INSTALLED_APPS = (
|
||||||
'django.contrib.sites',
|
'django.contrib.sites',
|
||||||
'easy_thumbnails',
|
'easy_thumbnails',
|
||||||
'utils',
|
'utils',
|
||||||
|
'stored_messages',
|
||||||
'mptt',
|
'mptt',
|
||||||
'parler',
|
'parler',
|
||||||
'taggit',
|
'taggit',
|
||||||
|
@ -132,6 +133,7 @@ TEMPLATES = [
|
||||||
'DIRS': [os.path.join(PROJECT_DIR, 'cms_templates/'),
|
'DIRS': [os.path.join(PROJECT_DIR, 'cms_templates/'),
|
||||||
os.path.join(PROJECT_DIR, 'cms_templates/djangocms_blog/'),
|
os.path.join(PROJECT_DIR, 'cms_templates/djangocms_blog/'),
|
||||||
os.path.join(PROJECT_DIR, 'membership'),
|
os.path.join(PROJECT_DIR, 'membership'),
|
||||||
|
os.path.join(PROJECT_DIR, 'hosting/templates/'),
|
||||||
os.path.join(PROJECT_DIR, 'ungleich/templates/djangocms_blog/'),
|
os.path.join(PROJECT_DIR, 'ungleich/templates/djangocms_blog/'),
|
||||||
os.path.join(PROJECT_DIR, 'ungleich/templates/cms/ungleichch'),
|
os.path.join(PROJECT_DIR, 'ungleich/templates/cms/ungleichch'),
|
||||||
os.path.join(PROJECT_DIR, 'ungleich/templates/ungleich')
|
os.path.join(PROJECT_DIR, 'ungleich/templates/ungleich')
|
||||||
|
|
|
@ -1,5 +1,32 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import VirtualMachineType
|
|
||||||
|
from utils.mailer import BaseEmail
|
||||||
|
from .models import VirtualMachineType, VirtualMachinePlan
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualMachinePlanAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('name', 'id', 'email')
|
||||||
|
|
||||||
|
def email(self, obj):
|
||||||
|
return obj.hosting_orders.latest('id').customer.user.email
|
||||||
|
|
||||||
|
def save_model(self, request, obj, form, change):
|
||||||
|
email = self.email(obj)
|
||||||
|
if 'status' in form.changed_data and obj.status == VirtualMachinePlan.ONLINE_STATUS:
|
||||||
|
context = {
|
||||||
|
'vm': obj
|
||||||
|
}
|
||||||
|
email_data = {
|
||||||
|
'subject': 'Your VM has been activated',
|
||||||
|
'to': email,
|
||||||
|
'context': context,
|
||||||
|
'template_name': 'vm_activated',
|
||||||
|
'template_path': 'emails/'
|
||||||
|
}
|
||||||
|
email = BaseEmail(**email_data)
|
||||||
|
email.send()
|
||||||
|
obj.save()
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(VirtualMachineType)
|
admin.site.register(VirtualMachineType)
|
||||||
|
admin.site.register(VirtualMachinePlan, VirtualMachinePlanAdmin)
|
||||||
|
|
20
hosting/migrations/0019_virtualmachineplan_status.py
Normal file
20
hosting/migrations/0019_virtualmachineplan_status.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-05-26 02:57
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('hosting', '0018_virtualmachineplan_public_key'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='virtualmachineplan',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(choices=[('pending', 'Pending for activation'), ('online', 'Online')], default='online', max_length=20),
|
||||||
|
),
|
||||||
|
]
|
20
hosting/migrations/0020_auto_20160526_0258.py
Normal file
20
hosting/migrations/0020_auto_20160526_0258.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-05-26 02:58
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('hosting', '0019_virtualmachineplan_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='virtualmachineplan',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(choices=[('pending', 'Pending for activation'), ('online', 'Online')], default='pending', max_length=20),
|
||||||
|
),
|
||||||
|
]
|
20
hosting/migrations/0021_auto_20160526_0445.py
Normal file
20
hosting/migrations/0021_auto_20160526_0445.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-05-26 04:45
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('hosting', '0020_auto_20160526_0258'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='virtualmachineplan',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(choices=[('pending', 'Pending for activation'), ('online', 'Online'), ('canceled', 'Canceled')], default='pending', max_length=20),
|
||||||
|
),
|
||||||
|
]
|
|
@ -3,12 +3,13 @@ import os
|
||||||
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.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from membership.models import StripeCustomer
|
|
||||||
from utils.models import BillingAddress
|
|
||||||
|
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
|
from stored_messages.settings import stored_messages_settings
|
||||||
|
|
||||||
|
from membership.models import StripeCustomer
|
||||||
|
from utils.models import BillingAddress
|
||||||
from .managers import VMPlansManager
|
from .managers import VMPlansManager
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,12 +82,24 @@ class VirtualMachineType(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class VirtualMachinePlan(models.Model):
|
class VirtualMachinePlan(models.Model):
|
||||||
|
|
||||||
|
PENDING_STATUS = 'pending'
|
||||||
|
ONLINE_STATUS = 'online'
|
||||||
|
CANCELED_STATUS = 'canceled'
|
||||||
|
|
||||||
|
VM_STATUS_CHOICES = (
|
||||||
|
(PENDING_STATUS, 'Pending for activation'),
|
||||||
|
(ONLINE_STATUS, 'Online'),
|
||||||
|
(CANCELED_STATUS, 'Canceled')
|
||||||
|
)
|
||||||
|
|
||||||
cores = models.IntegerField()
|
cores = models.IntegerField()
|
||||||
memory = models.IntegerField()
|
memory = models.IntegerField()
|
||||||
disk_size = models.IntegerField()
|
disk_size = models.IntegerField()
|
||||||
vm_type = models.ForeignKey(VirtualMachineType)
|
vm_type = models.ForeignKey(VirtualMachineType)
|
||||||
price = models.FloatField()
|
price = models.FloatField()
|
||||||
public_key = models.TextField()
|
public_key = models.TextField()
|
||||||
|
status = models.CharField(max_length=20, choices=VM_STATUS_CHOICES, default=PENDING_STATUS)
|
||||||
|
|
||||||
objects = VMPlansManager()
|
objects = VMPlansManager()
|
||||||
|
|
||||||
|
@ -97,11 +110,22 @@ class VirtualMachinePlan(models.Model):
|
||||||
def hosting_company_name(self):
|
def hosting_company_name(self):
|
||||||
return self.vm_type.get_hosting_company_display()
|
return self.vm_type.get_hosting_company_display()
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def location(self):
|
||||||
|
return self.vm_type.get_location_display()
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def name(self):
|
def name(self):
|
||||||
name = 'vm-%s' % self.id
|
name = 'vm-%s' % self.id
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def notifications(self):
|
||||||
|
stripe_customer = StripeCustomer.objects.get(hostingorder__vm_plan=self)
|
||||||
|
backend = stored_messages_settings.STORAGE_BACKEND()
|
||||||
|
messages = backend.inbox_list(stripe_customer.user)
|
||||||
|
return messages
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, data, user):
|
def create(cls, data, user):
|
||||||
instance = cls.objects.create(**data)
|
instance = cls.objects.create(**data)
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 376 B After Width: | Height: | Size: 741 B |
12
hosting/templates/emails/new_booked_vm.html
Normal file
12
hosting/templates/emails/new_booked_vm.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
NEW VM BOOKED
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
12
hosting/templates/emails/new_booked_vm.txt
Normal file
12
hosting/templates/emails/new_booked_vm.txt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
NEW VM BOOKED
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
13
hosting/templates/emails/vm_activated.html
Normal file
13
hosting/templates/emails/vm_activated.html
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
{% load staticfiles bootstrap3%}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
You virtual machine {{vm.name}} has been activated. You can manage your vm on this <a href="{{request.HOS}}{% url 'hosting:virtual_machines' vm.id %}"> link </a>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
15
hosting/templates/emails/vm_activated.txt
Normal file
15
hosting/templates/emails/vm_activated.txt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
{% load staticfiles bootstrap3%}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
You virtual machine {{vm.name}} has been activated. You can manage your vm in this <a href="{% url 'hosting:virtual_machines' vm.id %}"> link </a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -71,7 +71,11 @@
|
||||||
<i class="fa fa-credit-card"></i> My Orders
|
<i class="fa fa-credit-card"></i> My Orders
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'hosting:notifications' %}">
|
||||||
|
<i class="fa fa-bell"></i> Notifications
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a class="dropdown-toggle" role="button" data-toggle="dropdown" href="#">
|
<a class="dropdown-toggle" role="button" data-toggle="dropdown" href="#">
|
||||||
<i class="glyphicon glyphicon-user"></i> {{request.user.name}} <span class="caret"></span></a>
|
<i class="glyphicon glyphicon-user"></i> {{request.user.name}} <span class="caret"></span></a>
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
<ul class="pricing {% cycle 'p-red' 'p-black' 'p-red' 'p-yel' %}">
|
<ul class="pricing {% cycle 'p-red' 'p-black' 'p-red' 'p-yel' %}">
|
||||||
<li class="type">
|
<li class="type">
|
||||||
<!-- <img src="http://bread.pp.ua/n/settings_g.svg" alt=""> -->
|
<!-- <img src="http://bread.pp.ua/n/settings_g.svg" alt=""> -->
|
||||||
<h3 >{{vm.hosting_company_name}}</h3>
|
<h3 >{{vm.location_code}}</h3>
|
||||||
<br/>
|
<br/>
|
||||||
<img class="img-responsive" src="{{ STATIC_URL }}hosting/img/{{vm.location_code}}_flag.png" alt="">
|
<img class="img-responsive" src="{{ STATIC_URL }}hosting/img/{{vm.location_code}}_flag.png" alt="">
|
||||||
|
|
||||||
|
|
90
hosting/templates/hosting/notifications.html
Normal file
90
hosting/templates/hosting/notifications.html
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
{% 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-bell" aria-hidden="true"></i> Notifications</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="#unread-v" data-toggle="tab">
|
||||||
|
Unread <span class="badge">{{unread_notifications|length}}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#all-v" data-toggle="tab">
|
||||||
|
All
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-9">
|
||||||
|
<!-- Tab panes -->
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane active" id="unread-v">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<h3>Unread notifications</h3>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
{% for notification in unread_notifications %}
|
||||||
|
<form method="POST" action="{% url 'hosting:read_notification' notification.id %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<span>{{notification}} -</span>
|
||||||
|
<button type="submit" class="btn btn-link">Mark as read</button>
|
||||||
|
<span class="pull-right" style="font-size: 11px;color: #999;">{{notification.date}}</span>
|
||||||
|
</form>
|
||||||
|
<hr/>
|
||||||
|
{% endfor %}
|
||||||
|
</div><!--/col-12-->
|
||||||
|
</div><!--/row-->
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane" id="all-v">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<h3>All notifications</h3>
|
||||||
|
<hr>
|
||||||
|
{% for notification in all_notifications %}
|
||||||
|
<span>{{notification.message}} </span>
|
||||||
|
<span class="pull-right" style="font-size: 11px;color: #999;">{{notification.message.date}}</span>
|
||||||
|
<hr/>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
</div><!--/col-12-->
|
||||||
|
</div><!--/row-->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{%endblock%}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -122,12 +122,16 @@
|
||||||
</div><!--/row-->
|
</div><!--/row-->
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="status-v">
|
<div class="tab-pane" id="status-v">
|
||||||
|
|
||||||
<div class="row ">
|
<div class="row ">
|
||||||
<div class="col-md-12 inline-headers">
|
<div class="col-md-12 inline-headers">
|
||||||
<h3>Current status</h3>
|
<h3>Current status</h3>
|
||||||
<span class="h3 pull-right label label-success"><strong>Online</strong></span>
|
{% if virtual_machine.status == 'pending' %}
|
||||||
<hr>
|
<span class="h3 pull-right label label-warning"><strong>{{virtual_machine.get_status_display}}</strong></span>
|
||||||
|
{% elif virtual_machine.status == 'online' %}
|
||||||
|
<span class="h3 pull-right label label-success"><strong>{{virtual_machine.get_status_display}}</strong></span>
|
||||||
|
{% else %}
|
||||||
|
<span class="h3 pull-right label label-error"><strong>{{virtual_machine.get_status_display}}</strong></span>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>Type</th>
|
<th>Location</th>
|
||||||
<th>Amount</th>
|
<th>Amount</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
{% for vm in vms %}
|
{% for vm in vms %}
|
||||||
<tr>
|
<tr>
|
||||||
<td scope="row">{{vm.name}}</td>
|
<td scope="row">{{vm.name}}</td>
|
||||||
<td>{{vm.hosting_company_name}}</td>
|
<td>{{vm.location}}</td>
|
||||||
<td>{{vm.price}} CHF</td>
|
<td>{{vm.price}} CHF</td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-default"><a href="{% url 'hosting:virtual_machines' vm.id %}">View Detail</a></button>
|
<button type="button" class="btn btn-default"><a href="{% url 'hosting:virtual_machines' vm.id %}">View Detail</a></button>
|
||||||
|
|
|
@ -3,7 +3,8 @@ from django.conf.urls import url
|
||||||
from .views import DjangoHostingView, RailsHostingView, PaymentVMView,\
|
from .views import DjangoHostingView, RailsHostingView, PaymentVMView,\
|
||||||
NodeJSHostingView, LoginView, SignupView, IndexView, \
|
NodeJSHostingView, LoginView, SignupView, IndexView, \
|
||||||
OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\
|
OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\
|
||||||
VirtualMachineDetailView, GenerateVMSSHKeysView, OrdersHostingDeleteView
|
VirtualMachineDetailView, GenerateVMSSHKeysView, OrdersHostingDeleteView, NotificationsView, \
|
||||||
|
MarkAsReadNotificationView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# url(r'pricing/?$', VMPricingView.as_view(), name='pricing'),
|
# url(r'pricing/?$', VMPricingView.as_view(), name='pricing'),
|
||||||
|
@ -20,6 +21,9 @@ urlpatterns = [
|
||||||
name='virtual_machines'),
|
name='virtual_machines'),
|
||||||
url(r'my-virtual-machines/(?P<pk>\d+)/key/?$', GenerateVMSSHKeysView.as_view(),
|
url(r'my-virtual-machines/(?P<pk>\d+)/key/?$', GenerateVMSSHKeysView.as_view(),
|
||||||
name='virtual_machine_key'),
|
name='virtual_machine_key'),
|
||||||
|
url(r'^notifications/$', NotificationsView.as_view(), name='notifications'),
|
||||||
|
url(r'^notifications/(?P<pk>\d+)/?$', MarkAsReadNotificationView.as_view(),
|
||||||
|
name='read_notification'),
|
||||||
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'^logout/?$', 'django.contrib.auth.views.logout',
|
url(r'^logout/?$', 'django.contrib.auth.views.logout',
|
||||||
|
|
|
@ -2,17 +2,22 @@
|
||||||
from django.shortcuts import get_object_or_404, render,render_to_response
|
from django.shortcuts import get_object_or_404, render,render_to_response
|
||||||
from django.core.urlresolvers import reverse_lazy, reverse
|
from django.core.urlresolvers import reverse_lazy, reverse
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.views.generic import View, CreateView, FormView, ListView, DetailView,\
|
||||||
from django.views.generic import View, CreateView, FormView, ListView, DetailView, UpdateView, DeleteView
|
DeleteView, TemplateView, UpdateView
|
||||||
from django.http import HttpResponseRedirect, HttpResponse
|
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 django.conf import settings
|
||||||
from django.contrib import messages
|
|
||||||
|
|
||||||
|
from stored_messages.settings import stored_messages_settings
|
||||||
|
from stored_messages.models import Message
|
||||||
|
from stored_messages.api import mark_read
|
||||||
|
|
||||||
|
|
||||||
from membership.models import CustomUser, StripeCustomer
|
from membership.models import CustomUser, StripeCustomer
|
||||||
from utils.stripe_utils import StripeUtils
|
from utils.stripe_utils import StripeUtils
|
||||||
from utils.forms import BillingAddressForm
|
from utils.forms import BillingAddressForm
|
||||||
from utils.models import BillingAddress
|
from utils.mailer import BaseEmail
|
||||||
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder
|
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder
|
||||||
from .forms import HostingUserSignupForm, HostingUserLoginForm
|
from .forms import HostingUserSignupForm, HostingUserLoginForm
|
||||||
from .mixins import ProcessVMSelectionMixin
|
from .mixins import ProcessVMSelectionMixin
|
||||||
|
@ -145,6 +150,34 @@ class SignupView(CreateView):
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationsView(TemplateView):
|
||||||
|
template_name = 'hosting/notifications.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(NotificationsView, self).get_context_data(**kwargs)
|
||||||
|
backend = stored_messages_settings.STORAGE_BACKEND()
|
||||||
|
unread_notifications = backend.inbox_list(self.request.user)
|
||||||
|
read_notifications = backend.archive_list(self.request.user)
|
||||||
|
context.update({
|
||||||
|
'unread_notifications': unread_notifications,
|
||||||
|
'all_notifications': read_notifications + unread_notifications
|
||||||
|
})
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class MarkAsReadNotificationView(LoginRequiredMixin, UpdateView):
|
||||||
|
model = Message
|
||||||
|
success_url = reverse_lazy('hosting:notifications')
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def post(self, *args, **kwargs):
|
||||||
|
message = self.get_object()
|
||||||
|
backend = stored_messages_settings.STORAGE_BACKEND()
|
||||||
|
backend.archive_store([self.request.user], message)
|
||||||
|
mark_read(self.request.user, message)
|
||||||
|
return HttpResponseRedirect(reverse('hosting:notifications'))
|
||||||
|
|
||||||
|
|
||||||
class GenerateVMSSHKeysView(LoginRequiredMixin, DetailView):
|
class GenerateVMSSHKeysView(LoginRequiredMixin, DetailView):
|
||||||
model = VirtualMachinePlan
|
model = VirtualMachinePlan
|
||||||
template_name = 'hosting/virtual_machine_key.html'
|
template_name = 'hosting/virtual_machine_key.html'
|
||||||
|
@ -174,6 +207,7 @@ class PaymentVMView(LoginRequiredMixin, FormView):
|
||||||
context.update({
|
context.update({
|
||||||
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
|
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
|
||||||
})
|
})
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
@ -233,6 +267,18 @@ class PaymentVMView(LoginRequiredMixin, FormView):
|
||||||
|
|
||||||
# If the Stripe payment was successed, set order status approved
|
# If the Stripe payment was successed, set order status approved
|
||||||
order.set_approved()
|
order.set_approved()
|
||||||
|
|
||||||
|
# Send notification to ungleich as soon as VM has been booked
|
||||||
|
# TODO send email using celery
|
||||||
|
email_data = {
|
||||||
|
'subject': 'New VM request',
|
||||||
|
'to': 'info@ungleich.ch',
|
||||||
|
'template_name': 'new_booked_vm',
|
||||||
|
'template_path': 'emails/'
|
||||||
|
}
|
||||||
|
email = BaseEmail(**email_data)
|
||||||
|
email.send()
|
||||||
|
|
||||||
request.session.update({
|
request.session.update({
|
||||||
'charge': charge,
|
'charge': charge,
|
||||||
'order': order.id,
|
'order': order.id,
|
||||||
|
@ -269,6 +315,7 @@ class OrdersHostingDeleteView(LoginRequiredMixin, DeleteView):
|
||||||
success_url = reverse_lazy('hosting:orders')
|
success_url = reverse_lazy('hosting:orders')
|
||||||
model = HostingOrder
|
model = HostingOrder
|
||||||
|
|
||||||
|
|
||||||
class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
|
class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
|
||||||
template_name = "hosting/virtual_machines.html"
|
template_name = "hosting/virtual_machines.html"
|
||||||
login_url = reverse_lazy('hosting:login')
|
login_url = reverse_lazy('hosting:login')
|
||||||
|
|
31
membership/migrations/0006_auto_20160526_0445.py
Normal file
31
membership/migrations/0006_auto_20160526_0445.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2016-05-26 04:45
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0007_alter_validators_add_error_messages'),
|
||||||
|
('membership', '0005_customuser_is_admin'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customuser',
|
||||||
|
name='groups',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customuser',
|
||||||
|
name='is_superuser',
|
||||||
|
field=models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customuser',
|
||||||
|
name='user_permissions',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -2,7 +2,7 @@ from datetime import datetime
|
||||||
|
|
||||||
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.contrib.auth.models import User, AbstractBaseUser, BaseUserManager, AbstractUser
|
from django.contrib.auth.models import User, AbstractBaseUser, BaseUserManager, AbstractUser, PermissionsMixin
|
||||||
from django.contrib.auth.hashers import make_password
|
from django.contrib.auth.hashers import make_password
|
||||||
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
|
||||||
|
@ -47,7 +47,7 @@ class MyUserManager(BaseUserManager):
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
class CustomUser(AbstractBaseUser):
|
class CustomUser(AbstractBaseUser, PermissionsMixin):
|
||||||
VALIDATED_CHOICES = ((0, 'Not validated'), (1, 'Validated'))
|
VALIDATED_CHOICES = ((0, 'Not validated'), (1, 'Validated'))
|
||||||
site = models.ForeignKey(Site, default=1)
|
site = models.ForeignKey(Site, default=1)
|
||||||
name = models.CharField(max_length=50)
|
name = models.CharField(max_length=50)
|
||||||
|
|
|
@ -18,6 +18,7 @@ easy_thumbnails
|
||||||
django-polymorphic
|
django-polymorphic
|
||||||
model-mommy
|
model-mommy
|
||||||
pycryptodome
|
pycryptodome
|
||||||
|
django-stored-messages
|
||||||
|
|
||||||
#PLUGINS
|
#PLUGINS
|
||||||
djangocms_flash
|
djangocms_flash
|
||||||
|
|
|
@ -1,9 +1,32 @@
|
||||||
import six
|
import six
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
|
from django.core.mail import EmailMultiAlternatives
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
class BaseEmail(object):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.to = kwargs.get('to')
|
||||||
|
self.template_name = kwargs.get('template_name')
|
||||||
|
self.template_path = kwargs.get('template_path')
|
||||||
|
self.subject = kwargs.get('subject')
|
||||||
|
self.context = kwargs.get('context', {})
|
||||||
|
self.template_full_path = '%s%s' % (self.template_path, self.template_name)
|
||||||
|
text_content = render_to_string('%s.txt' % self.template_full_path, self.context)
|
||||||
|
html_content = render_to_string('%s.html' % self.template_full_path, self.context)
|
||||||
|
|
||||||
|
self.email = EmailMultiAlternatives(self.subject, text_content)
|
||||||
|
self.email.attach_alternative(html_content, "text/html")
|
||||||
|
self.email.to = ['info@digitalglarus.ch']
|
||||||
|
|
||||||
|
def send(self):
|
||||||
|
self.email.send()
|
||||||
|
|
||||||
|
|
||||||
class BaseMailer(object):
|
class BaseMailer(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._slug = None
|
self._slug = None
|
||||||
|
@ -50,3 +73,4 @@ class DigitalGlarusRegistrationMailer(BaseMailer):
|
||||||
self.registration = self.message
|
self.registration = self.message
|
||||||
self._message = self._message.format(slug=self._slug)
|
self._message = self._message.format(slug=self._slug)
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue