Added command make_donations_charges in order to create stripe current monthly donations from all donators. Now the user can logout using navbar. Added restriction to user in order to make a donation when he has an active monthly donation . Added donations view where the user can view their recents donations. Now users receive an email after making his first donation.

This commit is contained in:
Levi 2016-07-29 00:17:34 -05:00
parent b5557a63a2
commit 4e07385949
14 changed files with 467 additions and 50 deletions

View File

@ -347,8 +347,6 @@ class PaymentVMView(LoginRequiredMixin, FormView):
order.set_approved() order.set_approved()
# Send notification to ungleich as soon as VM has been booked # Send notification to ungleich as soon as VM has been booked
# TODO send email using celery
context = { context = {
'vm': plan, 'vm': plan,
'order': order, 'order': order,

View File

@ -1,51 +1,72 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from nosystem.models import DonatorStatus, Donation
from datetime import datetime from datetime import datetime
from utils.stripe_utils import StripeUtils from utils.stripe_utils import StripeUtils
from .forms import DonationForm
from nosystemd.models import DonatorStatus, Donation
from nosystemd.forms import DonationForm
class Command(BaseCommand): class Command(BaseCommand):
help = 'Make the monthly stripe charge to all donators' help = 'Make the monthly stripe charge to all donators'
CURRENCY = 'usd'
def handle(self, *args, **options): def handle(self, *args, **options):
donators = DonatorStatus.objects.filter(status=DonatorStatus.ACTIVE) donators = DonatorStatus.objects.filter(status=DonatorStatus.ACTIVE)
current_month = datetime.now().month current_month = datetime.now().month
current_year = datetime.now().year current_year = datetime.now().year
for donator in donators:
current_month_donation = Donation.objects.get(created_at__month=current_month,
created_at__year=current_year)
if not current_month_donation:
last_donation = Donation.objects.filter(donator=donator).last()
donation_amount = last_donation.donation print("--------- STARTING DONATIONS SCRIPT ---------")
# Make stripe charge to a customer print("Donations date: %s-%s" % (current_month, current_year))
stripe_utils = StripeUtils()
stripe_utils.CURRENCY = 'usd'
charge_response = stripe_utils.make_charge(amount=donation_amount,
customer=donator.stripe_id)
charge = charge_response.get('response_object')
# Check if the payment was approved for donator_status in donators:
if not charge: donator = donator_status.user.stripecustomer
# TODO save error try:
context = { Donation.objects.get(created_at__month=current_month,
'paymentError': charge_response.get('error'), created_at__year=current_year,
donator=donator)
except Donation.DoesNotExist:
try:
# Get donator last donation amount
last_donation = Donation.objects.filter(donator=donator).last()
donation_amount = last_donation.donation
# Make stripe charge to a customer
stripe_utils = StripeUtils()
stripe_utils.CURRENCY = self.CURRENCY
charge_response = stripe_utils.make_charge(amount=donation_amount,
customer=donator.stripe_id)
charge = charge_response.get('response_object')
# Check if the payment was approved
if not charge:
# There is an error trying to creating the stripe charge
context = {
'paymentError': charge_response.get('error'),
}
print("--------- STRIPE PAYMENT ERROR ---------")
print(context)
print("-------------------------")
# Create a donation
charge = charge_response.get('response_object')
donation_data = {
'cc_brand': charge.source.brand,
'stripe_charge_id': charge.id,
'last4': charge.source.last4,
'billing_address': last_donation.billing_address.id,
'donator': donator.id,
'donation': donation_amount
} }
print(context) donation_form = DonationForm(donation_data)
# Create a donation if donation_form.is_valid():
charge = charge_response.get('response_object') donation = donation_form.save()
donation_data = { print("--------- PAYMENT DONATION SUCCESSFULL ---------")
'cc_brand': charge.source.brand, print("Donator: %s" % donation.donator.user.email)
'stripe_charge_id': charge.id, print("Amount: %s %s" % (donation.donation, self.CURRENCY))
'last4': charge.source.last4, print("-----------------------------------------------")
'billing_address': last_donation.billing_address.id, except Exception as e:
'donator': donator.id, print("--------- ERROR ---------")
'donation': donation_amount print(e)
} print("-------------------------")
donation_form = DonationForm(donation_data) continue
if donation_form.is_valid():
donation = donation_form.save()
print(donation)

View File

@ -14,6 +14,9 @@ class DonatorStatus(models.Model):
user = models.OneToOneField(CustomUser) user = models.OneToOneField(CustomUser)
status = models.CharField(choices=STATUS_CHOICES, max_length=10, default=ACTIVE) status = models.CharField(choices=STATUS_CHOICES, max_length=10, default=ACTIVE)
def __str__(self):
return "%s - %s " % (self.user.email, self.status)
@classmethod @classmethod
def create(cls, user): def create(cls, user):
cls.objects.get_or_create(user=user) cls.objects.get_or_create(user=user)
@ -34,6 +37,10 @@ class Donation(models.Model):
obj = cls.objects.create(**data) obj = cls.objects.create(**data)
return obj return obj
@classmethod
def get_total_donations_amount(cls):
return sum(cls.objects.all().values_list('donation', flat=True))
def set_stripe_charge(self, stripe_charge): def set_stripe_charge(self, stripe_charge):
self.stripe_charge_id = stripe_charge.id self.stripe_charge_id = stripe_charge.id
self.last4 = stripe_charge.source.last4 self.last4 = stripe_charge.source.last4

View File

@ -10,7 +10,7 @@
<meta name="description" content=""> <meta name="description" content="">
<meta name="author" content=""> <meta name="author" content="">
<title>Creative - Start Bootstrap Theme</title> <title>NOSYSTEMD</title>
<!-- Bootstrap Core CSS --> <!-- Bootstrap Core CSS -->
<link href="{% static 'nosystemd/vendor/bootstrap/css/bootstrap.min.css' %}" rel="stylesheet"> <link href="{% static 'nosystemd/vendor/bootstrap/css/bootstrap.min.css' %}" rel="stylesheet">
@ -44,7 +44,7 @@
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span> Menu <i class="fa fa-bars"></i> <span class="sr-only">Toggle navigation</span> Menu <i class="fa fa-bars"></i>
</button> </button>
<a class="navbar-brand page-scroll" href="#page-top">nosystemd</a> <a class="navbar-brand page-scroll" href="{% url 'nosystemd:landing' %}">nosystemd</a>
</div> </div>
<!-- Collect the nav links, forms, and other content for toggling --> <!-- Collect the nav links, forms, and other content for toggling -->
@ -64,8 +64,9 @@
<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>
<ul id="g-account-menu" class="dropdown-menu" role="menu"> <ul id="g-account-menu" class="dropdown-menu" role="menu">
<li><a href="{% url 'nosystemd:donations' %}"><i class="fa fa-heart-o" aria-hidden="true"></i> {% trans "Donations"%} </a></li>
<li><a href="{% url 'nosystemd:donator_status' %}"><i class="fa fa-credit-card" aria-hidden="true"></i> {% trans "Subscription"%} </a></li>
<li><a href="{% url 'nosystemd:logout' %}"><i class="glyphicon glyphicon-lock"></i> {% trans "Logout"%} </a></li> <li><a href="{% url 'nosystemd:logout' %}"><i class="glyphicon glyphicon-lock"></i> {% trans "Logout"%} </a></li>
<li><a href="{% url 'nosystemd:donator_status' %}"><i class="fa fa-heart-o" aria-hidden="true"></i> {% trans "Donation"%} </a></li>
</ul> </ul>
</li> </li>
{% else %} {% else %}

View File

@ -8,7 +8,7 @@
<div class="header-content-inner"> <div class="header-content-inner">
<div class="container payment-container"> <div class="container payment-container">
<form role="form" id="donation-form" name="donation-form" method="post" action="{% url 'nosystemd:donations' %}" novalidate> <form role="form" id="donation-form" name="donation-form" method="post" action="{% url 'nosystemd:make_donation' %}" novalidate>
<div class="row"> <div class="row">
<div class="col-xs-12 col-md-4 col-md-offset-3" > <div class="col-xs-12 col-md-4 col-md-offset-3" >
<h3><b>Monthly Donation</b></h3> <h3><b>Monthly Donation</b></h3>

View File

@ -60,13 +60,13 @@
<div class="row"> <div class="row">
<div class="col-md-8 col-md-offset-2"> <div class="col-md-8 col-md-offset-2">
<hr> <hr>
{% trans "Thanks for you donation, you can cancel your monthly donation at any time going to profile > cancel danation"%} {% trans "Thanks for you donation, you can cancel your monthly donation at any time going to profile > subscription "%}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-8 col-md-offset-2"> <div class="col-md-8 col-md-offset-2">
<div class=" content pull-right"> <div class=" content pull-right">
<a href="{% url 'nosystemd:landing' %}" ><button class="btn btn-info">{% trans "Return to home"%}</button></a> <a href="{% url 'nosystemd:donations' %}" ><button class="btn btn-info">{% trans "View Donations"%}</button></a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,60 @@
{% extends "nosystemd/base.html" %}
{% load staticfiles bootstrap3 i18n %}
{% block content %}
<div>
<div class="container orders-container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<table class="table borderless table-hover">
<h3><i class="fa fa-heart-o" aria-hidden="true"></i> {% trans "Donations"%}</h3>
<br/>
<thead>
<tr>
<th>#</th>
<th>{% trans "Donation"%}</th>
<th>{% trans "Date" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for donation in donations %}
<tr>
<td scope="row">{{ donation.id }}</td>
<td>{{ donation.donation }} USD</td>
<td>{{ donation.created_at|date:"M Y" }}</td>
<td>
<button type="button" class="btn btn-default"><a
href="{% url 'nosystemd:donations' donation.id %}">{% trans "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 }}">{% trans "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 }}">{% trans "next"%}</a>
{% endif %}
</span>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,136 @@
{% load static from staticfiles %}
<!-- Inliner Build Version 4380b7741bb759d6cb997545f3add21ad48f010b -->
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Oxygen Invoice</title>
</head>
<body bgcolor="#f7f7f7" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; color: white; margin: 0;">
<style type="text/css">
@media only screen and (max-width: 480px) {
table[class*="container-for-gmail-android"] {
min-width: 290px !important; width: 100% !important;
}
img[class="force-width-gmail"] {
display: none !important; width: 0 !important; height: 0 !important;
}
table[class="w320"] {
width: 320px !important;
}
td[class*="mobile-header-padding-left"] {
width: 160px !important; padding-left: 0 !important;
}
td[class*="mobile-header-padding-right"] {
width: 160px !important; padding-right: 0 !important;
}
td[class="header-lg"] {
font-size: 24px !important; padding-bottom: 5px !important;
}
td[class="content-padding"] {
padding: 5px 0 5px !important;
}
td[class="button"] {
padding: 5px 5px 30px !important;
}
td[class*="free-text"] {
padding: 10px 18px 30px !important;
}
td[class~="mobile-hide-img"] {
display: none !important; height: 0 !important; width: 0 !important; line-height: 0 !important;
}
td[class~="item"] {
width: 140px !important; vertical-align: top !important;
}
td[class~="quantity"] {
width: 50px !important;
}
td[class~="price"] {
width: 90px !important;
}
td[class="item-table"] {
padding: 30px 20px !important;
}
td[class="mini-container-left"] {
padding: 0 15px 15px !important; display: block !important; width: 290px !important;
}
td[class="mini-container-right"] {
padding: 0 15px 15px !important; display: block !important; width: 290px !important;
}
}
</style>
<table align="center" cellpadding="0" cellspacing="0" class="container-for-gmail-android" width="100%" style="border-collapse: collapse !important; min-width: 600px; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td align="left" valign="top" width="100%" style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; background: #ffffff url(http://s3.amazonaws.com/swu-filepicker/4E687TRe69Ld95IDWyEg_bg_top_02.jpg) repeat-x;" bgcolor="#ffffff">
<center style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<table cellspacing="0" cellpadding="0" width="100%" bgcolor="#ffffff" background="http://s3.amazonaws.com/swu-filepicker/4E687TRe69Ld95IDWyEg_bg_top_02.jpg" style="border-collapse: collapse !important; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; background: transparent;"><tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td width="100%" height="80" valign="top" style="text-align: center; vertical-align: middle; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; line-height: 21px;" align="center">
<!--[if gte mso 9]>
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="mso-width-percent:1000;height:80px; v-text-anchor:middle;">
<v:fill type="tile" src="http://s3.amazonaws.com/swu-filepicker/4E687TRe69Ld95IDWyEg_bg_top_02.jpg" color="#ffffff" />
<v:textbox inset="0,0,0,0">
<![endif]-->
<center style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<table cellpadding="0" cellspacing="0" width="600" class="w320" style="border-collapse: collapse !important; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"><tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td class="pull-left mobile-header-padding-left" style="vertical-align: middle; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: left; line-height: 21px; width: 290px; padding-left: 10px;" align="left" valign="middle">
<a href="{{base_url}}" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; color: #676767; text-decoration: none !important;"><img width="137" src="{{base_url}}{% static "hosting/img/logo_black.png" %}" alt="logo" style="max-width: 600px; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; border: none;"></a>
</td>
<td class="pull-right mobile-header-padding-right" style="color: #4d4d4d; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; text-align: right; line-height: 21px; width: 290px; padding-left: 10px;" align="right">
</td>
</tr></table>
</center>
<!--[if gte mso 9]>
</v:textbox>
</v:rect>
<![endif]-->
</td>
</tr></table>
</center>
</td>
</tr>
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td align="center" valign="top" width="100%" style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; background: #f7f7f7; padding: 20px 0 5px;" class="content-padding" bgcolor="#f7f7f7">
<center style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<table cellspacing="0" cellpadding="0" width="600" class="w320" style="border-collapse: collapse !important; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td class="header-lg" style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 32px; color: #4d4d4d; text-align: center; line-height: normal; font-weight: 700; padding: 35px 0 0;" align="center">
Thank you for you support.
</td>
</tr>
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td class="free-text" style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; width: 100% !important; padding: 10px 60px 0px;" align="center">
Your monthly donation for {{donation.donation}} USD has been charged. <br/> you can view your invoice clicking on the button below.
</td>
</tr>
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td class="button" style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; padding: 30px 0;" align="center">
<div style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<!--[if mso]>
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="http://" style="height:45px;v-text-anchor:middle;width:155px;" arcsize="15%" strokecolor="#ffffff" fillcolor="#ff6f6f">
<w:anchorlock/>
<center style="color:#ffffff;font-family:Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;">My Account</center>
</v:roundrect>
<![endif]--><a href="{{ base_url }}{% url 'nosystemd:donations' donation.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">View Invoice</a>
</div>
</td>
</tr>
</table>
</center>
</td>
</tr>
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td align="center" valign="top" width="100%" style="height: 100px; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; background: #f7f7f7;" bgcolor="#f7f7f7">
<center style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<table cellspacing="0" cellpadding="0" width="600" class="w320" style="border-collapse: collapse !important; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"><tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; padding: 25px 0;" align="center">
<strong style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">ungleich</strong><br style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
</td>
</tr></table>
</center>
</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,136 @@
{% load static from staticfiles %}
<!-- Inliner Build Version 4380b7741bb759d6cb997545f3add21ad48f010b -->
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Oxygen Invoice</title>
</head>
<body bgcolor="#f7f7f7" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; color: white; margin: 0;">
<style type="text/css">
@media only screen and (max-width: 480px) {
table[class*="container-for-gmail-android"] {
min-width: 290px !important; width: 100% !important;
}
img[class="force-width-gmail"] {
display: none !important; width: 0 !important; height: 0 !important;
}
table[class="w320"] {
width: 320px !important;
}
td[class*="mobile-header-padding-left"] {
width: 160px !important; padding-left: 0 !important;
}
td[class*="mobile-header-padding-right"] {
width: 160px !important; padding-right: 0 !important;
}
td[class="header-lg"] {
font-size: 24px !important; padding-bottom: 5px !important;
}
td[class="content-padding"] {
padding: 5px 0 5px !important;
}
td[class="button"] {
padding: 5px 5px 30px !important;
}
td[class*="free-text"] {
padding: 10px 18px 30px !important;
}
td[class~="mobile-hide-img"] {
display: none !important; height: 0 !important; width: 0 !important; line-height: 0 !important;
}
td[class~="item"] {
width: 140px !important; vertical-align: top !important;
}
td[class~="quantity"] {
width: 50px !important;
}
td[class~="price"] {
width: 90px !important;
}
td[class="item-table"] {
padding: 30px 20px !important;
}
td[class="mini-container-left"] {
padding: 0 15px 15px !important; display: block !important; width: 290px !important;
}
td[class="mini-container-right"] {
padding: 0 15px 15px !important; display: block !important; width: 290px !important;
}
}
</style>
<table align="center" cellpadding="0" cellspacing="0" class="container-for-gmail-android" width="100%" style="border-collapse: collapse !important; min-width: 600px; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td align="left" valign="top" width="100%" style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; background: #ffffff url(http://s3.amazonaws.com/swu-filepicker/4E687TRe69Ld95IDWyEg_bg_top_02.jpg) repeat-x;" bgcolor="#ffffff">
<center style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<table cellspacing="0" cellpadding="0" width="100%" bgcolor="#ffffff" background="http://s3.amazonaws.com/swu-filepicker/4E687TRe69Ld95IDWyEg_bg_top_02.jpg" style="border-collapse: collapse !important; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; background: transparent;"><tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td width="100%" height="80" valign="top" style="text-align: center; vertical-align: middle; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; line-height: 21px;" align="center">
<!--[if gte mso 9]>
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="mso-width-percent:1000;height:80px; v-text-anchor:middle;">
<v:fill type="tile" src="http://s3.amazonaws.com/swu-filepicker/4E687TRe69Ld95IDWyEg_bg_top_02.jpg" color="#ffffff" />
<v:textbox inset="0,0,0,0">
<![endif]-->
<center style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<table cellpadding="0" cellspacing="0" width="600" class="w320" style="border-collapse: collapse !important; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"><tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td class="pull-left mobile-header-padding-left" style="vertical-align: middle; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: left; line-height: 21px; width: 290px; padding-left: 10px;" align="left" valign="middle">
<a href="{{base_url}}" style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; color: #676767; text-decoration: none !important;"><img width="137" src="{{base_url}}{% static "hosting/img/logo_black.png" %}" alt="logo" style="max-width: 600px; outline: none; text-decoration: none; -ms-interpolation-mode: bicubic; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; border: none;"></a>
</td>
<td class="pull-right mobile-header-padding-right" style="color: #4d4d4d; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; text-align: right; line-height: 21px; width: 290px; padding-left: 10px;" align="right">
</td>
</tr></table>
</center>
<!--[if gte mso 9]>
</v:textbox>
</v:rect>
<![endif]-->
</td>
</tr></table>
</center>
</td>
</tr>
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td align="center" valign="top" width="100%" style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; background: #f7f7f7; padding: 20px 0 5px;" class="content-padding" bgcolor="#f7f7f7">
<center style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<table cellspacing="0" cellpadding="0" width="600" class="w320" style="border-collapse: collapse !important; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td class="header-lg" style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 32px; color: #4d4d4d; text-align: center; line-height: normal; font-weight: 700; padding: 35px 0 0;" align="center">
Thank you for you support.
</td>
</tr>
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td class="free-text" style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; width: 100% !important; padding: 10px 60px 0px;" align="center">
Your monthly donation for {{donation.donation}} has been charged. Thank you for your support. <br/> you can view your invoice clicking on the button below.
</td>
</tr>
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td class="button" style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; padding: 30px 0;" align="center">
<div style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<!--[if mso]>
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="http://" style="height:45px;v-text-anchor:middle;width:155px;" arcsize="15%" strokecolor="#ffffff" fillcolor="#ff6f6f">
<w:anchorlock/>
<center style="color:#ffffff;font-family:Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;">My Account</center>
</v:roundrect>
<![endif]--><a href="{{ base_url }}{% url 'nosystemd:donations' donation.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">View Invoice</a>
</div>
</td>
</tr>
</table>
</center>
</td>
</tr>
<tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td align="center" valign="top" width="100%" style="height: 100px; border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; background: #f7f7f7;" bgcolor="#f7f7f7">
<center style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<table cellspacing="0" cellpadding="0" width="600" class="w320" style="border-collapse: collapse !important; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;"><tr style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
<td style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; padding: 25px 0;" align="center">
<strong style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">ungleich</strong><br style="font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important;">
</td>
</tr></table>
</center>
</td>
</tr>
</table>
</body>
</html>

View File

@ -8,7 +8,9 @@
<h1 id="homeHeading">No more SYSTEMD</h1> <h1 id="homeHeading">No more SYSTEMD</h1>
<hr> <hr>
<p>We want to remove systemd from the famous linux distros and create a good replacement</p> <p>We want to remove systemd from the famous linux distros and create a good replacement</p>
<a href="{% url 'nosystemd:donations' %}" class="btn btn-primary btn-xl page-scroll">DONATE NOW</a> <a href="{% url 'nosystemd:make_donation' %}" class="btn btn-primary btn-xl page-scroll">DONATE NOW</a>
<h2>We have collected <strong class="text-primary" >{{total_donations_amount}} USD</strong> at time. Thanks you all.</h2>
<p></p>
</div> </div>
</div> </div>
</header> </header>

View File

@ -30,6 +30,7 @@
<form action="{% url 'nosystemd:login' %}" method="post" class="form" novalidate> <form action="{% url 'nosystemd:login' %}" method="post" class="form" novalidate>
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="" value="{{ request.GET.next }}">
{% for field in form %} {% for field in form %}
{% bootstrap_field field show_label=False type='fields'%} {% bootstrap_field field show_label=False type='fields'%}
{% endfor %} {% endfor %}

View File

@ -17,6 +17,7 @@
<h2 class="section-heading">{% trans "Sign up"%}</h2> <h2 class="section-heading">{% trans "Sign up"%}</h2>
<form action="{% url 'nosystemd:signup' %}" method="post" class="form" novalidate> <form action="{% url 'nosystemd:signup' %}" method="post" class="form" novalidate>
<input type="hidden" name="next" value="{{ request.GET.next }}">
{% csrf_token %} {% csrf_token %}
{% for field in form %} {% for field in form %}
{% bootstrap_field field show_label=False %} {% bootstrap_field field show_label=False %}

View File

@ -2,7 +2,7 @@ from django.conf.urls import url
from .views import LandingView, LoginView, SignupView, PasswordResetView,\ from .views import LandingView, LoginView, SignupView, PasswordResetView,\
PasswordResetConfirmView, DonationView, DonationDetailView, ChangeDonatorStatusDetailView,\ PasswordResetConfirmView, DonationView, DonationDetailView, ChangeDonatorStatusDetailView,\
DonatorStatusDetailView DonatorStatusDetailView, DonationListView
urlpatterns = [ urlpatterns = [
url(r'^$', LandingView.as_view(), name='landing'), url(r'^$', LandingView.as_view(), name='landing'),
@ -13,9 +13,9 @@ urlpatterns = [
url(r'reset-password/?$', PasswordResetView.as_view(), name='reset_password'), url(r'reset-password/?$', PasswordResetView.as_view(), name='reset_password'),
url(r'reset-password-confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$', url(r'reset-password-confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
PasswordResetConfirmView.as_view(), name='reset_password_confirm'), PasswordResetConfirmView.as_view(), name='reset_password_confirm'),
url(r'^donations/?$', DonationListView.as_view(), name='donations'),
url(r'^donations/?$', DonationView.as_view(), name='donations'),
url(r'donations/(?P<pk>\d+)/?$', DonationDetailView.as_view(), name='donations'), url(r'donations/(?P<pk>\d+)/?$', DonationDetailView.as_view(), name='donations'),
url(r'^make_donation/?$', DonationView.as_view(), name='make_donation'),
url(r'donations/status/?$', DonatorStatusDetailView.as_view(), url(r'donations/status/?$', DonatorStatusDetailView.as_view(),
name='donator_status'), name='donator_status'),
url(r'donations/status/(?P<pk>\d+)/?$', ChangeDonatorStatusDetailView.as_view(), url(r'donations/status/(?P<pk>\d+)/?$', ChangeDonatorStatusDetailView.as_view(),

View File

@ -1,4 +1,5 @@
from django.views.generic import TemplateView, CreateView, FormView, DetailView, UpdateView from django.views.generic import TemplateView, CreateView, FormView, DetailView, UpdateView,\
ListView
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import render from django.shortcuts import render
from django.core.urlresolvers import reverse_lazy, reverse from django.core.urlresolvers import reverse_lazy, reverse
@ -12,6 +13,7 @@ from membership.models import CustomUser, StripeCustomer
from utils.stripe_utils import StripeUtils from utils.stripe_utils import StripeUtils
from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin
from utils.forms import PasswordResetRequestForm from utils.forms import PasswordResetRequestForm
from utils.mailer import BaseEmail
from .forms import LoginForm, SignupForm, DonationForm, DonationBillingForm from .forms import LoginForm, SignupForm, DonationForm, DonationBillingForm
from .models import Donation, DonatorStatus from .models import Donation, DonatorStatus
@ -20,12 +22,23 @@ from .models import Donation, DonatorStatus
class LandingView(TemplateView): class LandingView(TemplateView):
template_name = "nosystemd/landing.html" template_name = "nosystemd/landing.html"
def get_context_data(self, *args, **kwargs):
total_donations_amount = Donation.get_total_donations_amount()
context = {
'total_donations_amount': total_donations_amount
}
return context
class LoginView(FormView): class LoginView(FormView):
template_name = "nosystemd/login.html" template_name = "nosystemd/login.html"
form_class = LoginForm form_class = LoginForm
success_url = reverse_lazy('nosystemd:landing') success_url = reverse_lazy('nosystemd:landing')
def get_success_url(self):
next_url = self.request.session.get('next', self.success_url)
return next_url
def form_valid(self, form): def form_valid(self, form):
email = form.cleaned_data.get('email') email = form.cleaned_data.get('email')
password = form.cleaned_data.get('password') password = form.cleaned_data.get('password')
@ -38,8 +51,10 @@ class LoginView(FormView):
return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url())
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
if self.request.user.is_authenticated(): if self.request.user.is_authenticated():
return HttpResponseRedirect(reverse('nosystemd:landing')) return HttpResponseRedirect(reverse('nosystemd:landing'))
return super(LoginView, self).get(request, *args, **kwargs) return super(LoginView, self).get(request, *args, **kwargs)
@ -49,7 +64,7 @@ class SignupView(CreateView):
form_class = SignupForm form_class = SignupForm
def get_success_url(self): def get_success_url(self):
next_url = self.request.session.get('next', reverse_lazy('nosystemd:signup')) next_url = self.request.POST.get('next', reverse('nosystemd:login'))
return next_url return next_url
def form_valid(self, form): def form_valid(self, form):
@ -78,9 +93,12 @@ class PasswordResetConfirmView(PasswordResetConfirmViewMixin):
class DonationView(LoginRequiredMixin, FormView): class DonationView(LoginRequiredMixin, FormView):
template_name = 'nosystemd/donation.html' template_name = 'nosystemd/donation.html'
login_url = reverse_lazy('nosystemd:login')
form_class = DonationBillingForm form_class = DonationBillingForm
success_url = reverse_lazy('nosystemd:donations') success_url = reverse_lazy('nosystemd:make_donation')
def get_login_url(self):
return "%s?next=%s" % (reverse('nosystemd:signup'),
reverse('nosystemd:make_donation'))
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(DonationView, self).get_context_data(**kwargs) context = super(DonationView, self).get_context_data(**kwargs)
@ -90,6 +108,14 @@ class DonationView(LoginRequiredMixin, FormView):
return context return context
def get(self, request, *args, **kwargs):
if DonatorStatus.objects.filter(user=self.request.user).exists():
messages.success(self.request, 'Your already are a monthly contributor')
return HttpResponseRedirect(reverse_lazy('nosystemd:donator_status'))
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
form = self.get_form() form = self.get_form()
@ -137,6 +163,22 @@ class DonationView(LoginRequiredMixin, FormView):
donation_form = DonationForm(donation_data) donation_form = DonationForm(donation_data)
if donation_form.is_valid(): if donation_form.is_valid():
donation = donation_form.save() donation = donation_form.save()
context = {
'donation': donation,
'base_url': "{0}://{1}".format(request.scheme, request.get_host())
}
email_data = {
'subject': 'Your donation have been charged',
'to': request.user.email,
'context': context,
'template_name': 'donation_charge',
'template_path': 'nosystemd/emails/'
}
email = BaseEmail(**email_data)
email.send()
return HttpResponseRedirect(reverse('nosystemd:donations', return HttpResponseRedirect(reverse('nosystemd:donations',
kwargs={'pk': donation.id})) kwargs={'pk': donation.id}))
else: else:
@ -153,6 +195,18 @@ class DonationDetailView(LoginRequiredMixin, DetailView):
model = Donation model = Donation
class DonationListView(LoginRequiredMixin, ListView):
template_name = "nosystemd/donations.html"
context_object_name = "donations"
login_url = reverse_lazy('nosystemd:login')
model = Donation
def get_queryset(self):
queryset = super(DonationListView, self).get_queryset()
queryset = queryset.filter(donator__user=self.request.user)
return queryset
class DonatorStatusDetailView(LoginRequiredMixin, TemplateView): class DonatorStatusDetailView(LoginRequiredMixin, TemplateView):
template_name = "nosystemd/donator_status.html" template_name = "nosystemd/donator_status.html"
login_url = reverse_lazy('nosystemd:login') login_url = reverse_lazy('nosystemd:login')