Compare commits

...

8 Commits

48 changed files with 630 additions and 190 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@
k4ever/k4ever.db
k4ever/media/img/
k4ever/settings.py
k4ever/k4ever/settings.py
*.kdev4
.kdev4
*.kate-swp

22
README.piston Normal file
View File

@ -0,0 +1,22 @@
Django piston is pretty old and does not work with django1.11 out of the box.
For the transition these are the changes required to get piston working
Start out by creating a new virtualenv, install the requirements, then change the following lines:
In piston/emitters.py:
# instead of the original simplejson import
import json as simplejson
# instead of the DateTimeAwareJsonEncoder import
from django.core.serializers.json import DjangoJSONEncoder as DateTimeAwareJSONEncoder
# in ~186 replace the following line
for f in data._meta.fields + data._meta.virtual_fields])
# with this line
for f in data._meta.fields + tuple(data._meta.virtual_fields)])
In piston/resource.py
# in ~207 replace
resp = HttpResponse(stream, mimetype=ct, status=status_code)
# with this line
resp = HttpResponse(stream, content_type=ct, status=status_code)

View File

@ -10,7 +10,7 @@ from django.contrib.auth.models import User, Group
from piston.utils import rc
from main.models import Plugin, PluginPermission
from settings import PLUGIN_GROUP_ID
from k4ever.settings import PLUGIN_GROUP_ID
def manglePluginPerms(apiFunc):
""" Changes to a given user when the authenticated user is an plugin.

View File

@ -8,8 +8,8 @@
from easy_thumbnails.files import get_thumbnailer
from piston.handler import BaseHandler
from piston.utils import rc
from k4ever.buyable.models import *
from k4ever.transaction.models import *
from buyable.models import *
from transaction.models import *
from django.contrib.auth.decorators import user_passes_test
from django.contrib.auth.models import Group
from django.core.exceptions import MultipleObjectsReturned
@ -116,7 +116,7 @@ class BuyableItemHandler(BaseHandler):
p.save(saveOrder=False)
order.save()
return {'success': True, 'balance': request.user.get_profile().balance}
return {'success': True, 'balance': request.user.userprofile.balance}
def bulkBuy(self, request):
"""Buy a :class:`Buyable <buyable.models.Buyable>` item.
@ -179,7 +179,7 @@ class BuyableItemHandler(BaseHandler):
p.save(saveOrder=False)
order.save()
return {'success': True, 'balance': request.user.get_profile().balance}
return {'success': True, 'balance': request.user.userprofile.balance}
class BuyableTypeHandler(BaseHandler):
@ -290,7 +290,7 @@ class TransactionTransactHandler(BaseHandler):
return getError(rc.BAD_REQUEST, "Your TransactionType could not be found")
trans = Transaction(user=request.user, transactionType=tType, amount=amount, checked=not tType.needsCheck)
trans.save()
return {'success': True, 'balance': request.user.get_profile().balance}
return {'success': True, 'balance': request.user.userprofile.balance}
class TransactionTypeHandler(BaseHandler):
"""Handler for :class:`Transaction Types <transaction.models.TransactionType>`
@ -348,7 +348,7 @@ class TransactionVirtualHandler(BaseHandler):
return getError(rc.BAD_REQUEST, "The recipient user does not exist.")
trans = VirtualTransaction(user=request.user, recipient=recipient, amount=amount, comment=comment)
trans.save()
return {'success': True, 'balance': request.user.get_profile().balance}
return {'success': True, 'balance': request.user.userprofile.balance}
class AccountBalanceHandler(BaseHandler):
"""Handler for the user's account balance"""
@ -357,7 +357,7 @@ class AccountBalanceHandler(BaseHandler):
@manglePluginPerms
def read(self, request):
"""Returns the user's current account balance"""
balance = request.user.get_profile().balance
balance = request.user.userprofile.balance
return {'balance': balance}
class AuthBlobHandler(BaseHandler):

View File

