commit
ba3eaa6546
|
@ -1,5 +1,4 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import ProductCategory, Product
|
from .models import ProductCategory, Product
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,5 +11,3 @@ class ProductCategoryAdmin(admin.ModelAdmin):
|
||||||
class ProductAdmin(admin.ModelAdmin):
|
class ProductAdmin(admin.ModelAdmin):
|
||||||
list_display = ['name', 'price', 'category', 'in_stock']
|
list_display = ['name', 'price', 'category', 'in_stock']
|
||||||
list_editable = ['in_stock']
|
list_editable = ['in_stock']
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from utils.models import CampRelatedModel
|
from utils.models import CampRelatedModel
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,4 +6,3 @@ class MenuView(ListView):
|
||||||
model = ProductCategory
|
model = ProductCategory
|
||||||
template_name = "bar_menu.html"
|
template_name = "bar_menu.html"
|
||||||
context_object_name = "categories"
|
context_object_name = "categories"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
from django.conf import settings
|
|
||||||
from .models import Camp
|
from .models import Camp
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
|
|
||||||
def camp(request):
|
def camp(request):
|
||||||
|
|
|
@ -17,10 +17,8 @@ class CampViewMixin(object):
|
||||||
if queryset:
|
if queryset:
|
||||||
# check if we have a foreignkey to Camp, filter if so
|
# check if we have a foreignkey to Camp, filter if so
|
||||||
for field in queryset.model._meta.fields:
|
for field in queryset.model._meta.fields:
|
||||||
if field.name=="camp" and field.related_model._meta.label == "camps.Camp":
|
if field.name == "camp" and field.related_model._meta.label == "camps.Camp":
|
||||||
return queryset.filter(camp=self.camp)
|
return queryset.filter(camp=self.camp)
|
||||||
|
|
||||||
# Camp relation not found, or queryset is empty, return it unaltered
|
# Camp relation not found, or queryset is empty, return it unaltered
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,14 @@ from camps.models import Camp
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
|
|
||||||
def get_current_camp():
|
def get_current_camp():
|
||||||
try:
|
try:
|
||||||
return Camp.objects.get(camp__contains=timezone.now())
|
return Camp.objects.get(camp__contains=timezone.now())
|
||||||
except Camp.DoesNotExist:
|
except Camp.DoesNotExist:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class CampPropertyListFilter(admin.SimpleListFilter):
|
class CampPropertyListFilter(admin.SimpleListFilter):
|
||||||
"""
|
"""
|
||||||
SimpleListFilter to filter models by camp when camp is
|
SimpleListFilter to filter models by camp when camp is
|
||||||
|
@ -27,7 +29,6 @@ class CampPropertyListFilter(admin.SimpleListFilter):
|
||||||
for camp in unique_camps:
|
for camp in unique_camps:
|
||||||
yield (camp.slug, camp.title)
|
yield (camp.slug, camp.title)
|
||||||
|
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
def queryset(self, request, queryset):
|
||||||
# if self.value() is None return everything
|
# if self.value() is None return everything
|
||||||
if not self.value():
|
if not self.value():
|
||||||
|
@ -45,4 +46,3 @@ class CampPropertyListFilter(admin.SimpleListFilter):
|
||||||
if item.camp != camp:
|
if item.camp != camp:
|
||||||
queryset = queryset.exclude(pk=item.pk)
|
queryset = queryset.exclude(pk=item.pk)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import *
|
from .models import (
|
||||||
|
InfoItem,
|
||||||
|
InfoCategory
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(InfoItem)
|
@admin.register(InfoItem)
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,22 +11,22 @@ class InfoCategory(CampRelatedModel):
|
||||||
|
|
||||||
camp = models.ForeignKey(
|
camp = models.ForeignKey(
|
||||||
'camps.Camp',
|
'camps.Camp',
|
||||||
related_name = 'infocategories',
|
related_name='infocategories',
|
||||||
on_delete = models.PROTECT
|
on_delete=models.PROTECT
|
||||||
)
|
)
|
||||||
|
|
||||||
headline = models.CharField(
|
headline = models.CharField(
|
||||||
max_length = 100,
|
max_length=100,
|
||||||
help_text = "The headline of this info category"
|
help_text="The headline of this info category"
|
||||||
)
|
)
|
||||||
|
|
||||||
anchor = models.SlugField(
|
anchor = models.SlugField(
|
||||||
help_text = "The HTML anchor to use for this info category."
|
help_text="The HTML anchor to use for this info category."
|
||||||
)
|
)
|
||||||
|
|
||||||
weight = models.PositiveIntegerField(
|
weight = models.PositiveIntegerField(
|
||||||
help_text = 'Determines sorting/ordering. Heavier categories sink to the bottom. Categories with the same weight are ordered alphabetically. Defaults to 100.',
|
help_text='Determines sorting/ordering. Heavier categories sink to the bottom. Categories with the same weight are ordered alphabetically. Defaults to 100.',
|
||||||
default = 100,
|
default=100,
|
||||||
)
|
)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
@ -45,26 +45,26 @@ class InfoItem(CampRelatedModel):
|
||||||
|
|
||||||
category = models.ForeignKey(
|
category = models.ForeignKey(
|
||||||
'info.InfoCategory',
|
'info.InfoCategory',
|
||||||
related_name = 'infoitems',
|
related_name='infoitems',
|
||||||
on_delete = models.PROTECT
|
on_delete=models.PROTECT
|
||||||
)
|
)
|
||||||
|
|
||||||
headline = models.CharField(
|
headline = models.CharField(
|
||||||
max_length = 100,
|
max_length=100,
|
||||||
help_text = "Headline of this info item."
|
help_text="Headline of this info item."
|
||||||
)
|
)
|
||||||
|
|
||||||
anchor = models.SlugField(
|
anchor = models.SlugField(
|
||||||
help_text = "The HTML anchor to use for this info item."
|
help_text="The HTML anchor to use for this info item."
|
||||||
)
|
)
|
||||||
|
|
||||||
body = models.TextField(
|
body = models.TextField(
|
||||||
help_text = 'Body of this info item. Markdown is supported.'
|
help_text='Body of this info item. Markdown is supported.'
|
||||||
)
|
)
|
||||||
|
|
||||||
weight = models.PositiveIntegerField(
|
weight = models.PositiveIntegerField(
|
||||||
help_text = 'Determines sorting/ordering. Heavier items sink to the bottom. Items with the same weight are ordered alphabetically. Defaults to 100.',
|
help_text='Determines sorting/ordering. Heavier items sink to the bottom. Items with the same weight are ordered alphabetically. Defaults to 100.',
|
||||||
default = 100,
|
default=100,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -78,5 +78,3 @@ class InfoItem(CampRelatedModel):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '%s (%s)' % (self.headline, self.category)
|
return '%s (%s)' % (self.headline, self.category)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import *
|
from .models import OutgoingIrcMessage
|
||||||
|
|
||||||
admin.site.register(OutgoingIrcMessage)
|
admin.site.register(OutgoingIrcMessage)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import logging, irc3
|
import logging
|
||||||
|
import irc3
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger('bornhack.%s' % __name__)
|
logger = logging.getLogger('bornhack.%s' % __name__)
|
||||||
|
|
||||||
|
@ -24,5 +25,5 @@ def do_work():
|
||||||
irc3.IrcBot(**config).run(forever=True)
|
irc3.IrcBot(**config).run(forever=True)
|
||||||
except Exception as E:
|
except Exception as E:
|
||||||
logger.exception("Got exception inside do_work for %s" % self.workermodule)
|
logger.exception("Got exception inside do_work for %s" % self.workermodule)
|
||||||
raise
|
raise E
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import encoding
|
from django.utils import encoding
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.views.generic import ListView, DetailView
|
from django.views.generic import ListView, DetailView
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from .models import *
|
from .models import NewsItem
|
||||||
|
|
||||||
|
|
||||||
class NewsIndex(ListView):
|
class NewsIndex(ListView):
|
||||||
model = NewsItem
|
model = NewsItem
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import Profile
|
from .models import Profile
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Profile)
|
@admin.register(Profile)
|
||||||
class OrderAdmin(admin.ModelAdmin):
|
class OrderAdmin(admin.ModelAdmin):
|
||||||
actions = ['approve_public_credit_names']
|
actions = ['approve_public_credit_names']
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
|
@ -1,7 +1,14 @@
|
||||||
from channels.generic.websockets import JsonWebsocketConsumer
|
from channels.generic.websockets import JsonWebsocketConsumer
|
||||||
|
|
||||||
from camps.models import Camp
|
from camps.models import Camp
|
||||||
from .models import Event, EventInstance, Favorite, EventLocation, EventType, Speaker
|
from .models import (
|
||||||
|
Event,
|
||||||
|
EventInstance,
|
||||||
|
Favorite,
|
||||||
|
EventLocation,
|
||||||
|
EventType,
|
||||||
|
Speaker
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ScheduleConsumer(JsonWebsocketConsumer):
|
class ScheduleConsumer(JsonWebsocketConsumer):
|
||||||
|
|
|
@ -3,8 +3,9 @@ from django.shortcuts import redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from . import models
|
from . import models
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
import sys, mimetypes
|
|
||||||
from django.http import Http404, HttpResponse
|
from django.http import Http404, HttpResponse
|
||||||
|
import sys
|
||||||
|
import mimetypes
|
||||||
|
|
||||||
|
|
||||||
class EnsureCFSOpenMixin(SingleObjectMixin):
|
class EnsureCFSOpenMixin(SingleObjectMixin):
|
||||||
|
@ -12,7 +13,9 @@ class EnsureCFSOpenMixin(SingleObjectMixin):
|
||||||
# do not permit editing if call for speakers is not open
|
# do not permit editing if call for speakers is not open
|
||||||
if not self.camp.call_for_speakers_open:
|
if not self.camp.call_for_speakers_open:
|
||||||
messages.error(request, "The Call for Speakers is not open.")
|
messages.error(request, "The Call for Speakers is not open.")
|
||||||
return redirect(reverse('proposal_list', kwargs={'camp_slug': self.camp.slug}))
|
return redirect(
|
||||||
|
reverse('proposal_list', kwargs={'camp_slug': self.camp.slug})
|
||||||
|
)
|
||||||
|
|
||||||
# alright, continue with the request
|
# alright, continue with the request
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
@ -23,8 +26,10 @@ class CreateProposalMixin(SingleObjectMixin):
|
||||||
# set camp and user before saving
|
# set camp and user before saving
|
||||||
form.instance.camp = self.camp
|
form.instance.camp = self.camp
|
||||||
form.instance.user = self.request.user
|
form.instance.user = self.request.user
|
||||||
speaker = form.save()
|
form.save()
|
||||||
return redirect(reverse('proposal_list', kwargs={'camp_slug': self.camp.slug}))
|
return redirect(
|
||||||
|
reverse('proposal_list', kwargs={'camp_slug': self.camp.slug})
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EnsureUnapprovedProposalMixin(SingleObjectMixin):
|
class EnsureUnapprovedProposalMixin(SingleObjectMixin):
|
||||||
|
@ -54,7 +59,9 @@ class EnsureUserOwnsProposalMixin(SingleObjectMixin):
|
||||||
# make sure that this proposal belongs to the logged in user
|
# make sure that this proposal belongs to the logged in user
|
||||||
if self.get_object().user.username != request.user.username:
|
if self.get_object().user.username != request.user.username:
|
||||||
messages.error(request, "No thanks")
|
messages.error(request, "No thanks")
|
||||||
return redirect(reverse('proposal_list', kwargs={'camp_slug': self.camp.slug}))
|
return redirect(
|
||||||
|
reverse('proposal_list', kwargs={'camp_slug': self.camp.slug})
|
||||||
|
)
|
||||||
|
|
||||||
# alright, continue with the request
|
# alright, continue with the request
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
@ -83,7 +90,10 @@ class PictureViewMixin(SingleObjectMixin):
|
||||||
def get_picture_response(self, path):
|
def get_picture_response(self, path):
|
||||||
if 'runserver' in sys.argv or 'runserver_plus' in sys.argv:
|
if 'runserver' in sys.argv or 'runserver_plus' in sys.argv:
|
||||||
# this is a local devserver situation, guess mimetype from extension and return picture directly
|
# this is a local devserver situation, guess mimetype from extension and return picture directly
|
||||||
response = HttpResponse(self.picture, content_type=mimetypes.types_map[".%s" % self.picture.name.split(".")[-1]])
|
response = HttpResponse(
|
||||||
|
self.picture,
|
||||||
|
content_type=mimetypes.types_map[".%s" % self.picture.name.split(".")[-1]]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# make nginx serve the picture using X-Accel-Redirect
|
# make nginx serve the picture using X-Accel-Redirect
|
||||||
# (this works for nginx only, other webservers use x-sendfile)
|
# (this works for nginx only, other webservers use x-sendfile)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import uuid
|
import uuid
|
||||||
import os
|
import os
|
||||||
import icalendar
|
import icalendar
|
||||||
import CommonMark
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -10,12 +9,9 @@ from django.contrib.postgres.fields import DateTimeRangeField
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
from django.dispatch import receiver
|
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.urlresolvers import reverse_lazy
|
from django.core.urlresolvers import reverse_lazy
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from django.core.urlresolvers import reverse_lazy, reverse
|
|
||||||
from django.core.files.storage import FileSystemStorage
|
from django.core.files.storage import FileSystemStorage
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
@ -644,7 +640,6 @@ class Speaker(CampRelatedModel):
|
||||||
def get_large_picture_url(self):
|
def get_large_picture_url(self):
|
||||||
return self.get_picture_url('large')
|
return self.get_picture_url('large')
|
||||||
|
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
data = {
|
data = {
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
|
@ -1,4 +1,3 @@
|
||||||
import datetime
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
@ -12,7 +11,6 @@ from django.contrib.admin.views.decorators import staff_member_required
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.db.models import Q
|
|
||||||
from django.template import Engine, Context
|
from django.template import Engine, Context
|
||||||
|
|
||||||
import icalendar
|
import icalendar
|
||||||
|
@ -34,7 +32,7 @@ from . import models
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
############## ical calendar ########################################################
|
# ical calendar
|
||||||
|
|
||||||
|
|
||||||
class ICSView(CampViewMixin, View):
|
class ICSView(CampViewMixin, View):
|
||||||
|
@ -90,7 +88,7 @@ class ICSView(CampViewMixin, View):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
############## proposals ########################################################
|
# proposals
|
||||||
|
|
||||||
|
|
||||||
class ProposalListView(LoginRequiredMixin, CampViewMixin, ListView):
|
class ProposalListView(LoginRequiredMixin, CampViewMixin, ListView):
|
||||||
|
@ -241,7 +239,7 @@ class EventProposalDetailView(LoginRequiredMixin, CampViewMixin, EnsureUserOwnsP
|
||||||
template_name = 'eventproposal_detail.html'
|
template_name = 'eventproposal_detail.html'
|
||||||
|
|
||||||
|
|
||||||
################## speakers ###############################################
|
# speakers
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(require_safe, name='dispatch')
|
@method_decorator(require_safe, name='dispatch')
|
||||||
|
@ -268,7 +266,7 @@ class SpeakerListView(CampViewMixin, ListView):
|
||||||
template_name = 'speaker_list.html'
|
template_name = 'speaker_list.html'
|
||||||
|
|
||||||
|
|
||||||
################## events ##############################################
|
# events
|
||||||
|
|
||||||
|
|
||||||
class EventListView(CampViewMixin, ListView):
|
class EventListView(CampViewMixin, ListView):
|
||||||
|
@ -281,7 +279,7 @@ class EventDetailView(CampViewMixin, DetailView):
|
||||||
template_name = 'schedule_event_detail.html'
|
template_name = 'schedule_event_detail.html'
|
||||||
|
|
||||||
|
|
||||||
################## schedule #############################################
|
# schedule
|
||||||
|
|
||||||
|
|
||||||
class NoScriptScheduleView(CampViewMixin, TemplateView):
|
class NoScriptScheduleView(CampViewMixin, TemplateView):
|
||||||
|
@ -293,13 +291,12 @@ class NoScriptScheduleView(CampViewMixin, TemplateView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ScheduleView(CampViewMixin, TemplateView):
|
class ScheduleView(CampViewMixin, TemplateView):
|
||||||
template_name = 'schedule_overview.html'
|
template_name = 'schedule_overview.html'
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
context = super(ScheduleView, self).get_context_data(**kwargs)
|
context = super(ScheduleView, self).get_context_data(**kwargs)
|
||||||
context['schedule_midnight_offset_hours'] = settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS;
|
context['schedule_midnight_offset_hours'] = settings.SCHEDULE_MIDNIGHT_OFFSET_HOURS
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@ -308,8 +305,8 @@ class CallForSpeakersView(CampViewMixin, TemplateView):
|
||||||
return '%s_call_for_speakers.html' % self.camp.slug
|
return '%s_call_for_speakers.html' % self.camp.slug
|
||||||
|
|
||||||
|
|
||||||
|
# control center
|
||||||
|
|
||||||
################## control center #############################################
|
|
||||||
|
|
||||||
class ProgramControlCenter(CampViewMixin, TemplateView):
|
class ProgramControlCenter(CampViewMixin, TemplateView):
|
||||||
template_name = "control/index.html"
|
template_name = "control/index.html"
|
||||||
|
|
|
@ -40,6 +40,7 @@ def available_from(product):
|
||||||
return "None"
|
return "None"
|
||||||
available_from.short_description = 'Available from'
|
available_from.short_description = 'Available from'
|
||||||
|
|
||||||
|
|
||||||
def available_to(product):
|
def available_to(product):
|
||||||
if product.available_in.upper:
|
if product.available_in.upper:
|
||||||
return product.available_in.upper.strftime("%c")
|
return product.available_in.upper.strftime("%c")
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.apps import AppConfig
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
class ShopConfig(AppConfig):
|
class ShopConfig(AppConfig):
|
||||||
name = 'shop'
|
name = 'shop'
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from vendor.coinify.coinify_api import CoinifyAPI
|
from vendor.coinify.coinify_api import CoinifyAPI
|
||||||
from vendor.coinify.coinify_callback import CoinifyCallback
|
|
||||||
from .models import CoinifyAPIRequest, CoinifyAPIInvoice, CoinifyAPICallback
|
from .models import CoinifyAPIRequest, CoinifyAPIInvoice, CoinifyAPICallback
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import json, logging
|
import json
|
||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ def save_coinify_callback(request, order):
|
||||||
# now attempt to parse json
|
# now attempt to parse json
|
||||||
try:
|
try:
|
||||||
parsed = json.loads(request.body.decode('utf-8'))
|
parsed = json.loads(request.body.decode('utf-8'))
|
||||||
except Exception as E:
|
except Exception:
|
||||||
parsed = ''
|
parsed = ''
|
||||||
|
|
||||||
# save this callback to db
|
# save this callback to db
|
||||||
|
@ -89,8 +90,8 @@ def handle_coinify_api_response(req, order):
|
||||||
if req.response['success']:
|
if req.response['success']:
|
||||||
# save this new coinify invoice to the DB
|
# save this new coinify invoice to the DB
|
||||||
coinifyinvoice = process_coinify_invoice_json(
|
coinifyinvoice = process_coinify_invoice_json(
|
||||||
invoicejson = req.response['data'],
|
invoicejson=req.response['data'],
|
||||||
order = order,
|
order=order,
|
||||||
)
|
)
|
||||||
return coinifyinvoice
|
return coinifyinvoice
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
|
|
||||||
def current_order(request):
|
def current_order(request):
|
||||||
if request.user.is_authenticated():
|
if request.user.is_authenticated():
|
||||||
order = None
|
order = None
|
||||||
|
@ -11,5 +8,3 @@ def current_order(request):
|
||||||
|
|
||||||
return {'current_order': order}
|
return {'current_order': order}
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
def calculate_epay_hash(order, request):
|
def calculate_epay_hash(order, request):
|
||||||
hashstring = (
|
hashstring = (
|
||||||
'{merchant_number}{description}11{amount}DKK'
|
'{merchant_number}{description}11{amount}DKK'
|
||||||
|
@ -10,9 +11,9 @@ def calculate_epay_hash(order, request):
|
||||||
description=order.description,
|
description=order.description,
|
||||||
amount=order.total*100,
|
amount=order.total*100,
|
||||||
order_id=order.pk,
|
order_id=order.pk,
|
||||||
accept_url = order.get_epay_accept_url(request),
|
accept_url=order.get_epay_accept_url(request),
|
||||||
cancel_url = order.get_cancel_url(request),
|
cancel_url=order.get_cancel_url(request),
|
||||||
callback_url = order.get_epay_callback_url(request),
|
callback_url=order.get_epay_callback_url(request),
|
||||||
md5_secret=settings.EPAY_MD5_SECRET,
|
md5_secret=settings.EPAY_MD5_SECRET,
|
||||||
)
|
)
|
||||||
epay_hash = hashlib.md5(hashstring.encode('utf-8')).hexdigest()
|
epay_hash = hashlib.md5(hashstring.encode('utf-8')).hexdigest()
|
||||||
|
@ -24,6 +25,7 @@ def validate_epay_callback(query):
|
||||||
for key, value in query.items():
|
for key, value in query.items():
|
||||||
if key != 'hash':
|
if key != 'hash':
|
||||||
hashstring += value
|
hashstring += value
|
||||||
hash = hashlib.md5((hashstring + settings.EPAY_MD5_SECRET).encode('utf-8')).hexdigest()
|
hash = hashlib.md5(
|
||||||
|
(hashstring + settings.EPAY_MD5_SECRET).encode('utf-8')
|
||||||
|
).hexdigest()
|
||||||
return hash == query['hash']
|
return hash == query['hash']
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from .models import Order
|
|
||||||
|
|
||||||
|
|
||||||
class AddToOrderForm(forms.Form):
|
class AddToOrderForm(forms.Form):
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from psycopg2.extras import DateTimeTZRange
|
|
||||||
|
|
||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
import io
|
|
||||||
import logging
|
import logging
|
||||||
import hashlib
|
|
||||||
import base64
|
|
||||||
import qrcode
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -202,7 +198,7 @@ class Order(CreatedUpdatedModel):
|
||||||
if request:
|
if request:
|
||||||
messages.success(request, "Created %s tickets of type: %s" % (order_product.quantity, order_product.product.ticket_type.name))
|
messages.success(request, "Created %s tickets of type: %s" % (order_product.quantity, order_product.product.ticket_type.name))
|
||||||
# and mark the OPR as handed_out=True
|
# and mark the OPR as handed_out=True
|
||||||
order_product.handed_out=True
|
order_product.handed_out = True
|
||||||
order_product.save()
|
order_product.save()
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
@ -210,8 +206,8 @@ class Order(CreatedUpdatedModel):
|
||||||
if not self.paid:
|
if not self.paid:
|
||||||
messages.error(request, "Order %s is not paid, so cannot mark it as refunded!" % self.pk)
|
messages.error(request, "Order %s is not paid, so cannot mark it as refunded!" % self.pk)
|
||||||
else:
|
else:
|
||||||
self.refunded=True
|
self.refunded = True
|
||||||
### delete any tickets related to this order
|
# delete any tickets related to this order
|
||||||
if self.tickets.all():
|
if self.tickets.all():
|
||||||
messages.success(request, "Order %s marked as refunded, deleting %s tickets..." % (self.pk, self.tickets.count()))
|
messages.success(request, "Order %s marked as refunded, deleting %s tickets..." % (self.pk, self.tickets.count()))
|
||||||
self.tickets.all().delete()
|
self.tickets.all().delete()
|
||||||
|
@ -259,7 +255,6 @@ class Order(CreatedUpdatedModel):
|
||||||
if not self.coinify_api_invoices.exists():
|
if not self.coinify_api_invoices.exists():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
coinifyinvoice = None
|
|
||||||
for tempinvoice in self.coinify_api_invoices.all():
|
for tempinvoice in self.coinify_api_invoices.all():
|
||||||
# we already have a coinifyinvoice for this order, check if it expired
|
# we already have a coinifyinvoice for this order, check if it expired
|
||||||
if not tempinvoice.expired:
|
if not tempinvoice.expired:
|
||||||
|
@ -512,7 +507,7 @@ class CoinifyAPIInvoice(CreatedUpdatedModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def expired(self):
|
def expired(self):
|
||||||
return parse_datetime(self.invoicejson['expire_time']) < timezone.now()
|
return parse_datetime(self.invoicejson['expire_time']) < timezone.now()
|
||||||
|
|
||||||
|
|
||||||
class CoinifyAPICallback(CreatedUpdatedModel):
|
class CoinifyAPICallback(CreatedUpdatedModel):
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
|
@ -1,6 +1,5 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from .views import *
|
from .views import *
|
||||||
from tickets.views import ShopTicketListView
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', ShopIndexView.as_view(), name='index'),
|
url(r'^$', ShopIndexView.as_view(), name='index'),
|
||||||
|
|
|
@ -3,21 +3,23 @@ from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.core.urlresolvers import reverse, reverse_lazy
|
from django.core.urlresolvers import reverse, reverse_lazy
|
||||||
from django.db.models import Count, F
|
from django.db.models import Count, F
|
||||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest, Http404
|
from django.http import (
|
||||||
|
HttpResponse,
|
||||||
|
HttpResponseRedirect,
|
||||||
|
HttpResponseBadRequest,
|
||||||
|
Http404
|
||||||
|
)
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.views.generic import (
|
from django.views.generic import (
|
||||||
View,
|
View,
|
||||||
TemplateView,
|
|
||||||
ListView,
|
ListView,
|
||||||
DetailView,
|
DetailView,
|
||||||
FormView,
|
FormView,
|
||||||
UpdateView,
|
|
||||||
)
|
)
|
||||||
from django.views.generic.base import RedirectView
|
from django.views.generic.base import RedirectView
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.utils.dateparse import parse_datetime
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from shop.models import (
|
from shop.models import (
|
||||||
|
@ -27,24 +29,22 @@ from shop.models import (
|
||||||
ProductCategory,
|
ProductCategory,
|
||||||
EpayCallback,
|
EpayCallback,
|
||||||
EpayPayment,
|
EpayPayment,
|
||||||
CoinifyAPIInvoice,
|
|
||||||
CoinifyAPICallback,
|
|
||||||
CreditNote,
|
CreditNote,
|
||||||
)
|
)
|
||||||
from .forms import AddToOrderForm
|
from .forms import AddToOrderForm
|
||||||
from .epay import calculate_epay_hash, validate_epay_callback
|
from .epay import calculate_epay_hash, validate_epay_callback
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from vendor.coinify.coinify_api import CoinifyAPI
|
|
||||||
from vendor.coinify.coinify_callback import CoinifyCallback
|
from vendor.coinify.coinify_callback import CoinifyCallback
|
||||||
from .coinify import create_coinify_invoice, save_coinify_callback, process_coinify_invoice_json
|
from .coinify import (
|
||||||
import json, time
|
create_coinify_invoice,
|
||||||
|
save_coinify_callback,
|
||||||
|
process_coinify_invoice_json
|
||||||
|
)
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
|
# Mixins
|
||||||
#################################################################################
|
|
||||||
### Mixins
|
|
||||||
class EnsureCreditNoteHasPDFMixin(SingleObjectMixin):
|
class EnsureCreditNoteHasPDFMixin(SingleObjectMixin):
|
||||||
model = CreditNote
|
model = CreditNote
|
||||||
|
|
||||||
|
@ -92,7 +92,9 @@ class EnsureUnpaidOrderMixin(SingleObjectMixin):
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
if self.get_object().paid:
|
if self.get_object().paid:
|
||||||
messages.error(request, "This order is already paid for!")
|
messages.error(request, "This order is already paid for!")
|
||||||
return HttpResponseRedirect(reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk}))
|
return HttpResponseRedirect(
|
||||||
|
reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk})
|
||||||
|
)
|
||||||
|
|
||||||
return super(EnsureUnpaidOrderMixin, self).dispatch(
|
return super(EnsureUnpaidOrderMixin, self).dispatch(
|
||||||
request, *args, **kwargs
|
request, *args, **kwargs
|
||||||
|
@ -105,7 +107,9 @@ class EnsurePaidOrderMixin(SingleObjectMixin):
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
if not self.get_object().paid:
|
if not self.get_object().paid:
|
||||||
messages.error(request, "This order is not paid for!")
|
messages.error(request, "This order is not paid for!")
|
||||||
return HttpResponseRedirect(reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk}))
|
return HttpResponseRedirect(
|
||||||
|
reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk})
|
||||||
|
)
|
||||||
|
|
||||||
return super(EnsurePaidOrderMixin, self).dispatch(
|
return super(EnsurePaidOrderMixin, self).dispatch(
|
||||||
request, *args, **kwargs
|
request, *args, **kwargs
|
||||||
|
@ -118,7 +122,9 @@ class EnsureClosedOrderMixin(SingleObjectMixin):
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
if self.get_object().open is not None:
|
if self.get_object().open is not None:
|
||||||
messages.error(request, 'This order is still open!')
|
messages.error(request, 'This order is still open!')
|
||||||
return HttpResponseRedirect(reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk}))
|
return HttpResponseRedirect(
|
||||||
|
reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk})
|
||||||
|
)
|
||||||
|
|
||||||
return super(EnsureClosedOrderMixin, self).dispatch(
|
return super(EnsureClosedOrderMixin, self).dispatch(
|
||||||
request, *args, **kwargs
|
request, *args, **kwargs
|
||||||
|
@ -160,15 +166,16 @@ class EnsureOrderHasInvoicePDFMixin(SingleObjectMixin):
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
if not self.get_object().invoice.pdf:
|
if not self.get_object().invoice.pdf:
|
||||||
messages.error(request, "This order has no invoice yet!")
|
messages.error(request, "This order has no invoice yet!")
|
||||||
return HttpResponseRedirect(reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk}))
|
return HttpResponseRedirect(
|
||||||
|
reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk})
|
||||||
|
)
|
||||||
|
|
||||||
return super(EnsureOrderHasInvoicePDFMixin, self).dispatch(
|
return super(EnsureOrderHasInvoicePDFMixin, self).dispatch(
|
||||||
request, *args, **kwargs
|
request, *args, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
#################################################################################
|
# Shop views
|
||||||
### Shop views
|
|
||||||
class ShopIndexView(ListView):
|
class ShopIndexView(ListView):
|
||||||
model = Product
|
model = Product
|
||||||
template_name = "shop_index.html"
|
template_name = "shop_index.html"
|
||||||
|
@ -215,7 +222,7 @@ class ProductDetailView(FormView, DetailView):
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
if not self.get_object().category.public:
|
if not self.get_object().category.public:
|
||||||
### this product is not publicly available
|
# this product is not publicly available
|
||||||
raise Http404("Product not found")
|
raise Http404("Product not found")
|
||||||
|
|
||||||
return super(ProductDetailView, self).dispatch(
|
return super(ProductDetailView, self).dispatch(
|
||||||
|
@ -264,7 +271,9 @@ class ProductDetailView(FormView, DetailView):
|
||||||
return super(ProductDetailView, self).form_valid(form)
|
return super(ProductDetailView, self).form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return Order.objects.get(user=self.request.user, open__isnull=False).get_absolute_url()
|
return Order.objects.get(
|
||||||
|
user=self.request.user, open__isnull=False
|
||||||
|
).get_absolute_url()
|
||||||
|
|
||||||
|
|
||||||
class OrderListView(LoginRequiredMixin, ListView):
|
class OrderListView(LoginRequiredMixin, ListView):
|
||||||
|
@ -289,7 +298,9 @@ class OrderDetailView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureOrderH
|
||||||
if payment_method in order.PAYMENT_METHODS:
|
if payment_method in order.PAYMENT_METHODS:
|
||||||
if not request.POST.get('accept_terms'):
|
if not request.POST.get('accept_terms'):
|
||||||
messages.error(request, "You need to accept the general terms and conditions before you can continue!")
|
messages.error(request, "You need to accept the general terms and conditions before you can continue!")
|
||||||
return HttpResponseRedirect(reverse_lazy('shop:order_detail', kwargs={'pk': order.pk}))
|
return HttpResponseRedirect(
|
||||||
|
reverse_lazy('shop:order_detail', kwargs={'pk': order.pk})
|
||||||
|
)
|
||||||
|
|
||||||
# Set payment method and mark the order as closed
|
# Set payment method and mark the order as closed
|
||||||
order.payment_method = payment_method
|
order.payment_method = payment_method
|
||||||
|
@ -389,9 +400,7 @@ class OrderMarkAsPaidView(LoginRequiredMixin, SingleObjectMixin, View):
|
||||||
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
|
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
|
||||||
|
|
||||||
|
|
||||||
#################################################################################
|
# Epay views
|
||||||
### Epay views
|
|
||||||
|
|
||||||
class EpayFormView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureClosedOrderMixin, EnsureOrderHasProductsMixin, DetailView):
|
class EpayFormView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureClosedOrderMixin, EnsureOrderHasProductsMixin, DetailView):
|
||||||
model = Order
|
model = Order
|
||||||
template_name = 'epay_form.html'
|
template_name = 'epay_form.html'
|
||||||
|
@ -435,18 +444,18 @@ class EpayCallbackView(SingleObjectMixin, View):
|
||||||
return HttpResponse(status=400)
|
return HttpResponse(status=400)
|
||||||
|
|
||||||
if order.paid:
|
if order.paid:
|
||||||
### this order is already paid, perhaps we are seeing a double callback?
|
# this order is already paid, perhaps we are seeing a double callback?
|
||||||
return HttpResponse('OK')
|
return HttpResponse('OK')
|
||||||
|
|
||||||
### epay callback is valid - has the order been paid in full?
|
# epay callback is valid - has the order been paid in full?
|
||||||
if int(query['amount']) == order.total * 100:
|
if int(query['amount']) == order.total * 100:
|
||||||
### create an EpayPayment object linking the callback to the order
|
# create an EpayPayment object linking the callback to the order
|
||||||
EpayPayment.objects.create(
|
EpayPayment.objects.create(
|
||||||
order=order,
|
order=order,
|
||||||
callback=callback,
|
callback=callback,
|
||||||
txnid=query.get('txnid'),
|
txnid=query.get('txnid'),
|
||||||
)
|
)
|
||||||
### and mark order as paid (this will create tickets)
|
# and mark order as paid (this will create tickets)
|
||||||
order.mark_as_paid(request)
|
order.mark_as_paid(request)
|
||||||
else:
|
else:
|
||||||
logger.error("valid epay callback with wrong amount detected")
|
logger.error("valid epay callback with wrong amount detected")
|
||||||
|
@ -464,15 +473,16 @@ class EpayThanksView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureClosedO
|
||||||
if request.GET:
|
if request.GET:
|
||||||
# epay redirects the user back to our accepturl with a long
|
# epay redirects the user back to our accepturl with a long
|
||||||
# and ugly querystring, redirect user to the clean url
|
# and ugly querystring, redirect user to the clean url
|
||||||
return HttpResponseRedirect(reverse('shop:epay_thanks', kwargs={'pk': self.get_object().pk}))
|
return HttpResponseRedirect(
|
||||||
|
reverse('shop:epay_thanks', kwargs={'pk': self.get_object().pk})
|
||||||
|
)
|
||||||
|
|
||||||
return super(EpayThanksView, self).dispatch(
|
return super(EpayThanksView, self).dispatch(
|
||||||
request, *args, **kwargs
|
request, *args, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
#################################################################################
|
# Bank Transfer view
|
||||||
### Bank Transfer view
|
|
||||||
|
|
||||||
class BankTransferView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureOrderHasProductsMixin, DetailView):
|
class BankTransferView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureOrderHasProductsMixin, DetailView):
|
||||||
model = Order
|
model = Order
|
||||||
|
@ -489,16 +499,14 @@ class BankTransferView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpai
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
#################################################################################
|
# Cash payment view
|
||||||
### Cash payment view
|
|
||||||
|
|
||||||
class CashView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureOrderHasProductsMixin, DetailView):
|
class CashView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureOrderHasProductsMixin, DetailView):
|
||||||
model = Order
|
model = Order
|
||||||
template_name = 'cash.html'
|
template_name = 'cash.html'
|
||||||
|
|
||||||
|
|
||||||
#################################################################################
|
# Coinify views
|
||||||
### Coinify views
|
|
||||||
|
|
||||||
class CoinifyRedirectView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureClosedOrderMixin, EnsureOrderHasProductsMixin, SingleObjectMixin, RedirectView):
|
class CoinifyRedirectView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUnpaidOrderMixin, EnsureClosedOrderMixin, EnsureOrderHasProductsMixin, SingleObjectMixin, RedirectView):
|
||||||
model = Order
|
model = Order
|
||||||
|
@ -511,7 +519,9 @@ class CoinifyRedirectView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureUn
|
||||||
coinifyinvoice = create_coinify_invoice(order, request)
|
coinifyinvoice = create_coinify_invoice(order, request)
|
||||||
if not coinifyinvoice:
|
if not coinifyinvoice:
|
||||||
messages.error(request, "There was a problem with the payment provider. Please try again later")
|
messages.error(request, "There was a problem with the payment provider. Please try again later")
|
||||||
return HttpResponseRedirect(reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk}))
|
return HttpResponseRedirect(
|
||||||
|
reverse_lazy('shop:order_detail', kwargs={'pk': self.get_object().pk})
|
||||||
|
)
|
||||||
|
|
||||||
return super(CoinifyRedirectView, self).dispatch(
|
return super(CoinifyRedirectView, self).dispatch(
|
||||||
request, *args, **kwargs
|
request, *args, **kwargs
|
||||||
|
@ -544,14 +554,17 @@ class CoinifyCallbackView(SingleObjectMixin, View):
|
||||||
# attemt to validate the callbackc
|
# attemt to validate the callbackc
|
||||||
if sdk.validate_callback(request.body, request.META['HTTP_X_COINIFY_CALLBACK_SIGNATURE']):
|
if sdk.validate_callback(request.body, request.META['HTTP_X_COINIFY_CALLBACK_SIGNATURE']):
|
||||||
# mark callback as valid in db
|
# mark callback as valid in db
|
||||||
callbackobject.valid=True
|
callbackobject.valid = True
|
||||||
callbackobject.save()
|
callbackobject.save()
|
||||||
else:
|
else:
|
||||||
logger.error("invalid coinify callback detected")
|
logger.error("invalid coinify callback detected")
|
||||||
return HttpResponseBadRequest('something is fucky')
|
return HttpResponseBadRequest('something is fucky')
|
||||||
|
|
||||||
if callbackobject.payload['event'] == 'invoice_state_change' or callbackobject.payload['event'] == 'invoice_manual_resend':
|
if callbackobject.payload['event'] == 'invoice_state_change' or callbackobject.payload['event'] == 'invoice_manual_resend':
|
||||||
coinifyinvoice = process_coinify_invoice_json(callbackobject.payload['data'], self.get_object())
|
process_coinify_invoice_json(
|
||||||
|
callbackobject.payload['data'],
|
||||||
|
self.get_object()
|
||||||
|
)
|
||||||
return HttpResponse('OK')
|
return HttpResponse('OK')
|
||||||
else:
|
else:
|
||||||
logger.error("unsupported callback event %s" % callbackobject.payload['event'])
|
logger.error("unsupported callback event %s" % callbackobject.payload['event'])
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ class Sponsor(CampRelatedModel):
|
||||||
def camp(self):
|
def camp(self):
|
||||||
return self.tier.camp
|
return self.tier.camp
|
||||||
|
|
||||||
|
|
||||||
class SponsorTier(CampRelatedModel):
|
class SponsorTier(CampRelatedModel):
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=25,
|
max_length=25,
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@ class EnsureWritableCampMixin(SingleObjectMixin):
|
||||||
# do not permit view if camp is in readonly mode
|
# do not permit view if camp is in readonly mode
|
||||||
if self.camp.read_only:
|
if self.camp.read_only:
|
||||||
messages.error(request, "No thanks")
|
messages.error(request, "No thanks")
|
||||||
return redirect(reverse('village_list', kwargs={'camp_slug': self.camp.slug}))
|
return redirect(
|
||||||
|
reverse('village_list', kwargs={'camp_slug': self.camp.slug})
|
||||||
|
)
|
||||||
|
|
||||||
# alright, continue with the request
|
# alright, continue with the request
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue