parent
a3fbcbfece
commit
5ce8046928
631 changed files with 416 additions and 69438 deletions
@ -0,0 +1,34 @@ |
|||||||
|
# SITE SETTINGS |
||||||
|
SITE_HOST=78.155.219.170 |
||||||
|
USE_SSL=True |
||||||
|
ALLOWED_HOSTS=78.155.219.170, |
||||||
|
|
||||||
|
# APP SETTINGS |
||||||
|
SECRET_KEY=+fd(^upr^y9++w^k_p+2&k+^rtp5-um-pzvsrm9ycq++@_$5i* |
||||||
|
|
||||||
|
# DEBUG SETTINGS |
||||||
|
DEBUG=False |
||||||
|
DJANGO_DEBUG_TOOLBAR=False |
||||||
|
|
||||||
|
# DATABASE SETTINGS |
||||||
|
DB_NAME=eshop_db |
||||||
|
DB_USER=eshop_admin |
||||||
|
DB_PASSWORD=12345678 |
||||||
|
DB_HOST=localhost |
||||||
|
DB_PORT=5432 |
||||||
|
|
||||||
|
# DATABASE PRODUCTION SETTINGS |
||||||
|
DATABASE_URL=psql://eshop_admin:eshop_password@127.0.0.1:5432/eshop |
||||||
|
|
||||||
|
# EMAIL SETTINGS |
||||||
|
EMAIL_BACKEND=djcelery_email.backends.CeleryEmailBackend |
||||||
|
EMAIL_USE_TLS=True |
||||||
|
EMAIL_USE_SSL=False |
||||||
|
EMAIL_HOST=smtp.gmail.com |
||||||
|
EMAIL_PORT=587 |
||||||
|
EMAIL_HOST_USER=gmail_user |
||||||
|
EMAIL_HOST_PASSWORD=gmail_password |
||||||
|
|
||||||
|
# CELERY SETTINGS |
||||||
|
REDIS_CELERY_URL=redis://127.0.0.1 |
||||||
|
|
||||||
@ -1 +0,0 @@ |
|||||||
from .celery import app as celery_app |
|
||||||
@ -1,10 +0,0 @@ |
|||||||
import os |
|
||||||
from celery import Celery |
|
||||||
from django.conf import settings |
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Eshop.settings') |
|
||||||
|
|
||||||
app = Celery('Eshop') |
|
||||||
|
|
||||||
app.config_from_object('django.conf:settings') |
|
||||||
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) |
|
||||||
Binary file not shown.
Binary file not shown.
@ -1,3 +0,0 @@ |
|||||||
from django.contrib import admin |
|
||||||
|
|
||||||
# Register your models here. |
|
||||||
@ -1,5 +0,0 @@ |
|||||||
from django.apps import AppConfig |
|
||||||
|
|
||||||
|
|
||||||
class CartConfig(AppConfig): |
|
||||||
name = 'cart' |
|
||||||
@ -1,86 +0,0 @@ |
|||||||
from decimal import Decimal |
|
||||||
from django.conf import settings |
|
||||||
from django.contrib import auth |
|
||||||
from products.models import Product, Offer |
|
||||||
# from discount.models import Discount |
|
||||||
|
|
||||||
class Cart(object): |
|
||||||
def __init__(self, request): |
|
||||||
self.session = request.session |
|
||||||
self.discount_id = self.session.get('discount_id') |
|
||||||
if request.user.is_authenticated(): |
|
||||||
self.points = self.session.get('points') |
|
||||||
self.points_quant = auth.get_user(request).profile.user_points |
|
||||||
cart = self.session.get(settings.CART_SESSION_ID) |
|
||||||
if not cart: |
|
||||||
request.session['points'] = False |
|
||||||
cart = self.session[settings.CART_SESSION_ID] = {} |
|
||||||
self.cart = cart |
|
||||||
|
|
||||||
def add(self, offer, price_per_itom, quantity=1, update_quantity=False): |
|
||||||
offer_slug = offer.slug |
|
||||||
if offer_slug not in self.cart: |
|
||||||
self.cart[offer_slug] = {'quantity': 0, |
|
||||||
'price': str(price_per_itom)} |
|
||||||
if update_quantity: |
|
||||||
self.cart[offer_slug]['quantity'] = int(quantity) |
|
||||||
else: |
|
||||||
self.cart[offer_slug]['quantity'] += int(quantity) |
|
||||||
self.save() |
|
||||||
|
|
||||||
def save(self): |
|
||||||
self.session[settings.CART_SESSION_ID] = self.cart |
|
||||||
self.session.modified = True |
|
||||||
|
|
||||||
def remove(self, offer_slug): |
|
||||||
# product_id = str(product.id) |
|
||||||
if offer_slug in self.cart: |
|
||||||
del self.cart[offer_slug] |
|
||||||
self.save() |
|
||||||
|
|
||||||
def __iter__(self): |
|
||||||
offers_ids = self.cart.keys() |
|
||||||
offers = Offer.objects.filter(slug__in=offers_ids) |
|
||||||
|
|
||||||
for offer in offers: |
|
||||||
self.cart[str(offer.slug)]['offer'] = offer |
|
||||||
|
|
||||||
for item in self.cart.values(): |
|
||||||
item['price'] = Decimal(item['price']) |
|
||||||
item['total_price'] = item['price'] * item['quantity'] |
|
||||||
yield item |
|
||||||
|
|
||||||
def __len__(self): |
|
||||||
return sum(item['quantity'] for item in self.cart.values()) |
|
||||||
|
|
||||||
def get_total_price(self): |
|
||||||
return sum(Decimal(item['price']) * item['quantity'] for item in self.cart.values()) |
|
||||||
|
|
||||||
def clear(self): |
|
||||||
del self.session[settings.CART_SESSION_ID] |
|
||||||
self.session.modified = True |
|
||||||
|
|
||||||
# @property |
|
||||||
# def discount(self): |
|
||||||
# if self.discount_id: |
|
||||||
# return Discount.objects.get(id=self.discount_id) |
|
||||||
# return None |
|
||||||
|
|
||||||
# def get_discount(self): |
|
||||||
# if self.discount: |
|
||||||
# return (self.discount.discount / Decimal('100')) * self.get_total_price() |
|
||||||
# return Decimal('0') |
|
||||||
|
|
||||||
# def get_total_price_after_discount(self): |
|
||||||
# return self.get_total_price() - self.get_discount() |
|
||||||
|
|
||||||
def get_total_deduct_points(self): |
|
||||||
total_price = self.get_total_price() |
|
||||||
print(total_price, self.points_quant) |
|
||||||
if total_price <= self.points_quant: |
|
||||||
print('Less') |
|
||||||
self.points_quant = self.points_quant - total_price + 1 |
|
||||||
return 1 |
|
||||||
print('More') |
|
||||||
return total_price - self.points_quant |
|
||||||
|
|
||||||
@ -1,6 +0,0 @@ |
|||||||
from .cart import Cart |
|
||||||
|
|
||||||
|
|
||||||
def cart(request): |
|
||||||
return {'cart': Cart(request)} |
|
||||||
|
|
||||||
@ -1,22 +0,0 @@ |
|||||||
from django import forms |
|
||||||
|
|
||||||
#PRODUCT_QUANTITY_CHOICES = [(i, str(i)) for i in range(1, 21)] |
|
||||||
|
|
||||||
class CartAddProductForm(forms.Form): |
|
||||||
#quantity = forms.TypedChoiceField(choices=PRODUCT_QUANTITY_CHOICES, coerce=int) |
|
||||||
quantity = forms.CharField(required=True, widget=forms.TextInput(attrs={ |
|
||||||
'id': 'quantity', |
|
||||||
'name': 'quantity', |
|
||||||
'type': 'number', |
|
||||||
'value': '0', |
|
||||||
'onchange': 'calculate()'})) |
|
||||||
product_slug = forms.CharField(label="product_slug", widget=forms.TextInput(attrs={ |
|
||||||
'id': 'product_slug', |
|
||||||
'name': 'product_slug', |
|
||||||
'type': 'hidden'})) |
|
||||||
price_per_itom = forms.IntegerField(label="price_per_itom", widget=forms.TextInput(attrs={ |
|
||||||
'id': 'price_per_itom', |
|
||||||
'name': 'price_per_itom', |
|
||||||
'type': 'hidden'})) |
|
||||||
update = forms.BooleanField(required=False, initial=False, widget=forms.HiddenInput) |
|
||||||
|
|
||||||
@ -1,3 +0,0 @@ |
|||||||
from django.db import models |
|
||||||
|
|
||||||
# Create your models here. |
|
||||||
@ -1,3 +0,0 @@ |
|||||||
from django.test import TestCase |
|
||||||
|
|
||||||
# Create your tests here. |
|
||||||
@ -1,8 +0,0 @@ |
|||||||
from django.conf.urls import url |
|
||||||
from . import views |
|
||||||
|
|
||||||
urlpatterns = [ |
|
||||||
url(r'^$', views.CartDetail, name='CartDetail'), |
|
||||||
url(r'^remove/(?P<offer_slug>[-\w]+)/$', views.CartRemove, name='CartRemove'), |
|
||||||
url(r'^add/$', views.CartAdd, name='CartAdd'), |
|
||||||
] |
|
||||||
@ -1,41 +0,0 @@ |
|||||||
from django.shortcuts import render, redirect, get_object_or_404 |
|
||||||
from django.views.decorators.http import require_POST |
|
||||||
from django.views.decorators.csrf import csrf_exempt |
|
||||||
from django.contrib import auth |
|
||||||
from products.models import Product, Offer |
|
||||||
from .cart import Cart |
|
||||||
from .forms import CartAddProductForm |
|
||||||
# from discount.forms import DiscountApllyForm |
|
||||||
|
|
||||||
@csrf_exempt |
|
||||||
@require_POST |
|
||||||
def CartAdd(request): |
|
||||||
cart = Cart(request) |
|
||||||
form = CartAddProductForm(request.POST) |
|
||||||
if form.is_valid(): |
|
||||||
cd = form.cleaned_data |
|
||||||
offer = get_object_or_404(Offer, slug=cd['product_slug']) |
|
||||||
cart.add(offer=offer, price_per_itom=cd['price_per_itom'], quantity=cd['quantity'], |
|
||||||
update_quantity=cd['update']) |
|
||||||
return redirect('cart:CartDetail') |
|
||||||
|
|
||||||
def CartRemove(request, offer_slug): |
|
||||||
cart = Cart(request) |
|
||||||
# offer = get_object_or_404(Offer, slug=offer_slug) |
|
||||||
cart.remove(offer_slug) |
|
||||||
return redirect('cart:CartDetail') |
|
||||||
|
|
||||||
def CartDetail(request, points=False): |
|
||||||
user = auth.get_user(request) |
|
||||||
cart = Cart(request) |
|
||||||
for item in cart: |
|
||||||
item['update_quantity_form'] = CartAddProductForm( |
|
||||||
initial={ |
|
||||||
'quantity': item['quantity'], |
|
||||||
'product_slug': item['offer'].slug, |
|
||||||
'price_per_itom': item['price'], |
|
||||||
'update': True |
|
||||||
}) |
|
||||||
# discount_apply_form = DiscountApllyForm() |
|
||||||
return render(request, 'cart/detail.html', {'username': user.username, 'points': points}) |
|
||||||
# 'discount_apply_form': discount_apply_form}) |
|
||||||
@ -1,10 +0,0 @@ |
|||||||
from django.contrib import admin |
|
||||||
from .models import Discount |
|
||||||
|
|
||||||
|
|
||||||
class DiscountAdmin(admin.ModelAdmin): |
|
||||||
list_display = ['code', 'valid_from', 'valid_to', 'discount', 'active'] |
|
||||||
list_filter = ['valid_from', 'valid_to', 'active'] |
|
||||||
search_field = ['code'] |
|
||||||
|
|
||||||
admin.site.register(Discount, DiscountAdmin) |
|
||||||
@ -1,5 +0,0 @@ |
|||||||
from django.apps import AppConfig |
|
||||||
|
|
||||||
|
|
||||||
class DiscountConfig(AppConfig): |
|
||||||
name = 'discount' |
|
||||||
@ -1,4 +0,0 @@ |
|||||||
from django import forms |
|
||||||
|
|
||||||
class DiscountApllyForm(forms.Form): |
|
||||||
code = forms.CharField() |
|
||||||
@ -1,26 +0,0 @@ |
|||||||
from django.db import models |
|
||||||
import uuid |
|
||||||
from django.db.models.signals import post_save |
|
||||||
from datetime import datetime, timedelta |
|
||||||
from django.core.validators import MinValueValidator, MaxValueValidator |
|
||||||
from django.contrib.auth.models import User |
|
||||||
|
|
||||||
class Discount(models.Model): |
|
||||||
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True) |
|
||||||
code = models.CharField(max_length=50, blank=True, unique=True, default=str(uuid.uuid4())) |
|
||||||
valid_from = models.DateTimeField(default=datetime.now, blank=True) |
|
||||||
valid_to = models.DateTimeField(default=datetime.now()+timedelta(days=7), blank=True) |
|
||||||
discount = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(100)], default=10) |
|
||||||
active = models.BooleanField(default=True) |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return self.code |
|
||||||
|
|
||||||
def create_discount(sender, **kwargs): |
|
||||||
if kwargs['created']: |
|
||||||
user_discount = Discount.objects.create(user=kwargs['instance']) |
|
||||||
|
|
||||||
# post_save.connect(create_discount, sender=User) |
|
||||||
|
|
||||||
User.discount = property(lambda u: Discount.objects.get_or_create(user=u)[0]) |
|
||||||
|
|
||||||
@ -1,3 +0,0 @@ |
|||||||
from django.test import TestCase |
|
||||||
|
|
||||||
# Create your tests here. |
|
||||||
@ -1,9 +0,0 @@ |
|||||||
from django.conf.urls import url |
|
||||||
from . import views |
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [ |
|
||||||
url(r'^apply', views.DiscountApply, name='apply'), |
|
||||||
url(r'^create', views.CreateDiscount, name='create'), |
|
||||||
url(r'^points', views.PointsApply, name='points') |
|
||||||
] |
|
||||||
@ -1,47 +0,0 @@ |
|||||||
import uuid |
|
||||||
from datetime import datetime |
|
||||||
from django.shortcuts import render, redirect |
|
||||||
from django.views.decorators.csrf import csrf_exempt |
|
||||||
from datetime import datetime, timedelta |
|
||||||
from django.contrib import auth |
|
||||||
from django.views.decorators.http import require_POST |
|
||||||
from django.contrib.auth.decorators import login_required |
|
||||||
from .models import Discount |
|
||||||
from .forms import DiscountApllyForm |
|
||||||
|
|
||||||
@login_required |
|
||||||
@require_POST |
|
||||||
@csrf_exempt |
|
||||||
def PointsApply(request): |
|
||||||
# request.session['points'] = True |
|
||||||
return redirect('cart:CartDetail', points=True) |
|
||||||
|
|
||||||
@require_POST |
|
||||||
def DiscountApply(request): |
|
||||||
now = datetime.now() |
|
||||||
form = DiscountApllyForm(request.POST) |
|
||||||
if form.is_valid(): |
|
||||||
code = form.cleaned_data['code'] |
|
||||||
try: |
|
||||||
discount = Discount.objects.get(code__iexact=code, |
|
||||||
valid_from__lte=now, |
|
||||||
valid_to__gte=now, |
|
||||||
active=True) |
|
||||||
request.session['discount_id'] = discount.id |
|
||||||
except Discount.DoesNotExist: |
|
||||||
request.session['discount_id'] = None |
|
||||||
|
|
||||||
return redirect('cart:CartDetail') |
|
||||||
|
|
||||||
@login_required |
|
||||||
@require_POST |
|
||||||
@csrf_exempt |
|
||||||
def CreateDiscount(request): |
|
||||||
user = auth.get_user(request) |
|
||||||
Discount.objects.update_or_create(user=user, defaults={'code': str(uuid.uuid4()), 'valid_from': datetime.now(), |
|
||||||
'valid_to': datetime.now()+timedelta(days=7), 'active': True}) |
|
||||||
return redirect('profile:user_profile') |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,3 @@ |
|||||||
|
from .celery import app as celery_app |
||||||
|
|
||||||
|
__all__ = ['celery_app'] |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
import os |
||||||
|
from celery import Celery |
||||||
|
from django.conf import settings |
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'eshop_project.settings.celery') |
||||||
|
|
||||||
|
app = Celery('eshop_project') |
||||||
|
|
||||||
|
app.config_from_object('django.conf:settings') |
||||||
|
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) |
||||||
@ -0,0 +1,178 @@ |
|||||||
|
[ |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 1, |
||||||
|
"fields": { |
||||||
|
"app_label": "admin", |
||||||
|
"model": "logentry" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 2, |
||||||
|
"fields": { |
||||||
|
"app_label": "auth", |
||||||
|
"model": "group" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 3, |
||||||
|
"fields": { |
||||||
|
"app_label": "auth", |
||||||
|
"model": "permission" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 4, |
||||||
|
"fields": { |
||||||
|
"app_label": "auth", |
||||||
|
"model": "user" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 5, |
||||||
|
"fields": { |
||||||
|
"app_label": "contenttypes", |
||||||
|
"model": "contenttype" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 6, |
||||||
|
"fields": { |
||||||
|
"app_label": "sessions", |
||||||
|
"model": "session" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 7, |
||||||
|
"fields": { |
||||||
|
"app_label": "landing", |
||||||
|
"model": "subscriber" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 8, |
||||||
|
"fields": { |
||||||
|
"app_label": "orders", |
||||||
|
"model": "order" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 9, |
||||||
|
"fields": { |
||||||
|
"app_label": "orders", |
||||||
|
"model": "productsinbasket" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 10, |
||||||
|
"fields": { |
||||||
|
"app_label": "orders", |
||||||
|
"model": "status" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 11, |
||||||
|
"fields": { |
||||||
|
"app_label": "orders", |
||||||
|
"model": "productsinorder" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 12, |
||||||
|
"fields": { |
||||||
|
"app_label": "userprofile", |
||||||
|
"model": "userprofile" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 13, |
||||||
|
"fields": { |
||||||
|
"app_label": "products", |
||||||
|
"model": "productclass" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 14, |
||||||
|
"fields": { |
||||||
|
"app_label": "products", |
||||||
|
"model": "productcategory" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 15, |
||||||
|
"fields": { |
||||||
|
"app_label": "products", |
||||||
|
"model": "product" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 16, |
||||||
|
"fields": { |
||||||
|
"app_label": "products", |
||||||
|
"model": "productimage" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 17, |
||||||
|
"fields": { |
||||||
|
"app_label": "products", |
||||||
|
"model": "productattribute" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 18, |
||||||
|
"fields": { |
||||||
|
"app_label": "products", |
||||||
|
"model": "offer" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 19, |
||||||
|
"fields": { |
||||||
|
"app_label": "products", |
||||||
|
"model": "attributechoicevalue" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 20, |
||||||
|
"fields": { |
||||||
|
"app_label": "ipn", |
||||||
|
"model": "paypalipn" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 21, |
||||||
|
"fields": { |
||||||
|
"app_label": "discount", |
||||||
|
"model": "discount" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"model": "contenttypes.contenttype", |
||||||
|
"pk": 22, |
||||||
|
"fields": { |
||||||
|
"app_label": "userprofile", |
||||||
|
"model": "pickuprequest" |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
from .base import * |
||||||
|
|
||||||
|
CELERY_BROKER_URL= env.str('CELERY_BROKER_URL') |
||||||
|
CELERY_RESULT_BACKEND = env.str('CELERY_RESULT_BACKEND') |
||||||
|
CELERY_ACCEPT_CONTENT = tuple(env.list("CELERY_ACCEPT_CONTENT")) |
||||||
|
CELERY_RESULT_SERIALIZER = env.str('json') |
||||||
|
CELERY_TASK_SERIALIZER = env.str('json') |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
from .base import * |
||||||
|
|
||||||
|
DATABASES = { |
||||||
|
**DATABASES, |
||||||
|
'default': { |
||||||
|
'ENGINE': 'django.db.backends.postgresql_psycopg2', |
||||||
|
'NAME': env.str('DB_NAME'), |
||||||
|
'USER': env.str('DB_USER'), |
||||||
|
'PASSWORD': env.str('DB_PASSWORD'), |
||||||
|
'HOST': env.str('DB_HOST'), |
||||||
|
'PORT': env.str('DB_PORT') |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
EMAIL_BACKEND = 'djcelery_email.backends.CeleryEmailBackend' |
||||||
|
|
||||||
@ -0,0 +1,15 @@ |
|||||||
|
import environ |
||||||
|
|
||||||
|
__all__ = ( |
||||||
|
'env', |
||||||
|
) |
||||||
|
|
||||||
|
env = environ.Env() |
||||||
|
|
||||||
|
|
||||||
|
env.read_env( |
||||||
|
env.path( |
||||||
|
'ENV_FILE_PATH', |
||||||
|
default=(environ.Path(__file__) - 3).path('.env')() |
||||||
|
)() |
||||||
|
) |
||||||
@ -0,0 +1,24 @@ |
|||||||
|
from .base import * |
||||||
|
from .env import env |
||||||
|
|
||||||
|
# Database settings. There must a database url |
||||||
|
DATABASES += { |
||||||
|
'default': env.db() |
||||||
|
} |
||||||
|
|
||||||
|
# Email backend settings |
||||||
|
EMAIL_BACKEND = env.str('EMAIL_BACKEND') |
||||||
|
EMAIL_USE_TLS = env.bool('EMAIL_USE_TLS') |
||||||
|
EMAIL_USE_SSL = env.bool("EMAIL_USE_SSL") |
||||||
|
EMAIL_HOST = env.str('EMAIL_HOST') |
||||||
|
EMAIL_PORT = env.int('EMAIL_PORT') |
||||||
|
EMAIL_HOST_USER = env.str('EMAIL_HOST_USER') |
||||||
|
EMAIL_HOST_PASSWORD = env.str('EMAIL_HOST_PASSWORD') |
||||||
|
|
||||||
|
# Email user settings |
||||||
|
ADMINS = ( |
||||||
|
('Dmitriy Belousov', 'dimkasp@mail.ru'), |
||||||
|
) |
||||||
|
|
||||||
|
MANAGERS = ADMINS |
||||||
|
DEFAULT_FROM_EMAIL = 'notreply@russianprograms' |
||||||
@ -0,0 +1 @@ |
|||||||
|
__all__ = () |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
""" |
||||||
|
Two things are wrong with Django's default `SECRET_KEY` system: |
||||||
|
1. It is not random but pseudo-random |
||||||
|
2. It saves and displays the SECRET_KEY in `settings.py` |
||||||
|
This snippet |
||||||
|
1. uses `SystemRandom()` instead to generate a random key |
||||||
|
2. saves a local `secret.txt` |
||||||
|
The result is a random and safely hidden `SECRET_KEY`. |
||||||
|
""" |
||||||
|
try: |
||||||
|
SECRET_KEY |
||||||
|
except NameError as ne: |
||||||
|
import os |
||||||
|
from eshop_project.settings.base import BASE_DIR |
||||||
|
SECRET_FILE = os.path.join(BASE_DIR, 'secret.txt') |
||||||
|
try: |
||||||
|
SECRET_KEY = open(SECRET_FILE).read().strip() |
||||||
|
except IOError: |
||||||
|
try: |
||||||
|
import random |
||||||
|
SECRET_KEY = ''.join([random.SystemRandom().choice('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range(50)]) |
||||||
|
with open(SECRET_FILE,'w') as f: |
||||||
|
f.write(SECRET_KEY) |
||||||
|
except IOError: |
||||||
|
Exception('Please create a %s file with random characters \ |
||||||
|
to generate your secret key!' % SECRET_FILE) |
||||||
File diff suppressed because it is too large
Load Diff
@ -1,15 +0,0 @@ |
|||||||
from django.contrib import admin |
|
||||||
from .models import * |
|
||||||
|
|
||||||
# Register your models here. |
|
||||||
|
|
||||||
class SubscriberAdmin(admin.ModelAdmin): |
|
||||||
|
|
||||||
list_display = [field.name for field in Subscriber._meta.fields] |
|
||||||
list_filter = ['name'] |
|
||||||
search_fields = ['name', 'email'] |
|
||||||
|
|
||||||
class Meta: |
|
||||||
model = Subscriber |
|
||||||
|
|
||||||
admin.site.register(Subscriber, SubscriberAdmin) |
|
||||||
@ -1,5 +0,0 @@ |
|||||||
from django.apps import AppConfig |
|
||||||
|
|
||||||
|
|
||||||
class LandingConfig(AppConfig): |
|
||||||
name = 'landing' |
|
||||||
@ -1,8 +0,0 @@ |
|||||||
from django import forms |
|
||||||
from .models import * |
|
||||||
|
|
||||||
class SubscriberForm(forms.ModelForm): |
|
||||||
|
|
||||||
class Meta: |
|
||||||
model = Subscriber |
|
||||||
exclude = [""] |
|
||||||
@ -1,16 +0,0 @@ |
|||||||
from django.db import models |
|
||||||
|
|
||||||
# Create your models here. |
|
||||||
class Subscriber(models.Model): |
|
||||||
email = models.EmailField() |
|
||||||
name = models.CharField(max_length=128) |
|
||||||
|
|
||||||
class Meta: |
|
||||||
verbose_name = 'MySubsciber' |
|
||||||
verbose_name_plural = 'List of Subscribers' |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
try: |
|
||||||
return self.name |
|
||||||
except: |
|
||||||
return '{0!s}'.format(self.id) |
|
||||||
@ -1,3 +0,0 @@ |
|||||||
from django.test import TestCase |
|
||||||
|
|
||||||
# Create your tests here. |
|
||||||
@ -1,7 +0,0 @@ |
|||||||
from django.conf.urls import url |
|
||||||
from . import views |
|
||||||
|
|
||||||
urlpatterns = [ |
|
||||||
url(r'^landing/', views.landing, name='landing'), |
|
||||||
#url(r'^$', views.home, name='home'), |
|
||||||
] |
|
||||||
@ -1,21 +0,0 @@ |
|||||||
from django.shortcuts import render |
|
||||||
from .forms import SubscriberForm |
|
||||||
from django.contrib import auth |
|
||||||
from products.models import * |
|
||||||
|
|
||||||
def landing(request): |
|
||||||
form = SubscriberForm(request.POST or None) |
|
||||||
|
|
||||||
if request.method == "POST" and form.is_valid(): |
|
||||||
print(form.cleaned_data) |
|
||||||
form.save() |
|
||||||
|
|
||||||
return render(request, 'landing/landing.html', locals()) |
|
||||||
|
|
||||||
|
|
||||||
def home(request): |
|
||||||
product_images = ProductImage.objects.filter(is_active=True, is_main=True, product__is_active=True) |
|
||||||
product_images_phones = product_images.filter(product__category__id=1) |
|
||||||
product_images_watches = product_images.filter(product__category__id=2) |
|
||||||
username = auth.get_user(request).username |
|
||||||
return render(request, 'landing/home.html', locals()) |
|
||||||
@ -1,3 +0,0 @@ |
|||||||
from django.contrib import admin |
|
||||||
|
|
||||||
# Register your models here. |
|
||||||
@ -1,5 +0,0 @@ |
|||||||
from django.apps import AppConfig |
|
||||||
|
|
||||||
|
|
||||||
class LoginsysConfig(AppConfig): |
|
||||||
name = 'loginsys' |
|
||||||
@ -1,23 +0,0 @@ |
|||||||
from django import forms |
|
||||||
from django.contrib.auth.models import User |
|
||||||
from django.contrib.auth.forms import UserCreationForm |
|
||||||
|
|
||||||
class RegistrationForm(UserCreationForm): |
|
||||||
email = forms.EmailField(required=True) |
|
||||||
parent = forms.CharField(required = False) |
|
||||||
|
|
||||||
class Meta: |
|
||||||
model = User |
|
||||||
fields = ('username', 'email', 'password1', 'password2', 'parent') |
|
||||||
|
|
||||||
def save(self, commit=True): |
|
||||||
user = super(UserCreationForm, self).save(commit=False) |
|
||||||
user.email = self.cleaned_data['email'] |
|
||||||
if self.cleaned_data.get('parent'): |
|
||||||
user.parent = User.objects.get(username=self.cleaned_data['parent']) |
|
||||||
user.set_password(self.cleaned_data['password1']) |
|
||||||
|
|
||||||
if commit: |
|
||||||
user.save() |
|
||||||
|
|
||||||
return user |
|
||||||
@ -1,2 +0,0 @@ |
|||||||
from django.db import models |
|
||||||
|
|
||||||
@ -1,3 +0,0 @@ |
|||||||
from django.test import TestCase |
|
||||||
|
|
||||||
# Create your tests here. |
|
||||||
@ -1,24 +0,0 @@ |
|||||||
"""Eshop URL Configuration |
|
||||||
|
|
||||||
The `urlpatterns` list routes URLs to views. For more information please see: |
|
||||||
https://docs.djangoproject.com/en/1.10/topics/http/urls/ |
|
||||||
Examples: |
|
||||||
Function views |
|
||||||
1. Add an import: from my_app import views |
|
||||||
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') |
|
||||||
Class-based views |
|
||||||
1. Add an import: from other_app.views import Home |
|
||||||
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') |
|
||||||
Including another URLconf |
|
||||||
1. Import the include() function: from django.conf.urls import url, include |
|
||||||
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) |
|
||||||
""" |
|
||||||
from django.conf.urls import url |
|
||||||
from . import views |
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [ |
|
||||||
url(r'^login/', views.login, name='login'), |
|
||||||
url(r'^logout/', views.logout, name='logout'), |
|
||||||
url(r'^register/', views.register, name='register'), |
|
||||||
] |
|
||||||
@ -1,44 +0,0 @@ |
|||||||
from django.shortcuts import render_to_response, redirect |
|
||||||
from django.contrib import auth |
|
||||||
from django.views.decorators.csrf import csrf_exempt |
|
||||||
from .forms import RegistrationForm |
|
||||||
|
|
||||||
@csrf_exempt |
|
||||||
def login(request): |
|
||||||
args = {} |
|
||||||
if request.POST: |
|
||||||
username = request.POST.get('username', '') |
|
||||||
password = request.POST.get('password', '') |
|
||||||
user = auth.authenticate(username=username, password=password) |
|
||||||
if user is not None: |
|
||||||
auth.login(request, user) |
|
||||||
return redirect('/') |
|
||||||
else: |
|
||||||
args['login_error'] = "User is not found" |
|
||||||
return render_to_response('login/login.html', args) |
|
||||||
|
|
||||||
else: |
|
||||||
return render_to_response('login/login.html', args) |
|
||||||
|
|
||||||
def logout(request): |
|
||||||
auth.logout(request) |
|
||||||
return redirect('/') |
|
||||||
|
|
||||||
@csrf_exempt |
|
||||||
def register(request): |
|
||||||
args= {} |
|
||||||
args['form'] = RegistrationForm() |
|
||||||
if request.POST: |
|
||||||
newuser_form = RegistrationForm(request.POST) |
|
||||||
if newuser_form.is_valid(): |
|
||||||
newuser_form.save() |
|
||||||
newuser = auth.authenticate(username=newuser_form.cleaned_data['username'], |
|
||||||
password=newuser_form.cleaned_data['password1']) |
|
||||||
auth.login(request, newuser) |
|
||||||
return redirect('/') |
|
||||||
else: |
|
||||||
args['form'] = newuser_form |
|
||||||
return render_to_response('login/register.html', args) |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,95 +0,0 @@ |
|||||||
from django.contrib import admin |
|
||||||
from django.http import HttpResponse |
|
||||||
from django.core.urlresolvers import reverse |
|
||||||
from django.utils.html import format_html |
|
||||||
from decimal import Decimal |
|
||||||
import csv |
|
||||||
import datetime |
|
||||||
from django.contrib.auth.models import User |
|
||||||
from userprofile.models import UserProfile |
|
||||||
from .models import * |
|
||||||
|
|
||||||
def OrderDetail(obj): |
|
||||||
return format_html('<a href="{}">View</a>'.format( |
|
||||||
reverse('orders:AdminOrderDetail', args=[obj.id]) |
|
||||||
)) |
|
||||||
|
|
||||||
def ExportToCSV(modeladmin, request, queryset): |
|
||||||
opts = modeladmin.model._meta |
|
||||||
response = HttpResponse(content_type='text/csv') |
|
||||||
response['Content-Disposition'] = 'attachment; filename=Orders-{}.csv'.format(datetime.datetime.now().strftime("%d/%m/%Y")) |
|
||||||
writer = csv.writer(response) |
|
||||||
|
|
||||||
fields = [field for field in opts.get_fields() if not field.many_to_many and not field.one_to_many] |
|
||||||
|
|
||||||
writer.writerow([field.verbose_name for field in fields]) |
|
||||||
|
|
||||||
for obj in queryset: |
|
||||||
data_row = [] |
|
||||||
for field in fields: |
|
||||||
value = getattr(obj, field.name) |
|
||||||
if isinstance(value, datetime.datetime): |
|
||||||
value = value.strftime('%d/%m/%Y') |
|
||||||
|
|
||||||
data_row.append(value) |
|
||||||
writer.writerow(data_row) |
|
||||||
return response |
|
||||||
ExportToCSV.short_description = 'Export CSV' |
|
||||||
|
|
||||||
def OrderPDF(obj): |
|
||||||
return format_html('<a href="{}">PDF</a>'.format( |
|
||||||
reverse('orders:AdminOrderPDF', args=[obj.id]) |
|
||||||
)) |
|
||||||
OrderPDF.short_description = 'In PDF format' |
|
||||||
|
|
||||||
class ProductsInOrderInline(admin.TabularInline): |
|
||||||
model = ProductsInOrder |
|
||||||
extra = 0 |
|
||||||
|
|
||||||
class StatusAdmin(admin.ModelAdmin): |
|
||||||
list_display = [field.name for field in Status._meta.fields] |
|
||||||
|
|
||||||
class Meta: |
|
||||||
model = Status |
|
||||||
|
|
||||||
def delete_model(modeladmin, request, queryset): |
|
||||||
for obj in queryset: |
|
||||||
print(obj.user) |
|
||||||
user_profile = obj.user.profile |
|
||||||
parent_profile = user_profile.parent.profile |
|
||||||
parent_profile.user_points += round(obj.total_price * Decimal(0.05)) |
|
||||||
parent_profile.save() |
|
||||||
obj.delete() |
|
||||||
delete_model.short_description = "Удалить как завершенные" |
|
||||||
|
|
||||||
class OrderAdmin (admin.ModelAdmin): |
|
||||||
list_display = ['id', 'customer_name', 'customer_email', 'customer_phone', 'city', 'customer_address', |
|
||||||
'paid', 'status', 'created', 'updated', OrderDetail, OrderPDF] |
|
||||||
|
|
||||||
list_filter = ['paid', 'created', 'updated'] |
|
||||||
inlines = [ProductsInOrderInline] |
|
||||||
actions = [ExportToCSV, delete_model] |
|
||||||
|
|
||||||
class Meta: |
|
||||||
model = Order |
|
||||||
|
|
||||||
class ProductsInOrderAdmin(admin.ModelAdmin): |
|
||||||
list_display = [field.name for field in ProductsInOrder._meta.fields] |
|
||||||
|
|
||||||
class Meta: |
|
||||||
model = ProductsInOrder |
|
||||||
|
|
||||||
class ProductsInBasketAdmin(admin.ModelAdmin): |
|
||||||
list_display = [field.name for field in ProductsInBasket._meta.fields] |
|
||||||
|
|
||||||
class Meta: |
|
||||||
model = ProductsInBasket |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Status, StatusAdmin) |
|
||||||
admin.site.register(Order, OrderAdmin) |
|
||||||
admin.site.register(ProductsInOrder, ProductsInOrderAdmin) |
|
||||||
admin.site.register(ProductsInBasket, ProductsInBasketAdmin) |
|
||||||
@ -1,5 +0,0 @@ |
|||||||
from django.apps import AppConfig |
|
||||||
|
|
||||||
|
|
||||||
class OrdersConfig(AppConfig): |
|
||||||
name = 'orders' |
|
||||||
@ -1,11 +0,0 @@ |
|||||||
from .models import ProductsInBasket |
|
||||||
|
|
||||||
def getting_basket_info(request): |
|
||||||
session_key = request.session.session_key |
|
||||||
if not session_key: |
|
||||||
request.session.cycle_key() |
|
||||||
|
|
||||||
products_in_basket = ProductsInBasket.objects.filter(session_key=session_key, is_active=True) |
|
||||||
products_total_nmb = products_in_basket.count() |
|
||||||
|
|
||||||
return locals() |
|
||||||
@ -1,15 +0,0 @@ |
|||||||
from django import forms |
|
||||||
from phonenumber_field.formfields import PhoneNumberField |
|
||||||
from .models import Order |
|
||||||
|
|
||||||
class OrderCreateForm(forms.ModelForm): |
|
||||||
|
|
||||||
customer_name = forms.CharField(max_length=64, required=True, help_text='Введите Ваше полное Ф.И.О') |
|
||||||
customer_phone = PhoneNumberField(required=True, help_text='Введите Ваш номер телефона') |
|
||||||
customer_email = forms.EmailField(required=True, help_text='Введите Ваш e-mail') |
|
||||||
city = forms.CharField(max_length=100, help_text='Введите Ваш город') |
|
||||||
|
|
||||||
class Meta: |
|
||||||
model = Order |
|
||||||
fields = ['customer_name', 'customer_email', 'customer_phone', 'city'] |
|
||||||
|
|
||||||
@ -1,112 +0,0 @@ |
|||||||
from django.db import models |
|
||||||
from django.db.models.signals import post_save |
|
||||||
from django.contrib.auth.models import User |
|
||||||
from django.dispatch import receiver |
|
||||||
from phonenumber_field.modelfields import PhoneNumberField |
|
||||||
# from discount.models import Discount |
|
||||||
from decimal import Decimal |
|
||||||
from django.core.validators import MinValueValidator, MaxValueValidator |
|
||||||
from products.models import Product, Offer |
|
||||||
|
|
||||||
|
|
||||||
class Status(models.Model): |
|
||||||
name = models.CharField(max_length=16, blank=True, null=True, default=None) |
|
||||||
is_active = models.BooleanField(default=True) |
|
||||||
created = models.DateField(auto_now_add=True, auto_now=False) |
|
||||||
updated = models.DateField(auto_now_add=False, auto_now=True) |
|
||||||
|
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return self.name |
|
||||||
|
|
||||||
class Meta: |
|
||||||
verbose_name = 'State of order' |
|
||||||
verbose_name_plural = 'States' |
|
||||||
|
|
||||||
class Order(models.Model): |
|
||||||
user = models.ForeignKey(User, blank=True, null=True, default=None) |
|
||||||
total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0) |
|
||||||
customer_name = models.CharField(max_length=64, blank=True, null=True, default=None) |
|
||||||
customer_email = models.EmailField(blank=True, null=True, default=None) |
|
||||||
customer_phone = PhoneNumberField(blank=True, null=True, default=None) |
|
||||||
city = models.CharField(max_length=100, blank=True, null=True, default=None) |
|
||||||
customer_address = models.CharField(max_length=128, blank=True, null=True, default=None) |
|
||||||
comment = models.TextField(blank=True, null=True, default=None) |
|
||||||
status = models.ForeignKey(Status, blank=True, null=True, default=None) |
|
||||||
created = models.DateField(auto_now_add=True, auto_now=False) |
|
||||||
updated = models.DateField(auto_now_add=False, auto_now=True) |
|
||||||
paid = models.BooleanField(default=False) |
|
||||||
# discount = models.ForeignKey(Discount, related_name='orders', null=True, blank=True) |
|
||||||
# discount_value = models.IntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(100)]) |
|
||||||
points_quant = models.IntegerField(default=0) |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return "Order {!s} has status {}: ".format(self.id, self.status) |
|
||||||
|
|
||||||
class Meta: |
|
||||||
ordering = ('-created',) |
|
||||||
verbose_name = 'Order' |
|
||||||
verbose_name_plural = 'Orders' |
|
||||||
|
|
||||||
def save(self, *args, **kwargs): |
|
||||||
super(Order, self).save(*args, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
class ProductsInOrder(models.Model): |
|
||||||
order = models.ForeignKey(Order, blank=True, null=True, default=None, related_name='items') |
|
||||||
product = models.ForeignKey(Offer, blank=True, null=True, default=None) |
|
||||||
number = models.PositiveIntegerField(default=1) |
|
||||||
price_per_itom = models.DecimalField(max_digits=10, decimal_places=2, default=0) |
|
||||||
total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0) |
|
||||||
is_active = models.BooleanField(default=True) |
|
||||||
created = models.DateField(auto_now_add=True, auto_now=False) |
|
||||||
updated = models.DateField(auto_now_add=False, auto_now=True) |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return self.product.name |
|
||||||
|
|
||||||
class Meta: |
|
||||||
verbose_name = 'Product in Order' |
|
||||||
verbose_name_plural = 'Products in Order' |
|
||||||
|
|
||||||
def save(self, *args, **kwargs): |
|
||||||
# self.price_per_itom = self.product.price |
|
||||||
self.total_price = self.number * self.price_per_itom |
|
||||||
super(ProductsInOrder, self).save(*args, **kwargs) |
|
||||||
|
|
||||||
class ProductsInBasket(models.Model): |
|
||||||
session_key = models.CharField(max_length=128, blank=True, null=True, default=None) |
|
||||||
order = models.ForeignKey(Order, blank=True, null=True, default=None) |
|
||||||
product = models.ForeignKey(Product, blank=True, null=True, default=None) |
|
||||||
number = models.IntegerField(default=1) |
|
||||||
price_per_itom = models.DecimalField(max_digits=10, decimal_places=2, default=0) |
|
||||||
total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0) |
|
||||||
is_active = models.BooleanField(default=True) |
|
||||||
created = models.DateField(auto_now_add=True, auto_now=False) |
|
||||||
updated = models.DateField(auto_now_add=False, auto_now=True) |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return self.product.name |
|
||||||
|
|
||||||
class Meta: |
|
||||||
verbose_name = 'Product in Cart' |
|
||||||
verbose_name_plural = 'Products in Cart' |
|
||||||
|
|
||||||
def save(self, *args, **kwargs): |
|
||||||
self.price_per_itom = self.product.price |
|
||||||
self.total_price = int(self.number) * self.price_per_itom |
|
||||||
super(ProductsInBasket, self).save(*args, **kwargs) |
|
||||||
|
|
||||||
@receiver(post_save, sender=ProductsInOrder) |
|
||||||
def product_in_order_post_save(instance,**kwargs): |
|
||||||
order = instance.order |
|
||||||
all_products_in_order = ProductsInOrder.objects.filter(order=order, is_active=True) |
|
||||||
|
|
||||||
order_total_price = sum(item.total_price for item in all_products_in_order) |
|
||||||
# if order.discount: |
|
||||||
# order.total_price = order_total_price * (order.discount_value / Decimal('100')) |
|
||||||
if order.points_quant: |
|
||||||
order.total_price = order_total_price - order.points_quant |
|
||||||
else: |
|
||||||
order.total_price = order_total_price |
|
||||||
order.save(force_update=True) |
|
||||||
@ -1,43 +0,0 @@ |
|||||||
from django.conf import settings |
|
||||||
from celery import task |
|
||||||
from django.template.loader import render_to_string |
|
||||||
from django.core.mail import send_mail, EmailMessage |
|
||||||
from io import BytesIO |
|
||||||
import weasyprint |
|
||||||
import pytils |
|
||||||
from .models import Order |
|
||||||
|
|
||||||
SUPPLIER_INFO = '''ООО "Русские Программы", ИНН 7713409230, КПП 771301001, |
|
||||||
127411, Москва г, Дмитровское ш., дом № 157, корпус 7, тел.: +74957258950''' |
|
||||||
|
|
||||||
requisites = {'name': 'ООО "Русские Программы"', 'bank': 'АО "СМП БАНК" Г. МОСКВА', 'INN': '7713409230', |
|
||||||
'KPP': '771301001', 'BIK': '44525503', 'bank_acc': '30101810545250000503', 'acc': '40702810300750000177', |
|
||||||
'sup_info': SUPPLIER_INFO} |
|
||||||
|
|
||||||
@task |
|
||||||
def OrderCreated(order_id): |
|
||||||
""" |
|
||||||
Sending Email of order creating |
|
||||||
""" |
|
||||||
order = Order.objects.get(id=order_id) |
|
||||||
verb_price = pytils.numeral.in_words(round(order.total_price)) |
|
||||||
verb_cur = pytils.numeral.choose_plural(round(order.total_price), ("рубль", "рубля", "рублей")) |
|
||||||
subject = 'Order {}'.format(order.id) |
|
||||||
message = 'Dear, {}, You have successfully placed an order.\ |
|
||||||
Your order number {}'.format(order.customer_name, order.id) |
|
||||||
mail_send = EmailMessage(subject, message, 'admin@myshop.ru', [order.customer_email, 'bda2291@mail.ru']) |
|
||||||
|
|
||||||
# html = render_to_string('orders:AdminOrderPDF', args=[order_id]) |
|
||||||
|
|
||||||
html = render_to_string('orders/pdf.html', {**requisites, 'order': order, |
|
||||||
'verb_cur': verb_cur, 'verb_price': verb_price}) |
|
||||||
rendered_html = html.encode(encoding="UTF-8") |
|
||||||
out = BytesIO() |
|
||||||
weasyprint.HTML(string=rendered_html).write_pdf(out, |
|
||||||
stylesheets=[weasyprint.CSS(settings.STATIC_ROOT + 'css/bootstrap.min.css')]) |
|
||||||
|
|
||||||
# weasyprint.HTML(string=rendered_html, base_url=request.build_absolute_uri()).write_pdf(response, |
|
||||||
# stylesheets=[weasyprint.CSS(settings.STATIC_ROOT + '/css/bootstrap.min.css')]) |
|
||||||
mail_send.attach('order_{}.pdf'.format(order_id), out.getvalue(), 'application/pdf') |
|
||||||
mail_send.send() |
|
||||||
return mail_send |
|
||||||
@ -1,3 +0,0 @@ |
|||||||
from django.test import TestCase |
|
||||||
|
|
||||||
# Create your tests here. |
|
||||||
@ -1,25 +0,0 @@ |
|||||||
"""Eshop URL Configuration |
|
||||||
|
|
||||||
The `urlpatterns` list routes URLs to views. For more information please see: |
|
||||||
https://docs.djangoproject.com/en/1.10/topics/http/urls/ |
|
||||||
Examples: |
|
||||||
Function views |
|
||||||
1. Add an import: from my_app import views |
|
||||||
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') |
|
||||||
Class-based views |
|
||||||
1. Add an import: from other_app.views import Home |
|
||||||
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') |
|
||||||
Including another URLconf |
|
||||||
1. Import the include() function: from django.conf.urls import url, include |
|
||||||
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) |
|
||||||
""" |
|
||||||
from django.conf.urls import url |
|
||||||
from . import views |
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [ |
|
||||||
url(r'^basket_adding/', views.basket_adding, name='basket_adding'), |
|
||||||
url(r'^create/$', views.OrderCreate, name='OrderCreate'), |
|
||||||
url(r'^admin/order/(?P<order_id>\d+)/$', views.AdminOrderDetail, name='AdminOrderDetail'), |
|
||||||
url(r'^admin/order/(?P<order_id>\d+)/pdf/$', views.AdminOrderPDF, name='AdminOrderPDF') |
|
||||||
] |
|
||||||
@ -1,117 +0,0 @@ |
|||||||
from django.shortcuts import render, redirect, get_object_or_404, render_to_response |
|
||||||
from django.conf import settings |
|
||||||
from django.contrib import auth |
|
||||||
from django.http import HttpResponse |
|
||||||
from django.template.loader import render_to_string, get_template |
|
||||||
import weasyprint |
|
||||||
import pytils |
|
||||||
from django.core.urlresolvers import reverse |
|
||||||
from django.http import JsonResponse |
|
||||||
from django.contrib.admin.views.decorators import staff_member_required |
|
||||||
from .models import ProductsInBasket, ProductsInOrder, Order |
|
||||||
from .forms import OrderCreateForm |
|
||||||
from .tasks import OrderCreated |
|
||||||
from cart.cart import Cart |
|
||||||
|
|
||||||
SUPPLIER_INFO = '''ООО "Русские Программы", ИНН 7713409230, КПП 771301001, |
|
||||||
127411, Москва г, Дмитровское ш., дом № 157, корпус 7, тел.: +74957258950''' |
|
||||||
|
|
||||||
requisites = {'name': 'ООО "Русские Программы"', 'bank': 'АО "СМП БАНК" Г. МОСКВА', 'INN': '7713409230', |
|
||||||
'KPP': '771301001', 'BIK': '44525503', 'bank_acc': '30101810545250000503', 'acc': '40702810300750000177', |
|
||||||
'sup_info': SUPPLIER_INFO} |
|
||||||
|
|
||||||
def basket_adding(request): |
|
||||||
return_dict = {} |
|
||||||
session_key = request.session.session_key |
|
||||||
data = request.POST |
|
||||||
product_id = data.get("product_id") |
|
||||||
nmb = data.get("nmb") |
|
||||||
|
|
||||||
new_product, created = ProductsInBasket.objects.get_or_create(session_key=session_key, product_id=product_id, defaults={'number':nmb}) |
|
||||||
if not created: |
|
||||||
new_product.number += int(nmb) |
|
||||||
new_product.save(force_update=True) |
|
||||||
|
|
||||||
products_in_basket = ProductsInBasket.objects.filter(session_key=session_key, is_active=True) |
|
||||||
products_total_nmb = products_in_basket.count() |
|
||||||
return_dict["products_total_nmb"] = products_total_nmb |
|
||||||
return_dict["products"] = [] |
|
||||||
|
|
||||||
for item in products_in_basket: |
|
||||||
product_dict = {} |
|
||||||
product_dict["id"] = item.id |
|
||||||
product_dict["name"] = item.product.name |
|
||||||
product_dict["price_per_item"] = item.price_per_itom |
|
||||||
product_dict["nmb"] = item.number |
|
||||||
return_dict["products"].append(product_dict) |
|
||||||
|
|
||||||
return JsonResponse(return_dict) |
|
||||||
|
|
||||||
|
|
||||||
def basket_remove(request): |
|
||||||
return_dict = {} |
|
||||||
session_key = request.session.session_key |
|
||||||
data = request.POST |
|
||||||
product_id = data.get("product_id") |
|
||||||
|
|
||||||
|
|
||||||
def OrderCreate(request): |
|
||||||
cart = Cart(request) |
|
||||||
user = auth.get_user(request) |
|
||||||
profile = user.profile |
|
||||||
if not user.username: |
|
||||||
return redirect('auth:login') |
|
||||||
if request.method == 'POST': |
|
||||||
form = OrderCreateForm(request.POST) |
|
||||||
if form.is_valid(): |
|
||||||
order = form.save(commit=False) |
|
||||||
order.user = user |
|
||||||
# if cart.discount: |
|
||||||
# order.discount = cart.discount |
|
||||||
# order.discount_value = cart.discount.discount |
|
||||||
|
|
||||||
if cart.points: |
|
||||||
print(cart.points_quant) |
|
||||||
order.points_quant = cart.points_quant |
|
||||||
profile.user_points -= cart.points_quant |
|
||||||
profile.save() |
|
||||||
order.save() |
|
||||||
|
|
||||||
for item in cart: |
|
||||||
ProductsInOrder.objects.create(order=order, product=item['offer'], |
|
||||||
price_per_itom=item['price'], |
|
||||||
number=item['quantity']) |
|
||||||
cart.clear() |
|
||||||
|
|
||||||
# Asinc mail sending |
|
||||||
OrderCreated.delay(order.id) |
|
||||||
request.session['order_id'] = order.id |
|
||||||
|
|
||||||
# return redirect(reverse('payment:process')) |
|
||||||
return render(request, 'orders/created.html', {'username': user.username, 'order': order}) |
|
||||||
else: |
|
||||||
return render('orders/create.html', {'username': user.username, 'cart': cart, 'form': form}) |
|
||||||
|
|
||||||
form = OrderCreateForm(instance=profile) |
|
||||||
return render(request, 'orders/create.html', {'username': user.username, 'cart': cart, 'form': form}) |
|
||||||
|
|
||||||
|
|
||||||
@staff_member_required |
|
||||||
def AdminOrderDetail(request, order_id): |
|
||||||
order = get_object_or_404(Order, id=order_id) |
|
||||||
return render(request, 'admin/orders/detail.html', {'order': order}) |
|
||||||
|
|
||||||
|
|
||||||
@staff_member_required |
|
||||||
def AdminOrderPDF(request, order_id): |
|
||||||
order = get_object_or_404(Order, id=order_id) |
|
||||||
verb_price = pytils.numeral.in_words(round(order.total_price)) |
|
||||||
verb_cur = pytils.numeral.choose_plural(round(order.total_price), ("рубль", "рубля", "рублей")) |
|
||||||
html = render_to_string('orders/pdf.html', {**requisites, 'order': order, |
|
||||||
'verb_cur': verb_cur, 'verb_price': verb_price}) |
|
||||||
rendered_html = html.encode(encoding="UTF-8") |
|
||||||
response = HttpResponse(content_type='application/pdf') |
|
||||||
response['Content-Disposition'] = 'filename=order_{}.pdf'.format(order.id) |
|
||||||
weasyprint.HTML(string=rendered_html, base_url=request.build_absolute_uri()).write_pdf(response, |
|
||||||
stylesheets=[weasyprint.CSS(settings.STATIC_ROOT + '/css/bootstrap.min.css')]) |
|
||||||
return response |
|
||||||
@ -1,5 +0,0 @@ |
|||||||
{ |
|
||||||
"dependencies": { |
|
||||||
"bower": "latest" |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,206 +0,0 @@ |
|||||||
from django.contrib import admin |
|
||||||
# from mptt.admin import MPTTModelAdmin |
|
||||||
from import_export import resources, fields, widgets |
|
||||||
from import_export.admin import ImportExportModelAdmin |
|
||||||
from .models import * |
|
||||||
|
|
||||||
class CustomModelResource(resources.ModelResource): |
|
||||||
def before_import_row(self, row, **kwargs): |
|
||||||
""" |
|
||||||
Override to add additional logic. Does nothing by default. |
|
||||||
""" |
|
||||||
try: |
|
||||||
row['attributes'] = eval(row['attributes']) |
|
||||||
except: |
|
||||||
try: |
|
||||||
row['discount_policy'] = eval(row['discount_policy']) |
|
||||||
except: |
|
||||||
pass |
|
||||||
|
|
||||||
class CustomManyToManyWidget(widgets.ManyToManyWidget): |
|
||||||
def clean(self, value, row=None, *args, **kwargs): |
|
||||||
t1 = super(CustomManyToManyWidget, self).clean(value) |
|
||||||
return self.model.objects.get(name=t1) if t1 else None |
|
||||||
|
|
||||||
|
|
||||||
# class CustomForeignKeyWidget(widgets.ForeignKeyWidget): |
|
||||||
# def clean(self, value, row=None, *args, **kwargs): |
|
||||||
# return self.model.objects.get_or_create(name=value)[0] |
|
||||||
|
|
||||||
# class ProductImageInline(admin.TabularInline): |
|
||||||
# model = ProductImage |
|
||||||
# extra = 0 |
|
||||||
|
|
||||||
# class ProductAttributeInline(admin.TabularInline): |
|
||||||
# model = ProductAttribute |
|
||||||
# extra = 1 |
|
||||||
# verbose_name_plural = 'ProductAttribute' |
|
||||||
# suit_classes = 'suit-tab suit-tab-PA' |
|
||||||
# |
|
||||||
class AttributeChoiceValueInline(admin.TabularInline): |
|
||||||
model = AttributeChoiceValue |
|
||||||
# prepopulated_fields = {'slug': ('name',)} |
|
||||||
extra = 1 |
|
||||||
verbose_name_plural = 'AttributeChoiceValue' |
|
||||||
suit_classes = 'suit-tab suit-tab-ACV' |
|
||||||
# |
|
||||||
class OfferInline(admin.TabularInline): |
|
||||||
model = Offer |
|
||||||
extra = 1 |
|
||||||
verbose_name_plural = 'Offers' |
|
||||||
suit_classes = 'suit-tab suit-tab-offers' |
|
||||||
|
|
||||||
class ProductCategoryAdmin(admin.ModelAdmin): |
|
||||||
list_display = [field.name for field in ProductCategory._meta.fields] |
|
||||||
|
|
||||||
class Meta: |
|
||||||
model = ProductCategory |
|
||||||
|
|
||||||
# class AttributeChoiceValueAdmin(admin.ModelAdmin): |
|
||||||
# list_display = [field.name for field in ProductCategory._meta.fields] |
|
||||||
# |
|
||||||
# class Meta: |
|
||||||
# model = AttributeChoiceValue |
|
||||||
# |
|
||||||
# admin.site.register(AttributeChoiceValue, AttributeChoiceValueAdmin) |
|
||||||
|
|
||||||
class ProductAttributeAdmin(admin.ModelAdmin): |
|
||||||
list_display = [field.name for field in ProductAttribute._meta.fields] |
|
||||||
inlines = [AttributeChoiceValueInline] |
|
||||||
# prepopulated_fields = {'slug': ('name',)} |
|
||||||
|
|
||||||
suit_form_tabs = (('general', 'General'), |
|
||||||
('ACV', 'AttributeValues'),) |
|
||||||
|
|
||||||
class Meta: |
|
||||||
model = ProductAttribute |
|
||||||
|
|
||||||
admin.site.register(ProductAttribute, ProductAttributeAdmin) |
|
||||||
|
|
||||||
class ProducerAdmin(admin.ModelAdmin): |
|
||||||
list_display = [field.name for field in Producer._meta.fields] |
|
||||||
|
|
||||||
class Meta: |
|
||||||
model = Producer |
|
||||||
|
|
||||||
admin.site.register(Producer, ProducerAdmin) |
|
||||||
|
|
||||||
class ProductResource(CustomModelResource): |
|
||||||
# id = fields.Field(default=generate_Jid(prefix='J'), |
|
||||||
# readonly=True, |
|
||||||
# widget=widgets.CharWidget(), |
|
||||||
# ) |
|
||||||
|
|
||||||
name = fields.Field(column_name='name', attribute='name', |
|
||||||
default=None, |
|
||||||
widget=widgets.CharWidget(), |
|
||||||
) |
|
||||||
# price = fields.Field(column_name='price', attribute='price', |
|
||||||
# default=0, |
|
||||||
# widget=widgets.DecimalWidget(), |
|
||||||
# ) |
|
||||||
description = fields.Field(column_name='description', attribute='description', |
|
||||||
default=None, |
|
||||||
widget=widgets.CharWidget(), |
|
||||||
) |
|
||||||
|
|
||||||
# producer = fields.Field(column_name='producer', attribute='producer', |
|
||||||
# default=None, |
|
||||||
# widget=widgets.CharWidget(), |
|
||||||
# ) |
|
||||||
|
|
||||||
category = fields.Field(column_name='category', attribute='category', |
|
||||||
default=None, |
|
||||||
widget=widgets.ForeignKeyWidget(ProductCategory, field='name'), |
|
||||||
) |
|
||||||
producer = fields.Field(column_name='producer', attribute='producer', |
|
||||||
default=None, |
|
||||||
widget=widgets.ForeignKeyWidget(Producer, field='name'), |
|
||||||
) |
|
||||||
attributes = fields.Field(column_name='attributes', attribute='attributes', |
|
||||||
default=None, |
|
||||||
widget=CustomManyToManyWidget(ProductAttribute, field="name"), |
|
||||||
) |
|
||||||
is_active = fields.Field(column_name='is_active', attribute='is_active', |
|
||||||
default=1, |
|
||||||
widget=widgets.BooleanWidget()) |
|
||||||
|
|
||||||
discount_policy = fields.Field(column_name='discount_policy', attribute='discount_policy', |
|
||||||
default={}, |
|
||||||
widget=widgets.CharWidget()) |
|
||||||
|
|
||||||
# delete = fields.Field(column_name='delete', attribute='delete', |
|
||||||
# default=0, |
|
||||||
# widget=widgets.BooleanWidget()) |
|
||||||
|
|
||||||
# def for_delete(self, row, instance): |
|
||||||
# return self.fields['delete'].clean(row) |
|
||||||
|
|
||||||
class Meta: |
|
||||||
model = Product |
|
||||||
fields = ('id', 'name', 'description', 'producer', 'category', 'is_active', 'attributes', 'discount_policy') |
|
||||||
export_order = ('id', 'name', 'producer', 'is_active', 'category', 'attributes', 'description', 'discount_policy') |
|
||||||
# import_id_fields = ('name',) |
|
||||||
|
|
||||||
def dehydrate_str_choices(self, obj): |
|
||||||
if obj.id: |
|
||||||
return obj.str_choices() |
|
||||||
|
|
||||||
class ProductAdmin(ImportExportModelAdmin): |
|
||||||
list_display = ['id', 'name', 'category', 'producer', 'is_active'] |
|
||||||
inlines = [OfferInline] |
|
||||||
list_filter = ['is_active', 'created', 'updated', 'category'] |
|
||||||
list_editable = ['is_active'] |
|
||||||
# prepopulated_fields = {'slug': ('name',)} |
|
||||||
search_fields = ['name', 'id'] |
|
||||||
|
|
||||||
suit_form_tabs = (('general', 'General'), |
|
||||||
('offers', 'Offers'),) |
|
||||||
|
|
||||||
resource_class = ProductResource |
|
||||||
|
|
||||||
# class Meta: |
|
||||||
# model = Product |
|
||||||
|
|
||||||
class OfferResource(CustomModelResource): |
|
||||||
name = fields.Field(column_name='name', attribute='name', |
|
||||||
default=None, |
|
||||||
widget=widgets.CharWidget(), |
|
||||||
) |
|
||||||
|
|
||||||
price = fields.Field(column_name='price', attribute='price', |
|
||||||
default=0, |
|
||||||
widget=widgets.DecimalWidget(), |
|
||||||
) |
|
||||||
|
|
||||||
product = fields.Field(column_name='product', attribute='product', |
|
||||||
widget=widgets.ForeignKeyWidget(Product, field='name'), |
|
||||||
) |
|
||||||
|
|
||||||
is_active = fields.Field(column_name='is_active', attribute='is_active', |
|
||||||
default=1, |
|
||||||
widget=widgets.BooleanWidget()) |
|
||||||
|
|
||||||
attributes = fields.Field(column_name='attributes', attribute='attributes', |
|
||||||
default={}, |
|
||||||
widget=widgets.CharWidget()) |
|
||||||
|
|
||||||
class Meta: |
|
||||||
model = Offer |
|
||||||
fields = ('name', 'product', 'price', 'is_active', 'attributes') |
|
||||||
export_order = ('name', 'product', 'attributes', 'is_active', 'price') |
|
||||||
import_id_fields = ('name',) |
|
||||||
|
|
||||||
class OfferAdmin(ImportExportModelAdmin): |
|
||||||
list_display = ['id', 'name', 'product', 'price', 'is_active', 'attributes'] |
|
||||||
resource_class = OfferResource |
|
||||||
# class ProductImageAdmin(admin.ModelAdmin): |
|
||||||
# list_display = [field.name for field in ProductImage._meta.fields] |
|
||||||
# |
|
||||||
# class Meta: |
|
||||||
# model = ProductImage |
|
||||||
|
|
||||||
# admin.site.register(ProductImage, ProductImageAdmin) |
|
||||||
admin.site.register(ProductCategory, ProductCategoryAdmin) |
|
||||||
admin.site.register(Product, ProductAdmin) |
|
||||||
admin.site.register(Offer, OfferAdmin) |
|
||||||
@ -1,5 +0,0 @@ |
|||||||
from django.apps import AppConfig |
|
||||||
|
|
||||||
|
|
||||||
class ProductsConfig(AppConfig): |
|
||||||
name = 'products' |
|
||||||
@ -1,31 +0,0 @@ |
|||||||
from haystack.forms import FacetedSearchForm |
|
||||||
|
|
||||||
|
|
||||||
class FacetedProductSearchForm(FacetedSearchForm): |
|
||||||
def __init__(self, *args, **kwargs): |
|
||||||
data = dict(kwargs.get("data", [])) |
|
||||||
self.categories = data.get('category', []) |
|
||||||
self.producers = data.get('producer', []) |
|
||||||
super(FacetedProductSearchForm, self).__init__(*args, **kwargs) |
|
||||||
|
|
||||||
def search(self): |
|
||||||
sqs = super(FacetedProductSearchForm, self).search() |
|
||||||
if self.categories: |
|
||||||
query = None |
|
||||||
for category in self.categories: |
|
||||||
if query: |
|
||||||
query += u' OR ' |
|
||||||
else: |
|
||||||
query = u'' |
|
||||||
query += u'"%s"' % sqs.query.clean(category) |
|
||||||
sqs = sqs.narrow(u'category_exact:%s' % query) |
|
||||||
if self.producers: |
|
||||||
query = None |
|
||||||
for producer in self.producers: |
|
||||||
if query: |
|
||||||
query += u' OR ' |
|
||||||
else: |
|
||||||
query = u'' |
|
||||||
query += u'"%s"' % sqs.query.clean(producer) |
|
||||||
sqs = sqs.narrow(u'brand_exact:%s' % query) |
|
||||||
return sqs |
|
||||||
@ -1,169 +0,0 @@ |
|||||||
from django.db import models |
|
||||||
from django.core.urlresolvers import reverse |
|
||||||
from django.contrib.postgres.fields import HStoreField |
|
||||||
from autoslug import AutoSlugField |
|
||||||
import mptt |
|
||||||
import decimal |
|
||||||
from mptt.models import MPTTModel, TreeForeignKey |
|
||||||
|
|
||||||
|
|
||||||
class ProductAttribute(models.Model): |
|
||||||
name = models.CharField(max_length=64, blank=True, null=True, default=None) |
|
||||||
slug = AutoSlugField(populate_from='name') |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return self.name |
|
||||||
|
|
||||||
class Meta: |
|
||||||
ordering = ('slug',) |
|
||||||
verbose_name = 'Product attribute' |
|
||||||
verbose_name_plural = 'Product attributes' |
|
||||||
|
|
||||||
class AttributeChoiceValue(models.Model): |
|
||||||
name = models.CharField(max_length=64, blank=True, null=True, default=None) |
|
||||||
slug = AutoSlugField(populate_from='name') |
|
||||||
attribute = models.ForeignKey(ProductAttribute, on_delete=models.CASCADE, related_name='values') |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return self.name |
|
||||||
|
|
||||||
class Meta: |
|
||||||
unique_together = ('name', 'attribute') |
|
||||||
verbose_name = 'attribute choices value' |
|
||||||
verbose_name_plural = 'attribute choices values' |
|
||||||
|
|
||||||
class Producer(models.Model): |
|
||||||
name = models.CharField(max_length=64, blank=True, null=True, default=None) |
|
||||||
slug = AutoSlugField(populate_from='name') |
|
||||||
image = models.ImageField(upload_to='producers/%Y/%m/%d/', blank=True, verbose_name="image of producer") |
|
||||||
is_active = models.BooleanField(default=True) |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return self.name |
|
||||||
|
|
||||||
def get_absolute_url(self): |
|
||||||
return reverse('products:CategoriesListByProducer', args=[self.slug]) |
|
||||||
|
|
||||||
class Meta: |
|
||||||
verbose_name = 'Producer' |
|
||||||
verbose_name_plural = 'Producers' |
|
||||||
|
|
||||||
class ProductCategory(MPTTModel): |
|
||||||
name = models.CharField(db_index=True, unique=True, max_length=64, blank=True, null=True, default=None) |
|
||||||
slug = AutoSlugField(populate_from='name') |
|
||||||
is_active = models.BooleanField(default=True) |
|
||||||
producer = models.ForeignKey(Producer, null=True, blank=True, related_name='categories') |
|
||||||
parent = TreeForeignKey('self', null=True, blank=True, related_name='children') |
|
||||||
image = models.ImageField(upload_to='categories/%Y/%m/%d/', blank=True, verbose_name="image of category") |
|
||||||
# category_attributes = models.ManyToManyField(ProductAttribute, related_name='categories', blank=True) |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return self.name |
|
||||||
|
|
||||||
class Meta: |
|
||||||
verbose_name = 'Product''s category' |
|
||||||
verbose_name_plural = 'Category of products' |
|
||||||
ordering = ('tree_id', 'level') |
|
||||||
|
|
||||||
class MPTTMeta: |
|
||||||
order_insertion_by = ['name'] |
|
||||||
|
|
||||||
def get_absolute_url(self): |
|
||||||
return reverse('products:ProductListByCategory', args=[self.producer.slug, self.slug]) |
|
||||||
|
|
||||||
mptt.register(ProductCategory, order_insertion_py=['name']) |
|
||||||
|
|
||||||
# class ProductClass(models.Model): |
|
||||||
# name = models.CharField(max_length=64, blank=True, null=True, default=None) |
|
||||||
# has_variants = models.BooleanField(default=True) |
|
||||||
# # product_attributes = models.ManyToManyField(ProductAttribute, related_name='products_class', blank=True) |
|
||||||
# variant_attributes = models.ManyToManyField(ProductAttribute, related_name='variants_class', blank=True) |
|
||||||
# |
|
||||||
# def __str__(self): |
|
||||||
# return self.name |
|
||||||
# |
|
||||||
# class Meta: |
|
||||||
# verbose_name = 'product class' |
|
||||||
# verbose_name_plural = 'product classes' |
|
||||||
|
|
||||||
class Product(models.Model): |
|
||||||
name = models.CharField(max_length=64, db_index=True, blank=True, null=True, default=None) |
|
||||||
slug = AutoSlugField(populate_from='name') |
|
||||||
price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00) |
|
||||||
# points = models.DecimalField(max_digits=8, decimal_places=2, null=True, default=0.00) |
|
||||||
description = models.TextField(db_index=True, blank=True, null=True, default=None) |
|
||||||
# short_description = models.TextField(blank=True, null=True, default=None) |
|
||||||
producer = models.ForeignKey(Producer, on_delete=models.CASCADE, related_name='products') |
|
||||||
image = models.ImageField(upload_to='products/%Y/%m/%d/', blank=True, verbose_name="image of product") |
|
||||||
discount = models.IntegerField(blank=True, null=True, default=0) |
|
||||||
stock = models.PositiveIntegerField(blank=True, null=True, default=0, verbose_name="In stock") |
|
||||||
# category = TreeForeignKey(ProductCategory, blank=True, null=True, default=None, related_name='products') |
|
||||||
category = models.ForeignKey(ProductCategory, default=None, related_name='products') |
|
||||||
attributes = models.ManyToManyField(ProductAttribute, related_name='categories', blank=True) |
|
||||||
discount_policy = HStoreField(blank=True, null=True, default={}) |
|
||||||
is_active = models.BooleanField(default=True) |
|
||||||
# is_hit = models.BooleanField(default=False) |
|
||||||
# is_new = models.BooleanField(default=False) |
|
||||||
created = models.DateField(auto_now_add=True, auto_now=False) |
|
||||||
updated = models.DateField(auto_now_add=False, auto_now=True) |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return self.name |
|
||||||
|
|
||||||
class Meta: |
|
||||||
ordering = ['id'] |
|
||||||
index_together = [ |
|
||||||
['id', 'slug'] |
|
||||||
] |
|
||||||
verbose_name = 'Product' |
|
||||||
verbose_name_plural = 'Products' |
|
||||||
|
|
||||||
def get_absolute_url(self): |
|
||||||
return reverse('products:Product', args=[self.slug]) |
|
||||||
|
|
||||||
# def save(self, *args, **kwargs): |
|
||||||
# if self.category: |
|
||||||
# super(Product, self).save(*args, **kwargs) |
|
||||||
# |
|
||||||
# for cp in ProductClass.objects.filter(category=self.product_class): |
|
||||||
# pp = ProductProperty.objects.filter(category_property=cp, |
|
||||||
# product=self) |
|
||||||
# if not pp: |
|
||||||
# pp = ProductProperty(category_property=cp, product=self, value="--") |
|
||||||
# pp.save() |
|
||||||
|
|
||||||
# class ProductImage(models.Model): |
|
||||||
# product = models.ForeignKey(Product, on_delete=models.CASCADE, blank=True, null=True, default=None) |
|
||||||
# image = models.ImageField(upload_to='products_images') |
|
||||||
# is_main = models.BooleanField(default=False) |
|
||||||
# is_active = models.BooleanField(default=True) |
|
||||||
# created = models.DateField(auto_now_add=True, auto_now=False) |
|
||||||
# updated = models.DateField(auto_now_add=False, auto_now=True) |
|
||||||
# |
|
||||||
# def __str__(self): |
|
||||||
# return "{!s}".format(self.id) |
|
||||||
# |
|
||||||
# class Meta: |
|
||||||
# verbose_name = 'Photo' |
|
||||||
# verbose_name_plural = 'Photos' |
|
||||||
|
|
||||||
class Offer(models.Model): |
|
||||||
name = models.CharField(max_length=64, blank=True, null=True, default=None) |
|
||||||
slug = AutoSlugField(populate_from='name') |
|
||||||
price = models.DecimalField(max_digits=8, decimal_places=2, null=True, default=0.00) |
|
||||||
# points = models.DecimalField(max_digits=8, decimal_places=2, null=True, default=0.00) |
|
||||||
product = models.ForeignKey(Product, on_delete=models.CASCADE, blank=True, null=True, default=None, |
|
||||||
related_name='variants') |
|
||||||
is_active = models.BooleanField(default=True) |
|
||||||
attributes = HStoreField(blank=True, null=True, default={}) |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return self.name |
|
||||||
|
|
||||||
class Meta: |
|
||||||
verbose_name = 'Offer' |
|
||||||
verbose_name_plural = 'Offers' |
|
||||||
|
|
||||||
def save(self, *args, **kwargs): |
|
||||||
self.points = self.price * decimal.Decimal('0.1') |
|
||||||
super(Offer, self).save(*args, **kwargs) |
|
||||||
@ -1,20 +0,0 @@ |
|||||||
import datetime |
|
||||||
from haystack import indexes |
|
||||||
from .models import * |
|
||||||
|
|
||||||
class ProductIndex(indexes.SearchIndex, indexes.Indexable): |
|
||||||
text = indexes.EdgeNgramField(document=True, use_template=True, template_name="search/product_text.txt") |
|
||||||
name = indexes.EdgeNgramField(model_attr='name') |
|
||||||
description = indexes.EdgeNgramField(model_attr='description') |
|
||||||
category = indexes.CharField(model_attr='category', faceted=True) |
|
||||||
producer = indexes.CharField(model_attr='producer', faceted=True) |
|
||||||
|
|
||||||
content_auto = indexes.EdgeNgramField(model_attr='name') |
|
||||||
|
|
||||||
suggestions = indexes.FacetCharField() |
|
||||||
|
|
||||||
def get_model(self): |
|
||||||
return Product |
|
||||||
|
|
||||||
def index_queryset(self, using=None): |
|
||||||
return self.get_model().objects.all() |
|
||||||
@ -1,3 +0,0 @@ |
|||||||
from django.test import TestCase |
|
||||||
|
|
||||||
# Create your tests here. |
|
||||||
@ -1,33 +0,0 @@ |
|||||||
"""Eshop URL Configuration |
|
||||||
|
|
||||||
The `urlpatterns` list routes URLs to views. For more information please see: |
|
||||||
https://docs.djangoproject.com/en/1.10/topics/http/urls/ |
|
||||||
Examples: |
|
||||||
Function views |
|
||||||
1. Add an import: from my_app import views |
|
||||||
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') |
|
||||||
Class-based views |
|
||||||
1. Add an import: from other_app.views import Home |
|
||||||
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') |
|
||||||
Including another URLconf |
|
||||||
1. Import the include() function: from django.conf.urls import url, include |
|
||||||
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) |
|
||||||
""" |
|
||||||
from django.conf.urls import url |
|
||||||
from .views import productslist, product, categorieslist, producerslist |
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [ |
|
||||||
#url(r'^product/(?P<product_id>\w+)/$', views.product, name='product'), |
|
||||||
url(r'^$', producerslist, name='ProductList'), |
|
||||||
|
|
||||||
# Uncomment for elasticsearch |
|
||||||
|
|
||||||
# url(r'^autocomplete/$', autocomplete), |
|
||||||
# url(r'^find/$', FacetedSearchView.as_view(), name='haystack_search'), |
|
||||||
|
|
||||||
|
|
||||||
url(r'^product/(?P<product_slug>[-\w]+)/$', product, name='Product'), |
|
||||||
url(r'^(?P<producer_slug>[-\w]+)/$', categorieslist, name='CategoriesListByProducer'), |
|
||||||
url(r'^(?P<producer_slug>[-\w]+)/(?P<category_slug>[-\w]+)/$', productslist, name='ProductListByCategory') |
|
||||||
] |
|
||||||
@ -1,40 +0,0 @@ |
|||||||
from .models import Product |
|
||||||
|
|
||||||
def get_variant_picker_data(product): |
|
||||||
variants = product.variants.all() |
|
||||||
variant_attributes = product.attributes.all() |
|
||||||
data = {'variants': [], 'variantAttributes': [], 'discount_policy': product.discount_policy} |
|
||||||
|
|
||||||
for attribute in variant_attributes: |
|
||||||
data['variantAttributes'].append({ |
|
||||||
'name': attribute.name, |
|
||||||
'slug': attribute.slug, |
|
||||||
'values': [{'name': value.name, 'slug': value.slug} for value in attribute.values.all()] |
|
||||||
}) |
|
||||||
|
|
||||||
for variant in variants: |
|
||||||
price = variant.price |
|
||||||
|
|
||||||
variant_data = { |
|
||||||
'id': variant.id, |
|
||||||
'slug': variant.slug, |
|
||||||
'name': variant.name, |
|
||||||
'price': int(price), |
|
||||||
'attributes': variant.attributes, |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
data['variants'].append(variant_data) |
|
||||||
|
|
||||||
return data |
|
||||||
|
|
||||||
def expand_categories(categories): |
|
||||||
products = None |
|
||||||
new_categories = categories |
|
||||||
for e in categories: |
|
||||||
if e.name.startswith('None'): |
|
||||||
products = Product.objects.filter(category=e) |
|
||||||
new_categories = categories.exclude(pk=e.pk) |
|
||||||
return new_categories, products |
|
||||||
|
|
||||||
|
|
||||||
@ -1,79 +0,0 @@ |
|||||||
from django.shortcuts import render, render_to_response, get_object_or_404 |
|
||||||
from django.contrib import auth |
|
||||||
from django.http import JsonResponse |
|
||||||
import json |
|
||||||
import decimal |
|
||||||
from cart.forms import CartAddProductForm |
|
||||||
from .utils import * |
|
||||||
from cart.cart import Cart |
|
||||||
from .models import * |
|
||||||
|
|
||||||
# Uncomment for elasticsearch |
|
||||||
|
|
||||||
# from .forms import FacetedProductSearchForm |
|
||||||
# from haystack.generic_views import FacetedSearchView as BaseFacetedSearchView |
|
||||||
# from haystack.query import SearchQuerySet |
|
||||||
|
|
||||||
def serialize_decimal(obj): |
|
||||||
if isinstance(obj, decimal.Decimal): |
|
||||||
return str(obj) |
|
||||||
return json.JSONEncoder.default(obj) |
|
||||||
|
|
||||||
def producerslist(request): |
|
||||||
username = auth.get_user(request).username |
|
||||||
# category = None |
|
||||||
# categories = ProductCategory.objects.filter(level__lte=0) |
|
||||||
# products = Product.objects.filter(is_active=True) |
|
||||||
producers = Producer.objects.filter(is_active=True) |
|
||||||
# if category_slug: |
|
||||||
# category = get_object_or_404(ProductCategory, slug=category_slug) |
|
||||||
# products = products.filter(category__in=category.get_descendants(include_self=True)) |
|
||||||
return render(request, 'products/list.html', locals()) |
|
||||||
|
|
||||||
def categorieslist(request, producer_slug): |
|
||||||
username = auth.get_user(request).username |
|
||||||
producer = Producer.objects.get(slug=producer_slug) |
|
||||||
_categories = ProductCategory.objects.filter(is_active=True, producer=producer) |
|
||||||
categories, products = expand_categories(_categories) |
|
||||||
return render(request, 'products/categorieslist.html', {'username': username, 'categories':categories, |
|
||||||
'products': products}) |
|
||||||
|
|
||||||
def productslist(request, producer_slug, category_slug): |
|
||||||
username = auth.get_user(request).username |
|
||||||
category = ProductCategory.objects.get(slug=category_slug) |
|
||||||
products = Product.objects.filter(is_active=True, category=category) |
|
||||||
return render(request, 'products/productslist.html', locals()) |
|
||||||
|
|
||||||
def product(request, product_slug): |
|
||||||
username = auth.get_user(request).username |
|
||||||
product = get_object_or_404(Product, slug=product_slug, is_active=True) |
|
||||||
cart_product_form = CartAddProductForm() |
|
||||||
variant_picker_data = get_variant_picker_data(product) |
|
||||||
show_variant_picker = all([v.attributes for v in product.variants.all()]) |
|
||||||
# session_key = request.session.session_key |
|
||||||
# if not session_key: |
|
||||||
# request.session.cycle_key() |
|
||||||
|
|
||||||
return render(request, 'products/product.html', {'username': username, 'product': product, 'form': cart_product_form, |
|
||||||
'show_variant_picker': show_variant_picker, |
|
||||||
'variant_picker_data': variant_picker_data, |
|
||||||
}) |
|
||||||
|
|
||||||
# Uncomment for elasticsearch |
|
||||||
|
|
||||||
# def autocomplete(request): |
|
||||||
# sqs = SearchQuerySet().autocomplete(content_auto=request.GET.get('query', ''))[:5] |
|
||||||
# s = [] |
|
||||||
# for result in sqs: |
|
||||||
# print(result) |
|
||||||
# d = {"value": result.name, "data": result.object.slug} |
|
||||||
# s.append(d) |
|
||||||
# output = {'suggestions': s} |
|
||||||
# return JsonResponse(output) |
|
||||||
# |
|
||||||
# class FacetedSearchView(BaseFacetedSearchView): |
|
||||||
# form_class = FacetedProductSearchForm |
|
||||||
# facet_fields = ['category', 'producer'] |
|
||||||
# template_name = 'search/search.html' |
|
||||||
# paginate_by = 3 |
|
||||||
# context_object_name = 'object_list' |
|
||||||
@ -1,11 +0,0 @@ |
|||||||
List of databases |
|
||||||
Name | Owner | Encoding | Collate | Ctype | Access privileges |
|
||||||
-----------+----------+----------+-------------+-------------+----------------------- |
|
||||||
eshop_db | denis | UTF8 | en_US.UTF-8 | en_US.UTF-8 | |
|
||||||
postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | |
|
||||||
template0 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres + |
|
||||||
| | | | | postgres=CTc/postgres |
|
||||||
template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres + |
|
||||||
| | | | | postgres=CTc/postgres |
|
||||||
(4 rows) |
|
||||||
|
|
||||||
@ -1,17 +1,59 @@ |
|||||||
Django==1.10.6 |
amqp==2.3.2 |
||||||
celery==4.0.2 |
anyjson==0.3.3 |
||||||
|
appdirs==1.4.3 |
||||||
|
Babel==2.6.0 |
||||||
|
billiard==3.5.0.2 |
||||||
|
cairocffi==0.8.1 |
||||||
|
CairoSVG==2.1.3 |
||||||
|
celery==4.0.0 |
||||||
|
cffi==1.10.0 |
||||||
|
cssselect==1.0.3 |
||||||
|
cssselect2==0.2.1 |
||||||
|
defusedxml==0.5.0 |
||||||
|
diff-match-patch==20121119 |
||||||
dj-database-url==0.4.2 |
dj-database-url==0.4.2 |
||||||
setuptools==35.0.2 |
Django==2.0.7 |
||||||
django-suit==0.2.25 |
django-appconf==1.0.2 |
||||||
psycopg2==2.7.1 |
django-autoslug-iplweb==1.9.4.dev0 |
||||||
|
django-celery==3.2.2 |
||||||
|
django-celery-email==2.0.0 |
||||||
|
django-debug-toolbar==1.9.1 |
||||||
|
django-environ==0.4.5 |
||||||
|
django-haystack==2.5.1 |
||||||
django-import-export==0.5.1 |
django-import-export==0.5.1 |
||||||
django-mptt==0.8.7 |
django-mptt==0.8.7 |
||||||
django-haystack==2.5.1 |
django-mptt-urls==2.0.3 |
||||||
django-phonenumber-field==1.3.0pip |
django-phonenumber-field==1.3.0 |
||||||
WeasyPrint==0.36 |
django-suit==0.2.25 |
||||||
cffi-1.10.0 |
|
||||||
WeasyPrint==0.36 |
|
||||||
elasticsearch==5.0.1 |
elasticsearch==5.0.1 |
||||||
whitenoise==3.3.0 |
et-xmlfile==1.0.1 |
||||||
gunicorn==19.7.1 |
gunicorn==19.7.1 |
||||||
|
html5lib==1.0.1 |
||||||
|
jdcal==1.4 |
||||||
|
kombu==4.2.1 |
||||||
|
lxml==4.2.3 |
||||||
|
odfpy==1.3.6 |
||||||
|
openpyxl==2.5.4 |
||||||
|
packaging==17.1 |
||||||
|
phonenumberslite==8.9.9 |
||||||
|
Pillow==5.2.0 |
||||||
|
psycopg2-binary==2.7.5 |
||||||
|
pycparser==2.18 |
||||||
|
pyparsing==2.2.0 |
||||||
|
Pyphen==0.9.4 |
||||||
|
pytils==0.3 |
||||||
|
pytz==2018.5 |
||||||
|
PyYAML==3.13 |
||||||
|
six==1.11.0 |
||||||
|
sqlparse==0.2.4 |
||||||
|
tablib==0.12.1 |
||||||
|
tinycss==0.4 |
||||||
|
tinycss2==0.6.1 |
||||||
|
unicodecsv==0.14.1 |
||||||
|
urllib3==1.23 |
||||||
|
vine==1.1.4 |
||||||
|
WeasyPrint==0.36 |
||||||
|
webencodings==0.5.1 |
||||||
|
whitenoise==3.3.0 |
||||||
|
xlrd==1.1.0 |
||||||
|
xlwt==1.3.0 |
||||||
|
|||||||
@ -1 +0,0 @@ |
|||||||
python-3.5.2 |
|
||||||
@ -1,971 +0,0 @@ |
|||||||
/* |
|
||||||
DJANGO Admin styles |
|
||||||
*/ |
|
||||||
|
|
||||||
@import url("fonts.cc6140298ba7.css"); |
|
||||||
|
|
||||||
body { |
|
||||||
margin: 0; |
|
||||||
padding: 0; |
|
||||||
font-size: 14px; |
|
||||||
font-family: "Roboto","Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif; |
|
||||||
color: #333; |
|
||||||
background: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
/* LINKS */ |
|
||||||
|
|
||||||
a:link, a:visited { |
|
||||||
color: #447e9b; |
|
||||||
text-decoration: none; |
|
||||||
} |
|
||||||
|
|
||||||
a:focus, a:hover { |
|
||||||
color: #036; |
|
||||||
} |
|
||||||
|
|
||||||
a:focus { |
|
||||||
text-decoration: underline; |
|
||||||
} |
|
||||||
|
|
||||||
a img { |
|
||||||
border: none; |
|
||||||
} |
|
||||||
|
|
||||||
a.section:link, a.section:visited { |
|
||||||
color: #fff; |
|
||||||
text-decoration: none; |
|
||||||
} |
|
||||||
|
|
||||||
a.section:focus, a.section:hover { |
|
||||||
text-decoration: underline; |
|
||||||
} |
|
||||||
|
|
||||||
/* GLOBAL DEFAULTS */ |
|
||||||
|
|
||||||
p, ol, ul, dl { |
|
||||||
margin: .2em 0 .8em 0; |
|
||||||
} |
|
||||||
|
|
||||||
p { |
|
||||||
padding: 0; |
|
||||||
line-height: 140%; |
|
||||||
} |
|
||||||
|
|
||||||
h1,h2,h3,h4,h5 { |
|
||||||
font-weight: bold; |
|
||||||
} |
|
||||||
|
|
||||||
h1 { |
|
||||||
margin: 0 0 20px; |
|
||||||
font-weight: 300; |
|
||||||
font-size: 20px; |
|
||||||
color: #666; |
|
||||||
} |
|
||||||
|
|
||||||
h2 { |
|
||||||
font-size: 16px; |
|
||||||
margin: 1em 0 .5em 0; |
|
||||||
} |
|
||||||
|
|
||||||
h2.subhead { |
|
||||||
font-weight: normal; |
|
||||||
margin-top: 0; |
|
||||||
} |
|
||||||
|
|
||||||
h3 { |
|
||||||
font-size: 14px; |
|
||||||
margin: .8em 0 .3em 0; |
|
||||||
color: #666; |
|
||||||
font-weight: bold; |
|
||||||
} |
|
||||||
|
|
||||||
h4 { |
|
||||||
font-size: 12px; |
|
||||||
margin: 1em 0 .8em 0; |
|
||||||
padding-bottom: 3px; |
|
||||||
} |
|
||||||
|
|
||||||
h5 { |
|
||||||
font-size: 10px; |
|
||||||
margin: 1.5em 0 .5em 0; |
|
||||||
color: #666; |
|
||||||
text-transform: uppercase; |
|
||||||
letter-spacing: 1px; |
|
||||||
} |
|
||||||
|
|
||||||
ul li { |
|
||||||
list-style-type: square; |
|
||||||
padding: 1px 0; |
|
||||||
} |
|
||||||
|
|
||||||
li ul { |
|
||||||
margin-bottom: 0; |
|
||||||
} |
|
||||||
|
|
||||||
li, dt, dd { |
|
||||||
font-size: 13px; |
|
||||||
line-height: 20px; |
|
||||||
} |
|
||||||
|
|
||||||
dt { |
|
||||||
font-weight: bold; |
|
||||||
margin-top: 4px; |
|
||||||
} |
|
||||||
|
|
||||||
dd { |
|
||||||
margin-left: 0; |
|
||||||
} |
|
||||||
|
|
||||||
form { |
|
||||||
margin: 0; |
|
||||||
padding: 0; |
|
||||||
} |
|
||||||
|
|
||||||
fieldset { |
|
||||||
margin: 0; |
|
||||||
padding: 0; |
|
||||||
border: none; |
|
||||||
border-top: 1px solid #eee; |
|
||||||
} |
|
||||||
|
|
||||||
blockquote { |
|
||||||
font-size: 11px; |
|
||||||
color: #777; |
|
||||||
margin-left: 2px; |
|
||||||
padding-left: 10px; |
|
||||||
border-left: 5px solid #ddd; |
|
||||||
} |
|
||||||
|
|
||||||
code, pre { |
|
||||||
font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace; |
|
||||||
color: #666; |
|
||||||
font-size: 12px; |
|
||||||
} |
|
||||||
|
|
||||||
pre.literal-block { |
|
||||||
margin: 10px; |
|
||||||
background: #eee; |
|
||||||
padding: 6px 8px; |
|
||||||
} |
|
||||||
|
|
||||||
code strong { |
|
||||||
color: #930; |
|
||||||
} |
|
||||||
|
|
||||||
hr { |
|
||||||
clear: both; |
|
||||||
color: #eee; |
|
||||||
background-color: #eee; |
|
||||||
height: 1px; |
|
||||||
border: none; |
|
||||||
margin: 0; |
|
||||||
padding: 0; |
|
||||||
font-size: 1px; |
|
||||||
line-height: 1px; |
|
||||||
} |
|
||||||
|
|
||||||
/* TEXT STYLES & MODIFIERS */ |
|
||||||
|
|
||||||
.small { |
|
||||||
font-size: 11px; |
|
||||||
} |
|
||||||
|
|
||||||
.tiny { |
|
||||||
font-size: 10px; |
|
||||||
} |
|
||||||
|
|
||||||
p.tiny { |
|
||||||
margin-top: -2px; |
|
||||||
} |
|
||||||
|
|
||||||
.mini { |
|
||||||
font-size: 10px; |
|
||||||
} |
|
||||||
|
|
||||||
p.mini { |
|
||||||
margin-top: -3px; |
|
||||||
} |
|
||||||
|
|
||||||
.help, p.help, form p.help { |
|
||||||
font-size: 11px; |
|
||||||
color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
.help-tooltip { |
|
||||||
cursor: help; |
|
||||||
} |
|
||||||
|
|
||||||
p img, h1 img, h2 img, h3 img, h4 img, td img { |
|
||||||
vertical-align: middle; |
|
||||||
} |
|
||||||
|
|
||||||
.quiet, a.quiet:link, a.quiet:visited { |
|
||||||
color: #999; |
|
||||||
font-weight: normal; |
|
||||||
} |
|
||||||
|
|
||||||
.float-right { |
|
||||||
float: right; |
|
||||||
} |
|
||||||
|
|
||||||
.float-left { |
|
||||||
float: left; |
|
||||||
} |
|
||||||
|
|
||||||
.clear { |
|
||||||
clear: both; |
|
||||||
} |
|
||||||
|
|
||||||
.align-left { |
|
||||||
text-align: left; |
|
||||||
} |
|
||||||
|
|
||||||
.align-right { |
|
||||||
text-align: right; |
|
||||||
} |
|
||||||
|
|
||||||
.example { |
|
||||||
margin: 10px 0; |
|
||||||
padding: 5px 10px; |
|
||||||
background: #efefef; |
|
||||||
} |
|
||||||
|
|
||||||
.nowrap { |
|
||||||
white-space: nowrap; |
|
||||||
} |
|
||||||
|
|
||||||
/* TABLES */ |
|
||||||
|
|
||||||
table { |
|
||||||
border-collapse: collapse; |
|
||||||
border-color: #ccc; |
|
||||||
} |
|
||||||
|
|
||||||
td, th { |
|
||||||
font-size: 13px; |
|
||||||
line-height: 16px; |
|
||||||
border-bottom: 1px solid #eee; |
|
||||||
vertical-align: top; |
|
||||||
padding: 8px; |
|
||||||
font-family: "Roboto", "Lucida Grande", Verdana, Arial, sans-serif; |
|
||||||
} |
|
||||||
|
|
||||||
th { |
|
||||||
font-weight: 600; |
|
||||||
text-align: left; |
|
||||||
} |
|
||||||
|
|
||||||
thead th, |
|
||||||
tfoot td { |
|
||||||
color: #666; |
|
||||||
padding: 5px 10px; |
|
||||||
font-size: 11px; |
|
||||||
background: #fff; |
|
||||||
border: none; |
|
||||||
border-top: 1px solid #eee; |
|
||||||
border-bottom: 1px solid #eee; |
|
||||||
} |
|
||||||
|
|
||||||
tfoot td { |
|
||||||
border-bottom: none; |
|
||||||
border-top: 1px solid #eee; |
|
||||||
} |
|
||||||
|
|
||||||
thead th.required { |
|
||||||
color: #000; |
|
||||||
} |
|
||||||
|
|
||||||
tr.alt { |
|
||||||
background: #f6f6f6; |
|
||||||
} |
|
||||||
|
|
||||||
.row1 { |
|
||||||
background: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.row2 { |
|
||||||
background: #f9f9f9; |
|
||||||
} |
|
||||||
|
|
||||||
/* SORTABLE TABLES */ |
|
||||||
|
|
||||||
thead th { |
|
||||||
padding: 5px 10px; |
|
||||||
line-height: normal; |
|
||||||
text-transform: uppercase; |
|
||||||
background: #f6f6f6; |
|
||||||
} |
|
||||||
|
|
||||||
thead th a:link, thead th a:visited { |
|
||||||
color: #666; |
|
||||||
} |
|
||||||
|
|
||||||
thead th.sorted { |
|
||||||
background: #eee; |
|
||||||
} |
|
||||||
|
|
||||||
thead th.sorted .text { |
|
||||||
padding-right: 42px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th .text span { |
|
||||||
padding: 8px 10px; |
|
||||||
display: block; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th .text a { |
|
||||||
display: block; |
|
||||||
cursor: pointer; |
|
||||||
padding: 8px 10px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th .text a:focus, table thead th .text a:hover { |
|
||||||
background: #eee; |
|
||||||
} |
|
||||||
|
|
||||||
thead th.sorted a.sortremove { |
|
||||||
visibility: hidden; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted:hover a.sortremove { |
|
||||||
visibility: visible; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions { |
|
||||||
display: block; |
|
||||||
padding: 9px 5px 0 5px; |
|
||||||
float: right; |
|
||||||
text-align: right; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortpriority { |
|
||||||
font-size: .8em; |
|
||||||
min-width: 12px; |
|
||||||
text-align: center; |
|
||||||
vertical-align: 3px; |
|
||||||
margin-left: 2px; |
|
||||||
margin-right: 2px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a { |
|
||||||
position: relative; |
|
||||||
width: 14px; |
|
||||||
height: 14px; |
|
||||||
display: inline-block; |
|
||||||
background: url("../img/sorting-icons.3a097b59f104.svg") 0 0 no-repeat; |
|
||||||
background-size: 14px auto; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.sortremove { |
|
||||||
background-position: 0 0; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.sortremove:after { |
|
||||||
content: '\\'; |
|
||||||
position: absolute; |
|
||||||
top: -6px; |
|
||||||
left: 3px; |
|
||||||
font-weight: 200; |
|
||||||
font-size: 18px; |
|
||||||
color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.sortremove:focus:after, |
|
||||||
table thead th.sorted .sortoptions a.sortremove:hover:after { |
|
||||||
color: #447e9b; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.sortremove:focus, |
|
||||||
table thead th.sorted .sortoptions a.sortremove:hover { |
|
||||||
background-position: 0 -14px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.ascending { |
|
||||||
background-position: 0 -28px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.ascending:focus, |
|
||||||
table thead th.sorted .sortoptions a.ascending:hover { |
|
||||||
background-position: 0 -42px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.descending { |
|
||||||
top: 1px; |
|
||||||
background-position: 0 -56px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.descending:focus, |
|
||||||
table thead th.sorted .sortoptions a.descending:hover { |
|
||||||
background-position: 0 -70px; |
|
||||||
} |
|
||||||
|
|
||||||
/* FORM DEFAULTS */ |
|
||||||
|
|
||||||
input, textarea, select, .form-row p, form .button { |
|
||||||
margin: 2px 0; |
|
||||||
padding: 2px 3px; |
|
||||||
vertical-align: middle; |
|
||||||
font-family: "Roboto", "Lucida Grande", Verdana, Arial, sans-serif; |
|
||||||
font-weight: normal; |
|
||||||
font-size: 13px; |
|
||||||
} |
|
||||||
|
|
||||||
textarea { |
|
||||||
vertical-align: top; |
|
||||||
} |
|
||||||
|
|
||||||
input[type=text], input[type=password], input[type=email], input[type=url], |
|
||||||
input[type=number], textarea, select, .vTextField { |
|
||||||
border: 1px solid #ccc; |
|
||||||
border-radius: 4px; |
|
||||||
padding: 5px 6px; |
|
||||||
margin-top: 0; |
|
||||||
} |
|
||||||
|
|
||||||
input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, |
|
||||||
input[type=url]:focus, input[type=number]:focus, textarea:focus, select:focus, |
|
||||||
.vTextField:focus { |
|
||||||
border-color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
select { |
|
||||||
height: 30px; |
|
||||||
} |
|
||||||
|
|
||||||
select[multiple] { |
|
||||||
min-height: 150px; |
|
||||||
} |
|
||||||
|
|
||||||
/* FORM BUTTONS */ |
|
||||||
|
|
||||||
.button, input[type=submit], input[type=button], .submit-row input, a.button { |
|
||||||
background: #79aec8; |
|
||||||
padding: 10px 15px; |
|
||||||
border: none; |
|
||||||
border-radius: 4px; |
|
||||||
color: #fff; |
|
||||||
cursor: pointer; |
|
||||||
} |
|
||||||
|
|
||||||
a.button { |
|
||||||
padding: 4px 5px; |
|
||||||
} |
|
||||||
|
|
||||||
.button:active, input[type=submit]:active, input[type=button]:active, |
|
||||||
.button:focus, input[type=submit]:focus, input[type=button]:focus, |
|
||||||
.button:hover, input[type=submit]:hover, input[type=button]:hover { |
|
||||||
background: #609ab6; |
|
||||||
} |
|
||||||
|
|
||||||
.button[disabled], input[type=submit][disabled], input[type=button][disabled] { |
|
||||||
opacity: 0.4; |
|
||||||
} |
|
||||||
|
|
||||||
.button.default, input[type=submit].default, .submit-row input.default { |
|
||||||
float: right; |
|
||||||
border: none; |
|
||||||
font-weight: 400; |
|
||||||
background: #417690; |
|
||||||
} |
|
||||||
|
|
||||||
.button.default:active, input[type=submit].default:active, |
|
||||||
.button.default:focus, input[type=submit].default:focus, |
|
||||||
.button.default:hover, input[type=submit].default:hover { |
|
||||||
background: #205067; |
|
||||||
} |
|
||||||
|
|
||||||
.button[disabled].default, |
|
||||||
input[type=submit][disabled].default, |
|
||||||
input[type=button][disabled].default { |
|
||||||
opacity: 0.4; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/* MODULES */ |
|
||||||
|
|
||||||
.module { |
|
||||||
border: none; |
|
||||||
margin-bottom: 30px; |
|
||||||
background: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.module p, .module ul, .module h3, .module h4, .module dl, .module pre { |
|
||||||
padding-left: 10px; |
|
||||||
padding-right: 10px; |
|
||||||
} |
|
||||||
|
|
||||||
.module blockquote { |
|
||||||
margin-left: 12px; |
|
||||||
} |
|
||||||
|
|
||||||
.module ul, .module ol { |
|
||||||
margin-left: 1.5em; |
|
||||||
} |
|
||||||
|
|
||||||
.module h3 { |
|
||||||
margin-top: .6em; |
|
||||||
} |
|
||||||
|
|
||||||
.module h2, .module caption, .inline-group h2 { |
|
||||||
margin: 0; |
|
||||||
padding: 8px; |
|
||||||
font-weight: 400; |
|
||||||
font-size: 13px; |
|
||||||
text-align: left; |
|
||||||
background: #79aec8; |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.module caption, |
|
||||||
.inline-group h2 { |
|
||||||
font-size: 12px; |
|
||||||
letter-spacing: 0.5px; |
|
||||||
text-transform: uppercase; |
|
||||||
} |
|
||||||
|
|
||||||
.module table { |
|
||||||
border-collapse: collapse; |
|
||||||
} |
|
||||||
|
|
||||||
/* MESSAGES & ERRORS */ |
|
||||||
|
|
||||||
ul.messagelist { |
|
||||||
padding: 0; |
|
||||||
margin: 0; |
|
||||||
} |
|
||||||
|
|
||||||
ul.messagelist li { |
|
||||||
display: block; |
|
||||||
font-weight: 400; |
|
||||||
font-size: 13px; |
|
||||||
padding: 10px 10px 10px 65px; |
|
||||||
margin: 0 0 10px 0; |
|
||||||
background: #dfd url("../img/icon-yes.d2f9f035226a.svg") 40px 12px no-repeat; |
|
||||||
background-size: 16px auto; |
|
||||||
color: #333; |
|
||||||
} |
|
||||||
|
|
||||||
ul.messagelist li.warning { |
|
||||||
background: #ffc url("../img/icon-alert.034cc7d8a67f.svg") 40px 14px no-repeat; |
|
||||||
background-size: 14px auto; |
|
||||||
} |
|
||||||
|
|
||||||
ul.messagelist li.error { |
|
||||||
background: #ffefef url("../img/icon-no.439e821418cd.svg") 40px 12px no-repeat; |
|
||||||
background-size: 16px auto; |
|
||||||
} |
|
||||||
|
|
||||||
.errornote { |
|
||||||
font-size: 14px; |
|
||||||
font-weight: 700; |
|
||||||
display: block; |
|
||||||
padding: 10px 12px; |
|
||||||
margin: 0 0 10px 0; |
|
||||||
color: #ba2121; |
|
||||||
border: 1px solid #ba2121; |
|
||||||
border-radius: 4px; |
|
||||||
background-color: #fff; |
|
||||||
background-position: 5px 12px; |
|
||||||
} |
|
||||||
|
|
||||||
ul.errorlist { |
|
||||||
margin: 0 0 4px; |
|
||||||
padding: 0; |
|
||||||
color: #ba2121; |
|
||||||
background: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
ul.errorlist li { |
|
||||||
font-size: 13px; |
|
||||||
display: block; |
|
||||||
margin-bottom: 4px; |
|
||||||
} |
|
||||||
|
|
||||||
ul.errorlist li:first-child { |
|
||||||
margin-top: 0; |
|
||||||
} |
|
||||||
|
|
||||||
ul.errorlist li a { |
|
||||||
color: inherit; |
|
||||||
text-decoration: underline; |
|
||||||
} |
|
||||||
|
|
||||||
td ul.errorlist { |
|
||||||
margin: 0; |
|
||||||
padding: 0; |
|
||||||
} |
|
||||||
|
|
||||||
td ul.errorlist li { |
|
||||||
margin: 0; |
|
||||||
} |
|
||||||
|
|
||||||
.form-row.errors { |
|
||||||
margin: 0; |
|
||||||
border: none; |
|
||||||
border-bottom: 1px solid #eee; |
|
||||||
background: none; |
|
||||||
} |
|
||||||
|
|
||||||
.form-row.errors ul.errorlist li { |
|
||||||
padding-left: 0; |
|
||||||
} |
|
||||||
|
|
||||||
.errors input, .errors select, .errors textarea { |
|
||||||
border: 1px solid #ba2121; |
|
||||||
} |
|
||||||
|
|
||||||
div.system-message { |
|
||||||
background: #ffc; |
|
||||||
margin: 10px; |
|
||||||
padding: 6px 8px; |
|
||||||
font-size: .8em; |
|
||||||
} |
|
||||||
|
|
||||||
div.system-message p.system-message-title { |
|
||||||
padding: 4px 5px 4px 25px; |
|
||||||
margin: 0; |
|
||||||
color: #c11; |
|
||||||
background: #ffefef url("../img/icon-no.439e821418cd.svg") 5px 5px no-repeat; |
|
||||||
} |
|
||||||
|
|
||||||
.description { |
|
||||||
font-size: 12px; |
|
||||||
padding: 5px 0 0 12px; |
|
||||||
} |
|
||||||
|
|
||||||
/* BREADCRUMBS */ |
|
||||||
|
|
||||||
div.breadcrumbs { |
|
||||||
background: #79aec8; |
|
||||||
padding: 10px 40px; |
|
||||||
border: none; |
|
||||||
font-size: 14px; |
|
||||||
color: #c4dce8; |
|
||||||
text-align: left; |
|
||||||
} |
|
||||||
|
|
||||||
div.breadcrumbs a { |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
div.breadcrumbs a:focus, div.breadcrumbs a:hover { |
|
||||||
color: #c4dce8; |
|
||||||
} |
|
||||||
|
|
||||||
/* ACTION ICONS */ |
|
||||||
|
|
||||||
.addlink { |
|
||||||
padding-left: 16px; |
|
||||||
background: url("../img/icon-addlink.d519b3bab011.svg") 0 1px no-repeat; |
|
||||||
} |
|
||||||
|
|
||||||
.changelink, .inlinechangelink { |
|
||||||
padding-left: 16px; |
|
||||||
background: url("../img/icon-changelink.18d2fd706348.svg") 0 1px no-repeat; |
|
||||||
} |
|
||||||
|
|
||||||
.deletelink { |
|
||||||
padding-left: 16px; |
|
||||||
background: url("../img/icon-deletelink.564ef9dc3854.svg") 0 1px no-repeat; |
|
||||||
} |
|
||||||
|
|
||||||
a.deletelink:link, a.deletelink:visited { |
|
||||||
color: #CC3434; |
|
||||||
} |
|
||||||
|
|
||||||
a.deletelink:focus, a.deletelink:hover { |
|
||||||
color: #993333; |
|
||||||
text-decoration: none; |
|
||||||
} |
|
||||||
|
|
||||||
/* OBJECT TOOLS */ |
|
||||||
|
|
||||||
.object-tools { |
|
||||||
font-size: 10px; |
|
||||||
font-weight: bold; |
|
||||||
padding-left: 0; |
|
||||||
float: right; |
|
||||||
position: relative; |
|
||||||
margin-top: -48px; |
|
||||||
} |
|
||||||
|
|
||||||
.form-row .object-tools { |
|
||||||
margin-top: 5px; |
|
||||||
margin-bottom: 5px; |
|
||||||
float: none; |
|
||||||
height: 2em; |
|
||||||
padding-left: 3.5em; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools li { |
|
||||||
display: block; |
|
||||||
float: left; |
|
||||||
margin-left: 5px; |
|
||||||
height: 16px; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a { |
|
||||||
border-radius: 15px; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a:link, .object-tools a:visited { |
|
||||||
display: block; |
|
||||||
float: left; |
|
||||||
padding: 3px 12px; |
|
||||||
background: #999; |
|
||||||
font-weight: 400; |
|
||||||
font-size: 11px; |
|
||||||
text-transform: uppercase; |
|
||||||
letter-spacing: 0.5px; |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a:focus, .object-tools a:hover { |
|
||||||
background-color: #417690; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a:focus{ |
|
||||||
text-decoration: none; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a.viewsitelink, .object-tools a.golink,.object-tools a.addlink { |
|
||||||
background-repeat: no-repeat; |
|
||||||
background-position: 93% center; |
|
||||||
padding-right: 26px; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a.viewsitelink, .object-tools a.golink { |
|
||||||
background-image: url("../img/tooltag-arrowright.bbfb788a849e.svg"); |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a.addlink { |
|
||||||
background-image: url("../img/tooltag-add.e59d620a9742.svg"); |
|
||||||
} |
|
||||||
|
|
||||||
/* OBJECT HISTORY */ |
|
||||||
|
|
||||||
table#change-history { |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
table#change-history tbody th { |
|
||||||
width: 16em; |
|
||||||
} |
|
||||||
|
|
||||||
/* PAGE STRUCTURE */ |
|
||||||
|
|
||||||
#container { |
|
||||||
position: relative; |
|
||||||
width: 100%; |
|
||||||
min-width: 980px; |
|
||||||
padding: 0; |
|
||||||
} |
|
||||||
|
|
||||||
#content { |
|
||||||
padding: 20px 40px; |
|
||||||
} |
|
||||||
|
|
||||||
.dashboard #content { |
|
||||||
width: 600px; |
|
||||||
} |
|
||||||
|
|
||||||
#content-main { |
|
||||||
float: left; |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related { |
|
||||||
float: right; |
|
||||||
width: 260px; |
|
||||||
position: relative; |
|
||||||
margin-right: -300px; |
|
||||||
} |
|
||||||
|
|
||||||
#footer { |
|
||||||
clear: both; |
|
||||||
padding: 10px; |
|
||||||
} |
|
||||||
|
|
||||||
/* COLUMN TYPES */ |
|
||||||
|
|
||||||
.colMS { |
|
||||||
margin-right: 300px; |
|
||||||
} |
|
||||||
|
|
||||||
.colSM { |
|
||||||
margin-left: 300px; |
|
||||||
} |
|
||||||
|
|
||||||
.colSM #content-related { |
|
||||||
float: left; |
|
||||||
margin-right: 0; |
|
||||||
margin-left: -300px; |
|
||||||
} |
|
||||||
|
|
||||||
.colSM #content-main { |
|
||||||
float: right; |
|
||||||
} |
|
||||||
|
|
||||||
.popup .colM { |
|
||||||
width: auto; |
|
||||||
} |
|
||||||
|
|
||||||
/* HEADER */ |
|
||||||
|
|
||||||
#header { |
|
||||||
width: auto; |
|
||||||
height: 40px; |
|
||||||
padding: 10px 40px; |
|
||||||
background: #417690; |
|
||||||
line-height: 40px; |
|
||||||
color: #ffc; |
|
||||||
overflow: hidden; |
|
||||||
} |
|
||||||
|
|
||||||
#header a:link, #header a:visited { |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
#header a:focus , #header a:hover { |
|
||||||
text-decoration: underline; |
|
||||||
} |
|
||||||
|
|
||||||
#branding { |
|
||||||
float: left; |
|
||||||
} |
|
||||||
|
|
||||||
#branding h1 { |
|
||||||
padding: 0; |
|
||||||
margin: 0 20px 0 0; |
|
||||||
font-weight: 300; |
|
||||||
font-size: 24px; |
|
||||||
color: #f5dd5d; |
|
||||||
} |
|
||||||
|
|
||||||
#branding h1, #branding h1 a:link, #branding h1 a:visited { |
|
||||||
color: #f5dd5d; |
|
||||||
} |
|
||||||
|
|
||||||
#branding h2 { |
|
||||||
padding: 0 10px; |
|
||||||
font-size: 14px; |
|
||||||
margin: -8px 0 8px 0; |
|
||||||
font-weight: normal; |
|
||||||
color: #ffc; |
|
||||||
} |
|
||||||
|
|
||||||
#branding a:hover { |
|
||||||
text-decoration: none; |
|
||||||
} |
|
||||||
|
|
||||||
#user-tools { |
|
||||||
float: right; |
|
||||||
padding: 0; |
|
||||||
margin: 0 0 0 20px; |
|
||||||
font-weight: 300; |
|
||||||
font-size: 11px; |
|
||||||
letter-spacing: 0.5px; |
|
||||||
text-transform: uppercase; |
|
||||||
text-align: right; |
|
||||||
} |
|
||||||
|
|
||||||
#user-tools a { |
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.25); |
|
||||||
} |
|
||||||
|
|
||||||
#user-tools a:focus, #user-tools a:hover { |
|
||||||
text-decoration: none; |
|
||||||
border-bottom-color: #79aec8; |
|
||||||
color: #79aec8; |
|
||||||
} |
|
||||||
|
|
||||||
/* SIDEBAR */ |
|
||||||
|
|
||||||
#content-related { |
|
||||||
background: #f8f8f8; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related .module { |
|
||||||
background: none; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related h3 { |
|
||||||
font-size: 14px; |
|
||||||
color: #666; |
|
||||||
padding: 0 16px; |
|
||||||
margin: 0 0 16px; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related h4 { |
|
||||||
font-size: 13px; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related p { |
|
||||||
padding-left: 16px; |
|
||||||
padding-right: 16px; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related .actionlist { |
|
||||||
padding: 0; |
|
||||||
margin: 16px; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related .actionlist li { |
|
||||||
line-height: 1.2; |
|
||||||
margin-bottom: 10px; |
|
||||||
padding-left: 18px; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related .module h2 { |
|
||||||
background: none; |
|
||||||
padding: 16px; |
|
||||||
margin-bottom: 16px; |
|
||||||
border-bottom: 1px solid #eaeaea; |
|
||||||
font-size: 18px; |
|
||||||
color: #333; |
|
||||||
} |
|
||||||
|
|
||||||
.delete-confirmation form input[type="submit"] { |
|
||||||
background: #ba2121; |
|
||||||
border-radius: 4px; |
|
||||||
padding: 10px 15px; |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.delete-confirmation form input[type="submit"]:active, |
|
||||||
.delete-confirmation form input[type="submit"]:focus, |
|
||||||
.delete-confirmation form input[type="submit"]:hover { |
|
||||||
background: #a41515; |
|
||||||
} |
|
||||||
|
|
||||||
.delete-confirmation form .cancel-link { |
|
||||||
display: inline-block; |
|
||||||
vertical-align: middle; |
|
||||||
height: 15px; |
|
||||||
line-height: 15px; |
|
||||||
background: #ddd; |
|
||||||
border-radius: 4px; |
|
||||||
padding: 10px 15px; |
|
||||||
color: #333; |
|
||||||
margin: 0 0 0 10px; |
|
||||||
} |
|
||||||
|
|
||||||
.delete-confirmation form .cancel-link:active, |
|
||||||
.delete-confirmation form .cancel-link:focus, |
|
||||||
.delete-confirmation form .cancel-link:hover { |
|
||||||
background: #ccc; |
|
||||||
} |
|
||||||
|
|
||||||
/* POPUP */ |
|
||||||
.popup #content { |
|
||||||
padding: 20px; |
|
||||||
} |
|
||||||
|
|
||||||
.popup #container { |
|
||||||
min-width: 0; |
|
||||||
} |
|
||||||
|
|
||||||
.popup #header { |
|
||||||
padding: 10px 20px; |
|
||||||
} |
|
||||||
Binary file not shown.
@ -1,971 +0,0 @@ |
|||||||
/* |
|
||||||
DJANGO Admin styles |
|
||||||
*/ |
|
||||||
|
|
||||||
@import url(fonts.css); |
|
||||||
|
|
||||||
body { |
|
||||||
margin: 0; |
|
||||||
padding: 0; |
|
||||||
font-size: 14px; |
|
||||||
font-family: "Roboto","Lucida Grande","DejaVu Sans","Bitstream Vera Sans",Verdana,Arial,sans-serif; |
|
||||||
color: #333; |
|
||||||
background: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
/* LINKS */ |
|
||||||
|
|
||||||
a:link, a:visited { |
|
||||||
color: #447e9b; |
|
||||||
text-decoration: none; |
|
||||||
} |
|
||||||
|
|
||||||
a:focus, a:hover { |
|
||||||
color: #036; |
|
||||||
} |
|
||||||
|
|
||||||
a:focus { |
|
||||||
text-decoration: underline; |
|
||||||
} |
|
||||||
|
|
||||||
a img { |
|
||||||
border: none; |
|
||||||
} |
|
||||||
|
|
||||||
a.section:link, a.section:visited { |
|
||||||
color: #fff; |
|
||||||
text-decoration: none; |
|
||||||
} |
|
||||||
|
|
||||||
a.section:focus, a.section:hover { |
|
||||||
text-decoration: underline; |
|
||||||
} |
|
||||||
|
|
||||||
/* GLOBAL DEFAULTS */ |
|
||||||
|
|
||||||
p, ol, ul, dl { |
|
||||||
margin: .2em 0 .8em 0; |
|
||||||
} |
|
||||||
|
|
||||||
p { |
|
||||||
padding: 0; |
|
||||||
line-height: 140%; |
|
||||||
} |
|
||||||
|
|
||||||
h1,h2,h3,h4,h5 { |
|
||||||
font-weight: bold; |
|
||||||
} |
|
||||||
|
|
||||||
h1 { |
|
||||||
margin: 0 0 20px; |
|
||||||
font-weight: 300; |
|
||||||
font-size: 20px; |
|
||||||
color: #666; |
|
||||||
} |
|
||||||
|
|
||||||
h2 { |
|
||||||
font-size: 16px; |
|
||||||
margin: 1em 0 .5em 0; |
|
||||||
} |
|
||||||
|
|
||||||
h2.subhead { |
|
||||||
font-weight: normal; |
|
||||||
margin-top: 0; |
|
||||||
} |
|
||||||
|
|
||||||
h3 { |
|
||||||
font-size: 14px; |
|
||||||
margin: .8em 0 .3em 0; |
|
||||||
color: #666; |
|
||||||
font-weight: bold; |
|
||||||
} |
|
||||||
|
|
||||||
h4 { |
|
||||||
font-size: 12px; |
|
||||||
margin: 1em 0 .8em 0; |
|
||||||
padding-bottom: 3px; |
|
||||||
} |
|
||||||
|
|
||||||
h5 { |
|
||||||
font-size: 10px; |
|
||||||
margin: 1.5em 0 .5em 0; |
|
||||||
color: #666; |
|
||||||
text-transform: uppercase; |
|
||||||
letter-spacing: 1px; |
|
||||||
} |
|
||||||
|
|
||||||
ul li { |
|
||||||
list-style-type: square; |
|
||||||
padding: 1px 0; |
|
||||||
} |
|
||||||
|
|
||||||
li ul { |
|
||||||
margin-bottom: 0; |
|
||||||
} |
|
||||||
|
|
||||||
li, dt, dd { |
|
||||||
font-size: 13px; |
|
||||||
line-height: 20px; |
|
||||||
} |
|
||||||
|
|
||||||
dt { |
|
||||||
font-weight: bold; |
|
||||||
margin-top: 4px; |
|
||||||
} |
|
||||||
|
|
||||||
dd { |
|
||||||
margin-left: 0; |
|
||||||
} |
|
||||||
|
|
||||||
form { |
|
||||||
margin: 0; |
|
||||||
padding: 0; |
|
||||||
} |
|
||||||
|
|
||||||
fieldset { |
|
||||||
margin: 0; |
|
||||||
padding: 0; |
|
||||||
border: none; |
|
||||||
border-top: 1px solid #eee; |
|
||||||
} |
|
||||||
|
|
||||||
blockquote { |
|
||||||
font-size: 11px; |
|
||||||
color: #777; |
|
||||||
margin-left: 2px; |
|
||||||
padding-left: 10px; |
|
||||||
border-left: 5px solid #ddd; |
|
||||||
} |
|
||||||
|
|
||||||
code, pre { |
|
||||||
font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace; |
|
||||||
color: #666; |
|
||||||
font-size: 12px; |
|
||||||
} |
|
||||||
|
|
||||||
pre.literal-block { |
|
||||||
margin: 10px; |
|
||||||
background: #eee; |
|
||||||
padding: 6px 8px; |
|
||||||
} |
|
||||||
|
|
||||||
code strong { |
|
||||||
color: #930; |
|
||||||
} |
|
||||||
|
|
||||||
hr { |
|
||||||
clear: both; |
|
||||||
color: #eee; |
|
||||||
background-color: #eee; |
|
||||||
height: 1px; |
|
||||||
border: none; |
|
||||||
margin: 0; |
|
||||||
padding: 0; |
|
||||||
font-size: 1px; |
|
||||||
line-height: 1px; |
|
||||||
} |
|
||||||
|
|
||||||
/* TEXT STYLES & MODIFIERS */ |
|
||||||
|
|
||||||
.small { |
|
||||||
font-size: 11px; |
|
||||||
} |
|
||||||
|
|
||||||
.tiny { |
|
||||||
font-size: 10px; |
|
||||||
} |
|
||||||
|
|
||||||
p.tiny { |
|
||||||
margin-top: -2px; |
|
||||||
} |
|
||||||
|
|
||||||
.mini { |
|
||||||
font-size: 10px; |
|
||||||
} |
|
||||||
|
|
||||||
p.mini { |
|
||||||
margin-top: -3px; |
|
||||||
} |
|
||||||
|
|
||||||
.help, p.help, form p.help { |
|
||||||
font-size: 11px; |
|
||||||
color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
.help-tooltip { |
|
||||||
cursor: help; |
|
||||||
} |
|
||||||
|
|
||||||
p img, h1 img, h2 img, h3 img, h4 img, td img { |
|
||||||
vertical-align: middle; |
|
||||||
} |
|
||||||
|
|
||||||
.quiet, a.quiet:link, a.quiet:visited { |
|
||||||
color: #999; |
|
||||||
font-weight: normal; |
|
||||||
} |
|
||||||
|
|
||||||
.float-right { |
|
||||||
float: right; |
|
||||||
} |
|
||||||
|
|
||||||
.float-left { |
|
||||||
float: left; |
|
||||||
} |
|
||||||
|
|
||||||
.clear { |
|
||||||
clear: both; |
|
||||||
} |
|
||||||
|
|
||||||
.align-left { |
|
||||||
text-align: left; |
|
||||||
} |
|
||||||
|
|
||||||
.align-right { |
|
||||||
text-align: right; |
|
||||||
} |
|
||||||
|
|
||||||
.example { |
|
||||||
margin: 10px 0; |
|
||||||
padding: 5px 10px; |
|
||||||
background: #efefef; |
|
||||||
} |
|
||||||
|
|
||||||
.nowrap { |
|
||||||
white-space: nowrap; |
|
||||||
} |
|
||||||
|
|
||||||
/* TABLES */ |
|
||||||
|
|
||||||
table { |
|
||||||
border-collapse: collapse; |
|
||||||
border-color: #ccc; |
|
||||||
} |
|
||||||
|
|
||||||
td, th { |
|
||||||
font-size: 13px; |
|
||||||
line-height: 16px; |
|
||||||
border-bottom: 1px solid #eee; |
|
||||||
vertical-align: top; |
|
||||||
padding: 8px; |
|
||||||
font-family: "Roboto", "Lucida Grande", Verdana, Arial, sans-serif; |
|
||||||
} |
|
||||||
|
|
||||||
th { |
|
||||||
font-weight: 600; |
|
||||||
text-align: left; |
|
||||||
} |
|
||||||
|
|
||||||
thead th, |
|
||||||
tfoot td { |
|
||||||
color: #666; |
|
||||||
padding: 5px 10px; |
|
||||||
font-size: 11px; |
|
||||||
background: #fff; |
|
||||||
border: none; |
|
||||||
border-top: 1px solid #eee; |
|
||||||
border-bottom: 1px solid #eee; |
|
||||||
} |
|
||||||
|
|
||||||
tfoot td { |
|
||||||
border-bottom: none; |
|
||||||
border-top: 1px solid #eee; |
|
||||||
} |
|
||||||
|
|
||||||
thead th.required { |
|
||||||
color: #000; |
|
||||||
} |
|
||||||
|
|
||||||
tr.alt { |
|
||||||
background: #f6f6f6; |
|
||||||
} |
|
||||||
|
|
||||||
.row1 { |
|
||||||
background: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.row2 { |
|
||||||
background: #f9f9f9; |
|
||||||
} |
|
||||||
|
|
||||||
/* SORTABLE TABLES */ |
|
||||||
|
|
||||||
thead th { |
|
||||||
padding: 5px 10px; |
|
||||||
line-height: normal; |
|
||||||
text-transform: uppercase; |
|
||||||
background: #f6f6f6; |
|
||||||
} |
|
||||||
|
|
||||||
thead th a:link, thead th a:visited { |
|
||||||
color: #666; |
|
||||||
} |
|
||||||
|
|
||||||
thead th.sorted { |
|
||||||
background: #eee; |
|
||||||
} |
|
||||||
|
|
||||||
thead th.sorted .text { |
|
||||||
padding-right: 42px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th .text span { |
|
||||||
padding: 8px 10px; |
|
||||||
display: block; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th .text a { |
|
||||||
display: block; |
|
||||||
cursor: pointer; |
|
||||||
padding: 8px 10px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th .text a:focus, table thead th .text a:hover { |
|
||||||
background: #eee; |
|
||||||
} |
|
||||||
|
|
||||||
thead th.sorted a.sortremove { |
|
||||||
visibility: hidden; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted:hover a.sortremove { |
|
||||||
visibility: visible; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions { |
|
||||||
display: block; |
|
||||||
padding: 9px 5px 0 5px; |
|
||||||
float: right; |
|
||||||
text-align: right; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortpriority { |
|
||||||
font-size: .8em; |
|
||||||
min-width: 12px; |
|
||||||
text-align: center; |
|
||||||
vertical-align: 3px; |
|
||||||
margin-left: 2px; |
|
||||||
margin-right: 2px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a { |
|
||||||
position: relative; |
|
||||||
width: 14px; |
|
||||||
height: 14px; |
|
||||||
display: inline-block; |
|
||||||
background: url(../img/sorting-icons.svg) 0 0 no-repeat; |
|
||||||
background-size: 14px auto; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.sortremove { |
|
||||||
background-position: 0 0; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.sortremove:after { |
|
||||||
content: '\\'; |
|
||||||
position: absolute; |
|
||||||
top: -6px; |
|
||||||
left: 3px; |
|
||||||
font-weight: 200; |
|
||||||
font-size: 18px; |
|
||||||
color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.sortremove:focus:after, |
|
||||||
table thead th.sorted .sortoptions a.sortremove:hover:after { |
|
||||||
color: #447e9b; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.sortremove:focus, |
|
||||||
table thead th.sorted .sortoptions a.sortremove:hover { |
|
||||||
background-position: 0 -14px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.ascending { |
|
||||||
background-position: 0 -28px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.ascending:focus, |
|
||||||
table thead th.sorted .sortoptions a.ascending:hover { |
|
||||||
background-position: 0 -42px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.descending { |
|
||||||
top: 1px; |
|
||||||
background-position: 0 -56px; |
|
||||||
} |
|
||||||
|
|
||||||
table thead th.sorted .sortoptions a.descending:focus, |
|
||||||
table thead th.sorted .sortoptions a.descending:hover { |
|
||||||
background-position: 0 -70px; |
|
||||||
} |
|
||||||
|
|
||||||
/* FORM DEFAULTS */ |
|
||||||
|
|
||||||
input, textarea, select, .form-row p, form .button { |
|
||||||
margin: 2px 0; |
|
||||||
padding: 2px 3px; |
|
||||||
vertical-align: middle; |
|
||||||
font-family: "Roboto", "Lucida Grande", Verdana, Arial, sans-serif; |
|
||||||
font-weight: normal; |
|
||||||
font-size: 13px; |
|
||||||
} |
|
||||||
|
|
||||||
textarea { |
|
||||||
vertical-align: top; |
|
||||||
} |
|
||||||
|
|
||||||
input[type=text], input[type=password], input[type=email], input[type=url], |
|
||||||
input[type=number], textarea, select, .vTextField { |
|
||||||
border: 1px solid #ccc; |
|
||||||
border-radius: 4px; |
|
||||||
padding: 5px 6px; |
|
||||||
margin-top: 0; |
|
||||||
} |
|
||||||
|
|
||||||
input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, |
|
||||||
input[type=url]:focus, input[type=number]:focus, textarea:focus, select:focus, |
|
||||||
.vTextField:focus { |
|
||||||
border-color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
select { |
|
||||||
height: 30px; |
|
||||||
} |
|
||||||
|
|
||||||
select[multiple] { |
|
||||||
min-height: 150px; |
|
||||||
} |
|
||||||
|
|
||||||
/* FORM BUTTONS */ |
|
||||||
|
|
||||||
.button, input[type=submit], input[type=button], .submit-row input, a.button { |
|
||||||
background: #79aec8; |
|
||||||
padding: 10px 15px; |
|
||||||
border: none; |
|
||||||
border-radius: 4px; |
|
||||||
color: #fff; |
|
||||||
cursor: pointer; |
|
||||||
} |
|
||||||
|
|
||||||
a.button { |
|
||||||
padding: 4px 5px; |
|
||||||
} |
|
||||||
|
|
||||||
.button:active, input[type=submit]:active, input[type=button]:active, |
|
||||||
.button:focus, input[type=submit]:focus, input[type=button]:focus, |
|
||||||
.button:hover, input[type=submit]:hover, input[type=button]:hover { |
|
||||||
background: #609ab6; |
|
||||||
} |
|
||||||
|
|
||||||
.button[disabled], input[type=submit][disabled], input[type=button][disabled] { |
|
||||||
opacity: 0.4; |
|
||||||
} |
|
||||||
|
|
||||||
.button.default, input[type=submit].default, .submit-row input.default { |
|
||||||
float: right; |
|
||||||
border: none; |
|
||||||
font-weight: 400; |
|
||||||
background: #417690; |
|
||||||
} |
|
||||||
|
|
||||||
.button.default:active, input[type=submit].default:active, |
|
||||||
.button.default:focus, input[type=submit].default:focus, |
|
||||||
.button.default:hover, input[type=submit].default:hover { |
|
||||||
background: #205067; |
|
||||||
} |
|
||||||
|
|
||||||
.button[disabled].default, |
|
||||||
input[type=submit][disabled].default, |
|
||||||
input[type=button][disabled].default { |
|
||||||
opacity: 0.4; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/* MODULES */ |
|
||||||
|
|
||||||
.module { |
|
||||||
border: none; |
|
||||||
margin-bottom: 30px; |
|
||||||
background: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.module p, .module ul, .module h3, .module h4, .module dl, .module pre { |
|
||||||
padding-left: 10px; |
|
||||||
padding-right: 10px; |
|
||||||
} |
|
||||||
|
|
||||||
.module blockquote { |
|
||||||
margin-left: 12px; |
|
||||||
} |
|
||||||
|
|
||||||
.module ul, .module ol { |
|
||||||
margin-left: 1.5em; |
|
||||||
} |
|
||||||
|
|
||||||
.module h3 { |
|
||||||
margin-top: .6em; |
|
||||||
} |
|
||||||
|
|
||||||
.module h2, .module caption, .inline-group h2 { |
|
||||||
margin: 0; |
|
||||||
padding: 8px; |
|
||||||
font-weight: 400; |
|
||||||
font-size: 13px; |
|
||||||
text-align: left; |
|
||||||
background: #79aec8; |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.module caption, |
|
||||||
.inline-group h2 { |
|
||||||
font-size: 12px; |
|
||||||
letter-spacing: 0.5px; |
|
||||||
text-transform: uppercase; |
|
||||||
} |
|
||||||
|
|
||||||
.module table { |
|
||||||
border-collapse: collapse; |
|
||||||
} |
|
||||||
|
|
||||||
/* MESSAGES & ERRORS */ |
|
||||||
|
|
||||||
ul.messagelist { |
|
||||||
padding: 0; |
|
||||||
margin: 0; |
|
||||||
} |
|
||||||
|
|
||||||
ul.messagelist li { |
|
||||||
display: block; |
|
||||||
font-weight: 400; |
|
||||||
font-size: 13px; |
|
||||||
padding: 10px 10px 10px 65px; |
|
||||||
margin: 0 0 10px 0; |
|
||||||
background: #dfd url(../img/icon-yes.svg) 40px 12px no-repeat; |
|
||||||
background-size: 16px auto; |
|
||||||
color: #333; |
|
||||||
} |
|
||||||
|
|
||||||
ul.messagelist li.warning { |
|
||||||
background: #ffc url(../img/icon-alert.svg) 40px 14px no-repeat; |
|
||||||
background-size: 14px auto; |
|
||||||
} |
|
||||||
|
|
||||||
ul.messagelist li.error { |
|
||||||
background: #ffefef url(../img/icon-no.svg) 40px 12px no-repeat; |
|
||||||
background-size: 16px auto; |
|
||||||
} |
|
||||||
|
|
||||||
.errornote { |
|
||||||
font-size: 14px; |
|
||||||
font-weight: 700; |
|
||||||
display: block; |
|
||||||
padding: 10px 12px; |
|
||||||
margin: 0 0 10px 0; |
|
||||||
color: #ba2121; |
|
||||||
border: 1px solid #ba2121; |
|
||||||
border-radius: 4px; |
|
||||||
background-color: #fff; |
|
||||||
background-position: 5px 12px; |
|
||||||
} |
|
||||||
|
|
||||||
ul.errorlist { |
|
||||||
margin: 0 0 4px; |
|
||||||
padding: 0; |
|
||||||
color: #ba2121; |
|
||||||
background: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
ul.errorlist li { |
|
||||||
font-size: 13px; |
|
||||||
display: block; |
|
||||||
margin-bottom: 4px; |
|
||||||
} |
|
||||||
|
|
||||||
ul.errorlist li:first-child { |
|
||||||
margin-top: 0; |
|
||||||
} |
|
||||||
|
|
||||||
ul.errorlist li a { |
|
||||||
color: inherit; |
|
||||||
text-decoration: underline; |
|
||||||
} |
|
||||||
|
|
||||||
td ul.errorlist { |
|
||||||
margin: 0; |
|
||||||
padding: 0; |
|
||||||
} |
|
||||||
|
|
||||||
td ul.errorlist li { |
|
||||||
margin: 0; |
|
||||||
} |
|
||||||
|
|
||||||
.form-row.errors { |
|
||||||
margin: 0; |
|
||||||
border: none; |
|
||||||
border-bottom: 1px solid #eee; |
|
||||||
background: none; |
|
||||||
} |
|
||||||
|
|
||||||
.form-row.errors ul.errorlist li { |
|
||||||
padding-left: 0; |
|
||||||
} |
|
||||||
|
|
||||||
.errors input, .errors select, .errors textarea { |
|
||||||
border: 1px solid #ba2121; |
|
||||||
} |
|
||||||
|
|
||||||
div.system-message { |
|
||||||
background: #ffc; |
|
||||||
margin: 10px; |
|
||||||
padding: 6px 8px; |
|
||||||
font-size: .8em; |
|
||||||
} |
|
||||||
|
|
||||||
div.system-message p.system-message-title { |
|
||||||
padding: 4px 5px 4px 25px; |
|
||||||
margin: 0; |
|
||||||
color: #c11; |
|
||||||
background: #ffefef url(../img/icon-no.svg) 5px 5px no-repeat; |
|
||||||
} |
|
||||||
|
|
||||||
.description { |
|
||||||
font-size: 12px; |
|
||||||
padding: 5px 0 0 12px; |
|
||||||
} |
|
||||||
|
|
||||||
/* BREADCRUMBS */ |
|
||||||
|
|
||||||
div.breadcrumbs { |
|
||||||
background: #79aec8; |
|
||||||
padding: 10px 40px; |
|
||||||
border: none; |
|
||||||
font-size: 14px; |
|
||||||
color: #c4dce8; |
|
||||||
text-align: left; |
|
||||||
} |
|
||||||
|
|
||||||
div.breadcrumbs a { |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
div.breadcrumbs a:focus, div.breadcrumbs a:hover { |
|
||||||
color: #c4dce8; |
|
||||||
} |
|
||||||
|
|
||||||
/* ACTION ICONS */ |
|
||||||
|
|
||||||
.addlink { |
|
||||||
padding-left: 16px; |
|
||||||
background: url(../img/icon-addlink.svg) 0 1px no-repeat; |
|
||||||
} |
|
||||||
|
|
||||||
.changelink, .inlinechangelink { |
|
||||||
padding-left: 16px; |
|
||||||
background: url(../img/icon-changelink.svg) 0 1px no-repeat; |
|
||||||
} |
|
||||||
|
|
||||||
.deletelink { |
|
||||||
padding-left: 16px; |
|
||||||
background: url(../img/icon-deletelink.svg) 0 1px no-repeat; |
|
||||||
} |
|
||||||
|
|
||||||
a.deletelink:link, a.deletelink:visited { |
|
||||||
color: #CC3434; |
|
||||||
} |
|
||||||
|
|
||||||
a.deletelink:focus, a.deletelink:hover { |
|
||||||
color: #993333; |
|
||||||
text-decoration: none; |
|
||||||
} |
|
||||||
|
|
||||||
/* OBJECT TOOLS */ |
|
||||||
|
|
||||||
.object-tools { |
|
||||||
font-size: 10px; |
|
||||||
font-weight: bold; |
|
||||||
padding-left: 0; |
|
||||||
float: right; |
|
||||||
position: relative; |
|
||||||
margin-top: -48px; |
|
||||||
} |
|
||||||
|
|
||||||
.form-row .object-tools { |
|
||||||
margin-top: 5px; |
|
||||||
margin-bottom: 5px; |
|
||||||
float: none; |
|
||||||
height: 2em; |
|
||||||
padding-left: 3.5em; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools li { |
|
||||||
display: block; |
|
||||||
float: left; |
|
||||||
margin-left: 5px; |
|
||||||
height: 16px; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a { |
|
||||||
border-radius: 15px; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a:link, .object-tools a:visited { |
|
||||||
display: block; |
|
||||||
float: left; |
|
||||||
padding: 3px 12px; |
|
||||||
background: #999; |
|
||||||
font-weight: 400; |
|
||||||
font-size: 11px; |
|
||||||
text-transform: uppercase; |
|
||||||
letter-spacing: 0.5px; |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a:focus, .object-tools a:hover { |
|
||||||
background-color: #417690; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a:focus{ |
|
||||||
text-decoration: none; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a.viewsitelink, .object-tools a.golink,.object-tools a.addlink { |
|
||||||
background-repeat: no-repeat; |
|
||||||
background-position: 93% center; |
|
||||||
padding-right: 26px; |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a.viewsitelink, .object-tools a.golink { |
|
||||||
background-image: url(../img/tooltag-arrowright.svg); |
|
||||||
} |
|
||||||
|
|
||||||
.object-tools a.addlink { |
|
||||||
background-image: url(../img/tooltag-add.svg); |
|
||||||
} |
|
||||||
|
|
||||||
/* OBJECT HISTORY */ |
|
||||||
|
|
||||||
table#change-history { |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
table#change-history tbody th { |
|
||||||
width: 16em; |
|
||||||
} |
|
||||||
|
|
||||||
/* PAGE STRUCTURE */ |
|
||||||
|
|
||||||
#container { |
|
||||||
position: relative; |
|
||||||
width: 100%; |
|
||||||
min-width: 980px; |
|
||||||
padding: 0; |
|
||||||
} |
|
||||||
|
|
||||||
#content { |
|
||||||
padding: 20px 40px; |
|
||||||
} |
|
||||||
|
|
||||||
.dashboard #content { |
|
||||||
width: 600px; |
|
||||||
} |
|
||||||
|
|
||||||
#content-main { |
|
||||||
float: left; |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related { |
|
||||||
float: right; |
|
||||||
width: 260px; |
|
||||||
position: relative; |
|
||||||
margin-right: -300px; |
|
||||||
} |
|
||||||
|
|
||||||
#footer { |
|
||||||
clear: both; |
|
||||||
padding: 10px; |
|
||||||
} |
|
||||||
|
|
||||||
/* COLUMN TYPES */ |
|
||||||
|
|
||||||
.colMS { |
|
||||||
margin-right: 300px; |
|
||||||
} |
|
||||||
|
|
||||||
.colSM { |
|
||||||
margin-left: 300px; |
|
||||||
} |
|
||||||
|
|
||||||
.colSM #content-related { |
|
||||||
float: left; |
|
||||||
margin-right: 0; |
|
||||||
margin-left: -300px; |
|
||||||
} |
|
||||||
|
|
||||||
.colSM #content-main { |
|
||||||
float: right; |
|
||||||
} |
|
||||||
|
|
||||||
.popup .colM { |
|
||||||
width: auto; |
|
||||||
} |
|
||||||
|
|
||||||
/* HEADER */ |
|
||||||
|
|
||||||
#header { |
|
||||||
width: auto; |
|
||||||
height: 40px; |
|
||||||
padding: 10px 40px; |
|
||||||
background: #417690; |
|
||||||
line-height: 40px; |
|
||||||
color: #ffc; |
|
||||||
overflow: hidden; |
|
||||||
} |
|
||||||
|
|
||||||
#header a:link, #header a:visited { |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
#header a:focus , #header a:hover { |
|
||||||
text-decoration: underline; |
|
||||||
} |
|
||||||
|
|
||||||
#branding { |
|
||||||
float: left; |
|
||||||
} |
|
||||||
|
|
||||||
#branding h1 { |
|
||||||
padding: 0; |
|
||||||
margin: 0 20px 0 0; |
|
||||||
font-weight: 300; |
|
||||||
font-size: 24px; |
|
||||||
color: #f5dd5d; |
|
||||||
} |
|
||||||
|
|
||||||
#branding h1, #branding h1 a:link, #branding h1 a:visited { |
|
||||||
color: #f5dd5d; |
|
||||||
} |
|
||||||
|
|
||||||
#branding h2 { |
|
||||||
padding: 0 10px; |
|
||||||
font-size: 14px; |
|
||||||
margin: -8px 0 8px 0; |
|
||||||
font-weight: normal; |
|
||||||
color: #ffc; |
|
||||||
} |
|
||||||
|
|
||||||
#branding a:hover { |
|
||||||
text-decoration: none; |
|
||||||
} |
|
||||||
|
|
||||||
#user-tools { |
|
||||||
float: right; |
|
||||||
padding: 0; |
|
||||||
margin: 0 0 0 20px; |
|
||||||
font-weight: 300; |
|
||||||
font-size: 11px; |
|
||||||
letter-spacing: 0.5px; |
|
||||||
text-transform: uppercase; |
|
||||||
text-align: right; |
|
||||||
} |
|
||||||
|
|
||||||
#user-tools a { |
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.25); |
|
||||||
} |
|
||||||
|
|
||||||
#user-tools a:focus, #user-tools a:hover { |
|
||||||
text-decoration: none; |
|
||||||
border-bottom-color: #79aec8; |
|
||||||
color: #79aec8; |
|
||||||
} |
|
||||||
|
|
||||||
/* SIDEBAR */ |
|
||||||
|
|
||||||
#content-related { |
|
||||||
background: #f8f8f8; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related .module { |
|
||||||
background: none; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related h3 { |
|
||||||
font-size: 14px; |
|
||||||
color: #666; |
|
||||||
padding: 0 16px; |
|
||||||
margin: 0 0 16px; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related h4 { |
|
||||||
font-size: 13px; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related p { |
|
||||||
padding-left: 16px; |
|
||||||
padding-right: 16px; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related .actionlist { |
|
||||||
padding: 0; |
|
||||||
margin: 16px; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related .actionlist li { |
|
||||||
line-height: 1.2; |
|
||||||
margin-bottom: 10px; |
|
||||||
padding-left: 18px; |
|
||||||
} |
|
||||||
|
|
||||||
#content-related .module h2 { |
|
||||||
background: none; |
|
||||||
padding: 16px; |
|
||||||
margin-bottom: 16px; |
|
||||||
border-bottom: 1px solid #eaeaea; |
|
||||||
font-size: 18px; |
|
||||||
color: #333; |
|
||||||
} |
|
||||||
|
|
||||||
.delete-confirmation form input[type="submit"] { |
|
||||||
background: #ba2121; |
|
||||||
border-radius: 4px; |
|
||||||
padding: 10px 15px; |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.delete-confirmation form input[type="submit"]:active, |
|
||||||
.delete-confirmation form input[type="submit"]:focus, |
|
||||||
.delete-confirmation form input[type="submit"]:hover { |
|
||||||
background: #a41515; |
|
||||||
} |
|
||||||
|
|
||||||
.delete-confirmation form .cancel-link { |
|
||||||
display: inline-block; |
|
||||||
vertical-align: middle; |
|
||||||
height: 15px; |
|
||||||
line-height: 15px; |
|
||||||
background: #ddd; |
|
||||||
border-radius: 4px; |
|
||||||
padding: 10px 15px; |
|
||||||
color: #333; |
|
||||||
margin: 0 0 0 10px; |
|
||||||
} |
|
||||||
|
|
||||||
.delete-confirmation form .cancel-link:active, |
|
||||||
.delete-confirmation form .cancel-link:focus, |
|
||||||
.delete-confirmation form .cancel-link:hover { |
|
||||||
background: #ccc; |
|
||||||
} |
|
||||||
|
|
||||||
/* POPUP */ |
|
||||||
.popup #content { |
|
||||||
padding: 20px; |
|
||||||
} |
|
||||||
|
|
||||||
.popup #container { |
|
||||||
min-width: 0; |
|
||||||
} |
|
||||||
|
|
||||||
.popup #header { |
|
||||||
padding: 10px 20px; |
|
||||||
} |
|
||||||
Binary file not shown.
@ -1,342 +0,0 @@ |
|||||||
/* CHANGELISTS */ |
|
||||||
|
|
||||||
#changelist { |
|
||||||
position: relative; |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist table { |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list .hiddenfields { display:none; } |
|
||||||
|
|
||||||
.change-list .filtered table { |
|
||||||
border-right: none; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list .filtered { |
|
||||||
min-height: 400px; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list .filtered .results, .change-list .filtered .paginator, |
|
||||||
.filtered #toolbar, .filtered div.xfull { |
|
||||||
margin-right: 280px; |
|
||||||
width: auto; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list .filtered table tbody th { |
|
||||||
padding-right: 1em; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-form .results { |
|
||||||
overflow-x: auto; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .toplinks { |
|
||||||
border-bottom: 1px solid #ddd; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .paginator { |
|
||||||
color: #666; |
|
||||||
border-bottom: 1px solid #eee; |
|
||||||
background: #fff; |
|
||||||
overflow: hidden; |
|
||||||
} |
|
||||||
|
|
||||||
/* CHANGELIST TABLES */ |
|
||||||
|
|
||||||
#changelist table thead th { |
|
||||||
padding: 0; |
|
||||||
white-space: nowrap; |
|
||||||
vertical-align: middle; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist table thead th.action-checkbox-column { |
|
||||||
width: 1.5em; |
|
||||||
text-align: center; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist table tbody td.action-checkbox { |
|
||||||
text-align: center; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist table tfoot { |
|
||||||
color: #666; |
|
||||||
} |
|
||||||
|
|
||||||
/* TOOLBAR */ |
|
||||||
|
|
||||||
#changelist #toolbar { |
|
||||||
padding: 8px 10px; |
|
||||||
margin-bottom: 15px; |
|
||||||
border-top: 1px solid #eee; |
|
||||||
border-bottom: 1px solid #eee; |
|
||||||
background: #f8f8f8; |
|
||||||
color: #666; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist #toolbar form input { |
|
||||||
border-radius: 4px; |
|
||||||
font-size: 14px; |
|
||||||
padding: 5px; |
|
||||||
color: #333; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist #toolbar form #searchbar { |
|
||||||
height: 19px; |
|
||||||
border: 1px solid #ccc; |
|
||||||
padding: 2px 5px; |
|
||||||
margin: 0; |
|
||||||
vertical-align: top; |
|
||||||
font-size: 13px; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist #toolbar form #searchbar:focus { |
|
||||||
border-color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist #toolbar form input[type="submit"] { |
|
||||||
border: 1px solid #ccc; |
|
||||||
padding: 2px 10px; |
|
||||||
margin: 0; |
|
||||||
vertical-align: middle; |
|
||||||
background: #fff; |
|
||||||
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset; |
|
||||||
cursor: pointer; |
|
||||||
color: #333; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist #toolbar form input[type="submit"]:focus, |
|
||||||
#changelist #toolbar form input[type="submit"]:hover { |
|
||||||
border-color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist #changelist-search img { |
|
||||||
vertical-align: middle; |
|
||||||
margin-right: 4px; |
|
||||||
} |
|
||||||
|
|
||||||
/* FILTER COLUMN */ |
|
||||||
|
|
||||||
#changelist-filter { |
|
||||||
position: absolute; |
|
||||||
top: 0; |
|
||||||
right: 0; |
|
||||||
z-index: 1000; |
|
||||||
width: 240px; |
|
||||||
background: #f8f8f8; |
|
||||||
border-left: none; |
|
||||||
margin: 0; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter h2 { |
|
||||||
font-size: 14px; |
|
||||||
text-transform: uppercase; |
|
||||||
letter-spacing: 0.5px; |
|
||||||
padding: 5px 15px; |
|
||||||
margin-bottom: 12px; |
|
||||||
border-bottom: none; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter h3 { |
|
||||||
font-weight: 400; |
|
||||||
font-size: 14px; |
|
||||||
padding: 0 15px; |
|
||||||
margin-bottom: 10px; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter ul { |
|
||||||
margin: 5px 0; |
|
||||||
padding: 0 15px 15px; |
|
||||||
border-bottom: 1px solid #eaeaea; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter ul:last-child { |
|
||||||
border-bottom: none; |
|
||||||
padding-bottom: none; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter li { |
|
||||||
list-style-type: none; |
|
||||||
margin-left: 0; |
|
||||||
padding-left: 0; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter a { |
|
||||||
display: block; |
|
||||||
color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter li.selected { |
|
||||||
border-left: 5px solid #eaeaea; |
|
||||||
padding-left: 10px; |
|
||||||
margin-left: -15px; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter li.selected a { |
|
||||||
color: #5b80b2; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter a:focus, #changelist-filter a:hover, |
|
||||||
#changelist-filter li.selected a:focus, |
|
||||||
#changelist-filter li.selected a:hover { |
|
||||||
color: #036; |
|
||||||
} |
|
||||||
|
|
||||||
/* DATE DRILLDOWN */ |
|
||||||
|
|
||||||
.change-list ul.toplinks { |
|
||||||
display: block; |
|
||||||
float: left; |
|
||||||
padding: 0; |
|
||||||
margin: 0; |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list ul.toplinks li { |
|
||||||
padding: 3px 6px; |
|
||||||
font-weight: bold; |
|
||||||
list-style-type: none; |
|
||||||
display: inline-block; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list ul.toplinks .date-back a { |
|
||||||
color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list ul.toplinks .date-back a:focus, |
|
||||||
.change-list ul.toplinks .date-back a:hover { |
|
||||||
color: #036; |
|
||||||
} |
|
||||||
|
|
||||||
/* PAGINATOR */ |
|
||||||
|
|
||||||
.paginator { |
|
||||||
font-size: 13px; |
|
||||||
padding-top: 10px; |
|
||||||
padding-bottom: 10px; |
|
||||||
line-height: 22px; |
|
||||||
margin: 0; |
|
||||||
border-top: 1px solid #ddd; |
|
||||||
} |
|
||||||
|
|
||||||
.paginator a:link, .paginator a:visited { |
|
||||||
padding: 2px 6px; |
|
||||||
background: #79aec8; |
|
||||||
text-decoration: none; |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.paginator a.showall { |
|
||||||
padding: 0; |
|
||||||
border: none; |
|
||||||
background: none; |
|
||||||
color: #5b80b2; |
|
||||||
} |
|
||||||
|
|
||||||
.paginator a.showall:focus, .paginator a.showall:hover { |
|
||||||
background: none; |
|
||||||
color: #036; |
|
||||||
} |
|
||||||
|
|
||||||
.paginator .end { |
|
||||||
margin-right: 6px; |
|
||||||
} |
|
||||||
|
|
||||||
.paginator .this-page { |
|
||||||
padding: 2px 6px; |
|
||||||
font-weight: bold; |
|
||||||
font-size: 13px; |
|
||||||
vertical-align: top; |
|
||||||
} |
|
||||||
|
|
||||||
.paginator a:focus, .paginator a:hover { |
|
||||||
color: white; |
|
||||||
background: #036; |
|
||||||
} |
|
||||||
|
|
||||||
/* ACTIONS */ |
|
||||||
|
|
||||||
.filtered .actions { |
|
||||||
margin-right: 280px; |
|
||||||
border-right: none; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist table input { |
|
||||||
margin: 0; |
|
||||||
vertical-align: baseline; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist table tbody tr.selected { |
|
||||||
background-color: #FFFFCC; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions { |
|
||||||
padding: 10px; |
|
||||||
background: #fff; |
|
||||||
border-top: none; |
|
||||||
border-bottom: none; |
|
||||||
line-height: 24px; |
|
||||||
color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions.selected { |
|
||||||
background: #fffccf; |
|
||||||
border-top: 1px solid #fffee8; |
|
||||||
border-bottom: 1px solid #edecd6; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions span.all, |
|
||||||
#changelist .actions span.action-counter, |
|
||||||
#changelist .actions span.clear, |
|
||||||
#changelist .actions span.question { |
|
||||||
font-size: 13px; |
|
||||||
margin: 0 0.5em; |
|
||||||
display: none; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions:last-child { |
|
||||||
border-bottom: none; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions select { |
|
||||||
vertical-align: top; |
|
||||||
height: 24px; |
|
||||||
background: none; |
|
||||||
color: #000; |
|
||||||
border: 1px solid #ccc; |
|
||||||
border-radius: 4px; |
|
||||||
font-size: 14px; |
|
||||||
padding: 0 0 0 4px; |
|
||||||
margin: 0; |
|
||||||
margin-left: 10px; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions select:focus { |
|
||||||
border-color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions label { |
|
||||||
display: inline-block; |
|
||||||
vertical-align: middle; |
|
||||||
font-size: 13px; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions .button { |
|
||||||
font-size: 13px; |
|
||||||
border: 1px solid #ccc; |
|
||||||
border-radius: 4px; |
|
||||||
background: #fff; |
|
||||||
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset; |
|
||||||
cursor: pointer; |
|
||||||
height: 24px; |
|
||||||
line-height: 1; |
|
||||||
padding: 4px 8px; |
|
||||||
margin: 0; |
|
||||||
color: #333; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions .button:focus, #changelist .actions .button:hover { |
|
||||||
border-color: #999; |
|
||||||
} |
|
||||||
Binary file not shown.
@ -1,342 +0,0 @@ |
|||||||
/* CHANGELISTS */ |
|
||||||
|
|
||||||
#changelist { |
|
||||||
position: relative; |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist table { |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list .hiddenfields { display:none; } |
|
||||||
|
|
||||||
.change-list .filtered table { |
|
||||||
border-right: none; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list .filtered { |
|
||||||
min-height: 400px; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list .filtered .results, .change-list .filtered .paginator, |
|
||||||
.filtered #toolbar, .filtered div.xfull { |
|
||||||
margin-right: 280px; |
|
||||||
width: auto; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list .filtered table tbody th { |
|
||||||
padding-right: 1em; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-form .results { |
|
||||||
overflow-x: auto; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .toplinks { |
|
||||||
border-bottom: 1px solid #ddd; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .paginator { |
|
||||||
color: #666; |
|
||||||
border-bottom: 1px solid #eee; |
|
||||||
background: #fff; |
|
||||||
overflow: hidden; |
|
||||||
} |
|
||||||
|
|
||||||
/* CHANGELIST TABLES */ |
|
||||||
|
|
||||||
#changelist table thead th { |
|
||||||
padding: 0; |
|
||||||
white-space: nowrap; |
|
||||||
vertical-align: middle; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist table thead th.action-checkbox-column { |
|
||||||
width: 1.5em; |
|
||||||
text-align: center; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist table tbody td.action-checkbox { |
|
||||||
text-align: center; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist table tfoot { |
|
||||||
color: #666; |
|
||||||
} |
|
||||||
|
|
||||||
/* TOOLBAR */ |
|
||||||
|
|
||||||
#changelist #toolbar { |
|
||||||
padding: 8px 10px; |
|
||||||
margin-bottom: 15px; |
|
||||||
border-top: 1px solid #eee; |
|
||||||
border-bottom: 1px solid #eee; |
|
||||||
background: #f8f8f8; |
|
||||||
color: #666; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist #toolbar form input { |
|
||||||
border-radius: 4px; |
|
||||||
font-size: 14px; |
|
||||||
padding: 5px; |
|
||||||
color: #333; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist #toolbar form #searchbar { |
|
||||||
height: 19px; |
|
||||||
border: 1px solid #ccc; |
|
||||||
padding: 2px 5px; |
|
||||||
margin: 0; |
|
||||||
vertical-align: top; |
|
||||||
font-size: 13px; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist #toolbar form #searchbar:focus { |
|
||||||
border-color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist #toolbar form input[type="submit"] { |
|
||||||
border: 1px solid #ccc; |
|
||||||
padding: 2px 10px; |
|
||||||
margin: 0; |
|
||||||
vertical-align: middle; |
|
||||||
background: #fff; |
|
||||||
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset; |
|
||||||
cursor: pointer; |
|
||||||
color: #333; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist #toolbar form input[type="submit"]:focus, |
|
||||||
#changelist #toolbar form input[type="submit"]:hover { |
|
||||||
border-color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist #changelist-search img { |
|
||||||
vertical-align: middle; |
|
||||||
margin-right: 4px; |
|
||||||
} |
|
||||||
|
|
||||||
/* FILTER COLUMN */ |
|
||||||
|
|
||||||
#changelist-filter { |
|
||||||
position: absolute; |
|
||||||
top: 0; |
|
||||||
right: 0; |
|
||||||
z-index: 1000; |
|
||||||
width: 240px; |
|
||||||
background: #f8f8f8; |
|
||||||
border-left: none; |
|
||||||
margin: 0; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter h2 { |
|
||||||
font-size: 14px; |
|
||||||
text-transform: uppercase; |
|
||||||
letter-spacing: 0.5px; |
|
||||||
padding: 5px 15px; |
|
||||||
margin-bottom: 12px; |
|
||||||
border-bottom: none; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter h3 { |
|
||||||
font-weight: 400; |
|
||||||
font-size: 14px; |
|
||||||
padding: 0 15px; |
|
||||||
margin-bottom: 10px; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter ul { |
|
||||||
margin: 5px 0; |
|
||||||
padding: 0 15px 15px; |
|
||||||
border-bottom: 1px solid #eaeaea; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter ul:last-child { |
|
||||||
border-bottom: none; |
|
||||||
padding-bottom: none; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter li { |
|
||||||
list-style-type: none; |
|
||||||
margin-left: 0; |
|
||||||
padding-left: 0; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter a { |
|
||||||
display: block; |
|
||||||
color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter li.selected { |
|
||||||
border-left: 5px solid #eaeaea; |
|
||||||
padding-left: 10px; |
|
||||||
margin-left: -15px; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter li.selected a { |
|
||||||
color: #5b80b2; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist-filter a:focus, #changelist-filter a:hover, |
|
||||||
#changelist-filter li.selected a:focus, |
|
||||||
#changelist-filter li.selected a:hover { |
|
||||||
color: #036; |
|
||||||
} |
|
||||||
|
|
||||||
/* DATE DRILLDOWN */ |
|
||||||
|
|
||||||
.change-list ul.toplinks { |
|
||||||
display: block; |
|
||||||
float: left; |
|
||||||
padding: 0; |
|
||||||
margin: 0; |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list ul.toplinks li { |
|
||||||
padding: 3px 6px; |
|
||||||
font-weight: bold; |
|
||||||
list-style-type: none; |
|
||||||
display: inline-block; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list ul.toplinks .date-back a { |
|
||||||
color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
.change-list ul.toplinks .date-back a:focus, |
|
||||||
.change-list ul.toplinks .date-back a:hover { |
|
||||||
color: #036; |
|
||||||
} |
|
||||||
|
|
||||||
/* PAGINATOR */ |
|
||||||
|
|
||||||
.paginator { |
|
||||||
font-size: 13px; |
|
||||||
padding-top: 10px; |
|
||||||
padding-bottom: 10px; |
|
||||||
line-height: 22px; |
|
||||||
margin: 0; |
|
||||||
border-top: 1px solid #ddd; |
|
||||||
} |
|
||||||
|
|
||||||
.paginator a:link, .paginator a:visited { |
|
||||||
padding: 2px 6px; |
|
||||||
background: #79aec8; |
|
||||||
text-decoration: none; |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.paginator a.showall { |
|
||||||
padding: 0; |
|
||||||
border: none; |
|
||||||
background: none; |
|
||||||
color: #5b80b2; |
|
||||||
} |
|
||||||
|
|
||||||
.paginator a.showall:focus, .paginator a.showall:hover { |
|
||||||
background: none; |
|
||||||
color: #036; |
|
||||||
} |
|
||||||
|
|
||||||
.paginator .end { |
|
||||||
margin-right: 6px; |
|
||||||
} |
|
||||||
|
|
||||||
.paginator .this-page { |
|
||||||
padding: 2px 6px; |
|
||||||
font-weight: bold; |
|
||||||
font-size: 13px; |
|
||||||
vertical-align: top; |
|
||||||
} |
|
||||||
|
|
||||||
.paginator a:focus, .paginator a:hover { |
|
||||||
color: white; |
|
||||||
background: #036; |
|
||||||
} |
|
||||||
|
|
||||||
/* ACTIONS */ |
|
||||||
|
|
||||||
.filtered .actions { |
|
||||||
margin-right: 280px; |
|
||||||
border-right: none; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist table input { |
|
||||||
margin: 0; |
|
||||||
vertical-align: baseline; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist table tbody tr.selected { |
|
||||||
background-color: #FFFFCC; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions { |
|
||||||
padding: 10px; |
|
||||||
background: #fff; |
|
||||||
border-top: none; |
|
||||||
border-bottom: none; |
|
||||||
line-height: 24px; |
|
||||||
color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions.selected { |
|
||||||
background: #fffccf; |
|
||||||
border-top: 1px solid #fffee8; |
|
||||||
border-bottom: 1px solid #edecd6; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions span.all, |
|
||||||
#changelist .actions span.action-counter, |
|
||||||
#changelist .actions span.clear, |
|
||||||
#changelist .actions span.question { |
|
||||||
font-size: 13px; |
|
||||||
margin: 0 0.5em; |
|
||||||
display: none; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions:last-child { |
|
||||||
border-bottom: none; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions select { |
|
||||||
vertical-align: top; |
|
||||||
height: 24px; |
|
||||||
background: none; |
|
||||||
color: #000; |
|
||||||
border: 1px solid #ccc; |
|
||||||
border-radius: 4px; |
|
||||||
font-size: 14px; |
|
||||||
padding: 0 0 0 4px; |
|
||||||
margin: 0; |
|
||||||
margin-left: 10px; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions select:focus { |
|
||||||
border-color: #999; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions label { |
|
||||||
display: inline-block; |
|
||||||
vertical-align: middle; |
|
||||||
font-size: 13px; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions .button { |
|
||||||
font-size: 13px; |
|
||||||
border: 1px solid #ccc; |
|
||||||
border-radius: 4px; |
|
||||||
background: #fff; |
|
||||||
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset; |
|
||||||
cursor: pointer; |
|
||||||
height: 24px; |
|
||||||
line-height: 1; |
|
||||||
padding: 4px 8px; |
|
||||||
margin: 0; |
|
||||||
color: #333; |
|
||||||
} |
|
||||||
|
|
||||||
#changelist .actions .button:focus, #changelist .actions .button:hover { |
|
||||||
border-color: #999; |
|
||||||
} |
|
||||||
Binary file not shown.
@ -1,30 +0,0 @@ |
|||||||
/* DASHBOARD */ |
|
||||||
|
|
||||||
.dashboard .module table th { |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
.dashboard .module table td { |
|
||||||
white-space: nowrap; |
|
||||||
} |
|
||||||
|
|
||||||
.dashboard .module table td a { |
|
||||||
display: block; |
|
||||||
padding-right: .6em; |
|
||||||
} |
|
||||||
|
|
||||||
/* RECENT ACTIONS MODULE */ |
|
||||||
|
|
||||||
.module ul.actionlist { |
|
||||||
margin-left: 0; |
|
||||||
} |
|
||||||
|
|
||||||
ul.actionlist li { |
|
||||||
list-style-type: none; |
|
||||||
} |
|
||||||
|
|
||||||
ul.actionlist li { |
|
||||||
overflow: hidden; |
|
||||||
text-overflow: ellipsis; |
|
||||||
-o-text-overflow: ellipsis; |
|
||||||
} |
|
||||||
Binary file not shown.
@ -1,30 +0,0 @@ |
|||||||
/* DASHBOARD */ |
|
||||||
|
|
||||||
.dashboard .module table th { |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
.dashboard .module table td { |
|
||||||
white-space: nowrap; |
|
||||||
} |
|
||||||
|
|
||||||
.dashboard .module table td a { |
|
||||||
display: block; |
|
||||||
padding-right: .6em; |
|
||||||
} |
|
||||||
|
|
||||||
/* RECENT ACTIONS MODULE */ |
|
||||||
|
|
||||||
.module ul.actionlist { |
|
||||||
margin-left: 0; |
|
||||||
} |
|
||||||
|
|
||||||
ul.actionlist li { |
|
||||||
list-style-type: none; |
|
||||||
} |
|
||||||
|
|
||||||
ul.actionlist li { |
|
||||||
overflow: hidden; |
|
||||||
text-overflow: ellipsis; |
|
||||||
-o-text-overflow: ellipsis; |
|
||||||
} |
|
||||||
Binary file not shown.
@ -1,20 +0,0 @@ |
|||||||
@font-face { |
|
||||||
font-family: 'Roboto'; |
|
||||||
src: url("../fonts/Roboto-Bold-webfont.2ad99072841e.woff"); |
|
||||||
font-weight: 700; |
|
||||||
font-style: normal; |
|
||||||
} |
|
||||||
|
|
||||||
@font-face { |
|
||||||
font-family: 'Roboto'; |
|
||||||
src: url("../fonts/Roboto-Regular-webfont.ec39515ae8c6.woff"); |
|
||||||
font-weight: 400; |
|
||||||
font-style: normal; |
|
||||||
} |
|
||||||
|
|
||||||
@font-face { |
|
||||||
font-family: 'Roboto'; |
|
||||||
src: url("../fonts/Roboto-Light-webfont.b446c2399bb6.woff"); |
|
||||||
font-weight: 300; |
|
||||||
font-style: normal; |
|
||||||
} |
|
||||||
Binary file not shown.
@ -1,20 +0,0 @@ |
|||||||
@font-face { |
|
||||||
font-family: 'Roboto'; |
|
||||||
src: url('../fonts/Roboto-Bold-webfont.woff'); |
|
||||||
font-weight: 700; |
|
||||||
font-style: normal; |
|
||||||
} |
|
||||||
|
|
||||||
@font-face { |
|
||||||
font-family: 'Roboto'; |
|
||||||
src: url('../fonts/Roboto-Regular-webfont.woff'); |
|
||||||
font-weight: 400; |
|
||||||
font-style: normal; |
|
||||||
} |
|
||||||
|
|
||||||
@font-face { |
|
||||||
font-family: 'Roboto'; |
|
||||||
src: url('../fonts/Roboto-Light-webfont.woff'); |
|
||||||
font-weight: 300; |
|
||||||
font-style: normal; |
|
||||||
} |
|
||||||
Binary file not shown.
@ -1 +0,0 @@ |
|||||||
/* Empty CSS from Django Suit app to override original file */ |
|
||||||
@ -1 +0,0 @@ |
|||||||
/* Empty CSS from Django Suit app to override original file */ |
|
||||||
@ -1,78 +0,0 @@ |
|||||||
/* LOGIN FORM */ |
|
||||||
|
|
||||||
body.login { |
|
||||||
background: #f8f8f8; |
|
||||||
} |
|
||||||
|
|
||||||
.login #header { |
|
||||||
height: auto; |
|
||||||
padding: 5px 16px; |
|
||||||
} |
|
||||||
|
|
||||||
.login #header h1 { |
|
||||||
font-size: 18px; |
|
||||||
} |
|
||||||
|
|
||||||
.login #header h1 a { |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.login #content { |
|
||||||
padding: 20px 20px 0; |
|
||||||
} |
|
||||||
|
|
||||||
.login #container { |
|
||||||
background: #fff; |
|
||||||
border: 1px solid #eaeaea; |
|
||||||
border-radius: 4px; |
|
||||||
overflow: hidden; |
|
||||||
width: 28em; |
|
||||||
min-width: 300px; |
|
||||||
margin: 100px auto; |
|
||||||
} |
|
||||||
|
|
||||||
.login #content-main { |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
.login .form-row { |
|
||||||
padding: 4px 0; |
|
||||||
float: left; |
|
||||||
width: 100%; |
|
||||||
border-bottom: none; |
|
||||||
} |
|
||||||
|
|
||||||
.login .form-row label { |
|
||||||
padding-right: 0.5em; |
|
||||||
line-height: 2em; |
|
||||||
font-size: 1em; |
|
||||||
clear: both; |
|
||||||
color: #333; |
|
||||||
} |
|
||||||
|
|
||||||
.login .form-row #id_username, .login .form-row #id_password { |
|
||||||
clear: both; |
|
||||||
padding: 8px; |
|
||||||
width: 100%; |
|
||||||
-webkit-box-sizing: border-box; |
|
||||||
-moz-box-sizing: border-box; |
|
||||||
box-sizing: border-box; |
|
||||||
} |
|
||||||
|
|
||||||
.login span.help { |
|
||||||
font-size: 10px; |
|
||||||
display: block; |
|
||||||
} |
|
||||||
|
|
||||||
.login .submit-row { |
|
||||||
clear: both; |
|
||||||
padding: 1em 0 0 9.4em; |
|
||||||
margin: 0; |
|
||||||
border: none; |
|
||||||
background: none; |
|
||||||
text-align: left; |
|
||||||
} |
|
||||||
|
|
||||||
.login .password-reset-link { |
|
||||||
text-align: center; |
|
||||||
} |
|
||||||
Binary file not shown.
@ -1,78 +0,0 @@ |
|||||||
/* LOGIN FORM */ |
|
||||||
|
|
||||||
body.login { |
|
||||||
background: #f8f8f8; |
|
||||||
} |
|
||||||
|
|
||||||
.login #header { |
|
||||||
height: auto; |
|
||||||
padding: 5px 16px; |
|
||||||
} |
|
||||||
|
|
||||||
.login #header h1 { |
|
||||||
font-size: 18px; |
|
||||||
} |
|
||||||
|
|
||||||
.login #header h1 a { |
|
||||||
color: #fff; |
|
||||||
} |
|
||||||
|
|
||||||
.login #content { |
|
||||||
padding: 20px 20px 0; |
|
||||||
} |
|
||||||
|
|
||||||
.login #container { |
|
||||||
background: #fff; |
|
||||||
border: 1px solid #eaeaea; |
|
||||||
border-radius: 4px; |
|
||||||
overflow: hidden; |
|
||||||
width: 28em; |
|
||||||
min-width: 300px; |
|
||||||
margin: 100px auto; |
|
||||||
} |
|
||||||
|
|
||||||
.login #content-main { |
|
||||||
width: 100%; |
|
||||||
} |
|
||||||
|
|
||||||
.login .form-row { |
|
||||||
padding: 4px 0; |
|
||||||
float: left; |
|
||||||
width: 100%; |
|
||||||
border-bottom: none; |
|
||||||
} |
|
||||||
|
|
||||||
.login .form-row label { |
|
||||||
padding-right: 0.5em; |
|
||||||
line-height: 2em; |
|
||||||
font-size: 1em; |
|
||||||
clear: both; |
|
||||||
color: #333; |
|
||||||
} |
|
||||||
|
|
||||||
.login .form-row #id_username, .login .form-row #id_password { |
|
||||||
clear: both; |
|
||||||
padding: 8px; |
|
||||||
width: 100%; |
|
||||||
-webkit-box-sizing: border-box; |
|
||||||
-moz-box-sizing: border-box; |
|
||||||
box-sizing: border-box; |
|
||||||
} |
|
||||||
|
|
||||||
.login span.help { |
|
||||||
font-size: 10px; |
|
||||||
display: block; |
|
||||||
} |
|
||||||
|
|
||||||
.login .submit-row { |
|
||||||
clear: both; |
|
||||||
padding: 1em 0 0 9.4em; |
|
||||||
margin: 0; |
|
||||||
border: none; |
|
||||||
background: none; |
|
||||||
text-align: left; |
|
||||||
} |
|
||||||
|
|
||||||
.login .password-reset-link { |
|
||||||
text-align: center; |
|
||||||
} |
|
||||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue