From 02e716106e371cb4c319bc85173297373ea1fa31 Mon Sep 17 00:00:00 2001
From: Levi <levinoelvm@gmail.com>
Date: Wed, 25 May 2016 01:23:32 -0500
Subject: [PATCH 1/3] Created BaseEmail class , Now we are sending email to
info@ungleich.com after an user book a VM, Fixed pricing issue, Now Admin can
changed data about a booked VM
---
dynamicweb/settings/base.py | 1 +
hosting/admin.py | 3 +-
hosting/static/hosting/img/DE_flag.png | Bin 376 -> 741 bytes
hosting/templates/emails/new_booked_vm.html | 12 ++++++++
hosting/templates/emails/new_booked_vm.txt | 12 ++++++++
.../templates/hosting/includes/_pricing.html | 2 +-
hosting/views.py | 17 +++++++++--
utils/mailer.py | 27 ++++++++++++++++++
8 files changed, 70 insertions(+), 4 deletions(-)
create mode 100644 hosting/templates/emails/new_booked_vm.html
create mode 100644 hosting/templates/emails/new_booked_vm.txt
diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py
index 5f59a319..db2912bd 100644
--- a/dynamicweb/settings/base.py
+++ b/dynamicweb/settings/base.py
@@ -132,6 +132,7 @@ TEMPLATES = [
'DIRS': [os.path.join(PROJECT_DIR, 'cms_templates/'),
os.path.join(PROJECT_DIR, 'cms_templates/djangocms_blog/'),
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/cms/ungleichch'),
os.path.join(PROJECT_DIR, 'ungleich/templates/ungleich')
diff --git a/hosting/admin.py b/hosting/admin.py
index 70392113..d8cf3012 100644
--- a/hosting/admin.py
+++ b/hosting/admin.py
@@ -1,5 +1,6 @@
from django.contrib import admin
-from .models import VirtualMachineType
+from .models import VirtualMachineType, VirtualMachinePlan
admin.site.register(VirtualMachineType)
+admin.site.register(VirtualMachinePlan)
diff --git a/hosting/static/hosting/img/DE_flag.png b/hosting/static/hosting/img/DE_flag.png
index d4f4526e9e00847d0f5bcd13b4d270453cbe2f43..33db924c79d8b468e3b4644d4e726a57f5125b75 100644
GIT binary patch
literal 741
zcmeAS@N?(olHy`uVBq!ia0y~yU~~Yo4{)#n$<pv$$AA=DlDE4H!+#K5uy^@n1_q{C
zo-U3d6?5KRa};cF5MVyodT76i%z~#AHuk>^n{Yj3-Aqenfs%ZFh8s!^4$~NzBAGZ+
zI29(S4rR{y?Ryy#I@yQ5N8oXBt@NJdZDj_tilH1WaNj_a+26P#Hq8R(Q<cDU%i!ti
K=d#Wzp$PzRSh^wr
literal 376
zcmeAS@N?(olHy`uVBq!ia0vp^CxEz>g9%7Z{G(6}q}Y<Y-CY>|gW!U_%O^81FzR}`
zIEGZrd3)87x5a^h<=}1ZXCI4%{FwO)4$AvnxS0~XG(m3e84LRl``*7{T;my`(&Fkw
z2O=rv_Z!YN8F!8Y$rGZJ1jINKC(@kgeEWUAEh8|Pu!9G+{MUD}IjZ?`{Rb)VboFyt
I=akR{0M-L+MgRZ+
diff --git a/hosting/templates/emails/new_booked_vm.html b/hosting/templates/emails/new_booked_vm.html
new file mode 100644
index 00000000..8583ba1f
--- /dev/null
+++ b/hosting/templates/emails/new_booked_vm.html
@@ -0,0 +1,12 @@
+
+<<!DOCTYPE html>
+<html>
+<head>
+ <title></title>
+</head>
+<body>
+
+NEW VM BOOKED
+
+</body>
+</html>
\ No newline at end of file
diff --git a/hosting/templates/emails/new_booked_vm.txt b/hosting/templates/emails/new_booked_vm.txt
new file mode 100644
index 00000000..8583ba1f
--- /dev/null
+++ b/hosting/templates/emails/new_booked_vm.txt
@@ -0,0 +1,12 @@
+
+<<!DOCTYPE html>
+<html>
+<head>
+ <title></title>
+</head>
+<body>
+
+NEW VM BOOKED
+
+</body>
+</html>
\ No newline at end of file
diff --git a/hosting/templates/hosting/includes/_pricing.html b/hosting/templates/hosting/includes/_pricing.html
index 1f4e308e..2f8033bc 100644
--- a/hosting/templates/hosting/includes/_pricing.html
+++ b/hosting/templates/hosting/includes/_pricing.html
@@ -29,7 +29,7 @@
<ul class="pricing {% cycle 'p-red' 'p-black' 'p-red' 'p-yel' %}">
<li class="type">
<!-- <img src="http://bread.pp.ua/n/settings_g.svg" alt=""> -->
- <h3 >{{vm.hosting_company_name}}</h3>
+ <h3 >{{vm.location_code}}</h3>
<br/>
<img class="img-responsive" src="{{ STATIC_URL }}hosting/img/{{vm.location_code}}_flag.png" alt="">
diff --git a/hosting/views.py b/hosting/views.py
index ad892143..60733274 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -12,7 +12,7 @@ from django.contrib import messages
from membership.models import CustomUser, StripeCustomer
from utils.stripe_utils import StripeUtils
from utils.forms import BillingAddressForm
-from utils.models import BillingAddress
+from utils.mailer import BaseEmail
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder
from .forms import HostingUserSignupForm, HostingUserLoginForm
from .mixins import ProcessVMSelectionMixin
@@ -174,6 +174,7 @@ class PaymentVMView(LoginRequiredMixin, FormView):
context.update({
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
})
+
return context
def post(self, request, *args, **kwargs):
@@ -199,7 +200,7 @@ class PaymentVMView(LoginRequiredMixin, FormView):
customer = StripeCustomer.get_or_create(email=self.request.user.email,
token=token)
if not customer:
- form.add_error("__all__","Invalid credit card")
+ form.add_error("__all__", "Invalid credit card")
return self.render_to_response(self.get_context_data(form=form))
# Create Virtual Machine Plan
@@ -233,6 +234,18 @@ class PaymentVMView(LoginRequiredMixin, FormView):
# If the Stripe payment was successed, set order status 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({
'charge': charge,
'order': order.id,
diff --git a/utils/mailer.py b/utils/mailer.py
index a12ffba6..8081152d 100644
--- a/utils/mailer.py
+++ b/utils/mailer.py
@@ -1,9 +1,35 @@
import six
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
+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,
+ {'data': self.context})
+ html_content = render_to_string('%s.html' % self.template_full_path,
+ {'data': 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):
def __init__(self):
self._slug = None
@@ -50,3 +76,4 @@ class DigitalGlarusRegistrationMailer(BaseMailer):
self.registration = self.message
self._message = self._message.format(slug=self._slug)
super().__init__()
+
From 7a9ea58a891475d573abe4c681406e3da2545a55 Mon Sep 17 00:00:00 2001
From: Levi <levinoelvm@gmail.com>
Date: Fri, 27 May 2016 00:51:10 -0500
Subject: [PATCH 2/3] Added status field to a booked VM. Added the capability
to change a booked VM status from the admin panel. Send email to user after
his VM is approved
---
hosting/admin.py | 28 ++++++++++++++++-
.../0019_virtualmachineplan_status.py | 20 ++++++++++++
hosting/migrations/0020_auto_20160526_0258.py | 20 ++++++++++++
hosting/migrations/0021_auto_20160526_0445.py | 20 ++++++++++++
hosting/models.py | 16 ++++++++++
hosting/templates/emails/new_booked_vm.html | 2 +-
hosting/templates/emails/new_booked_vm.txt | 2 +-
hosting/templates/emails/vm_activated.html | 13 ++++++++
hosting/templates/emails/vm_activated.txt | 15 +++++++++
.../hosting/virtual_machine_detail.html | 10 ++++--
.../templates/hosting/virtual_machines.html | 4 +--
.../migrations/0006_auto_20160526_0445.py | 31 +++++++++++++++++++
membership/models.py | 4 +--
utils/mailer.py | 7 ++---
14 files changed, 177 insertions(+), 15 deletions(-)
create mode 100644 hosting/migrations/0019_virtualmachineplan_status.py
create mode 100644 hosting/migrations/0020_auto_20160526_0258.py
create mode 100644 hosting/migrations/0021_auto_20160526_0445.py
create mode 100644 hosting/templates/emails/vm_activated.html
create mode 100644 hosting/templates/emails/vm_activated.txt
create mode 100644 membership/migrations/0006_auto_20160526_0445.py
diff --git a/hosting/admin.py b/hosting/admin.py
index d8cf3012..ef6249a6 100644
--- a/hosting/admin.py
+++ b/hosting/admin.py
@@ -1,6 +1,32 @@
from django.contrib import admin
+
+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(VirtualMachinePlan)
+admin.site.register(VirtualMachinePlan, VirtualMachinePlanAdmin)
diff --git a/hosting/migrations/0019_virtualmachineplan_status.py b/hosting/migrations/0019_virtualmachineplan_status.py
new file mode 100644
index 00000000..e1bd5382
--- /dev/null
+++ b/hosting/migrations/0019_virtualmachineplan_status.py
@@ -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),
+ ),
+ ]
diff --git a/hosting/migrations/0020_auto_20160526_0258.py b/hosting/migrations/0020_auto_20160526_0258.py
new file mode 100644
index 00000000..7cafc1ac
--- /dev/null
+++ b/hosting/migrations/0020_auto_20160526_0258.py
@@ -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),
+ ),
+ ]
diff --git a/hosting/migrations/0021_auto_20160526_0445.py b/hosting/migrations/0021_auto_20160526_0445.py
new file mode 100644
index 00000000..90ed64b1
--- /dev/null
+++ b/hosting/migrations/0021_auto_20160526_0445.py
@@ -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),
+ ),
+ ]
diff --git a/hosting/models.py b/hosting/models.py
index 55709fca..ef33c056 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -81,12 +81,24 @@ class VirtualMachineType(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()
memory = models.IntegerField()
disk_size = models.IntegerField()
vm_type = models.ForeignKey(VirtualMachineType)
price = models.FloatField()
public_key = models.TextField()
+ status = models.CharField(max_length=20, choices=VM_STATUS_CHOICES, default=PENDING_STATUS)
objects = VMPlansManager()
@@ -97,6 +109,10 @@ class VirtualMachinePlan(models.Model):
def hosting_company_name(self):
return self.vm_type.get_hosting_company_display()
+ @cached_property
+ def location(self):
+ return self.vm_type.get_location_display()
+
@cached_property
def name(self):
name = 'vm-%s' % self.id
diff --git a/hosting/templates/emails/new_booked_vm.html b/hosting/templates/emails/new_booked_vm.html
index 8583ba1f..2785e686 100644
--- a/hosting/templates/emails/new_booked_vm.html
+++ b/hosting/templates/emails/new_booked_vm.html
@@ -1,5 +1,5 @@
-<<!DOCTYPE html>
+<!DOCTYPE html>
<html>
<head>
<title></title>
diff --git a/hosting/templates/emails/new_booked_vm.txt b/hosting/templates/emails/new_booked_vm.txt
index 8583ba1f..2785e686 100644
--- a/hosting/templates/emails/new_booked_vm.txt
+++ b/hosting/templates/emails/new_booked_vm.txt
@@ -1,5 +1,5 @@
-<<!DOCTYPE html>
+<!DOCTYPE html>
<html>
<head>
<title></title>
diff --git a/hosting/templates/emails/vm_activated.html b/hosting/templates/emails/vm_activated.html
new file mode 100644
index 00000000..92eec368
--- /dev/null
+++ b/hosting/templates/emails/vm_activated.html
@@ -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>
\ No newline at end of file
diff --git a/hosting/templates/emails/vm_activated.txt b/hosting/templates/emails/vm_activated.txt
new file mode 100644
index 00000000..f7f85457
--- /dev/null
+++ b/hosting/templates/emails/vm_activated.txt
@@ -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>
\ No newline at end of file
diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html
index 55d07b86..210739d6 100644
--- a/hosting/templates/hosting/virtual_machine_detail.html
+++ b/hosting/templates/hosting/virtual_machine_detail.html
@@ -122,12 +122,16 @@
</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>
+ {% if virtual_machine.status == 'pending' %}
+ <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>
diff --git a/hosting/templates/hosting/virtual_machines.html b/hosting/templates/hosting/virtual_machines.html
index fa673c99..208d70fc 100644
--- a/hosting/templates/hosting/virtual_machines.html
+++ b/hosting/templates/hosting/virtual_machines.html
@@ -11,7 +11,7 @@
<thead>
<tr>
<th>ID</th>
- <th>Type</th>
+ <th>Location</th>
<th>Amount</th>
<th></th>
</tr>
@@ -20,7 +20,7 @@
{% for vm in vms %}
<tr>
<td scope="row">{{vm.name}}</td>
- <td>{{vm.hosting_company_name}}</td>
+ <td>{{vm.location}}</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>
diff --git a/membership/migrations/0006_auto_20160526_0445.py b/membership/migrations/0006_auto_20160526_0445.py
new file mode 100644
index 00000000..6d92c564
--- /dev/null
+++ b/membership/migrations/0006_auto_20160526_0445.py
@@ -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'),
+ ),
+ ]
diff --git a/membership/models.py b/membership/models.py
index 97d3ee06..ea235f06 100644
--- a/membership/models.py
+++ b/membership/models.py
@@ -2,7 +2,7 @@ from datetime import datetime
from django.db import models
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.core.validators import RegexValidator
from django.contrib.auth.models import User
@@ -47,7 +47,7 @@ class MyUserManager(BaseUserManager):
return user
-class CustomUser(AbstractBaseUser):
+class CustomUser(AbstractBaseUser, PermissionsMixin):
VALIDATED_CHOICES = ((0, 'Not validated'), (1, 'Validated'))
site = models.ForeignKey(Site, default=1)
name = models.CharField(max_length=50)
diff --git a/utils/mailer.py b/utils/mailer.py
index 8081152d..130b74ee 100644
--- a/utils/mailer.py
+++ b/utils/mailer.py
@@ -16,11 +16,8 @@ class BaseEmail(object):
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,
- {'data': self.context})
- html_content = render_to_string('%s.html' % self.template_full_path,
- {'data': self.context})
+ 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")
From 63eebbb2b08ffa599b7f5cf96592dc7b795a8135 Mon Sep 17 00:00:00 2001
From: Levi <levinoelvm@gmail.com>
Date: Sun, 29 May 2016 13:37:43 -0500
Subject: [PATCH 3/3] Added mark a notification as read feature, Fixed some
errors
---
dynamicweb/settings/base.py | 1 +
hosting/models.py | 14 ++-
hosting/templates/hosting/base_short.html | 6 +-
hosting/templates/hosting/notifications.html | 90 ++++++++++++++++++++
hosting/urls.py | 8 +-
hosting/views.py | 44 ++++++++--
requirements.txt | 1 +
7 files changed, 153 insertions(+), 11 deletions(-)
create mode 100644 hosting/templates/hosting/notifications.html
diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py
index db2912bd..ab2d6763 100644
--- a/dynamicweb/settings/base.py
+++ b/dynamicweb/settings/base.py
@@ -54,6 +54,7 @@ INSTALLED_APPS = (
'django.contrib.sites',
'easy_thumbnails',
'utils',
+ 'stored_messages',
'mptt',
'parler',
'taggit',
diff --git a/hosting/models.py b/hosting/models.py
index ef33c056..518ba175 100644
--- a/hosting/models.py
+++ b/hosting/models.py
@@ -3,12 +3,13 @@ import os
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.functional import cached_property
-from membership.models import StripeCustomer
-from utils.models import BillingAddress
+
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
@@ -118,6 +119,13 @@ class VirtualMachinePlan(models.Model):
name = 'vm-%s' % self.id
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
def create(cls, data, user):
instance = cls.objects.create(**data)
diff --git a/hosting/templates/hosting/base_short.html b/hosting/templates/hosting/base_short.html
index 874b590f..55466815 100644
--- a/hosting/templates/hosting/base_short.html
+++ b/hosting/templates/hosting/base_short.html
@@ -71,7 +71,11 @@
<i class="fa fa-credit-card"></i> My Orders
</a>
</li>
-
+ <li>
+ <a href="{% url 'hosting:notifications' %}">
+ <i class="fa fa-bell"></i> Notifications
+ </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>
diff --git a/hosting/templates/hosting/notifications.html b/hosting/templates/hosting/notifications.html
new file mode 100644
index 00000000..959dfe4f
--- /dev/null
+++ b/hosting/templates/hosting/notifications.html
@@ -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%}
+
+
+
+
+
+
+
+
+
diff --git a/hosting/urls.py b/hosting/urls.py
index 702f2b30..bc72a636 100644
--- a/hosting/urls.py
+++ b/hosting/urls.py
@@ -1,9 +1,10 @@
from django.conf.urls import url
-from .views import DjangoHostingView, RailsHostingView, PaymentVMView, \
+from .views import DjangoHostingView, RailsHostingView, PaymentVMView,\
NodeJSHostingView, LoginView, SignupView, IndexView, \
OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\
- VirtualMachineDetailView, GenerateVMSSHKeysView, OrdersHostingDeleteView
+ VirtualMachineDetailView, GenerateVMSSHKeysView, OrdersHostingDeleteView, NotificationsView, \
+ MarkAsReadNotificationView
urlpatterns = [
# url(r'pricing/?$', VMPricingView.as_view(), name='pricing'),
@@ -20,6 +21,9 @@ urlpatterns = [
name='virtual_machines'),
url(r'my-virtual-machines/(?P<pk>\d+)/key/?$', GenerateVMSSHKeysView.as_view(),
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'signup/?$', SignupView.as_view(), name='signup'),
url(r'^logout/?$', 'django.contrib.auth.views.logout',
diff --git a/hosting/views.py b/hosting/views.py
index 60733274..bb4b5b1e 100644
--- a/hosting/views.py
+++ b/hosting/views.py
@@ -2,12 +2,17 @@
from django.shortcuts import get_object_or_404, render,render_to_response
from django.core.urlresolvers import reverse_lazy, reverse
from django.contrib.auth.mixins import LoginRequiredMixin
-
-from django.views.generic import View, CreateView, FormView, ListView, DetailView, UpdateView, DeleteView
-from django.http import HttpResponseRedirect, HttpResponse
+from django.views.generic import View, CreateView, FormView, ListView, DetailView,\
+ DeleteView, TemplateView, UpdateView
+from django.http import HttpResponseRedirect
from django.contrib.auth import authenticate, login
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 utils.stripe_utils import StripeUtils
@@ -145,6 +150,34 @@ class SignupView(CreateView):
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):
model = VirtualMachinePlan
template_name = 'hosting/virtual_machine_key.html'
@@ -278,10 +311,11 @@ class OrdersHostingListView(LoginRequiredMixin, ListView):
class OrdersHostingDeleteView(LoginRequiredMixin, DeleteView):
- login_url=reverse_lazy('hosting:login')
+ login_url = reverse_lazy('hosting:login')
success_url = reverse_lazy('hosting:orders')
model = HostingOrder
+
class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
template_name = "hosting/virtual_machines.html"
login_url = reverse_lazy('hosting:login')
diff --git a/requirements.txt b/requirements.txt
index dcf2b02b..4bd93ee0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -18,6 +18,7 @@ easy_thumbnails
django-polymorphic
model-mommy
pycryptodome
+django-stored-messages
#PLUGINS
djangocms_flash