1. Greg Reinbach
  2. saas-basic

Commits

Greg Reinbach  committed 83bbe97

start of project

  • Participants
  • Branches default

Comments (0)

Files changed (176)

File .hgignore

View file
+syntax: glob
+# ignore backup files
+*~
+
+# emacs lock files
+.\#*
+
+# hidden directory
+.DS_Store
+
+# compiled python files
+*.pyc
+
+# git files
+.git*

File INSTALL.txt

View file
+A base saas system to get going with that makes use of CheddarGetter as the payment gateway and runs on Google AppEngine.
+
+1. Update settings.py file.
+- ADMINS needs values
+- SECRET_KEY needs a value
+- COMPANY_* need values
+- CHEDDARGETTER_* need values
+
+2. Currently the following libs are included
+- pycheddar
+- httplib2
+

File __init__.py

Empty file added.

File app.yaml

View file
+application: saas-basic
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: /media
+  static_dir: media
+
+# only use if url is *.appspot.com
+#- url: /account/.*
+#  script: main.py
+#  secure: always
+
+- url: .*
+  script: main.py
+

File apps/account/__init__.py

Empty file added.

File apps/account/admin.py

View file
+import logging
+
+from google.appengine.api import users
+from google.appengine.ext import db
+
+from django import http
+from django.core.urlresolvers import reverse
+
+from shortcuts import request_to_response
+
+from account import forms, models, utils
+from lib import pycheddar as payment
+
+
+#---------------------------------------------------------------------------
+@utils.is_admin
+def plan_list(request):
+    plan_list = payment.Plan.all()
+    for plan in plan_list:
+        admin_plan = db.GqlQuery("SELECT * FROM Plan WHERE plan_key =:1", plan.id)
+        setattr(plan, 'admin', admin_plan.get())
+    data = dict(
+        plan_list=plan_list
+    )
+    return request_to_response(request, 'account/admin/plan_list.html', data)
+
+#---------------------------------------------------------------------------
+@utils.is_admin
+def plan_add(request, plan_key):
+    plan = payment.Plan.get(plan_key)
+    form = forms.AdminPlanForm(request, plan)
+    if request.method == "POST":
+        form = forms.AdminPlanForm(request, plan, data=request.POST)
+        if form.is_valid():
+            form.save()
+            return http.HttpResponseRedirect(reverse('account_admin_plan_list'))
+    data = dict(
+        form=form,
+        plan=plan
+    )
+    return request_to_response(request, 'account/admin/plan.html', data)
+
+#---------------------------------------------------------------------------
+@utils.is_admin
+def plan_edit(request, plan_key):
+    admin_plan = models.Plan.get(plan_key)
+    plan = payment.Plan.get(admin_plan.plan_key)
+    setattr(plan, 'admin', admin_plan)
+    form = forms.AdminPlanEditForm(request, plan=plan)
+    if request.method == 'POST':
+        form = forms.AdminPlanEditForm(request, plan=plan, data=request.POST)
+        if form.is_valid():
+            form.save()
+            return http.HttpResponseRedirect(reverse('account_admin_plan_list'))
+    data = dict(
+        form=form,
+        plan=plan
+    )
+    return request_to_response(request, 'account/admin/plan.html', data)
+
+#---------------------------------------------------------------------------
+@utils.is_admin
+def customer_list(request):
+    customer_list = payment.Customer.all()
+    data = dict(
+        customer_list=customer_list
+    )
+    return request_to_response(request, 'account/admin/customer_list.html', data)
+
+#---------------------------------------------------------------------------
+@utils.is_admin
+def customer_delete(request, customer_id):
+    '''
+    May only delete a customer if there are no charges for them
+    '''
+    try:
+        customer = payment.Customer.get(customer_id)
+        if not customer.subscription.invoices[0].paid_transaction_id:
+            customer.delete()
+            #TODO delete local record of customer
+            # need to change id/code reference from email address
+            message=u'Customer has been deleted.'
+        else:
+            message=u'Not able to delete Customer (charges associated).'
+    except payment.NotFound:
+        message = 'Customer does not exist.'
+    customer_list = payment.Customer.all()
+    data = dict(
+        customer_list=customer_list,
+        message=message
+    )        
+    return request_to_response(request, 'account/admin/customer_list.html', data)

File apps/account/forms.py

