adding some model changes around paid plans
This commit is contained in:
@@ -7,10 +7,12 @@ from .models import (
|
|||||||
Team,TeamInvite,TeamRequest,
|
Team,TeamInvite,TeamRequest,
|
||||||
WorkoutComment,C2WorldClassAgePerformance,PlannedSession,
|
WorkoutComment,C2WorldClassAgePerformance,PlannedSession,
|
||||||
GeoCourse,GeoPolygon,GeoPoint,VirtualRace,VirtualRaceResult,
|
GeoCourse,GeoPolygon,GeoPoint,VirtualRace,VirtualRaceResult,
|
||||||
|
PaidPlan
|
||||||
)
|
)
|
||||||
|
|
||||||
# Register your models here so you can use them in the Admin module
|
# Register your models here so you can use them in the Admin module
|
||||||
|
|
||||||
|
|
||||||
# Rower details directly under the User
|
# Rower details directly under the User
|
||||||
class RowerInline(admin.StackedInline):
|
class RowerInline(admin.StackedInline):
|
||||||
model = Rower
|
model = Rower
|
||||||
@@ -120,6 +122,9 @@ class VirtualRaceAdmin(admin.ModelAdmin):
|
|||||||
class VirtualRaceResultAdmin(admin.ModelAdmin):
|
class VirtualRaceResultAdmin(admin.ModelAdmin):
|
||||||
list_display = ('race','userid','username','boattype','age','weightcategory')
|
list_display = ('race','userid','username','boattype','age','weightcategory')
|
||||||
search_fields = ['race__name','username']
|
search_fields = ['race__name','username']
|
||||||
|
|
||||||
|
class PaidPlanAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('name','shortname','price','paymenttype')
|
||||||
|
|
||||||
admin.site.unregister(User)
|
admin.site.unregister(User)
|
||||||
admin.site.register(User,UserAdmin)
|
admin.site.register(User,UserAdmin)
|
||||||
@@ -137,3 +142,4 @@ admin.site.register(PlannedSession,PlannedSessionAdmin)
|
|||||||
admin.site.register(GeoCourse, GeoCourseAdmin)
|
admin.site.register(GeoCourse, GeoCourseAdmin)
|
||||||
admin.site.register(VirtualRace, VirtualRaceAdmin)
|
admin.site.register(VirtualRace, VirtualRaceAdmin)
|
||||||
admin.site.register(VirtualRaceResult, VirtualRaceResultAdmin)
|
admin.site.register(VirtualRaceResult, VirtualRaceResultAdmin)
|
||||||
|
admin.site.register(PaidPlan,PaidPlanAdmin)
|
||||||
|
|||||||
53
rowers/braintreestuff.py
Normal file
53
rowers/braintreestuff.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import braintree
|
||||||
|
|
||||||
|
from rowsandall_app.settings import (
|
||||||
|
BRAINTREE_MERCHANT_ID,BRAINTREE_PUBLIC_KEY,BRAINTREE_PRIVATE_KEY
|
||||||
|
)
|
||||||
|
|
||||||
|
gateway = braintree.BraintreeGateway(
|
||||||
|
braintree.Configuration(
|
||||||
|
braintree.Environment.Sandbox,
|
||||||
|
merchant_id=BRAINTREE_MERCHANT_ID,
|
||||||
|
public_key=BRAINTREE_PUBLIC_KEY,
|
||||||
|
private_key=BRAINTREE_PRIVATE_KEY,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
from rowers.models import Rower,PaidPlan
|
||||||
|
from rowers.utils import ProcessorCustomerError
|
||||||
|
|
||||||
|
def create_customer(rower):
|
||||||
|
if not rower.customer_id:
|
||||||
|
result = gateway.customer.create(
|
||||||
|
{
|
||||||
|
'first_name':rower.user.first_name,
|
||||||
|
'last_name':rower.user.last_name,
|
||||||
|
'email':rower.user.email,
|
||||||
|
})
|
||||||
|
if not result.is_success:
|
||||||
|
raise ProcessorCustomerError
|
||||||
|
else:
|
||||||
|
rower.customer_id = result.customer.id
|
||||||
|
rower.save()
|
||||||
|
else:
|
||||||
|
return rower.customer_id
|
||||||
|
|
||||||
|
def get_client_token(rower):
|
||||||
|
client_token = gateway.client_token.generate({
|
||||||
|
"customer_id":rower.customer_id,
|
||||||
|
})
|
||||||
|
|
||||||
|
return client_token
|
||||||
|
|
||||||
|
def get_plans_costs():
|
||||||
|
plans = gateway.plan.all()
|
||||||
|
|
||||||
|
localplans = PaidPlan.object.all()
|
||||||
|
|
||||||
|
for plan in localplans:
|
||||||
|
for btplan in btplans:
|
||||||
|
if int(btplan.id) == plan.braintree_id:
|
||||||
|
plan.price = float(x)
|
||||||
|
plan.save()
|
||||||
|
|
||||||
|
return plans
|
||||||
@@ -18,8 +18,8 @@ from django.forms import formset_factory
|
|||||||
from utils import landingpages
|
from utils import landingpages
|
||||||
from metrics import axes
|
from metrics import axes
|
||||||
|
|
||||||
# Braintree form
|
# BillingForm form
|
||||||
class BrainTreeForm(forms.Form):
|
class BillingForm(forms.Form):
|
||||||
amount = forms.FloatField(required=True)
|
amount = forms.FloatField(required=True)
|
||||||
payment_method_nonce = forms.CharField(max_length=255)
|
payment_method_nonce = forms.CharField(max_length=255)
|
||||||
|
|
||||||
|
|||||||
@@ -537,6 +537,27 @@ weightcategories = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Plan
|
||||||
|
plans = (
|
||||||
|
('basic','basic'),
|
||||||
|
('pro','pro'),
|
||||||
|
('plan','plan'),
|
||||||
|
('coach','coach')
|
||||||
|
)
|
||||||
|
|
||||||
|
paymenttypes = (
|
||||||
|
('single','single'),
|
||||||
|
('recurring','recurring')
|
||||||
|
)
|
||||||
|
|
||||||
|
class PaidPlan(models.Model):
|
||||||
|
shortname = models.CharField(max_length=50,choices=plans)
|
||||||
|
name = models.CharField(max_length=200)
|
||||||
|
braintree_id = models.IntegerField(blank=True,null=True,default=None)
|
||||||
|
price = models.FloatField(blank=True,null=True,default=None)
|
||||||
|
paymenttype = models.CharField(max_length=50,choices=paymenttypes)
|
||||||
|
clubsize = models.IntegerField(default=0)
|
||||||
|
|
||||||
# Extension of User with rowing specific data
|
# Extension of User with rowing specific data
|
||||||
class Rower(models.Model):
|
class Rower(models.Model):
|
||||||
adaptivetypes = mytypes.adaptivetypes
|
adaptivetypes = mytypes.adaptivetypes
|
||||||
@@ -694,13 +715,6 @@ class Rower(models.Model):
|
|||||||
blank=True,null=True)
|
blank=True,null=True)
|
||||||
runkeeper_auto_export = models.BooleanField(default=False)
|
runkeeper_auto_export = models.BooleanField(default=False)
|
||||||
|
|
||||||
# Plan
|
|
||||||
plans = (
|
|
||||||
('basic','basic'),
|
|
||||||
('pro','pro'),
|
|
||||||
('plan','plan'),
|
|
||||||
('coach','coach')
|
|
||||||
)
|
|
||||||
|
|
||||||
privacychoices = (
|
privacychoices = (
|
||||||
('visible','Visible'),
|
('visible','Visible'),
|
||||||
@@ -720,10 +734,7 @@ class Rower(models.Model):
|
|||||||
paymenttype = models.CharField(
|
paymenttype = models.CharField(
|
||||||
default='single',max_length=30,
|
default='single',max_length=30,
|
||||||
verbose_name='Payment Type',
|
verbose_name='Payment Type',
|
||||||
choices=(
|
choices=paymenttypes,
|
||||||
('single','single'),
|
|
||||||
('recurring','recurring')
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
planexpires = models.DateField(default=timezone.now)
|
planexpires = models.DateField(default=timezone.now)
|
||||||
|
|||||||
269
rowers/templates/paidplans.html
Normal file
269
rowers/templates/paidplans.html
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
{% extends "newbase.html" %}
|
||||||
|
{% block title %}Rowsandall Paid Membership{% endblock title %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
<h1>Paid Membership Plans</h1>
|
||||||
|
|
||||||
|
<ul class="main-content">
|
||||||
|
<li class="grid_4">
|
||||||
|
<p>Rowsandall.com offers free data and analysis for rowers, by rowers.
|
||||||
|
Of course, offering this service is not free. To help cover the
|
||||||
|
hosting costs, we have created paid plans offering extended
|
||||||
|
functionality.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<table class="plantable shortpadded" width="80%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th> </th>
|
||||||
|
<th>BASIC</th>
|
||||||
|
<th>PRO</th>
|
||||||
|
<th>SELF-COACH</th>
|
||||||
|
<th>COACH</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Basic rowing metrics (spm, time, distance, heart rate, power)</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Manual Import, Export, Synchronization and download of all your data</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Automatic Synchronization with other fitness sites</td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Heart rate and power zones</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Ranking Pieces, Stroke Analysis</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Advanced Analysis (Critical Power, Stats, Box Chart, Trend Flex)</td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Compare Workouts</td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Empower Stroke Profile</td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Sensor Fusion, Split Workout, In-stroke metrics</td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Create Training plans, tests and challenges for yourself. Track your performance
|
||||||
|
against plan.</td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Create Training plans, tests and challenges for your athletes. Track their performance
|
||||||
|
against plan. </td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Create and manage teams.</td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Manage your athlete's workouts</td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td>✔</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Pricing</td>
|
||||||
|
<td>FREE</td>
|
||||||
|
<td nowrap="nowrap">From 15€/year</td>
|
||||||
|
<td nowrap="nowrap">From 65€/year</td>
|
||||||
|
<td nowrap="nowrap">From 90€/year</td>
|
||||||
|
</tr>
|
||||||
|
{% if rower %}
|
||||||
|
<tr>
|
||||||
|
<td>Your current plan</td>
|
||||||
|
<td>
|
||||||
|
{% if rower.rowerplan == 'basic' %}
|
||||||
|
<h3>BASIC</h3>
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if rower.rowerplan == 'pro' %}
|
||||||
|
PRO
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if rower.rowerplan == 'plan' %}
|
||||||
|
SELF-COACH
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if rower.rowerplan == 'coach' %}
|
||||||
|
COACH
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Available trials
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if user.is_anonymous %}
|
||||||
|
<button style="width:100%">
|
||||||
|
<a href="/rowers/starttrial">Free PRO trial</a>
|
||||||
|
</button>
|
||||||
|
{% elif rower and rower.rowerplan == 'basic' and rower.protrialexpires|date_dif == 1 %}
|
||||||
|
<button style="width:100%">
|
||||||
|
<a href="/rowers/starttrial">Free PRO trial</a>
|
||||||
|
</button>
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if user.is_anonymous %}
|
||||||
|
<button style="width:100%">
|
||||||
|
<a href="/rowers/startplantrial">Free SELF-COACH trial</a>
|
||||||
|
</button>
|
||||||
|
{% elif rower and rower.rowerplan == 'basic' and rower.plantrialexpires|date_dif == 1 %}
|
||||||
|
<button style="width:100%">
|
||||||
|
<a href="/rowers/startplantrial">Free SELF-COACH trial</a>
|
||||||
|
</button>
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Available upgrades
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
{% if user.is_anonymous %}
|
||||||
|
<td colspan="3">
|
||||||
|
<button style="width:100%">
|
||||||
|
<a href="/rowers/upgrade">UPGRADE NOW</a>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
{% elif rower and rower.rowerplan == 'basic' %}
|
||||||
|
<td colspan="3">
|
||||||
|
<button style="width:100%">
|
||||||
|
<a href="/rowers/upgrade">UPGRADE NOW</a>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
{% elif rower and rower.rowerplan == 'pro' %}
|
||||||
|
<td> </td>
|
||||||
|
<td colspan="2">
|
||||||
|
<button style="width:100%">
|
||||||
|
<a href="/rowers/upgrade">UPGRADE NOW</a>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
{% elif rower and rower.rowerplan == 'plan' %}
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td>
|
||||||
|
<button style="width:100%">
|
||||||
|
<a href="/rowers/upgrade">UPGRADE NOW</a>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td colspan=3>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Coach and Self-Coach Membership</h2>
|
||||||
|
|
||||||
|
<p>The Coach plan functionality listed is available to the coach only. Individual athletes
|
||||||
|
can purchase upgrades to "Pro" and "Self-Coach" plans.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Rowsandall.com's Training Planning functionality
|
||||||
|
is part of the paid "Self-Coach" and "Coach" plans.</p>
|
||||||
|
|
||||||
|
<p>On the "Self-Coach" plan, you can plan your own sessions.</p>
|
||||||
|
|
||||||
|
<p>On the "Coach" plan, you can establish teams, see workouts done by
|
||||||
|
athletes on your team, and plan individual and group sessions for your
|
||||||
|
athletes.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>If you would like to find a coach who helps you plan your training
|
||||||
|
through rowsandall.com, contact me throught the contact form.</p>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block sidebar %}
|
||||||
|
{% include 'menu_help.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
@@ -438,6 +438,7 @@ urlpatterns = [
|
|||||||
url(r'^analysis/$', views.analysis_view,name='analysis'),
|
url(r'^analysis/$', views.analysis_view,name='analysis'),
|
||||||
url(r'^laboratory/$', views.laboratory_view,name='laboratory'),
|
url(r'^laboratory/$', views.laboratory_view,name='laboratory'),
|
||||||
url(r'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'),
|
url(r'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'),
|
||||||
|
url(r'^paidplans',views.paidplans_view,name='paidplans'),
|
||||||
url(r'^checkouts',views.checkouts_view,name='checkouts'),
|
url(r'^checkouts',views.checkouts_view,name='checkouts'),
|
||||||
url(r'^payments',views.payments_view,name='payments'),
|
url(r'^payments',views.payments_view,name='payments'),
|
||||||
url(r'^planrequired',views.planrequired_view),
|
url(r'^planrequired',views.planrequired_view),
|
||||||
|
|||||||
@@ -426,6 +426,13 @@ class NoTokenError(Exception):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return repr(self.value)
|
return repr(self.value)
|
||||||
|
|
||||||
|
class ProcessorCustomerError(Exception):
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value=value
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return repr(self.value)
|
||||||
|
|
||||||
# Custom exception handler, returns a 401 HTTP message
|
# Custom exception handler, returns a 401 HTTP message
|
||||||
# with exception details in the json data
|
# with exception details in the json data
|
||||||
def custom_exception_handler(exc,message):
|
def custom_exception_handler(exc,message):
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ from rowers.forms import (
|
|||||||
RaceResultFilterForm,PowerIntervalUpdateForm,FlexAxesForm,
|
RaceResultFilterForm,PowerIntervalUpdateForm,FlexAxesForm,
|
||||||
FlexOptionsForm,DataFrameColumnsForm,OteWorkoutTypeForm,
|
FlexOptionsForm,DataFrameColumnsForm,OteWorkoutTypeForm,
|
||||||
MetricsForm,DisqualificationForm,disqualificationreasons,
|
MetricsForm,DisqualificationForm,disqualificationreasons,
|
||||||
disqualifiers,SearchForm,BrainTreeForm
|
disqualifiers,SearchForm,BillingForm
|
||||||
)
|
)
|
||||||
from django.core.urlresolvers import reverse, reverse_lazy
|
from django.core.urlresolvers import reverse, reverse_lazy
|
||||||
|
|
||||||
@@ -1029,6 +1029,17 @@ def add_defaultfavorites(r):
|
|||||||
f.save()
|
f.save()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
def paidplans_view(request):
|
||||||
|
if not request.user.is_anonymous():
|
||||||
|
r = getrequestrower(request)
|
||||||
|
else:
|
||||||
|
r = None
|
||||||
|
|
||||||
|
|
||||||
|
return render(request,
|
||||||
|
'paidplans.html',
|
||||||
|
{'rower':r})
|
||||||
|
|
||||||
# Experimental - Payments
|
# Experimental - Payments
|
||||||
@login_required()
|
@login_required()
|
||||||
def payments_view(request):
|
def payments_view(request):
|
||||||
@@ -1091,7 +1102,7 @@ def checkouts_view(request):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
form = BrainTreeForm(request.POST)
|
form = BillingForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
nonce_from_the_client = form.cleaned_data['payment_method_nonce']
|
nonce_from_the_client = form.cleaned_data['payment_method_nonce']
|
||||||
amount = form.cleaned_data['amount']
|
amount = form.cleaned_data['amount']
|
||||||
|
|||||||
@@ -170,6 +170,15 @@ cox {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.plantable {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plantable > td {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
th.rotate {
|
th.rotate {
|
||||||
/* Something you can count on */
|
/* Something you can count on */
|
||||||
height: 78px;
|
height: 78px;
|
||||||
@@ -220,6 +229,7 @@ th.rotate > div > span {
|
|||||||
background-color: #fee;
|
background-color: #fee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.successmessage {
|
.successmessage {
|
||||||
border: 1px solid #000;
|
border: 1px solid #000;
|
||||||
background-color: #8f8;
|
background-color: #8f8;
|
||||||
@@ -907,3 +917,21 @@ a.wh:hover {
|
|||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.upgradebutton {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 4px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonr2 {border-radius: 2px;}
|
||||||
|
.buttonr4 {border-radius: 4px;}
|
||||||
|
.buttonr8 {border-radius: 8px;}
|
||||||
|
.buttonr12 {border-radius: 12px;}
|
||||||
|
.buttonround {border-radius: 50%;}
|
||||||
|
|||||||
Reference in New Issue
Block a user