@ -5,7 +5,7 @@
#
# Licensed under GNU Affero General Public License v3 or later
from django.conf.urls.defaults import *
from django.conf.urls import url, include
from piston.resource import Resource
from piston.authentication import HttpBasicAuthentication
from api2.authentication import DjangoAuthentication, MultiAuthentication
@ -47,7 +47,7 @@ authUserRes = CsrfExemptResource(handler=AuthUserHandler, **ad)
configRes = CsrfExemptResource(handler=ConfigHandler, **ad)
urlpatterns = patterns('',
urlpatterns = (
url(r'buyable/item/?$', buyableItemRes),
url(r'buyable/item/(?P<itemId>\d+)/?$', buyableItemRes),
url(r'buyable/item/bulkbuy/?$', buyableItemRes, {'bulkBuy': True}),

0
k4ever/api3/__init__.py Normal file
View File

6
k4ever/api3/admin.py Normal file
View File

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.contrib import admin
# Register your models here.

8
k4ever/api3/apps.py Normal file
View File

@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.apps import AppConfig
class Api3Config(AppConfig):
name = 'api3'

View File

6
k4ever/api3/models.py Normal file
View File

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.

View File

@ -0,0 +1,60 @@
from django.contrib.auth.models import User
from rest_framework import serializers
from buyable.models import BuyableType, Buyable, Order, Purchase
from transaction.models import TransactionType, Transaction, VirtualTransaction
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('id', 'username',)
class BuyableTypeSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = BuyableType
fields = ['url', 'id', 'name']
class BuyableSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Buyable
fields = ['url', 'id', 'name', 'description', 'price', 'barcode', 'deposit', 'image', 'buyableType']
class PurchaseSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Purchase
fields = ['id', 'price', 'isDeposit', 'buyable']
class OrderSerializer(serializers.HyperlinkedModelSerializer):
purchases = PurchaseSerializer(source='purchase_set', many=True)
class Meta:
model = Order
fields = ['url', 'id', 'price', 'dateTime', 'purchases']
class TransactionTypeSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = TransactionType
fields = ('url', 'id', 'name')
class TransactionSerializer(serializers.HyperlinkedModelSerializer):
transactionType = TransactionTypeSerializer()
class Meta:
model = Transaction
fields = ('url', 'id', 'amount', 'dateTime', 'checked', 'transactionType')
class VirtualTransactionSerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer()
recipient = UserSerializer()
class Meta:
model = VirtualTransaction
fields = ('url', 'id', 'amount', 'dateTime', 'comment', 'user', 'recipient')

6
k4ever/api3/tests.py Normal file
View File

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.test import TestCase
# Create your tests here.

20
k4ever/api3/urls.py Normal file
View File

@ -0,0 +1,20 @@
from django.conf.urls import include, url
from rest_framework import routers
from .views import BuyableTypeViewSet, BuyableViewSet, OrderViewSet
from .views import TransactionTypeViewSet, TransactionViewSet, VirtualTransactionViewSet
router = routers.DefaultRouter()
router.register('buyabletypes', BuyableTypeViewSet)
router.register('buyables', BuyableViewSet)
router.register('orders', OrderViewSet, basename='order')
router.register('transactiontypes', TransactionTypeViewSet)
router.register('transactions', TransactionViewSet, 'transaction')
router.register('virtualtransactions', VirtualTransactionViewSet, 'virtualtransaction')
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

55
k4ever/api3/views.py Normal file
View File

@ -0,0 +1,55 @@
from django.db.models import Q
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from .serializers import BuyableTypeSerializer, BuyableSerializer, OrderSerializer
from .serializers import TransactionTypeSerializer, TransactionSerializer, VirtualTransactionSerializer
from buyable.models import BuyableType, Buyable, Order
from transaction.models import TransactionType, Transaction, VirtualTransaction
class BuyableTypeViewSet(viewsets.ReadOnlyModelViewSet):
queryset = BuyableType.objects.all()
serializer_class = BuyableTypeSerializer
class BuyableViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Buyable.objects.all()
serializer_class = BuyableSerializer
class OrderViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = [IsAuthenticated]
serializer_class = OrderSerializer
def get_queryset(self):
return Order.objects.filter(user=self.request.user)
class TransactionTypeViewSet(viewsets.ReadOnlyModelViewSet):
queryset = TransactionType.objects.all()
serializer_class = TransactionTypeSerializer
class TransactionViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = [IsAuthenticated]
serializer_class = TransactionSerializer
def get_queryset(self):
return Transaction.objects.filter(user=self.request.user).order_by("-dateTime")
class VirtualTransactionViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = [IsAuthenticated]
serializer_class = VirtualTransactionSerializer
def get_queryset(self):
return (VirtualTransaction.objects.filter(Q(user=self.request.user) | Q(recipient=self.request.user))
.order_by("-dateTime"))
# class BalanceView(APIView):
# def get(self, request):
# return Response({'balance': request.user.userprofile.balance}
class BalanceViewSet(viewsets.ReadOnlyModelViewSet):
pass

View File

@ -15,6 +15,7 @@ class BuyableAdminForm(forms.ModelForm):
""" Special BuyableAdminForm which checks the buyable image for an 1:1 aspect ratio. """
class Meta:
model = Buyable
exclude = []
def clean_image(self):
img = self.cleaned_data['image']

View File

@ -1,4 +1,4 @@
from django.db.models.signals import post_syncdb
from django.db.models.signals import post_migrate
from django.core.management import commands, call_command
import buyable.models
@ -6,5 +6,5 @@ def createBuyableTypes(sender, app, created_models, **kwargs):
if buyable.models.BuyableType in created_models:
call_command("loaddata", "buyable_types")
post_syncdb.connect(createBuyableTypes, sender=buyable.models)
post_migrate.connect(createBuyableTypes, sender=buyable.models)

View File

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2018-10-24 01:07
from __future__ import unicode_literals
from decimal import Decimal
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Buyable',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('price', models.DecimalField(decimal_places=2, max_digits=8)),
('image', models.ImageField(help_text=b'<strong>The Image needs to have a 1:1 aspect ratio.</strong>', upload_to=b'img/buyable/')),
('deposit', models.DecimalField(decimal_places=2, max_digits=8)),
('description', models.TextField()),
('barcode', models.CharField(blank=True, default=b'', max_length=100)),
],
),
migrations.CreateModel(
name='BuyableType',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
],
),
migrations.CreateModel(
name='Order',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('price', models.DecimalField(decimal_places=2, default=Decimal('0'), max_digits=8)),
('dateTime', models.DateTimeField(auto_now_add=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Purchase',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('price', models.DecimalField(decimal_places=2, default=None, max_digits=8)),
('isDeposit', models.BooleanField()),
('buyable', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='buyable.Buyable')),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='buyable.Order')),
],
),
migrations.AddField(
model_name='buyable',
name='buyableType',
field=models.ManyToManyField(to='buyable.BuyableType'),
),
]

View File

View File

@ -79,7 +79,7 @@ class Order(models.Model):
return l.rstrip(u", ")
def save(self, *args, **kwargs):
profile = self.user.get_profile()
profile = self.user.userprofile
if self.id == None:
# new item, get it!
profile.balance -= self.price
@ -88,12 +88,12 @@ class Order(models.Model):
# get old
oldobj = Order.objects.get(id=self.id)
if oldobj.user == self.user:
profile = self.user.get_profile()
profile = self.user.userprofile
profile.balance -= (self.price - oldobj.price)
profile.save()
else:
oldProfile = oldobj.user.get_profile()
newProfile = self.user.get_profile()
oldProfile = oldobj.user.userprofile
newProfile = self.user.userprofile
oldProfile.balance += oldobj.price
oldProfile.save()
newprofile.balance -= self.price
@ -111,7 +111,7 @@ class Order(models.Model):
# where all the buyables have not updated the price
updOrder = Order.objects.get(id=instance.id)
if updOrder.price != Decimal("0"):
profile = updOrder.user.get_profile()
profile = updOrder.user.userprofile
profile.balance += updOrder.price
profile.save()

View File