View file
+import datetime
+import logging
+import re
+
+from google.appengine.api import users
+
+from django import forms
+from django.contrib.localflavor.us.forms import USZipCodeField
+
+from account import models
+from lib import pycheddar as payment
+import shortcuts
+
+TRACKING_CODE_LENGTH = 8
+
+
+#===============================================================================
+class CreditCardField(forms.IntegerField):
+
+    #---------------------------------------------------------------------------
+    @staticmethod
+    def get_cc_type(number):
+        """
+        Gets credit card type given number. Based on values from Wikipedia page
+        'Credit card number'.
+        http://en.wikipedia.org/w/index.php?title=Credit_card_number
+        """
+        number = str(number)
+        #group checking by ascending length of number
+        if len(number) == 13:
+            if number[0] == "4":
+                return "Visa"
+        elif len(number) == 14:
+            if number[:2] == "36":
+                return "MasterCard"
+        elif len(number) == 15:
+            if number[:2] in ("34", "37"):
+                return "American Express"
+        elif len(number) == 16:
+            if number[:4] == "6011":
+                return "Discover"
+            if number[:2] in ("51", "52", "53", "54", "55"):
+                return "MasterCard"
+            if number[0] == "4":
+                return "Visa"
+        return "Unknown"
+
+    #---------------------------------------------------------------------------
+    def clean(self, value):
+        """
+        Check if given CC number is valid and one of the card types we accept
+        """
+        if value and (len(value) < 13 or len(value) > 16):
+            raise forms.ValidationError("Please enter in a valid "+\
+                "credit card number.")
+        elif self.get_cc_type(value) not in (
+            "Visa",
+            "MasterCard",
+            "American Express"
+        ):
+            raise forms.ValidationError("Please enter in a Visa, "+\
+                "Master Card, or American Express credit card number.")
+        return super(CreditCardField, self).clean(value)
+
+    
+#===============================================================================
+class PaymentForm(forms.Form):
+    first_name = forms.CharField(max_length=20)
+    last_name = forms.CharField(max_length=20)
+    company = forms.CharField(max_length=50, required=False)
+    address = forms.CharField(max_length=60)
+    city = forms.CharField(max_length=40)
+    state = forms.ChoiceField()
+    zip_code = USZipCodeField()
+    country = forms.ChoiceField(
+        choices=[('USA', 'USA')]
+    )
+    phone = forms.CharField(
+        widget=forms.TextInput(attrs={'maxlength': 20, 'class': 'phone'})
+    )
+
+    #---------------------------------------------------------------------------
+    def __init__(self, *args, **kws):
+        super(PaymentForm, self).__init__(*args, **kws)
+        
+        self.fields['state'].choices=shortcuts.state_choices()
+        self.fields['state'].choices.reverse()
+        self.fields['state'].choices.append(('', '- select -'))
+        self.fields['state'].choices.reverse()
+        
+
+#===============================================================================
+class CCForm(PaymentForm):
+    cc_number = CreditCardField()
+    cc_cardcode = forms.IntegerField(
+        widget=forms.TextInput(attrs={'maxlength': 4, 'class': 'cvv'}),
+        max_value=9999
+    )
+    cc_expiration = forms.CharField(
+        widget=forms.TextInput(attrs={'maxlength': 7, 'class': 'expires'}),
+    )
+
+    #---------------------------------------------------------------------------
+    def clean_cc_expiration(self):
+        '''
+        expect format to be %m/%Y
+        '''
+        data = self.cleaned_data
+        if not re.match("^\d{2}.\d{4}$", data['cc_expiration']):
+            raise forms.ValidationError(message=u'Invalid format, needs to be MM/YYYY.')
+        month = int(data['cc_expiration'][0:2])
+        year = int(data['cc_expiration'][-4:])
+        try:
+            cc_expiration = (datetime.date(year, month, 1))
+        except ValueError:
+            raise forms.ValidationError(message=u'A valid expiration date is required.')
+        return cc_expiration
+    
+
+#===============================================================================
+class SignUpForm(CCForm):
+    plan = forms.ChoiceField()
+
+    #---------------------------------------------------------------------------
+    def __init__(self, request, plan_key=None, *args, **kws):
+        super(SignUpForm, self).__init__(*args, **kws)
+        self.request = request
+        self.plan_key = plan_key
+
+        self.fields['plan'].choices = [('', '- select -')]
+        for plan in models.Plan.all():
+            self.fields['plan'].choices.append((plan.key(), plan.name))
+        self.fields['plan'].initial = plan_key
+
+    #---------------------------------------------------------------------------
+    def clean_plan(self):
+        #TODO ensure this validation is taking place
+        data = self.cleaned_data
+        if data['plan'] == '':
+            raise forms.ValidationError(message=u'Plan selection is required.')
+        return data['plan']
+    
+    #---------------------------------------------------------------------------
+    def save(self):
+        # handle the account choice for user
+        # if account exists, upgrade/downgrade if different account selected
+        # else create account for user
+        # if account associated with user redirect to account home
+        data = self.cleaned_data
+        user_account = shortcuts.get_user_account()
+        # check that we have a valid plan
+        plan = models.Plan.get(data['plan'])
+        if not plan:
+            logging.error('Found no matching plan for %s' % data['plan'])
+            message = 'Plan appears to be invalid.'
+            return False
+            
+        if user_account:
+            if user_account.account.is_active:
+                if user_account.account.upgrade(plan, user_account, data):
+                    return True, ''
+                return False, 'Failed to upgrade your account.'
+            return False, 'Account is inactive.'
+
+        models.create_account(plan.plan_key, plan, data)
+
+        return True, ''
+
+
+#===============================================================================
+class AdminPlanForm(forms.Form):
+    name = forms.CharField(
+        max_length=100,
+        widget=forms.TextInput(attrs={'class': 'input-text'})
+    )
+    product_number = forms.IntegerField(
+        widget=forms.TextInput(attrs={'class': 'input-text'})
+    )
+    staff_number = forms.IntegerField(
+        widget=forms.TextInput(attrs={'class': 'input-text'})
+    )
+    is_active = forms.BooleanField(
+        required=False,
+        widget=forms.CheckboxInput(attrs={'class': 'input-text'})
+    )
+    default = forms.BooleanField(
+        required=False,
+        widget=forms.CheckboxInput(attrs={'class': 'input-text'})
+    )
+
+    #---------------------------------------------------------------------------
+    def __init__(self, request, plan, *args, **kws):
+        super(AdminPlanForm, self).__init__(*args, **kws)
+        self.request = request
+        self.fields['name'].initial = plan.name
+        self.plan = plan
+
+    #---------------------------------------------------------------------------
+    def save(self):
+        data = self.cleaned_data
+
+        plan = models.Plan(
+            name=data['name'],
+            plan_key=self.plan.id,
+            product_number=data['product_number'],
+            staff_number=data['staff_number'],
+            is_active=data['is_active'],
+            default=data['default']
+        )
+        plan.put()
+        plan.set_default()
+        
+
+#===============================================================================
+class AdminPlanEditForm(AdminPlanForm):
+
+    #---------------------------------------------------------------------------
+    def __init__(self, request, plan, *args, **kws):
+        super(AdminPlanEditForm, self).__init__(request, plan, *args, **kws)
+        self.plan = plan
+
+        self.fields['name'].initial = plan.admin.name
+        self.fields['product_number'].initial = plan.admin.product_number
+        self.fields['staff_number'].initial = plan.admin.staff_number
+        self.fields['is_active'].initial = plan.admin.is_active
+        self.fields['default'].initial = plan.admin.default
+        
+    #---------------------------------------------------------------------------
+    def clean_plan_code(self):
+        return self.cleaned_data['plan_code']
+    
+    #---------------------------------------------------------------------------
+    def save(self):
+        data = self.cleaned_data
+
+        self.plan.admin.name = data['name']
+        self.plan.admin.plan_key = self.plan.id
+        self.plan.admin.product_number = data['product_number']
+        self.plan.admin.staff_number = data['staff_number']
+        self.plan.admin.is_active = data['is_active']
+        self.plan.admin.default = data['default']
+        self.plan.admin.put()
+        self.plan.admin.set_default()
+
+
+#===============================================================================
+class SetURLForm(forms.Form):
+    slug = forms.SlugField(
+        widget=forms.TextInput(attrs={'class': 'input-text'})
+    )
+
+    #---------------------------------------------------------------------------
+    def __init__(self, request, user_account, *args, **kws):
+        super(SetURLForm, self).__init__(*args, **kws)
+        self.request = request
+        self.user_account = user_account
+
+        self.fields['slug'].initial = self.user_account.account.slug
+
+    #---------------------------------------------------------------------------
+    def save(self):
+        data = self.cleaned_data
+
+        self.user_account.account.slug = data['slug']
+        self.user_account.account.put()
+
+#===============================================================================
+class SetTrackingCodeForm(forms.Form):
+    tracking_code = forms.CharField(
+        widget=forms.TextInput(attrs={'class': 'input-text'})
+    )
+
+    #---------------------------------------------------------------------------
+    def __init__(self, request, user_account, *args, **kws):
+        super(SetTrackingCodeForm, self).__init__(*args, **kws)
+        self.request = request
+        self.user_account = user_account
+
+        self.fields['tracking_code'].initial = self.user_account.account.tracking_code
+
+    #---------------------------------------------------------------------------
+    def clean_tracking_code(self):
+        '''
+        Google Analytics currently has the format off xxxxxx-x
+        '''
+        code = self.cleaned_data['tracking_code']
+        if len(code) != TRACKING_CODE_LENGTH or code[-2:][0] != "-":
+            raise forms.ValidationError(message=u'The format appears to be incorrect.')
+        return code
+
+    #---------------------------------------------------------------------------
+    def save(self):
+        data = self.cleaned_data
+        self.user_account.account.tracking_code = data['tracking_code']
+        self.user_account.account.put()

File apps/account/models.py

View file
+import datetime
+import logging
+
+from lib import pycheddar as payment
+from google.appengine.ext import db
+
+from lib import pycheddar as payment
+
+
+#---------------------------------------------------------------------------
+def get_user_account(user):
+    return db.GqlQuery("SELECT * FROM User WHERE user = :1", user).get()
+
+#---------------------------------------------------------------------------
+def get_plan_list():
+    payment_plan_list = {}
+    for plan in payment.Plan.all():
+        payment_plan_list[plan.id] = plan.recurring_charge_amount
+    plan_list = Plan.all().filter('is_active =', True)
+    merged_plan_list = []
+    for plan in plan_list:
+        setattr(plan, 'rate', payment_plan_list.get(plan.plan_key, None))
+        merged_plan_list.append(plan)
+    return merged_plan_list
+
+#---------------------------------------------------------------------------
+def create_account(plan_key, plan=None, data={}):
+    account = Account(is_active=True).put()
+    user = User(
+        user=users.get_current_user(),
+        user_type=str(User.USER_TYPE_ADMIN),
+        account=account
+    )
+    user.put()
+
+    if not plan:
+        plan = Plan.get(plan_key)
+        
+    payment_plan = payment.Plan.get(plan.plan_key)
+    
+    account_plan = AccountPlan(
+        account=account,
+        plan=plan,
+        rate=payment_plan.recurring_charge_amount,
+        period=payment_plan.billing_frequency,
+        product_number=plan.product_number,
+        staff_number=plan.staff_number
+    )
+    account_plan.put()
+    account_plan.set_payment(plan, data)
+
+
+#===============================================================================
+class Plan(db.Model):
+    name = db.StringProperty(required=True)
+    plan_key = db.StringProperty(required=True)
+    product_number = db.IntegerProperty(required=True, default=1)
+    staff_number = db.IntegerProperty(required=True, default=1)
+    is_active = db.BooleanProperty()
+    created = db.DateTimeProperty(auto_now_add=True)
+    default = db.BooleanProperty()
+
+    #---------------------------------------------------------------------------
+    def __unicode__(self):
+        return '%s (%s)' % (self.name, self.plan_code)
+
+    #---------------------------------------------------------------------------
+    def put(self):
+        #TODO when upgrading # of companies/staff then need to upgrade
+        # plans already in existance at the same level
+        # use the plan code to determine plans at the same level
+        super(Plan, self).put()
+
+    #---------------------------------------------------------------------------
+    def set_default(self):
+        # if default mark all others as not default
+        if self.default:
+            plan_list = Plan.all().filter('plan_key !=', self.plan_key)
+            for plan in plan_list.fetch(plan_list.count()):
+                plan.default = False
+                plan.put()
+
+
+#===============================================================================
+class Account(db.Model):
+    slug = db.StringProperty(multiline=False)
+    tracking_code = db.StringProperty(multiline=False)
+    is_active = db.BooleanProperty()
+    created = db.DateTimeProperty(auto_now_add=True)
+
+    #---------------------------------------------------------------------------
+    def __unicode__(self):
+        return '%s (active: %s)' % (self.slug, self.is_active)
+
+    #---------------------------------------------------------------------------
+    def upgrade(self, plan, user_account, data=None):
+        self.is_active = True
+        self.put()
+
+        payment_plan = payment.Plan.get(plan.plan_key)
+        
+        account_plan = user_account.get_current_account_plan()
+        if not account_plan:
+            account_plan = AccountPlan(
+                account=self,
+                plan=plan,
+                rate=payment_plan.recurring_charge_amount,
+                period=payment_plan.billing_frequency,
+                product_number=plan.product_number,
+                staff_number=plan.staff_number
+            )
+            account_plan.put()
+        if data:
+            account_plan = account_plan.upgrade(plan, data)
+        else:
+            account_plan = account_plan.change(plan)
+        return account_plan
+
+    #---------------------------------------------------------------------------
+    def get_current_account_plan(self):
+        query = AccountPlan.all().filter('account =', self).filter(
+            'datetime_end =', None
+        )
+
+        if query.count():
+            return query.fetch(1)[0]
+
+        return None
+
+    #---------------------------------------------------------------------------
+    def delete(self):
+        '''
+        Removed all data associated with this user
+        '''
+        from product import models as product_models
+        product_query = product_models.Product.all().filter('account =', self)
+        for product in product_query.fetch(product_query.count()):
+            product.delete()
+        
+        accountplan_query = AccountPlan.all().filter('account =', self)
+        results = accountplan_query.fetch(accountplan_query.count())
+        db.delete(results)
+
+        user_query = User.all().filter('account =', self)
+        results = user_query.fetch(user_query.count())
+        db.delete(results)
+        
+        super(Account, self).delete()
+        return True
+    
+    
+#===============================================================================
+class AccountPlan(db.Model):
+    account = db.ReferenceProperty(Account, required=True)
+    plan = db.ReferenceProperty(Plan, required=True)
+    rate = db.FloatProperty(required=True)
+    period = db.StringProperty(required=True)
+    product_number = db.IntegerProperty(required=True, default=1)
+    staff_number = db.IntegerProperty(required=True, default=1)
+    slug = db.StringProperty(required=False)
+    datetime_start = db.DateTimeProperty(auto_now_add=True)
+    datetime_end = db.DateTimeProperty()
+
+    #---------------------------------------------------------------------------
+    def __unicode__(self):
+        return '%s' % self.plan.name
+
+    #---------------------------------------------------------------------------
+    def put(self):
+        #TODO downgrading need to make sure # active companies associated
+        # with account plan is less than allowed
+        #TODO downgrading need to make sure # of active staff associated
+        # with account plan is less than allowed
+        super(AccountPlan, self).put()
+
+    #---------------------------------------------------------------------------
+    def set_payment(self, plan, data):
+        payment_plan = payment.Plan.get(plan.plan_key)
+
+        user = db.GqlQuery(
+            "SELECT * FROM User WHERE account =:1",
+            self.account
+        ).get()
+
+        try:
+            customer = payment.Customer.get(user.user.email())
+        except payment.NotFound:
+            customer = payment.Customer()
+            customer.code = user.user.email()
+        
+        customer.firstName = data.get('first_name', user.user.email()[:20])
+        customer.lastName = data.get('last_name', user.user.email()[:20])
+        customer.email = user.user.email()
+        customer.company = data.get('company', None)
+
+        subscription = customer.subscription
+        subscription.plan_code = payment_plan._code
+
+        # if selected plan is free no need for cc info
+        if not payment_plan.is_free():
+            subscription.ccNumber = str(data['cc_number'])
+            subscription.ccExpiration = data['cc_expiration'].strftime('%m/%Y')
+            subscription.ccCardCode = data['cc_cardcode']
+            subscription.ccFirstName = data['first_name']
+            subscription.ccLastName = data['last_name']
+            subscription.ccCompany = data['company']
+            subscription.ccAddress = data['address']
+            subscription.ccCity = data['city']
+            subscription.ccState = data['state']
+            subscription.ccZip = data['zip_code']
+            subscription.ccCountry = data['country']
+        subscription.save()
+        logging.info("Customer payment has been setup: %s" % subscription)
+
+    #---------------------------------------------------------------------------
+    def change(self, plan):
+        '''
+        change the current plan to a new plan, only if we already have payment
+        information for the account
+        '''
+        # if we are attempting to change to the same plan, do nothing and so we did
+        if self.plan.plan_key == plan.plan_key:
+            return True
+        
+        user = db.GqlQuery(
+            "SELECT * FROM User WHERE account =:1",
+            self.account
+        ).get()
+
+        try:
+            customer = payment.Customer.get(user.user.email())
+        except payment.NotFound:
+            return False
+
+        # check that we have payment details for customer
+        # if so change plan and update subscription to new plan
+        if customer.subscription.cc_type:
+            self.datetime_end = datetime.datetime.now()
+            self.put()
+            payment_plan = payment.Plan.get(plan.plan_key)
+            new_account_plan = AccountPlan(
+                account=self.account,
+                plan=plan,
+                rate=payment_plan.recurring_charge_amount,
+                period=payment_plan.billing_frequency,
+                product_number=plan.product_number,
+                staff_number=plan.staff_number
+            )
+            new_account_plan.put()
+            customer.subscription.plan_code = payment_plan._code
+            customer.subscription.save()
+            return True
+        return False
+        
+    #---------------------------------------------------------------------------
+    def upgrade(self, plan, data):
+        """
+        upgrading plan from a free plan to a paying plan
+        """
+        if self.plan.plan_key == plan.plan_key:
+            logging.warning('Plan (%s) exists already for this account (%s).' % (
+                plan.name,
+                self.account.key()
+            ))
+            return self
+        self.datetime_end = datetime.datetime.now()
+        self.put()
+
+        payment_plan = payment.Plan.get(plan.plan_key)
+
+        new_account_plan = AccountPlan(
+            account=self.account,
+            plan=plan,
+            rate=payment_plan.recurring_charge_amount,
+            period=payment_plan.billing_frequency,
+            product_number=plan.product_number,
+            staff_number=plan.staff_number
+        )
+        new_account_plan.put()
+        new_account_plan.set_payment(plan, data)
+        return new_account_plan
+        
+
+#===============================================================================
+class User(db.Model):
+
+    USER_TYPE_ADMIN = 1
+    USER_TYPE_STAFF = 2
+    USER_TYPE_CHOICES = (
+        USER_TYPE_ADMIN, 'Administrator',
+        USER_TYPE_STAFF, 'Staff',
+    )
+    account = db.ReferenceProperty(Account, required=True)
+    user = db.UserProperty(required=True)
+    user_type = db.StringProperty(required=True, default=str(USER_TYPE_ADMIN))
+    created = db.DateTimeProperty(auto_now_add=True)
+
+    #---------------------------------------------------------------------------
+    def __unicode__(self):
+        return '%s (%s)' % (self.user, self.account.slug)
+
+    #---------------------------------------------------------------------------
+    def get_current_account_plan(self):
+        return db.GqlQuery(
+            "SELECT * FROM AccountPlan WHERE account =:1 AND datetime_end = NULL",
+            self.account
+        ).get()