@ -15,21 +15,21 @@
{% for buyable in buyables %}
<li class="span2" title="{{ buyable.name }}" rel="tooltip">
<div class="thumbnail">
<a class="image" href="{% url buyable_show buyable.id %}">
<a class="image" href="{% url 'buyable_show' buyable.id %}">
<img src="{% thumbnail buyable.image 128x128 %}" alt="{{ buyable.name }}" />
</a>
<div class="caption">
<h4><a href="{% url buyable_show buyable.id %}">{{ buyable.name }}</a></h4>
<h4><a href="{% url 'buyable_show' buyable.id %}">{{ buyable.name }}</a></h4>
<div class="btn-toolbar">
<div class="btn-group" data-id="{{ buyable.id}}" data-name="{{ buyable.name }}" data-image="{% thumbnail buyable.image 48x48 %}">
<a class="btn btn-success dropdown-toggle" data-toggle="dropdown" href="#">Kaufen<span class="caret"></span></a>
<ul class="dropdown-menu" style="text-align: left">
{% if buyable.hasDeposit %}
<li><a class="buy" href="{% url buyable_buy buyable.id %}"><i class="icon-tag"></i> Kaufen (Ohne Pfand) - {{ buyable.price|floatformat:2 }}</a></li>
<li><a class="buy inclDeposit" href="{% url buyable_buy buyable.id "with/deposit" %}"><i class="icon-tags"></i> Kaufen (Mit Pfand) - {{ buyable.price|floatformat:2 }} / {{ buyable.deposit|floatformat:2 }}</a></li>
<li><a class="buy onlyDeposit" href="{% url buyable_buy buyable.id "only/deposit" %}"><i class="icon-retweet"></i> Kaufen (Nur Pfand) - {{ buyable.deposit|floatformat:2 }}</a></li>
<li><a class="buy" href="{% url 'buyable_buy' buyable.id %}"><i class="icon-tag"></i> Kaufen (Ohne Pfand) - {{ buyable.price|floatformat:2 }}</a></li>
<li><a class="buy inclDeposit" href="{% url 'buyable_buy' buyable.id "with/deposit" %}"><i class="icon-tags"></i> Kaufen (Mit Pfand) - {{ buyable.price|floatformat:2 }} / {{ buyable.deposit|floatformat:2 }}</a></li>
<li><a class="buy onlyDeposit" href="{% url 'buyable_buy' buyable.id "only/deposit" %}"><i class="icon-retweet"></i> Kaufen (Nur Pfand) - {{ buyable.deposit|floatformat:2 }}</a></li>
{% else %}
<li><a class="buy" href="{% url buyable_buy buyable.id %}"><i class="icon-tag"></i> Kaufen (Ohne Pfand) - {{ buyable.price|floatformat:2 }}</a></li>
<li><a class="buy" href="{% url 'buyable_buy' buyable.id %}"><i class="icon-tag"></i> Kaufen (Ohne Pfand) - {{ buyable.price|floatformat:2 }}</a></li>
{% endif %}
</ul>
</div>

View File

@ -5,14 +5,16 @@
#
# Licensed under GNU Affero General Public License v3 or later
from django.conf.urls.defaults import *
from django.conf.urls import url
import buyable.views
#/store/
urlpatterns = patterns('',
(r'^$', 'buyable.views.showItems'),
url(r'^show/(\d+)/$', 'buyable.views.showItem', name='buyable_show'),
url(r'^buy/(\d+)/$', 'buyable.views.buyItem', name='buyable_buy'),
url(r'^buy/(\d+)/(with/deposit|only/deposit)/$', 'buyable.views.buyItem', name='buyable_buy'),
(r'^bought/(\d+)/?$', 'buyable.views.boughtItem'),
(r'^history/?$', 'buyable.views.history'),
urlpatterns = (
url(r'^$', buyable.views.showItems, name='buyable_overview'),
url(r'^show/(\d+)/$', buyable.views.showItem, name='buyable_show'),
url(r'^buy/(\d+)/$', buyable.views.buyItem, name='buyable_buy'),
url(r'^buy/(\d+)/(with/deposit|only/deposit)/$', buyable.views.buyItem, name='buyable_buy'),
url(r'^bought/(\d+)/?$', buyable.views.boughtItem),
url(r'^history/?$', buyable.views.history, name='buyable_history'),
)

View File

@ -6,7 +6,7 @@
#
# Licensed under GNU Affero General Public License v3 or later
from django.shortcuts import render_to_response
from django.shortcuts import render
from django.template import RequestContext
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
@ -29,7 +29,7 @@ def showItems(request):
if order_by == 'price':
items = items.order_by('price')
return render_to_response("buyables/showItems.html", {'items': items}, RequestContext(request))
return render(request, "buyables/showItems.html", {'items': items})
@login_required
def showItem(request, itemid):
@ -38,7 +38,7 @@ def showItem(request, itemid):
except Buyable.DoesNotExist:
# baww, kein item mit der id :( (oder mutax trollt rum)
return HttpResponseRedirect("/buy/")
return render_to_response("buyables/showItem.html", {'item': item}, RequestContext(request))
return render(request, "buyables/showItem.html", {'item': item})
@login_required
@ -76,7 +76,7 @@ def boughtItem(request, orderid):
if error == None and (item.user != request.user):
item1 = None
error = u"Diese Items gehören dir nicht"
return render_to_response("buyables/itemBought.html", {'order': item, 'error': error}, RequestContext(request))
return render(request, "buyables/itemBought.html", {'order': item, 'error': error})
@login_required
def history(request):
@ -98,4 +98,4 @@ def history(request):
# If page is out of range, deliver last page
histpage = paginator.page(paginator.num_pages)
return render_to_response("buyables/history.html", {'history': histpage}, RequestContext(request))
return render(request, "buyables/history.html", {'history': histpage})

View File

View File

@ -1,7 +1,10 @@
# Django settings for k4ever project.
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DEBUG = False
TEMPLATE_DEBUG = DEBUG
PISTON_DISPLAY_ERRORS = DEBUG
DEBUG_PROPOGATE_EXCEPTIONS = DEBUG
@ -14,7 +17,7 @@ MANAGERS = ADMINS
# use sqlite by default
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'NAME': 'k4ever.db', # Or path to database file if using sqlite3.
'USER': '', # Not used with sqlite3.
'PASSWORD': '', # Not used with sqlite3.
@ -59,6 +62,7 @@ MEDIA_ROOT = 'media/'
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = '/media/'
STATIC_URL = '/static/'
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
@ -77,18 +81,38 @@ AUTH_PROFILE_MODULE = 'main.UserProfile'
LOGIN_URL = '/user/login/'
LOGIN_REDIRECT_URL = '/'
ALLOWED_HOSTS = ['*']
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
'django.template.loaders.app_directories.load_template_source',
)
MIDDLEWARE_CLASSES = (
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, "templates/")],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
#TEMPLATE_CONTEXT_PROCESSORS = (
# "django.contrib.auth.context_processors.auth",
# "django.core.context_processors.debug",
# "django.core.context_processors.i18n",
# "django.core.context_processors.media",
# "django.core.context_processors.static",
#)
MIDDLEWARE = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
@ -98,31 +122,28 @@ MIDDLEWARE_CLASSES = (
ROOT_URLCONF = 'k4ever.urls'
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'buyable',
'transaction',
'main',
'django.contrib.admin',
'easy_thumbnails',
'piston',
'rest_framework',
)
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.contrib.messages.context_processors.messages",
"django.core.context_processors.request",
)
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "media"),
]
STATIC_ROOT = os.path.join(BASE_DIR, "static")
REST_FRAMEWORK = {
'DEFAULT_PREMISSION_CLASSES': 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
}