File apps/account/templates/account/admin/base.html

View file
+{% extends "base.html" %}
+{% block title %}
+    {% block page-title %}{% endblock page-title %}
+    Admin
+{% endblock title %}
+{% block header %}
+    {% block page-header %}{% endblock page-header %}
+    Admin
+{% endblock header %}
+{% block sidebar %}
+    <div class="sidebar right">
+        <div class="widget contacts">
+            <h2>Admin Options</h2>
+            <ul>
+                <li><a href="{% url account_admin_customer_list %}">Customers</a></li>
+                <li><a href="{% url news-admin %}">News</a></li>
+                <li><a href="{% url account_admin_plan_list %}">Plans</a></li>
+            </ul>
+        </div>
+
+        {% block page-sidebar %}{% endblock page-sidebar %}
+    </div>
+{% endblock sidebar %}

File apps/account/templates/account/admin/customer_list.html

View file
+{% extends "account/admin/base.html" %}
+{% load account_tags %}
+{% block page-title %}Customer List{% endblock page-title %}
+{% block page-header %}Customer List{% endblock page-header %}
+{% block content %}
+    <table class="plan">
+        <thead>
+            <tr>
+                <th>Customer</th>
+                <th>Email</th>
+                <th>Plan</th>
+                <th>Amount</th>
+                <th>Created</th>
+                <th>Active</th>
+                <th>Action</th>
+            </tr>
+        </thead>
+        <tbody>
+            {% for customer in customer_list %}
+                <tr>
+                    <td>
+                        {{ customer.first_name }}
+                        {{ customer.last_name }}
+                    </td>
+                    <td>
+                        {{ customer.email }}
+                    </td>
+                    <td>{{ customer.subscription.plan.name }}</td>
+                    <td class="td-right">
+                        {{ customer.subscription.plan.recurring_charge_amount|stringformat:".2f" }}
+                    </td>
+                    <td class="td-center">{{ customer.created_datetime|slice:":10" }}</td>
+                    <td class="td-center">
+                        {% if customer.subscription.canceled_datetime %}
+                            <img src="{{ MEDIA_URL }}images/i_no.png" alt="X" />
+                        {% else %}
+                            <img src="{{ MEDIA_URL }}images/i_sticky.png" alt="&radic;" />
+                        {% endif %}
+                    </td>
+                    <td>
+                        <a href="{% url account_admin_customer_delete customer.id %}">
+                            Delete
+                        </a>
+                    </td>
+                </tr>
+            {% empty %}
+                <tr>
+                    <td colspan="7">None. What!!! Getting selling!</td>
+                </tr>
+            {% endfor %}
+        </tbody>
+    </table>
+{% endblock content %}

File apps/account/templates/account/admin/plan.html

View file
+{% extends "base.html" %}
+{% block title %}Plan{% endblock title %}
+{% block header %}Save Plan{% endblock header %}
+{% block content %}
+    <div>
+        <div class="formDiv">
+            <div>
+                <h2>Save Plan</h2>
+                <form action="" class="plan" method="post">
+                    <div class="half left">
+                        <label>Name:</label>
+                        <div class="input-box">
+                            {{ form.name }}
+                        </div>
+                        {{ form.name.errors }}
+                    </div> 
+     
+                    <div class="half right">
+                        <label>Rate:</label>
+                        <div class="input-box display">
+                            {% if plan.is_free %}
+                                Free
+                            {% else %}
+                                {{ plan.recurring_charge_amount }}
+                            {% endif %}
+                        </div>
+                    </div>
+     
+                    <div class="half left">
+                        <label>Period:</label>
+                        <div class="input-box display">
+                            {{ plan.billing_frequency|capfirst }}
+                        </div>
+                    </div>
+     
+                    <div class="half right">
+                        <label>Code:</label>
+                        <div class="input-box display">
+                            {{ plan.code }}
+                        </div>
+                    </div>
+     
+                    <div class="half left">
+                        <label>Product Allowed:</label>
+                        <div class="input-box">
+                            {{ form.product_number }}
+                        </div>
+                        {{ form.product_number.errors }}
+                    </div>
+     
+                    <div class="half right">
+                        <label>Staff Allowed:</label>
+                        <div class="input-box">
+                            {{ form.staff_number }}
+                        </div>
+                        {{ form.staff_number.errors }}
+                    </div>
+     
+                    <div class="half left">
+                        <label>Active:</label>
+                        <div class="input-checkbox">
+                            {{ form.is_active }}
+                        </div>
+                        {{ form.is_active.errors }}
+                    </div>
+
+                    <div class="half left">
+                        <label>Default:</label>
+                        <div class="input-checkbox">
+                            {{ form.default }}
+                        </div>
+                        {{ form.default.errors }}
+                    </div>
+     
+                    <div class="item a-right">
+                        <div class="button">
+                            <span><input type="submit" value="Save Plan" /></span>
+                        </div>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+{% endblock content %}
+{% block sidebar %}
+    <div class="sidebar right">
+        <div class="widget contacts">
+            <h2>Other Options</h2>
+            <ul>
+                <li><a href="{% url account_admin_plan_list %}">Back to Plan List</a></li>
+            </ul>
+        </div>
+    </div>
+{% endblock sidebar %}

File apps/account/templates/account/admin/plan_list.html

View file
+{% extends "account/admin/base.html" %}
+{% load account_tags %}
+{% block page-title %}Plans{% endblock page-title %}
+{% block page-header %}Plans{% endblock page-header %}
+{% block content %}
+    <table class="plan">
+        <thead>
+            <tr>
+                <th>Plan Name</th>
+                <th>Rate</th>
+                <th>Period</th>
+                <th>Code</th>
+                <th>Prod / Staff</th>
+                <th>Active / Default</th>
+                <th>Created</th>
+            </tr>
+        </thead>
+        <tbody>
+            {% for plan in plan_list %}
+                <tr>
+                    <td>
+                        {% if plan.admin.key %}
+                            <a href="{% url account_admin_plan_edit plan.admin.key %}">
+                                {{ plan.name }}
+                            </a>
+                        {% else %}
+                            {{ plan.name }}
+                        {% endif %}
+                    </td>
+                    <td class="td-right">
+                        {% if plan.is_free %}
+                            Free
+                        {% else %}
+                            ${{ plan.recurring_charge_amount|stringformat:".2f" }}
+                        {% endif %}
+                    </td>
+                    <td>{{ plan.billing_frequency|capfirst }}</td>
+                    <td>{{ plan.code }}</td>
+                    <td class="td-center">
+                        {{ plan.admin.product_number }} / {{ plan.admin.staff_number }}
+                    </td>
+                    <td class="td-center">
+                        {% if plan.is_active %}
+                            <img src="{{ MEDIA_URL }}images/i_sticky.png" alt="&radic;" />
+                        {% else %}
+                            <img src="{{ MEDIA_URL }}images/i_no.png" alt="X" />
+                        {% endif %}
+                        {% if plan.admin.default %}
+                            <img src="{{ MEDIA_URL }}images/i_sticky.png" alt="&radic;" />
+                        {% else %}
+                            <img src="{{ MEDIA_URL }}images/i_no.png" alt="X" />
+                        {% endif %}
+                    </td>
+                    <td>
+                        {% if plan.admin %}
+                            {{ plan.admin.created|date:"m/d/Y" }}
+                        {% else %}
+                            <a href="{% url account_admin_plan_add plan.id %}">
+                                Add Plan
+                            </a>
+                        {% endif %}
+                    </td>
+                </tr>
+            {% endfor %}
+        </tbody>
+    </table>
+{% endblock content %}

File apps/account/templates/account/base.html

View file
+{% extends "base.html" %}
+{% block title %}
+    {% block page-title %}Home{% endblock page-title %} - Account
+{% endblock title %}
+{% block header %}
+    Account: 
+    {% block page-header %}Home{% endblock page-header %}
+{% endblock header %}
+{% block sidebar %}
+    <div class="sidebar right">
+        {% block page-sidebar %}{% endblock page-sidebar %}
+        <div class="widget contacts">
+            <h2>Account Management</h2>
+            <ul>
+                <li><a href="{% url product_home %}">Manage Products</a></li>
+                <li>
+                    <a href="{% url account_url %}">
+                        {% if user_account.account.slug %}
+                            Change URL
+                        {% else %}
+                            Set URL
+                        {% endif %}
+                    </a>
+                </li>
+                <li><a href="">Manage Staff Accounts</a></li>
+                <li>
+                    <a href="{% url account_tracking_code %}">
+                        {% if user_account.account.tracking_code %}
+                            Change Tracking Code
+                        {% else %}
+                            Set Tracking Code
+                        {% endif %}
+                    </a>
+                </li>
+                <li>
+                    <a href="{% url account_delete %}" class="delete">
+                        Delete Account
+                    </a>
+                </li>
+                <li><a href="{% url account_upgrade %}">Change Account Plan</a></li>
+            </ul>
+        </div>
+    </div>
+{% endblock sidebar %}
+{% block content %}
+    {% block page-content %}{% endblock page-content %}
+{% endblock content %}

File apps/account/templates/account/delete.html

View file
+<html>
+<body>
+<h2>Confirm Account Deletion</h2>
+
+<br />
+
+<p>Are you sure you want to delete your account. All your data will be deleted.</p>
+
+<br />
+
+<p class="p-center">
+    <a href="{% url account_delete_confirm "confirm" %}" class="button">
+        <span>Delete Account</span>
+    </a>
+</p>
+
+<br />
+
+<p class="p-center">Sorry you are leaving!</p>
+
+</body>
+</html>

File apps/account/templates/account/home.html

View file
+{% extends "account/base.html" %}
+{% block title %}Account{% endblock title %}
+{% block page-title %}Account{% endblock page-title %}
+{% block page-content %}
+    <div class="step">
+        <img src="{{ MEDIA_URL }}images/step_1.png" />
+        <div class="instructions">
+            <h2>Products</h2>
+            {% if product_list.count %}
+                You have created {{ product_list.count }} product(s) out of an allowed {{ plan.product_number }} products.
+                You can <a href="{% url product_home %}">configure</a> your products any time
+            {% else %}
+                You have to <a href="{% url product_add %}">create products</a>.
+            {% endif %}
+        </div>
+    </div>
+
+    <div class="step">
+        <img src="{{ MEDIA_URL }}images/step_2.png" />
+        <div class="instructions">
+            <h2>Customer View</h2>
+            {% if user_account.account.slug %}
+                You have created your Customer URL
+                <a href="{% url catalog_account user_account.account.slug %}">
+                    <strong>
+                        {{ full_path }}{% url catalog_all %}{{ user_account.account.slug }}
+                    </strong>
+                </a>
+                <br />
+                You can <a href="{% url account_url %}">configure</a> this URL at any time.
+            {% else %}
+                You have to <a href="{% url account_url %}">create the URL</a> that your customers use to view your products.
+            {% endif %}
+        </div>
+    </div>
+
+    <div class="step">
+        <img src="{{ MEDIA_URL }}images/step_3.png" />
+        <div class="instructions">
+            <h2>Staff</h2>
+            You have created {{ staff_list.count }} staff account(s) out of an allowed {{ plan.staff_number }} staff account(s). You can <a href="#">configure</a> your staff accounts at any time.
+        </div>
+    </div>
+{% endblock page-content %}

File apps/account/templates/account/plan_list.html

View file
+<div class="grid">
+    <aside>
+        <ul>
+        <li>Set up Fee</li>
+        <li># of Products</li>
+        <li># of Staff</li>
+            <li>Cool thing</li>
+            <li>Cancel Anytime</li>
+            <li>Reporting</li>
+        </ul>
+    </aside>
+    <div class="sections col5">
+        {% for plan in plan_list %}
+            <section {% if plan.default %}class="on"{% endif %}>
+                <header>
+                    <h1>{{ plan.name }}</h1>
+                    {% if plan.rate %}
+                        <h2>
+                            ${{ plan.rate|stringformat:".0f" }}
+                            <span>{{ plan.period_display }}Monthly</span>
+                        </h2>
+                    {% else %}
+                        <h2>
+                            Free
+                            <span>&nbsp;</span>
+                        </h2>
+                    {% endif %}
+                </header>
+                <footer>
+                    <ul>
+                        <li><span>None</span></li>
+                        <li>
+                            <span>{{ plan.product_number }}</span>
+                            <div class="tooltip">
+                                <div>
+                                    <h3># of Products</h3>
+                                    <p>The # of active products you can have at any one time.</p>
+                                </div>
+                            </div>
+
+                        </li>
+                        <li>
+                            <span>{{ plan.staff_number }}</span>
+                            <div class="tooltip">
+                                <div>
+                                    <h3># of Staff</h3>
+                                    <p>The # of user accounts you can have to access your account.</p>
+                                </div>
+                            </div>
+                        </li>
+                        <li>
+                            <img src="{{ MEDIA_URL }}images/i_sticky.png" alt="&radic;" />
+                        </li>
+                        <li>
+                            <img src="{{ MEDIA_URL }}images/i_sticky.png" alt="&radic;" />
+                        </li>
+                        <li>
+                            <img src="{{ MEDIA_URL }}images/i_sticky.png" alt="&radic;" />
+                        </li>
+                        <li class="last">
+                            <a class="btn" href="{% url account_add plan.key %}">
+                                <span>Choose Plan</span>
+                            </a>
+                        </li>
+                    </ul>
+                </footer>
+            </section>
+        {% endfor %}
+    </div>
+</div>

File apps/account/templates/account/pricing.html

View file
+{% extends "base.html" %}
+{% block page-title %}Sign Up{% endblock page-title %}
+{% block page-js %}
+    <script type="text/javascript">
+        $(document).ready(function() {
+            var $gridSections = $('div.grid div.sections section');
+            $gridSections.hover
+            (
+                function()
+                {
+                    $gridSections.removeClass('on');
+                    $(this).addClass('on');
+                }
+            );
+        });
+    </script>
+{% endblock page-js %}
+{% block header-override %}
+    {% include "account/plan_list.html" %}
+{% endblock header-override %}
+{% block content %}
+    <div class="features4">
+	<h2>Features Included with all Plans</h2>
+	<ul>
+	    <li>
+		<div class="icon">
+		    <img src="{{ MEDIA_URL }}images/i_calendar.png" alt="Feature Set 1" />
+		</div>
+		<div class="content">
+		    <h3>30 Day Money Back Guarantee</h3>
+		    <p>Cancel anytime within your first 30 days for any reason whatsoever and you will get a full refund.</p>
+		</div>
+	    </li>
+            
+	    <li>
+		<div class="icon">
+		    <img src="{{ MEDIA_URL }}images/i_basket.png" alt="Donec convallis" />
+		</div>
+		<div class="content">
+		    <h3>We're Growing</h3>
+		    <p>Features and functionality that you want and ask for are constantly being added to the system. We want to grow the way you want us to.</p>
+		</div>
+	    </li>
+            
+	    <li>
+		<div class="icon">
+		    <img src="{{ MEDIA_URL }}images/i_clock.png" alt="Sed pharetra" />
+		</div>
+		<div class="content">
+		    <h3>Cancel Anytime</h3>
+		    <p>You can cancel your account anytime. There is no contract, you pay month to month. And cancelling is easy, you can do it from your account.</p>
+		</div>
+	    </li>
+				
+	    <li>
+		<div class="icon">
+		    <img src="{{ MEDIA_URL }}images/i_buoy.png" alt="Nam adipiscing" />
+		</div>
+		<div class="content">
+		    <h3>Get Help</h3>
+		    <p>There are plenty of helpful resources when you need them. Including FAQs, How Tos and if you need to you can email us.</p>
+		</div>
+	    </li>
+	</ul>
+    </div>
+{% endblock content %}

File apps/account/templates/account/signup.html

View file
+{% extends "base.html" %}
+{% block title %}Sign Up{% endblock title %}
+{% block header %}Sign Up{% endblock header %}
+{% block content %}
+    <form class="signupform" method="post" action="#">
+	<h2>Pricing Plan</h2>
+        <em>( <span style="color: red;">*</span> Indicates required information )</em>
+	<ul>
+	    <li>
+		<label class="plan" for="plan">Plan:</label>
+                {{ form.plan }}
+		<em>*</em>
+	    </li>
+	</ul>
+	
+	<h2>Billing Info</h2>
+	<ul>
+	    <li>
+		<label for="id_first_name">First Name:</label>
+		{{ form.first_name }}
+		<em>*</em>
+		{{ form.first_name.errors }}
+	    </li>
+	    
+	    <li>
+		<label for="id_last_name">Last Name:</label>
+		{{ form.last_name }}
+		<em>*</em>
+                {{ form.last_name.errors }}
+	    </li>
+	    
+	    <li>
+		<label for="id_company">Company Name:</label>
+		{{ form.company }}
+		<em>*</em>
+                {{ form.company.errors }}
+	    </li>
+	    
+	    <li>
+		<label for="id_address">Street Address:</label>
+		{{ form.address }}
+		<em>*</em>
+                {{ form.address.errors }}
+	    </li>
+	    
+	    <li>
+		<label for="id_city">City:</label>
+		{{ form.city }}
+		<em>*</em>
+                {{ form.city.errors }}
+	    </li>
+	    
+	    <li>
+		<label for="id_zip_code">ZIP:</label>
+		{{ form.zip_code }}
+		<em>*</em>
+                {{ form.zip_code.errors }}
+            </li>
+		
+            <li>
+		<label for="id_state">State:</label>
+		{{ form.state }}
+		<em>*</em>
+                {{ form.state.errors }}
+	    </li>
+	    
+	    <li>
+		<label for="id_country">Country:</label>
+                {{ form.country }}
+		<em>*</em>
+                {{ form.country.errors }}
+            </li>
+		
+            <li>
+		<label for="id_phone">Phone:</label>
+		{{ form.phone }}
+		<em>*</em>
+                {{ form.phone.errors }}
+	    </li>
+	</ul>
+	
+	<h2>Secure Payment Info</h2>
+	<ul>
+	    <li>
+		<label for="ccType">Credit Card:</label>
+		<img src="{{ MEDIA_URL }}images/order-cc.png" alt="Credit Card" />
+	    </li>
+	    
+	    <li>
+		<label for="id_cc_number">Card Number:</label>
+		{{ form.cc_number }}
+		<em>*</em>
+                {{ form.cc_number.errors }}
+	    </li>
+
+            <li>
+		<label for="id_cc_expiration">Expires:</label>
+                {{ form.cc_expiration }}
+		<em>*</em>
+                {{ form.cc_expiration.errors }}
+	    </li>
+	    
+	    <li>
+		<label for="id_cc_cardcode">CVV:</label>
+		{{ form.cc_cardcode }}
+		<em>*</em>
+		<img src="{{ MEDIA_URL }}images/order-cvv.png" alt="CVV" />
+                {{ form.cc_cardcode.errors }}
+	    </li>
+	    
+	    <li class="a-center">
+		<button type="submit"><span class="button"><span>Sign Up!</span></span></button>
+	    </li>
+	</ul>
+    </form>
+{% endblock content %}

File apps/account/templates/account/tracking_code.html

View file
+{% extends "account/base.html" %}
+{% block page-title %}Set Tracking Code{% endblock page-title %}
+{% block page-header %}Set Tracking Code{% endblock page-header %}
+{% block page-content %}
+    <div>
+        <div class="formDiv">
+            <div>
+                <h2>Set Tracking Code</h2>
+
+                <p>
+                    You can setup your customer view pages to be tracked through <a href="https://www.google.com/analytics">Google Analytics</a>. You will need to setup your Google Analytics account first and get your tracking code from there and enter it in the space provided below.
+                </p>
+
+                <br />
+
+                <form action="" class="set_tracking_code" method="post">
+                    <div class="left">
+                        <label>Tracking Code</label>
+                        <div class="input-box">
+                            UA-{{ form.tracking_code }} (xxxxxx-x)
+                        </div>
+                        {{ form.tracking_code.errors }}
+                    </div>
+                    
+                    <div class="item a-right">
+                        <div class="button">
+                            <span><input type="submit" value="Set Tracking Code" /></span>
+                        </div>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+{% endblock page-content %}
+{% block page-sidebar %}
+    <div class="widget contacts">
+        <h2>Other Options</h2>
+        <ul>
+            <li><a href="{% url account_home %}">Back to Account</a></li>
+        </ul>
+    </div>
+{% endblock page-sidebar %}

File apps/account/templates/account/upgrade.html

View file
+{% extends "account/base.html" %}
+{% block page-title %}Upgrade{% endblock page-title %}
+{% block page-header %}Upgrade{% endblock page-header %}
+{% block page-content %}
+    <h2>Current Plan: {{ plan }}</h2>
+    {% include "account/plan_list.html" %}
+{% endblock page-content %}
+{% block sidebar %}
+{% endblock sidebar %}

File apps/account/templates/account/url.html

View file
+{% extends "account/base.html" %}
+{% block page-title %}Set URL{% endblock page-title %}
+{% block page-header %}Set URL{% endblock page-header %}
+{% block page-content %}
+    <div>
+        <div class="formDiv">
+            <div>
+                <h2>Set URL</h2>
+                <form action="" class="set_url" method="post">
+                    <div class="left">
+                        <label>URL</label>
+                        <div class="input-box">
+                            {{ full_path }}{% url catalog_all %}{{ form.slug }}
+                        </div>
+                        {{ form.slug.errors }}
+                    </div>
+                    
+                    <div class="item a-right">
+                        <div class="button">
+                            <span><input type="submit" value="Set URL" /></span>
+                        </div>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+{% endblock page-content %}
+{% block page-sidebar %}
+    <div class="widget contacts">
+        <h2>Other Options</h2>
+        <ul>
+            <li><a href="{% url account_home %}">Back to Account</a></li>
+        </ul>
+    </div>
+{% endblock page-sidebar %}

File apps/account/templatetags/__init__.py

Empty file added.

File apps/account/templatetags/account_tags.py

View file
+from django import template
+
+from account import models
+
+register = template.Library()
+
+#---------------------------------------------------------------------------
+@register.filter
+def get_period_display(value):
+    period_dict = dict(models.Plan.PERIOD_CHOICES)
+    return period_dict.get(int(value), '')

File apps/account/urls.py

View file
+from django.conf.urls.defaults import *
+
+from account import views, admin
+
+urlpatterns = patterns('',
+    url(r'^login/$', views.login, name='login'),
+    url(r'^logout/$', views.logout, name='logout'),
+    url(r'^$', views.home, name='account_home'),
+
+    # signup process
+    url(r'^pricing/$', views.pricing, name='account_pricing'),
+    url(r'^signup/$', views.signup, name='account_signup'),
+    url(r'^add/(?P<plan_key>\w+)/$', views.signup, name='account_add'),
+    url(r'^upgrade/$', views.upgrade, name='account_upgrade'),
+    url(r'^delete/$', views.delete, name='account_delete'),
+    url(r'^delete/(?P<confirm>\w+)/$', views.delete, name='account_delete_confirm'),
+
+    # general
+    url(r'^url/$', views.set_url, name='account_url'),
+    url(r'^tracking/$', views.set_tracking_code, name='account_tracking_code'),
+
+    # staff section
+    url(r'^staff/$', views.staff, name='account_staff'),
+
+    # admin section
+    url(r'^admin/$', admin.plan_list, name='account_admin_plan_list'),
+    url(
+        r'^admin/plan/add/(?P<plan_key>[-\w]+)/$',
+        admin.plan_add,
+        name='account_admin_plan_add'
+    ),
+    url(
+        r'^admin/plan/edit/(?P<plan_key>[-\w]+)/$',
+        admin.plan_edit,
+        name='account_admin_plan_edit'
+    ),
+    url(r'^admin/customer/$', admin.customer_list, name='account_admin_customer_list'),
+    url(
+        r'^admin/customer/delete/(?P<customer_id>[-\w]+)/$',
+        admin.customer_delete,
+        name='account_admin_customer_delete'
+    ),
+)

File apps/account/utils.py

View file
+import logging
+
+from google.appengine.api import users
+
+from django import http
+from django.core.urlresolvers import reverse
+from django.utils.functional import wraps
+
+from shortcuts import get_user_account
+
+#---------------------------------------------------------------------------
+def has_account(func):
+    """
+    Make sure the user has an account
+    """
+    @wraps(func)
+    def inner(request, *args, **kws):
+        user_account = get_user_account()
+        if not user_account:
+            user = users.get_current_user()
+            if user:
+                return http.HttpResponseRedirect(reverse("account_signup"))
+            else:
+                return http.HttpResponseRedirect(users.create_login_url(
+                    reverse('account_home')
+                ))
+        return func(request, *args, **kws)
+    return inner
+
+#---------------------------------------------------------------------------
+def is_admin(func):
+    '''
+    make sure the user is admin user
+    '''
+    @wraps(func)
+    def inner(request, *args, **kws):
+        if not users.is_current_user_admin():
+            return http.HttpResponseRedirect(reverse('error', args=['permission']))
+        return func(request, *args, **kws)
+    return inner