View File

@ -5,11 +5,14 @@
#
# Licensed under GNU Affero General Public License v3 or later
from django.conf.urls.defaults import *
from django.conf.urls import url, include
from django.contrib import admin
admin.autodiscover()
import django.views.static
urlpatterns = patterns('',
import main.views
urlpatterns = (
# Example:
# (r'^k4ever/', include('k4ever.foo.urls')),
@ -17,15 +20,16 @@ urlpatterns = patterns('',
# to INSTALLED_APPS to enable admin documentation:
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
# user stuff? go to main
(r'^$', 'main.views.startpage'),
(r'^api2/', include('api2.urls')),
(r'^user/', include('main.urls')),
(r'^transaction/', include('transaction.urls')),
(r'^store/', include('buyable.urls')),
(r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': 'media'}),
(r'^docs/(?P<path>.*)$', 'django.views.static.serve', {'document_root': 'docs'}),
url(r'^$', main.views.startpage, name='main.views.startpage'),
url(r'^api2/', include('api2.urls')),
url(r'^api3/', include('api3.urls')),
url(r'^user/', include('main.urls')),
url(r'^transaction/', include('transaction.urls')),
url(r'^store/', include('buyable.urls')),
url(r'^media/(?P<path>.*)$', django.views.static.serve, {'document_root': 'media'}),
url(r'^docs/(?P<path>.*)$', django.views.static.serve, {'document_root': 'docs'}),
# Uncomment the next line to enable the admin:
(r'^admin/', include(admin.site.urls)),
url(r'^admin/', include(admin.site.urls)),
)

View File

@ -15,6 +15,7 @@ class PluginAdminForm(forms.ModelForm):
""" Form made to require that the user of a plugin is in the plugin group """
class Meta:
model = Plugin
exclude = []
def clean_user(self):
user = self.cleaned_data['user']

View File

@ -5,28 +5,32 @@
#
# Licensed under GNU Affero General Public License v3 or later
from django.contrib.admin.filterspecs import FilterSpec, BooleanFieldFilterSpec
# FIXME: this needs to be reimplemented (not compatible with django 1.10+)
# https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter
# http://stackoverflow.com/a/10644089
class IsNegativeFilterSpec(FilterSpec):
""" Custom filter to display only users with negative balance """
def __init__(self, f, request, *args, **kwargs):
super(IsNegativeFilterSpec, self).__init__(f, request, *args, **kwargs)
self.lookup_kwarg = '%s__lt' % f.name
self.lookup_val = request.GET.get(self.lookup_kwarg, None) # Check if current request already has a filter
def title(self):
return 'Kontostand'
def choices(self, cl):
yield {
'selected': self.lookup_val == None,
'query_string': "?",
'display': 'Alle'}
yield {
'selected': self.lookup_val == '0',
'query_string': cl.get_query_string(
{self.lookup_kwarg: 0},
['balance__isnull']),
'display': 'Nur Negative'}
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'has_negative_filter', False), IsNegativeFilterSpec))
#from django.contrib.admin.filterspecs import FilterSpec, BooleanFieldFilterSpec
#
#class IsNegativeFilterSpec(FilterSpec):
# """ Custom filter to display only users with negative balance """
# def __init__(self, f, request, *args, **kwargs):
# super(IsNegativeFilterSpec, self).__init__(f, request, *args, **kwargs)
# self.lookup_kwarg = '%s__lt' % f.name
# self.lookup_val = request.GET.get(self.lookup_kwarg, None) # Check if current request already has a filter
#
# def title(self):
# return 'Kontostand'
#
# def choices(self, cl):
# yield {
# 'selected': self.lookup_val == None,
# 'query_string': "?",
# 'display': 'Alle'}
# yield {
# 'selected': self.lookup_val == '0',
# 'query_string': cl.get_query_string(
# {self.lookup_kwarg: 0},
# ['balance__isnull']),
# 'display': 'Nur Negative'}
#
#FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'has_negative_filter', False), IsNegativeFilterSpec))

View File

@ -1,4 +1,4 @@
from django.db.models.signals import post_syncdb
from django.db.models.signals import post_migrate
from django.core.management import commands, call_command
import django.contrib.auth
@ -6,5 +6,5 @@ def createPluginGroup(sender, app, created_models, **kwargs):
if django.contrib.auth.models.Group in created_models:
call_command("loaddata", "group_plugin")
post_syncdb.connect(createPluginGroup, sender=django.contrib.auth.models)
post_migrate.connect(createPluginGroup, sender=django.contrib.auth.models)