File apps/account/views.py

View file
+import logging
+
+from google.appengine.api import users
+
+from django import http
+from django.core.urlresolvers import reverse
+
+from lib import pycheddar as payment
+from shortcuts import request_to_response, get_user_account, has_account
+
+from account import forms, models, utils
+
+
+#---------------------------------------------------------------------------
+@utils.has_account
+def home(request):
+    user_account = get_user_account()
+    plan = user_account.get_current_account_plan()
+
+    if not plan:
+        return http.HttpResponseRedirect(reverse('account_signup'))
+
+    data = dict(
+        user_account=user_account,
+        plan=plan,
+        page_id='account',
+        full_path='http://%s' % request.META['HTTP_HOST']
+    )
+    return request_to_response(request, 'account/home.html', data)
+
+#---------------------------------------------------------------------------
+def pricing(request):
+    plan_list = models.get_plan_list()
+    data = dict(
+        plan_list=plan_list,
+        page_id='pricing',
+    )
+    return request_to_response(request, 'account/pricing.html', data)
+    
+#---------------------------------------------------------------------------
+def signup(request, plan_key=None):
+    '''
+    if user associated with an account already send them to the account home page
+    '''
+    message = None
+
+    # check whether user is logged in
+    user = users.get_current_user()
+    if not user:
+        return http.HttpResponseRedirect(users.create_login_url(
+            reverse('account_add', args=[plan_key]))
+        )
+
+    if plan_key and plan_key != 'None':
+        # check whether user has an account already,
+        # if so and we have subscription information
+        # then just update account plan
+        user_account = get_user_account()
+        plan = models.Plan.get(plan_key)
+        if user_account:
+            if user_account.account.is_active:
+                if plan:
+                    if user_account.account.upgrade(plan, user_account):
+                        return http.HttpResponseRedirect(reverse('account_home'))
+            else:
+                message = 'Account is inactive'
+        else:
+            # if selected a free account, no need to get payment information
+            payment_plan = payment.Plan.get(plan.plan_key)
+            if payment_plan.is_free():
+                models.create_account(plan_key)
+                return http.HttpResponseRedirect(reverse('account_home'))
+    
+    form = forms.SignUpForm(request, plan_key)
+    if request.method == "POST":
+        form = forms.SignUpForm(request, plan_key, data=request.POST)
+        if form.is_valid():
+            success, message = form.save()
+            if success:
+                return http.HttpResponseRedirect(reverse('account_home'))
+    data = dict(
+        form=form,
+        page_id='signup',
+        message=message
+    )
+    return request_to_response(request, 'account/signup.html', data)
+
+#---------------------------------------------------------------------------
+@utils.has_account
+def upgrade(request):
+    user_account = get_user_account()
+    plan = user_account.get_current_account_plan()
+    plan_list = models.get_plan_list()
+    data = dict(
+        plan_list=plan_list,
+        page_id='account',
+        plan=plan,
+    )
+    return request_to_response(request, 'account/upgrade.html', data)
+
+#---------------------------------------------------------------------------
+@utils.has_account
+def delete(request, confirm=False):
+    if not confirm:
+        data = dict()
+        return request_to_response(request, 'account/delete.html', data)
+    user_account = get_user_account()
+    if not user_account or not user_account.account.is_active:
+        return http.HttpResponseRedirect(reverse('account_signup'))
+    plan = user_account.get_current_plan()
+    if plan.account.delete():
+        return http.HttpResponseRedirect(reverse('logout'))
+    return http.HttpResponseRedirect(reverse('account_home'))
+    
+#---------------------------------------------------------------------------
+def login(request):
+    data = dict()
+    return http.HttpResponseRedirect(users.create_login_url(reverse('account_home')))
+
+#---------------------------------------------------------------------------
+def logout(request):
+    user = users.get_current_user()
+    if user:
+        return http.HttpResponseRedirect(users.create_logout_url(reverse('logout')))
+    data = dict(
+        page_id='logout',
+    )
+    return request_to_response(request, 'logout.html', data)
+
+#---------------------------------------------------------------------------
+@utils.has_account
+def set_url(request):
+    user_account = get_user_account()
+    form = forms.SetURLForm(request, user_account)
+    if request.method == 'POST':
+        form = forms.SetURLForm(request, user_account, data=request.POST)
+        if form.is_valid():
+            form.save()
+            return http.HttpResponseRedirect(reverse('account_home'))
+    data = dict(
+        page_id='url',
+        user_account=user_account,
+        form=form,
+        full_path='http://%s' % request.META['HTTP_HOST']
+    )
+    return request_to_response(request, 'account/url.html', data)
+
+#---------------------------------------------------------------------------
+@utils.has_account
+def set_tracking_code(request):
+    user_account = get_user_account()
+    form = forms.SetTrackingCodeForm(request, user_account)
+    if request.method == 'POST':
+        form = forms.SetTrackingCodeForm(request, user_account, data=request.POST)
+        if form.is_valid():
+            form.save()
+            return http.HttpResponseRedirect(reverse('account_home'))
+    data = dict(
+        page_id='tracking',
+        user_account=user_account,
+        form=form,
+    )
+    return request_to_response(request, 'account/tracking_code.html', data)
+
+#---------------------------------------------------------------------------
+@utils.has_account
+def staff(request):
+    user_account = get_user_account()
+    return http.HttpResponseRedirect(reverse('account_home'))

File apps/base/__init__.py

Empty file added.

File apps/base/forms.py

View file
+import logging
+
+from google.appengine.api import mail
+
+from django import forms
+
+DEFAULT_EMAIL = 'greg@ironlabs.com'
+
+#===============================================================================
+class ContactForm(forms.Form):
+    name = forms.CharField(
+        max_length=40,
+        widget=forms.TextInput(attrs={'class': 'input-text'}),
+    )
+    email = forms.EmailField(
+        widget=forms.TextInput(attrs={'class': 'input-text'}),
+    )
+    comment = forms.CharField(
+        widget=forms.Textarea(attrs={'class': 'input-text', 'rows': '4'}),
+    )
+
+
+    #---------------------------------------------------------------------------
+    def __init__(self, request, *args, **kws):
+        super(ContactForm, self).__init__(*args, **kws)
+        self.request = request
+
+    #---------------------------------------------------------------------------
+    def save(self):
+        '''
+        send contact to default email for now
+
+        TODO save data to db and then have a triage system in place to handle
+        this communication, this is expecting that things grow nicely
+        '''
+        data = self.cleaned_data
+        message = 'Name: %s\nEmail: %s\n\nComment:\n%s' % (
+            data['name'],
+            data['email'],
+            data['comment']
+        )
+        mail.send_mail(
+            sender=DEFAULT_EMAIL,
+            to=data['email'],
+            subject='Contact',
+            body=message
+        )
+        return True

File apps/base/models.py

View file