View File

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2018-10-24 01:07
from __future__ import unicode_literals
from decimal import Decimal
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Plugin',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=40)),
('author', models.CharField(max_length=40)),
('version', models.CharField(max_length=40)),
('descr', models.TextField(default=b'')),
('uniqueAuthblob', models.BooleanField(default=False)),
('userCanWriteAuthblob', models.BooleanField(default=True)),
('maxLinesPerAuthblob', models.IntegerField(default=0)),
('userCanReadAuthblob', models.BooleanField(default=True)),
('pluginCanWriteAuthblob', models.BooleanField(default=False)),
('pluginCanReadAuthblob', models.BooleanField(default=False)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='PluginPermission',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('authblob', models.TextField(default=b'')),
('plugin', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.Plugin')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('balance', models.DecimalField(decimal_places=2, default=Decimal('0'), max_digits=9)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

View File

@ -12,7 +12,7 @@ from decimal import Decimal
class UserProfile(models.Model):
""" Contains data for a user, especially the account balance. """
user = models.ForeignKey(User, unique=True)
user = models.OneToOneField(User)
balance = models.DecimalField(max_digits=9, decimal_places=2, default=Decimal(0))
balance.has_negative_filter = True
@ -45,7 +45,7 @@ class Plugin(models.Model):
The other attributes are used for plugin/user read/write access to the
authblob.
"""
user = models.ForeignKey(User, unique=True)
user = models.OneToOneField(User)
# plugin info
name = models.CharField(max_length=40)

View File

@ -1,4 +1,5 @@
{% extends "base.html" %}
{% load static %}
{% comment %}
# This file is part of k4ever, a point-of-sale system
@ -11,16 +12,16 @@
{% block head %}
<link rel="stylesheet" media="screen" type="text/css"
href="/media/css/style.css" />
href="{% static "css/style.css" %}" />
{% block extrastyle %}{% endblock %}
<script type="text/javascript">
var MEDIA_URL = "/media/";
var MEDIA_URL = "/static/";
</script>
<script type='text/javascript' src="/media/js/jquery-1.5.1.js"></script>
<script type='text/javascript' src="/media/js/jquery-ui-1.8.10.custom.min.js"></script>
<script type='text/javascript' src="/media/js/jquery.ui.autocomplete.html.js"></script>
<script type='text/javascript' src="/media/js/jquery.gritter.min.js"></script>
<script type='text/javascript' src="/media/js/k4ever.js"></script>
<script type='text/javascript' src="{% static "js/jquery-1.5.1.js" %}"></script>
<script type='text/javascript' src="{% static "js/jquery-ui-1.8.10.custom.min.js" %}"></script>
<script type='text/javascript' src="{% static "js/jquery.ui.autocomplete.html.js" %}"></script>
<script type='text/javascript' src="{% static "js/jquery.gritter.min.js" %}"></script>
<script type='text/javascript' src="{% static "js/k4ever.js" %}"></script>
{% endblock head %}
{% block "content" %}

View File

@ -1,4 +1,5 @@
{% extends "base.html" %}
{% load static %}
{% comment %}
# This file is part of k4ever, a point-of-sale system
@ -12,13 +13,13 @@
{# load url from future #}
{% block extrastyle %}
<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{{ MEDIA_URL }}css/admin.css{% endblock %}" />
<!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="{% block stylesheet_ie %}{% load adminmedia %}{% admin_media_prefix %}css/ie.css{% endblock %}" /><![endif]-->
{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% admin_media_prefix %}css/rtl.css{% endblock %}" />{% endif %}
<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% static "css/admin.css" %}{% endblock %}" />
<!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="{% block stylesheet_ie %}{{ MEDIA_URL }}admin/css/ie.css{% endblock %}" /><![endif]-->
{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{{ MEDIA_URL }}admin/css/rtl.css{% endblock %}" />{% endif %}
{% endblock %}
{% block extrahead %}
<script type="text/javascript">window.__admin_media_prefix__ = "{% filter escapejs %}{% admin_media_prefix %}{% endfilter %}";</script>
<script type="text/javascript">window.__admin_media_prefix__ = "{% filter escapejs %}{{ MEDIA_URL }}admin{% endfilter %}";</script>
{% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE" />{% endblock %}
{% endblock %}

View File

@ -6,6 +6,7 @@
#
# Licensed under GNU Affero General Public License v3 or later
{% endcomment %}
{% load static %}
<!DOCTYPE html>
<html {% if user.is_authenticated %}class="loggedIn"{% endif %}>
@ -13,20 +14,20 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
{% block head %}
<link rel="stylesheet" media="screen" type="text/css"
href="{{ MEDIA_URL }}css/style.css" />
href="{% static "css/style.css" %}" />
{% block extrastyle %}{% endblock %}
<script type="text/javascript">
var MEDIA_URL = "{{ MEDIA_URL }}";
var MEDIA_URL = "/static/";
</script>
<script type='text/javascript' src="{{ MEDIA_URL }}js/jquery.min.js"></script>
<script type='text/javascript' src="{{ MEDIA_URL }}js/jquery-ui-1.8.10.custom.min.js"></script>
<script type='text/javascript' src="{{ MEDIA_URL }}js/jquery.ui.autocomplete.html.js"></script>
<script type='text/javascript' src="{{ MEDIA_URL }}js/jquery.gritter.min.js"></script>
<script type="text/javascript" src="{{ MEDIA_URL }}bootstrap/js/bootstrap.min.js"></script>
<script type='text/javascript' src="{{ MEDIA_URL }}js/k4ever.js"></script>
<script type='text/javascript' src="{% static "js/jquery.min.js" %}"></script>
<script type='text/javascript' src="{% static "js/jquery-ui-1.8.10.custom.min.js" %}"></script>
<script type='text/javascript' src="{% static "js/jquery.ui.autocomplete.html.js" %}"></script>
<script type='text/javascript' src="{% static "js/jquery.gritter.min.js" %}"></script>
<script type="text/javascript" src="{% static "bootstrap/js/bootstrap.min.js" %}"></script>
<script type='text/javascript' src="{% static "js/k4ever.js" %}"></script>
{% block extrahead %}{% endblock %}
{% endblock head %}
<title>Freitagsrundenkasse{% block "title" %}{% endblock %}</title>
<title>Unbranded K4Ever{% block "title" %}{% endblock %}</title>
</head>
<body{%block bodyargs %}{%endblock%}>
<div class="navbar navbar-fixed-top">

View File

@ -9,43 +9,43 @@
{% load navigation_extras %}
{% url transaction.views.overview as transactions %}
{% url main.views.startpage as home %}
{% url buyable.views.showItems as store %}
{% url buyable.views.history as history %}
{% url main.views.settings as settings %}
{% url django.contrib.auth.views.logout as logout %}
{% url django.contrib.auth.views.login as login %}
{% url main.views.register as register %}
{% url 'transactions' as transactions %}
{% url 'main.views.startpage' as home %}
{% url 'buyable_overview' as store %}
{% url 'buyable_history' as history %}
{% url 'main_settings' as settings %}
{% url 'logout' as logout %}
{% url 'login' as login %}
{% url 'register' as register %}
<ul role="navigation sitemap" class="nav">
{% if user.is_authenticated %}
<li class="{% active request home %}"><a href="{{ home }}">Home</a></li>
<li class="{% active request store %}"><a href="{{ store }}">Einkaufen</a></li>
<li class="{% active request transactions %}"><a href="{{ transactions }}">Konto</a></li>
<li class="{% active request history %}"><a href="{{ history }}">Frühere Einkäufe</a></li>
<li class="{% active home %}"><a href="{{ home }}">Home</a></li>
<li class="{% active store %}"><a href="{{ store }}">Einkaufen</a></li>
<li class="{% active transactions %}"><a href="{{ transactions }}">Konto</a></li>
<li class="{% active history %}"><a href="{{ history }}">Frühere Einkäufe</a></li>
<li class="divider-vertical" role="presentation"></li>
<ul class="nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="icon user"></span></a>
<ul class="dropdown-menu">
<li class="{% active request settings %}"><a href="{{ settings }}"><i class="icon-cog"></i> Einstellungen</a></li>
<li class="{% active settings %}"><a href="{{ settings }}"><i class="icon-cog"></i> Einstellungen</a></li>
{% if user.is_staff %}
<li class=""><a href="/admin/"><i class="icon-lock"></i> Administration</a></li>
{% endif %}
<li class="{% active request logout %}"><a href="{{ logout }}"><i class="icon-off"></i> Abmelden</a></li>
<li class="{% active logout %}"><a href="{{ logout }}"><i class="icon-off"></i> Abmelden</a></li>
</ul>
</li>
</ul>
{% else %}
<li class="{% active request login %}"><a href="{{ login }}">Anmelden</a></li>
<li class="{% active request register %}"><a href="{{ register }}">Registrieren</a></li>
<li class="{% active login %}"><a href="{{ login }}">Anmelden</a></li>
<li class="{% active register %}"><a href="{{ register }}">Registrieren</a></li>
{% endif %}
</ul>
{% if user.is_authenticated %}
<div class="pull-right">
<span class="brand balance">Kontostand: {{ user.get_profile.balance|floatformat:2 }} €</span>
<span class="brand balance">Kontostand: {{ user.userprofile.balance|floatformat:2 }} €</span>
<form class="navbar-search">
<input placeholder="Suche und kaufe..." class="search-query autocomplete"
type="search" name="search_term" value="Lade Daten..."

View File

@ -15,18 +15,18 @@
{% for buyable in buyables %}
<tr>
<td class="productImage">
<a href="{% url buyable_show buyable.id %}"><img src="{% thumbnail buyable.image 64x64 %}" /></a>
<a href="{% url 'buyable_show' buyable.id %}"><img src="{% thumbnail buyable.image 64x64 %}" /></a>
</td>
<td class="name"><span>{{ buyable.name }}</span> {% if buyable.num_buys %}({{ buyable.num_buys }} mal gekauft){% endif %}</td>
<td class="actions btn-group" data-image="{% thumbnail buyable.image 48x48 %}" data-name="{{ buyable.name }}" data-id="{{ buyable.id }}">
{% if buyable.deposit > 0 %}
<a class="button buy" href="{% url buyable_buy buyable.id %}" title="Kaufen (Ohne Pfand)"><span><span>{{ buyable.price|floatformat:2 }}€</span></span></a>
<a class="button buy inclDeposit" href="{% url buyable_buy buyable.id "with/deposit" %}" title="Kaufen (Mit Pfand)"><span><span>{{ buyable.price|floatformat:2 }}€ / {{ buyable.deposit|floatformat:2 }}€</span></span></a>
<a class="button buy" href="{% url 'buyable_buy' buyable.id %}" title="Kaufen (Ohne Pfand)"><span><span>{{ buyable.price|floatformat:2 }}€</span></span></a>
<a class="button buy inclDeposit" href="{% url 'buyable_buy' buyable.id "with/deposit" %}" title="Kaufen (Mit Pfand)"><span><span>{{ buyable.price|floatformat:2 }}€ / {{ buyable.deposit|floatformat:2 }}€</span></span></a>
{% if includeDeposit %}
<a class="button buy onlyDeposit" href="{% url buyable_buy buyable.id "only/deposit" %}" title="Kaufen (Nur Pfand)"><span><span>{{ buyable.deposit|floatformat:2 }}€</span></span></a>
<a class="button buy onlyDeposit" href="{% url 'buyable_buy' buyable.id "only/deposit" %}" title="Kaufen (Nur Pfand)"><span><span>{{ buyable.deposit|floatformat:2 }}€</span></span></a>
{% endif %}
{% else %}
<a class="button buy" href="{% url buyable_buy buyable.id %}" title="Kaufen"><span><span>{{ buyable.price|floatformat:2 }}€</span></span></a>
<a class="button buy" href="{% url 'buyable_buy' buyable.id %}" title="Kaufen"><span><span>{{ buyable.price|floatformat:2 }}€</span></span></a>
{% endif %}
</td>
</tr>

View File

@ -11,8 +11,9 @@ import re
register = Library()
@register.simple_tag
def active(request, pattern):
@register.simple_tag(takes_context=True)
def active(context, pattern):
request = context['request']
if pattern == request.path:
return "active"
return ""

View File

@ -6,19 +6,23 @@
#
# Licensed under GNU Affero General Public License v3 or later
from django.conf.urls.defaults import *
from django.conf import settings
from django.conf.urls import url
from django.views.generic import RedirectView
import main.views
import django.contrib.auth.views
urlpatterns = patterns('',
(r'^$', 'django.views.generic.simple.redirect_to', {'url': 'login/'}),
(r'^register/$', 'main.views.register'),
(r'^login/$', 'main.views.login'),
(r'^logout/$', 'django.contrib.auth.views.logout',
urlpatterns = (
#(r'^$', 'django.views.generic.simple.redirect_to', {'url': 'login/'}),
url(r'^$', RedirectView.as_view(url='login/')),
url(r'^register/$', main.views.register, name='register'),
url(r'^login/$', main.views.login, name='login'),
url(r'^logout/$', django.contrib.auth.views.logout,
{'template_name': 'registration/logout.html',
'next_page': '/user/login/'}),
(r'^settings/$', 'main.views.settings'),
'next_page': '/user/login/'}, name='logout'),
url(r'^settings/$', main.views.settings, name='main_settings'),
#plugin stuff
(r'^settings/plugin/(?P<method>(?:allow|deny))/(?P<pluginId>\d+)/$', 'main.views.pluginPermission'),
(r'^settings/plugin/authblob/(?P<pluginId>\d+)/$', 'main.views.pluginAuthblob'),
url(r'^settings/plugin/(?P<method>(?:allow|deny))/(?P<pluginId>\d+)/$', main.views.pluginPermission),
url(r'^settings/plugin/authblob/(?P<pluginId>\d+)/$', main.views.pluginAuthblob),
)

View File

@ -10,7 +10,7 @@ from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.decorators import login_required
from django.db.models import Count, Max
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.shortcuts import render
from django.template import RequestContext
from django.core.urlresolvers import reverse
import django.contrib.auth.views
@ -18,7 +18,7 @@ import django.contrib.auth.views
from buyable.models import Purchase, Buyable, BuyableType
from main.helper import getUserFromAuthblob
from main.models import Plugin, PluginPermission
from settings import SNACK_TYPE_ID, DRINK_TYPE_ID
from k4ever.settings import SNACK_TYPE_ID, DRINK_TYPE_ID
@login_required
def startpage(request):
@ -52,12 +52,12 @@ def startpage(request):
buyables = buyables.annotate(max_dateTime=Max('purchase__order__dateTime'))
context[context_vars[2]] = buyables.order_by('-max_dateTime')[:5]
return render_to_response("main/startpage.html", context, RequestContext(request))
return render(request, "main/startpage.html", context)
def register(request):
""" The "no registration available" page... """
return render_to_response("registration/register.html", RequestContext(request))
return render(request, "registration/register.html")
def getPluginDict(request):
@ -83,7 +83,7 @@ def settings(request):
form.save()
pdict['password_success'] = "Es wurde ein neues Passwort gesetzt."
pdict['form'] = form
return render_to_response("settings/settings.html", pdict, RequestContext(request))
return render(request, "settings/settings.html", pdict)
@login_required
def pluginPermission(request, method, pluginId):
@ -94,14 +94,14 @@ def pluginPermission(request, method, pluginId):
except Plugin.DoesNotExist:
d = getPluginDict(request)
d['pluginerror'] = "Ein Plugin mit der angegebenen ID existiert nicht"
return render_to_response("settings/settings.html", d, RequestContext(request))
return render(request, "settings/settings.html", d)
if method == "allow":
try:
p = PluginPermission.objects.get(user=request.user, plugin=plugin)
d = getPluginDict(request)
d['pluginerror'] = "Dieses Plugin wurde bereits erlaubt"
return render_to_response("settings/settings.html", d, RequestContext(request))
return render(request, "settings/settings.html", d)
except PluginPermission.DoesNotExist:
p = PluginPermission(user=request.user, plugin=plugin)
p.save()
@ -112,7 +112,7 @@ def pluginPermission(request, method, pluginId):
except PluginPermission.DoesNotExist:
d = getPluginDict(request)
d['pluginerror'] = "Keine Berechtigungen f&uuml;r dieses Plugin gefunden - kann also auch nicht zur&uuml;ckgezogen werden"
return render_to_response("settings/settings.html", d, RequestContext(request))
return render(request, "settings/settings.html", d)
return HttpResponseRedirect("/user/settings/")
@login_required
@ -128,7 +128,7 @@ def pluginAuthblob(request, pluginId):
except Plugin.DoesNotExist:
d = getPluginDict(request)
d['pluginerror'] = "Ein Plugin mit der angegebenen ID existiert nicht"
return render_to_response("settings/settings.html", d, RequestContext(request))
return render(request, "settings/settings.html", d)
# find plugin permission for user
p = None
@ -137,13 +137,13 @@ def pluginAuthblob(request, pluginId):
except PluginPermission.DoesNotExist:
d = getPluginDict(request)
d['pluginerror'] = "Vor dem editieren vom Authblob muss das Plugin ersteinmal erlaubt werden"
return render_to_response("settings/settings.html", d, RequestContext(request))
return render(request, "settings/settings.html", d)
# has the user write access to the authblob?
if not p.plugin.userCanWriteAuthblob or not "authblob" in request.POST:
d = getPluginDict(request)
d['pluginerror'] = "Der Authblob darf f&uuml;r dieses Plugin nicht vom User ver&auml;ndert werden (oder der Authblob war kaputt)"
return render_to_response("settings/settings.html", d, RequestContext(request))
return render(request, "settings/settings.html", d)
# clean authblob \r\n ==> \n
authblob = request.POST["authblob"].replace("\r\n", "\n")
@ -152,14 +152,14 @@ def pluginAuthblob(request, pluginId):
if p.plugin.maxLinesPerAuthblob > 0 and (authblob.rstrip().count("\n") + 1) > p.plugin.maxLinesPerAuthblob:
d = getPluginDict(request)
d['pluginerror'] = "Der Authblob darf maximal %d Zeilen haben" % (p.plugin.maxLinesPerAuthblob,)
return render_to_response("settings/settings.html", d, RequestContext(request))
return render(request, "settings/settings.html", d)
# check, if this is equal to the old users plugin
pluginsWithAuthblob = PluginPermission.objects.filter(plugin=plugin, user=request.user, authblob__exact=authblob)
if pluginsWithAuthblob.count() > 0:
d = getPluginDict(request)
d['pluginerror'] = "Das ist der gleiche Authblob, den du vorher auch hattest."
return render_to_response("settings/settings.html", d, RequestContext(request))
return render(request, "settings/settings.html", d)
# check for every authblob-line, if there is somebody who has it (if unique)
if p.plugin.uniqueAuthblob:
@ -173,13 +173,13 @@ def pluginAuthblob(request, pluginId):
else:
d = getPluginDict(request)
d['pluginerror'] = "Achtung! Dein Authblob (bzw. eine der Zeile davon) wird bereits von einer anderen Person benutzt. Bitte w&auml;hle einen anderen (eindeutigen) Authblob!"
return render_to_response("settings/settings.html", d, RequestContext(request))
return render(request, "settings/settings.html", d)
p.authblob = authblob
p.save()
d = getPluginDict(request)
d['pluginmsg'] = "Authblob erfolgreich ge&auml;ndert"
return render_to_response("settings/settings.html", d, RequestContext(request))
return render(request, "settings/settings.html", d)
def login(request):

View File

@ -1,11 +1,22 @@
#!/usr/bin/env python
from django.core.management import execute_manager
try:
import settings # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.exit(1)
import os
import sys
if __name__ == "__main__":
execute_manager(settings)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "k4ever.settings")
try:
from django.core.management import execute_from_command_line
except ImportError:
# The above import may fail for some other reason. Ensure that the
# issue is really that Django is missing to avoid masking other
# exceptions on Python 2.
try:
import django
except ImportError:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
)
raise
execute_from_command_line(sys.argv)

6
k4ever/requirements.txt Normal file
View File

@ -0,0 +1,6 @@
django==1.11
easy_thumbnails
oauth
django-auth-ldap
djangorestframework
django-piston

View File

@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2018-10-24 01:07
from __future__ import unicode_literals
from decimal import Decimal
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import transaction.validator
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Transaction',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('dateTime', models.DateTimeField(auto_now_add=True)),
('amount', models.DecimalField(decimal_places=2, max_digits=8, validators=[transaction.validator.validate_notZero])),
('checked', models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name='TransactionType',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('needsCheck', models.BooleanField(default=True)),
],
),
migrations.CreateModel(
name='VirtualTransaction',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('dateTime', models.DateTimeField(auto_now_add=True)),
('amount', models.DecimalField(decimal_places=2, max_digits=8, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))], verbose_name=b'Betrag')),
('comment', models.CharField(max_length=100, verbose_name=b'Verwendungszweck')),
('recipient', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='receivedtransaction', to=settings.AUTH_USER_MODEL, verbose_name='Empf\xe4nger')),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddField(
model_name='transaction',
name='transactionType',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='transaction.TransactionType', verbose_name=b'Typ'),
),
migrations.AddField(
model_name='transaction',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

View File

@ -43,7 +43,7 @@ class Transaction(models.Model):
(self.checked and " " or " not "))
def save(self, *args, **kwargs):
profile = self.user.get_profile()
profile = self.user.userprofile
if self.id == None:
# insert
profile.balance += self.amount
@ -51,11 +51,11 @@ class Transaction(models.Model):
# update
oldobj = Transaction.objects.get(id=self.id)
if oldobj.user != self.user:
oldprofile = oldobj.user.get_profile()
oldprofile = oldobj.user.userprofile
oldprofile.balance -= oldobj.amount
oldprofile.save()
# just to be save, reget profile
profile = self.user.get_profile()
profile = self.user.userprofile
profile.balance += self.amount
else:
profile.balance += (self.amount-oldobj.amount)
@ -64,7 +64,7 @@ class Transaction(models.Model):
@staticmethod
def pre_delete_signal(sender, instance, **kwargs):
profile = instance.user.get_profile()
profile = instance.user.userprofile
profile.balance -= instance.amount
profile.save()
@ -87,8 +87,8 @@ class VirtualTransaction(models.Model):
@staticmethod
def moveMoney(fromUser, toUser, amount, commit=True):
if not (fromUser == toUser):
fromProfile = fromUser.get_profile()
toProfile = toUser.get_profile()
fromProfile = fromUser.userprofile
toProfile = toUser.userprofile
fromProfile.balance -= amount
toProfile.balance += amount
if commit:

View File

@ -5,10 +5,11 @@
#
# Licensed under GNU Affero General Public License v3 or later
from django.conf.urls.defaults import patterns
from django.conf.urls import url
import transaction.views
urlpatterns = patterns('',
(r'^$', 'transaction.views.overview'),
(r'^state/(success|error|vsuccess|verror)/(payin|payout)/$', 'transaction.views.state'),
urlpatterns = (
url(r'^$', transaction.views.overview, name='transactions'),
url(r'^state/(success|error|vsuccess|verror)/(payin|payout)/$', transaction.views.state),
)

View File

@ -8,7 +8,7 @@
from django.contrib.auth.decorators import login_required
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.shortcuts import render
from django.template import RequestContext
from transaction.forms import TransactionForm, VirtualTransactionForm
@ -50,7 +50,7 @@ def overview(request):
else:
state = "verror"
return HttpResponseRedirect("state/%s/%s/" % (state,payway))
return render_to_response("transaction/overview.html", {'history': history, 'vhistory': vhistory, 'form': form, 'transacted': False, 'payout': None, 'error': False, 'vform': vform, 'vtransacted': False, 'verror': False}, RequestContext(request))
return render(request, "transaction/overview.html", {'history': history, 'vhistory': vhistory, 'form': form, 'transacted': False, 'payout': None, 'error': False, 'vform': vform, 'vtransacted': False, 'verror': False})
@login_required
@ -77,4 +77,4 @@ def state(request, state="", payway=""):
if payway == "payout":
payout = True
return render_to_response("transaction/overview.html", {'history': history, 'vhistory': vhistory, 'form': form, 'transacted': transacted, 'payout': payout, 'error': error, 'vform': vform, 'vtransacted': vtransacted, 'verror': verror}, RequestContext(request))
return render(request, "transaction/overview.html", {'history': history, 'vhistory': vhistory, 'form': form, 'transacted': transacted, 'payout': payout, 'error': error, 'vform': vform, 'vtransacted': vtransacted, 'verror': verror})

20
k4ever/wsgi.py Normal file
View File

@ -0,0 +1,20 @@
"""
WSGI config for cqtu project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
"""
import os
import sys
from django.core.wsgi import get_wsgi_application
sys.path.append("/home/k4ever/k4ever")
sys.path.append("/home/k4ever/k4ever/k4ever")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "k4ever.settings")
application = get_wsgi_application()