mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-25 10:34:21 +00:00
commit
fbcf525378
@ -35,5 +35,3 @@ admin.site.register(SimplifiedAccountingType)
|
||||
admin.site.register(Operation)
|
||||
admin.site.register(Label)
|
||||
admin.site.register(Company)
|
||||
|
||||
|
||||
|
@ -25,7 +25,6 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core import validators
|
||||
from django.db.models import Count
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
@ -37,6 +36,7 @@ from decimal import Decimal
|
||||
from core.models import User, SithFile
|
||||
from club.models import Club
|
||||
|
||||
|
||||
class CurrencyField(models.DecimalField):
|
||||
"""
|
||||
This is a custom database field used for currency
|
||||
@ -56,6 +56,7 @@ class CurrencyField(models.DecimalField):
|
||||
|
||||
# Accounting classes
|
||||
|
||||
|
||||
class Company(models.Model):
|
||||
name = models.CharField(_('name'), max_length=60)
|
||||
street = models.CharField(_('street'), max_length=60, blank=True)
|
||||
@ -104,6 +105,7 @@ class Company(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class BankAccount(models.Model):
|
||||
name = models.CharField(_('name'), max_length=30)
|
||||
iban = models.CharField(_('iban'), max_length=255, blank=True)
|
||||
@ -131,6 +133,7 @@ class BankAccount(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class ClubAccount(models.Model):
|
||||
name = models.CharField(_('name'), max_length=30)
|
||||
club = models.ForeignKey(Club, related_name="club_account", verbose_name=_("club"))
|
||||
@ -244,6 +247,7 @@ class GeneralJournal(models.Model):
|
||||
self.amount -= o.amount
|
||||
self.save()
|
||||
|
||||
|
||||
class Operation(models.Model):
|
||||
"""
|
||||
An operation is a line in the journal, a debit or a credit
|
||||
@ -352,6 +356,7 @@ class Operation(models.Model):
|
||||
self.amount, self.date, self.accounting_type, self.done,
|
||||
)
|
||||
|
||||
|
||||
class AccountingType(models.Model):
|
||||
"""
|
||||
Class describing the accounting types.
|
||||
@ -385,6 +390,7 @@ class AccountingType(models.Model):
|
||||
def __str__(self):
|
||||
return self.code + " - " + self.get_movement_type_display() + " - " + self.label
|
||||
|
||||
|
||||
class SimplifiedAccountingType(models.Model):
|
||||
"""
|
||||
Class describing the simplified accounting types.
|
||||
@ -410,6 +416,7 @@ class SimplifiedAccountingType(models.Model):
|
||||
def __str__(self):
|
||||
return self.get_movement_type_display() + " - " + self.accounting_type.code + " - " + self.label
|
||||
|
||||
|
||||
class Label(models.Model):
|
||||
"""Label allow a club to sort its operations"""
|
||||
name = models.CharField(_('label'), max_length=64)
|
||||
@ -432,4 +439,3 @@ class Label(models.Model):
|
||||
|
||||
def can_be_viewed_by(self, user):
|
||||
return self.club_account.can_be_viewed_by(user)
|
||||
|
||||
|
@ -22,15 +22,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.test import Client, TestCase
|
||||
from django.test import TestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.management import call_command
|
||||
from django.conf import settings
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
|
||||
from core.models import User
|
||||
from counter.models import Counter
|
||||
from accounting.models import GeneralJournal, Operation, Label, AccountingType, SimplifiedAccountingType
|
||||
|
||||
|
||||
@ -72,6 +69,7 @@ class RefoundAccountTest(TestCase):
|
||||
self.assertFalse(response_post.status_code == 403)
|
||||
self.assertTrue(self.skia.customer.amount == 0)
|
||||
|
||||
|
||||
class JournalTest(TestCase):
|
||||
def setUp(self):
|
||||
call_command("populate")
|
||||
@ -91,6 +89,7 @@ class JournalTest(TestCase):
|
||||
self.assertTrue(response_get.status_code == 403)
|
||||
self.assertFalse('<td>M\xc3\xa9thode de paiement</td>' in str(response_get.content))
|
||||
|
||||
|
||||
class OperationTest(TestCase):
|
||||
def setUp(self):
|
||||
call_command("populate")
|
||||
@ -137,7 +136,7 @@ class OperationTest(TestCase):
|
||||
|
||||
def test_bad_new_operation(self):
|
||||
self.client.login(username='comptable', password='plop')
|
||||
at = AccountingType.objects.filter(code = '604').first()
|
||||
AccountingType.objects.filter(code='604').first()
|
||||
response = self.client.post(reverse('accounting:op_new',
|
||||
args=[self.journal.id]),
|
||||
{'amount': 30,
|
||||
@ -216,7 +215,6 @@ class OperationTest(TestCase):
|
||||
response_get = self.client.get(reverse("accounting:journal_person_statement", args=[self.journal.id]))
|
||||
self.assertTrue("S' Kia</a></td>\\n \\n <td>3.00</td>" in str(response_get.content))
|
||||
|
||||
|
||||
def test_accounting_statement(self):
|
||||
self.client.login(username='comptable', password='plop')
|
||||
response_get = self.client.get(reverse("accounting:journal_accounting_statement", args=[self.journal.id]))
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from accounting.views import *
|
||||
|
||||
@ -71,5 +71,3 @@ urlpatterns = [
|
||||
# User account
|
||||
url(r'^refound/account$', RefoundAccountView.as_view(), name='refound_account'),
|
||||
]
|
||||
|
||||
|
||||
|
@ -22,25 +22,21 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.views.generic import ListView, DetailView, RedirectView
|
||||
from django.views.generic import ListView, DetailView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView, FormView
|
||||
from django.shortcuts import render
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.forms.models import modelform_factory
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.forms import HiddenInput, TextInput
|
||||
from django.forms import HiddenInput
|
||||
from django.db import transaction
|
||||
from django.db.models import Sum
|
||||
from django.conf import settings
|
||||
from django import forms
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.conf import settings
|
||||
|
||||
from django.http import HttpResponse
|
||||
import collections
|
||||
|
||||
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
|
||||
from ajax_select.fields import AutoCompleteSelectField
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
|
||||
from core.views.forms import SelectFile, SelectDate
|
||||
@ -49,6 +45,7 @@ from counter.models import Counter, Selling, Product
|
||||
|
||||
# Main accounting view
|
||||
|
||||
|
||||
class BankAccountListView(CanViewMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
@ -57,6 +54,7 @@ class BankAccountListView(CanViewMixin, ListView):
|
||||
template_name = 'accounting/bank_account_list.jinja'
|
||||
ordering = ['name']
|
||||
|
||||
|
||||
# Simplified accounting types
|
||||
|
||||
class SimplifiedAccountingTypeListView(CanViewMixin, ListView):
|
||||
@ -66,6 +64,7 @@ class SimplifiedAccountingTypeListView(CanViewMixin, ListView):
|
||||
model = SimplifiedAccountingType
|
||||
template_name = 'accounting/simplifiedaccountingtype_list.jinja'
|
||||
|
||||
|
||||
class SimplifiedAccountingTypeEditView(CanViewMixin, UpdateView):
|
||||
"""
|
||||
An edit view for the admins
|
||||
@ -75,6 +74,7 @@ class SimplifiedAccountingTypeEditView(CanViewMixin, UpdateView):
|
||||
fields = ['label', 'accounting_type']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class SimplifiedAccountingTypeCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create an accounting type (for the admins)
|
||||
@ -83,6 +83,7 @@ class SimplifiedAccountingTypeCreateView(CanCreateMixin, CreateView):
|
||||
fields = ['label', 'accounting_type']
|
||||
template_name = 'core/create.jinja'
|
||||
|
||||
|
||||
# Accounting types
|
||||
|
||||
class AccountingTypeListView(CanViewMixin, ListView):
|
||||
@ -92,6 +93,7 @@ class AccountingTypeListView(CanViewMixin, ListView):
|
||||
model = AccountingType
|
||||
template_name = 'accounting/accountingtype_list.jinja'
|
||||
|
||||
|
||||
class AccountingTypeEditView(CanViewMixin, UpdateView):
|
||||
"""
|
||||
An edit view for the admins
|
||||
@ -101,6 +103,7 @@ class AccountingTypeEditView(CanViewMixin, UpdateView):
|
||||
fields = ['code', 'label', 'movement_type']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class AccountingTypeCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create an accounting type (for the admins)
|
||||
@ -109,6 +112,7 @@ class AccountingTypeCreateView(CanCreateMixin, CreateView):
|
||||
fields = ['code', 'label', 'movement_type']
|
||||
template_name = 'core/create.jinja'
|
||||
|
||||
|
||||
# BankAccount views
|
||||
|
||||
class BankAccountEditView(CanViewMixin, UpdateView):
|
||||
@ -120,6 +124,7 @@ class BankAccountEditView(CanViewMixin, UpdateView):
|
||||
fields = ['name', 'iban', 'number', 'club']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class BankAccountDetailView(CanViewMixin, DetailView):
|
||||
"""
|
||||
A detail view, listing every club account
|
||||
@ -128,6 +133,7 @@ class BankAccountDetailView(CanViewMixin, DetailView):
|
||||
pk_url_kwarg = "b_account_id"
|
||||
template_name = 'accounting/bank_account_details.jinja'
|
||||
|
||||
|
||||
class BankAccountCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create a bank account (for the admins)
|
||||
@ -136,6 +142,7 @@ class BankAccountCreateView(CanCreateMixin, CreateView):
|
||||
fields = ['name', 'club', 'iban', 'number']
|
||||
template_name = 'core/create.jinja'
|
||||
|
||||
|
||||
class BankAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
|
||||
"""
|
||||
Delete a bank account (for the admins)
|
||||
@ -145,6 +152,7 @@ class BankAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete
|
||||
template_name = 'core/delete_confirm.jinja'
|
||||
success_url = reverse_lazy('accounting:bank_list')
|
||||
|
||||
|
||||
# ClubAccount views
|
||||
|
||||
class ClubAccountEditView(CanViewMixin, UpdateView):
|
||||
@ -156,6 +164,7 @@ class ClubAccountEditView(CanViewMixin, UpdateView):
|
||||
fields = ['name', 'club', 'bank_account']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class ClubAccountDetailView(CanViewMixin, DetailView):
|
||||
"""
|
||||
A detail view, listing every journal
|
||||
@ -164,6 +173,7 @@ class ClubAccountDetailView(CanViewMixin, DetailView):
|
||||
pk_url_kwarg = "c_account_id"
|
||||
template_name = 'accounting/club_account_details.jinja'
|
||||
|
||||
|
||||
class ClubAccountCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create a club account (for the admins)
|
||||
@ -180,6 +190,7 @@ class ClubAccountCreateView(CanCreateMixin, CreateView):
|
||||
ret['bank_account'] = obj.id
|
||||
return ret
|
||||
|
||||
|
||||
class ClubAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete to Close
|
||||
"""
|
||||
Delete a club account (for the admins)
|
||||
@ -189,6 +200,7 @@ class ClubAccountDeleteView(CanEditPropMixin, DeleteView): # TODO change Delete
|
||||
template_name = 'core/delete_confirm.jinja'
|
||||
success_url = reverse_lazy('accounting:bank_list')
|
||||
|
||||
|
||||
# Journal views
|
||||
|
||||
class JournalTabsMixin(TabedViewMixin):
|
||||
@ -219,6 +231,7 @@ class JournalTabsMixin(TabedViewMixin):
|
||||
})
|
||||
return tab_list
|
||||
|
||||
|
||||
class JournalCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create a general journal
|
||||
@ -236,6 +249,7 @@ class JournalCreateView(CanCreateMixin, CreateView):
|
||||
ret['club_account'] = obj.id
|
||||
return ret
|
||||
|
||||
|
||||
class JournalDetailView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
A detail view, listing every operation
|
||||
@ -245,6 +259,7 @@ class JournalDetailView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
template_name = 'accounting/journal_details.jinja'
|
||||
current_tab = 'journal'
|
||||
|
||||
|
||||
class JournalEditView(CanEditMixin, UpdateView):
|
||||
"""
|
||||
Update a general journal
|
||||
@ -254,6 +269,7 @@ class JournalEditView(CanEditMixin, UpdateView):
|
||||
fields = ['name', 'start_date', 'end_date', 'club_account', 'closed']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class JournalDeleteView(CanEditPropMixin, DeleteView):
|
||||
"""
|
||||
Delete a club account (for the admins)
|
||||
@ -270,6 +286,7 @@ class JournalDeleteView(CanEditPropMixin, DeleteView):
|
||||
else:
|
||||
raise PermissionDenied
|
||||
|
||||
|
||||
# Operation views
|
||||
|
||||
class OperationForm(forms.ModelForm):
|
||||
@ -350,6 +367,7 @@ class OperationForm(forms.ModelForm):
|
||||
self.save()
|
||||
return ret
|
||||
|
||||
|
||||
class OperationCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create an operation
|
||||
@ -376,6 +394,7 @@ class OperationCreateView(CanCreateMixin, CreateView):
|
||||
kwargs['object'] = self.journal
|
||||
return kwargs
|
||||
|
||||
|
||||
class OperationEditView(CanEditMixin, UpdateView):
|
||||
"""
|
||||
An edit view, working as detail for the moment
|
||||
@ -391,6 +410,7 @@ class OperationEditView(CanEditMixin, UpdateView):
|
||||
kwargs['object'] = self.object.journal
|
||||
return kwargs
|
||||
|
||||
|
||||
class OperationPDFView(CanViewMixin, DetailView):
|
||||
"""
|
||||
Display the PDF of a given operation
|
||||
@ -402,11 +422,10 @@ class OperationPDFView(CanViewMixin, DetailView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.lib.units import cm
|
||||
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
|
||||
from reportlab.platypus import Table, TableStyle
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import letter
|
||||
from reportlab.lib.utils import ImageReader
|
||||
from reportlab.graphics.shapes import Drawing
|
||||
from reportlab.pdfbase.ttfonts import TTFont
|
||||
from reportlab.pdfbase import pdfmetrics
|
||||
|
||||
@ -419,7 +438,6 @@ class OperationPDFView(CanViewMixin, DetailView):
|
||||
num = self.object.number
|
||||
date = self.object.date
|
||||
mode = self.object.mode
|
||||
cheque_number = self.object.cheque_number
|
||||
club_name = self.object.journal.club_account.name
|
||||
ti = self.object.journal.name
|
||||
op_label = self.object.label
|
||||
@ -455,7 +473,7 @@ class OperationPDFView(CanViewMixin, DetailView):
|
||||
|
||||
p.drawString(90, height - 100, _("Financial proof: ") + "OP%010d" % (id_op)) # Justificatif du libellé
|
||||
p.drawString(90, height - 130, _("Club: %(club_name)s") % ({"club_name": club_name}))
|
||||
p.drawString(90, height - 160, _("Label: %(op_label)s") % {"op_label": op_label if op_label != None else ""})
|
||||
p.drawString(90, height - 160, _("Label: %(op_label)s") % {"op_label": op_label if op_label is not None else ""})
|
||||
p.drawString(90, height - 190, _("Date: %(date)s") % {"date": date})
|
||||
|
||||
data = []
|
||||
@ -516,6 +534,7 @@ class OperationPDFView(CanViewMixin, DetailView):
|
||||
p.save()
|
||||
return response
|
||||
|
||||
|
||||
class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a statement sorted by labels
|
||||
@ -532,8 +551,10 @@ class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
for sat in [None] + list(SimplifiedAccountingType.objects.order_by('label').all()):
|
||||
sum = queryset.filter(accounting_type__movement_type=movement_type,
|
||||
simpleaccounting_type=sat).aggregate(amount_sum=Sum('amount'))['amount_sum']
|
||||
if sat: sat = sat.label
|
||||
else: sat = ""
|
||||
if sat:
|
||||
sat = sat.label
|
||||
else:
|
||||
sat = ""
|
||||
if sum:
|
||||
total_sum += sum
|
||||
statement[sat] = sum
|
||||
@ -566,6 +587,7 @@ class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['statement'] = self.big_statement()
|
||||
return kwargs
|
||||
|
||||
|
||||
class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Calculate a dictionary with operation target and sum of operations
|
||||
@ -598,6 +620,7 @@ class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['total_debit'] = self.total("DEBIT")
|
||||
return kwargs
|
||||
|
||||
|
||||
class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Calculate a dictionary with operation type and sum of operations
|
||||
@ -624,10 +647,12 @@ class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView)
|
||||
|
||||
# Company views
|
||||
|
||||
|
||||
class CompanyListView(CanViewMixin, ListView):
|
||||
model = Company
|
||||
template_name = 'accounting/co_list.jinja'
|
||||
|
||||
|
||||
class CompanyCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create a company
|
||||
@ -648,6 +673,7 @@ class CompanyEditView(CanCreateMixin, UpdateView):
|
||||
template_name = 'core/edit.jinja'
|
||||
success_url = reverse_lazy('accounting:co_list')
|
||||
|
||||
|
||||
# Label views
|
||||
|
||||
class LabelListView(CanViewMixin, DetailView):
|
||||
@ -655,6 +681,7 @@ class LabelListView(CanViewMixin, DetailView):
|
||||
pk_url_kwarg = "clubaccount_id"
|
||||
template_name = 'accounting/label_list.jinja'
|
||||
|
||||
|
||||
class LabelCreateView(CanCreateMixin, CreateView): # FIXME we need to check the rights before creating the object
|
||||
model = Label
|
||||
form_class = modelform_factory(Label, fields=['name', 'club_account'], widgets={
|
||||
@ -670,12 +697,14 @@ class LabelCreateView(CanCreateMixin, CreateView): # FIXME we need to check the
|
||||
ret['club_account'] = obj.id
|
||||
return ret
|
||||
|
||||
|
||||
class LabelEditView(CanEditMixin, UpdateView):
|
||||
model = Label
|
||||
pk_url_kwarg = "label_id"
|
||||
fields = ['name']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class LabelDeleteView(CanEditMixin, DeleteView):
|
||||
model = Label
|
||||
pk_url_kwarg = "label_id"
|
||||
@ -684,9 +713,11 @@ class LabelDeleteView(CanEditMixin, DeleteView):
|
||||
def get_success_url(self):
|
||||
return self.object.get_absolute_url()
|
||||
|
||||
|
||||
class CloseCustomerAccountForm(forms.Form):
|
||||
user = AutoCompleteSelectField('users', label=_('Refound this account'), help_text=None, required=True)
|
||||
|
||||
|
||||
class RefoundAccountView(FormView):
|
||||
"""
|
||||
Create a selling with the same amount than the current user money
|
||||
|
@ -29,4 +29,3 @@ from club.models import Club, Membership
|
||||
|
||||
admin.site.register(Club)
|
||||
admin.site.register(Membership)
|
||||
|
||||
|
@ -27,12 +27,13 @@ from django.core import validators
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.db import transaction
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from core.models import User, MetaGroup, Group, SithFile
|
||||
|
||||
|
||||
# Create your models here.
|
||||
|
||||
class Club(models.Model):
|
||||
@ -153,6 +154,7 @@ class Club(models.Model):
|
||||
return sub.is_subscribed
|
||||
|
||||
_memberships = {}
|
||||
|
||||
def get_membership_for(self, user):
|
||||
"""
|
||||
Returns the current membership the given user
|
||||
@ -168,6 +170,7 @@ class Club(models.Model):
|
||||
Club._memberships[self.id][user.id] = m
|
||||
return m
|
||||
|
||||
|
||||
class Membership(models.Model):
|
||||
"""
|
||||
The Membership class makes the connection between User and Clubs
|
||||
@ -216,4 +219,3 @@ class Membership(models.Model):
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('club:club_members', kwargs={'club_id': self.club.id})
|
||||
|
||||
|
@ -31,6 +31,7 @@ from club.models import Club
|
||||
|
||||
# Create your tests here.
|
||||
|
||||
|
||||
class ClubTest(TestCase):
|
||||
def setUp(self):
|
||||
call_command("populate")
|
||||
@ -103,4 +104,3 @@ class ClubTest(TestCase):
|
||||
"role": 10})
|
||||
self.assertTrue(response.status_code == 200)
|
||||
self.assertTrue("<li>Vous n'avez pas la permission de faire cela</li>" in str(response.content))
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from club.views import *
|
||||
|
||||
@ -40,4 +40,3 @@ urlpatterns = [
|
||||
url(r'^(?P<club_id>[0-9]+)/tools$', ClubToolsView.as_view(), name='tools'),
|
||||
url(r'^membership/(?P<membership_id>[0-9]+)/set_old$', MembershipSetOldView.as_view(), name='membership_set_old'),
|
||||
]
|
||||
|
||||
|
@ -23,27 +23,21 @@
|
||||
#
|
||||
|
||||
from django import forms
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import ListView, DetailView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, CreateView
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext as _t
|
||||
from django.conf import settings
|
||||
from ajax_select.fields import AutoCompleteSelectField
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin
|
||||
from core.views.forms import SelectDate, SelectSingle, SelectDateTime
|
||||
from core.views.forms import SelectDate, SelectDateTime
|
||||
from club.models import Club, Membership
|
||||
from core.models import User
|
||||
from sith.settings import SITH_MAXIMUM_FREE_ROLE, SITH_MAIN_BOARD_GROUP
|
||||
from counter.models import Product, Selling, Counter
|
||||
from sith.settings import SITH_MAXIMUM_FREE_ROLE
|
||||
from counter.models import Selling, Counter
|
||||
|
||||
|
||||
class ClubTabsMixin(TabedViewMixin):
|
||||
def get_tabs_title(self):
|
||||
@ -91,6 +85,7 @@ class ClubTabsMixin(TabedViewMixin):
|
||||
})
|
||||
return tab_list
|
||||
|
||||
|
||||
class ClubListView(ListView):
|
||||
"""
|
||||
List the Clubs
|
||||
@ -98,6 +93,7 @@ class ClubListView(ListView):
|
||||
model = Club
|
||||
template_name = 'club/club_list.jinja'
|
||||
|
||||
|
||||
class ClubView(ClubTabsMixin, DetailView):
|
||||
"""
|
||||
Front page of a Club
|
||||
@ -107,6 +103,7 @@ class ClubView(ClubTabsMixin, DetailView):
|
||||
template_name = 'club/club_detail.jinja'
|
||||
current_tab = "infos"
|
||||
|
||||
|
||||
class ClubToolsView(ClubTabsMixin, CanEditMixin, DetailView):
|
||||
"""
|
||||
Tools page of a Club
|
||||
@ -116,12 +113,14 @@ class ClubToolsView(ClubTabsMixin, CanEditMixin, DetailView):
|
||||
template_name = 'club/club_tools.jinja'
|
||||
current_tab = "tools"
|
||||
|
||||
|
||||
class ClubMemberForm(forms.ModelForm):
|
||||
"""
|
||||
Form handling the members of a club
|
||||
"""
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
|
||||
class Meta:
|
||||
model = Membership
|
||||
fields = ['user', 'role', 'start_date', 'description']
|
||||
@ -134,9 +133,10 @@ class ClubMemberForm(forms.ModelForm):
|
||||
"""
|
||||
Overloaded to return the club, and not to a Membership object that has no view
|
||||
"""
|
||||
ret = super(ClubMemberForm, self).save(*args, **kwargs)
|
||||
super(ClubMemberForm, self).save(*args, **kwargs)
|
||||
return self.instance.club
|
||||
|
||||
|
||||
class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
|
||||
"""
|
||||
View of a club's members
|
||||
@ -182,6 +182,7 @@ class ClubMembersView(ClubTabsMixin, CanViewMixin, UpdateView):
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
|
||||
|
||||
class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Old members of a club
|
||||
@ -191,11 +192,13 @@ class ClubOldMembersView(ClubTabsMixin, CanViewMixin, DetailView):
|
||||
template_name = 'club/club_old_members.jinja'
|
||||
current_tab = "elderlies"
|
||||
|
||||
|
||||
class SellingsFormBase(forms.Form):
|
||||
begin_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Begin date"), required=False, widget=SelectDateTime)
|
||||
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), required=False, widget=SelectDateTime)
|
||||
counter = forms.ModelChoiceField(Counter.objects.order_by('name').all(), label=_("Counter"), required=False)
|
||||
|
||||
|
||||
class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView):
|
||||
"""
|
||||
Sellings of a club
|
||||
@ -235,6 +238,7 @@ class ClubSellingView(ClubTabsMixin, CanEditMixin, DetailView):
|
||||
kwargs['form'] = form
|
||||
return kwargs
|
||||
|
||||
|
||||
class ClubSellingCSVView(ClubSellingView):
|
||||
"""
|
||||
Generate sellings in csv for a given period
|
||||
@ -258,21 +262,25 @@ class ClubSellingCSVView(ClubSellingView):
|
||||
row = [o.date, o.counter]
|
||||
if o.seller:
|
||||
row.append(o.seller.get_display_name())
|
||||
else: row.append('')
|
||||
else:
|
||||
row.append('')
|
||||
if o.customer:
|
||||
row.append(o.customer.user.get_display_name())
|
||||
else: row.append('')
|
||||
else:
|
||||
row.append('')
|
||||
row = row + [o.label, o.quantity, o.quantity * o.unit_price,
|
||||
o.get_payment_method_display()]
|
||||
if o.product:
|
||||
row.append(o.product.selling_price)
|
||||
row.append(o.product.purchase_price)
|
||||
row.append(o.product.selling_price - o.product.purchase_price)
|
||||
else: row = row + ['', '', '']
|
||||
else:
|
||||
row = row + ['', '', '']
|
||||
writer.writerow(row)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
class ClubEditView(ClubTabsMixin, CanEditMixin, UpdateView):
|
||||
"""
|
||||
Edit a Club's main informations (for the club's members)
|
||||
@ -283,6 +291,7 @@ class ClubEditView(ClubTabsMixin, CanEditMixin, UpdateView):
|
||||
template_name = 'core/edit.jinja'
|
||||
current_tab = "edit"
|
||||
|
||||
|
||||
class ClubEditPropView(ClubTabsMixin, CanEditPropMixin, UpdateView):
|
||||
"""
|
||||
Edit the properties of a Club object (for the Sith admins)
|
||||
@ -293,6 +302,7 @@ class ClubEditPropView(ClubTabsMixin, CanEditPropMixin, UpdateView):
|
||||
template_name = 'core/edit.jinja'
|
||||
current_tab = "props"
|
||||
|
||||
|
||||
class ClubCreateView(CanEditPropMixin, CreateView):
|
||||
"""
|
||||
Create a club (for the Sith admin)
|
||||
@ -302,6 +312,7 @@ class ClubCreateView(CanEditPropMixin, CreateView):
|
||||
fields = ['name', 'unix_name', 'parent']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class MembershipSetOldView(CanEditMixin, DetailView):
|
||||
"""
|
||||
Set a membership as beeing old
|
||||
@ -319,6 +330,7 @@ class MembershipSetOldView(CanEditMixin, DetailView):
|
||||
self.object = self.get_object()
|
||||
return HttpResponseRedirect(reverse('club:club_members', args=self.args, kwargs={'club_id': self.object.club.id}))
|
||||
|
||||
|
||||
class ClubStatView(TemplateView):
|
||||
template_name = "club/stats.jinja"
|
||||
|
||||
|
@ -30,5 +30,3 @@ admin.site.register(Sith)
|
||||
admin.site.register(News)
|
||||
admin.site.register(Weekmail)
|
||||
|
||||
|
||||
|
||||
|
@ -25,15 +25,14 @@
|
||||
from django.shortcuts import render
|
||||
from django.db import models, transaction
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.contrib.staticfiles.templatetags.staticfiles import static
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from core.models import User, Preferences
|
||||
from club.models import Club
|
||||
import os
|
||||
|
||||
|
||||
class Sith(models.Model):
|
||||
"""A one instance class storing all the modifiable infos"""
|
||||
@ -48,6 +47,7 @@ class Sith(models.Model):
|
||||
def __str__(self):
|
||||
return "⛩ Sith ⛩"
|
||||
|
||||
|
||||
NEWS_TYPES = [
|
||||
('NOTICE', _('Notice')),
|
||||
('EVENT', _('Event')),
|
||||
@ -55,6 +55,7 @@ NEWS_TYPES = [
|
||||
('CALL', _('Call')),
|
||||
]
|
||||
|
||||
|
||||
class News(models.Model):
|
||||
"""The news class"""
|
||||
title = models.CharField(_("title"), max_length=64)
|
||||
@ -81,6 +82,7 @@ class News(models.Model):
|
||||
def __str__(self):
|
||||
return "%s: %s" % (self.type, self.title)
|
||||
|
||||
|
||||
class NewsDate(models.Model):
|
||||
"""
|
||||
A date class, useful for weekly events, or for events that just have no date
|
||||
@ -95,6 +97,7 @@ class NewsDate(models.Model):
|
||||
def __str__(self):
|
||||
return "%s: %s - %s" % (self.news.title, self.start_date, self.end_date)
|
||||
|
||||
|
||||
class Weekmail(models.Model):
|
||||
"""
|
||||
The weekmail class
|
||||
@ -144,6 +147,7 @@ class Weekmail(models.Model):
|
||||
def is_owned_by(self, user):
|
||||
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
|
||||
|
||||
|
||||
class WeekmailArticle(models.Model):
|
||||
weekmail = models.ForeignKey(Weekmail, related_name="articles", verbose_name=_("weekmail"), null=True)
|
||||
title = models.CharField(_("title"), max_length=64)
|
||||
|
@ -28,7 +28,7 @@ from django.core.urlresolvers import reverse
|
||||
from django.core.management import call_command
|
||||
|
||||
from core.models import User, RealGroup
|
||||
from com.models import Sith
|
||||
|
||||
|
||||
class ComTest(TestCase):
|
||||
def setUp(self):
|
||||
@ -56,4 +56,3 @@ class ComTest(TestCase):
|
||||
r = self.client.get(reverse("core:index"))
|
||||
self.assertTrue(r.status_code == 200)
|
||||
self.assertTrue("""<div id="info_box">\\n <div class="markdown"><h3>INFO: <strong>Caaaataaaapuuuulte!!!!</strong></h3>""" in str(r.content))
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from com.views import *
|
||||
|
||||
|
46
com/views.py
46
com/views.py
@ -22,9 +22,9 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.shortcuts import redirect, get_object_or_404
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.views.generic import ListView, DetailView, RedirectView
|
||||
from django.views.generic import ListView, DetailView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
@ -49,6 +49,7 @@ from club.models import Club
|
||||
|
||||
sith = Sith.objects.first
|
||||
|
||||
|
||||
class ComTabsMixin(TabedViewMixin):
|
||||
def get_tabs_title(self):
|
||||
return _("Communication administration")
|
||||
@ -82,6 +83,7 @@ class ComTabsMixin(TabedViewMixin):
|
||||
})
|
||||
return tab_list
|
||||
|
||||
|
||||
class ComEditView(ComTabsMixin, CanEditPropMixin, UpdateView):
|
||||
model = Sith
|
||||
template_name = 'core/edit.jinja'
|
||||
@ -89,21 +91,25 @@ class ComEditView(ComTabsMixin, CanEditPropMixin, UpdateView):
|
||||
def get_object(self, queryset=None):
|
||||
return Sith.objects.first()
|
||||
|
||||
|
||||
class AlertMsgEditView(ComEditView):
|
||||
fields = ['alert_msg']
|
||||
current_tab = "alert"
|
||||
success_url = reverse_lazy('com:alert_edit')
|
||||
|
||||
|
||||
class InfoMsgEditView(ComEditView):
|
||||
fields = ['info_msg']
|
||||
current_tab = "info"
|
||||
success_url = reverse_lazy('com:info_edit')
|
||||
|
||||
|
||||
class IndexEditView(ComEditView):
|
||||
fields = ['index_page']
|
||||
current_tab = "index"
|
||||
success_url = reverse_lazy('com:index_edit')
|
||||
|
||||
|
||||
class WeekmailDestinationEditView(ComEditView):
|
||||
fields = ['weekmail_destinations']
|
||||
current_tab = "weekmail_destinations"
|
||||
@ -111,6 +117,7 @@ class WeekmailDestinationEditView(ComEditView):
|
||||
|
||||
# News
|
||||
|
||||
|
||||
class NewsForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = News
|
||||
@ -155,6 +162,7 @@ class NewsForm(forms.ModelForm):
|
||||
end_date += timedelta(days=7)
|
||||
return ret
|
||||
|
||||
|
||||
class NewsEditView(CanEditMixin, UpdateView):
|
||||
model = News
|
||||
form_class = NewsForm
|
||||
@ -165,15 +173,17 @@ class NewsEditView(CanEditMixin, UpdateView):
|
||||
init = {}
|
||||
try:
|
||||
init['start_date'] = self.object.dates.order_by('id').first().start_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
init['end_date'] = self.object.dates.order_by('id').first().end_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return init
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
form = self.get_form()
|
||||
if form.is_valid() and not 'preview' in request.POST.keys():
|
||||
if form.is_valid() and 'preview' not in request.POST.keys():
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
@ -192,6 +202,7 @@ class NewsEditView(CanEditMixin, UpdateView):
|
||||
Notification(user=u, url=reverse("com:news_detail", kwargs={'news_id': self.object.id}), type="NEWS_MODERATION").save()
|
||||
return super(NewsEditView, self).form_valid(form)
|
||||
|
||||
|
||||
class NewsCreateView(CanCreateMixin, CreateView):
|
||||
model = News
|
||||
form_class = NewsForm
|
||||
@ -201,12 +212,13 @@ class NewsCreateView(CanCreateMixin, CreateView):
|
||||
init = {'author': self.request.user}
|
||||
try:
|
||||
init['club'] = Club.objects.filter(id=self.request.GET['club']).first()
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return init
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
form = self.get_form()
|
||||
if form.is_valid() and not 'preview' in request.POST.keys():
|
||||
if form.is_valid() and 'preview' not in request.POST.keys():
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
self.object = form.instance
|
||||
@ -224,6 +236,7 @@ class NewsCreateView(CanCreateMixin, CreateView):
|
||||
Notification(user=u, url=reverse("com:news_detail", kwargs={'news_id': self.object.id}), type="NEWS_MODERATION").save()
|
||||
return super(NewsCreateView, self).form_valid(form)
|
||||
|
||||
|
||||
class NewsModerateView(CanEditMixin, SingleObjectMixin):
|
||||
model = News
|
||||
pk_url_kwarg = 'news_id'
|
||||
@ -240,11 +253,13 @@ class NewsModerateView(CanEditMixin, SingleObjectMixin):
|
||||
return redirect(self.request.GET['next'])
|
||||
return redirect('com:news_admin_list')
|
||||
|
||||
|
||||
class NewsAdminListView(CanEditMixin, ListView):
|
||||
model = News
|
||||
template_name = 'com/news_admin_list.jinja'
|
||||
queryset = News.objects.filter(dates__end_date__gte=timezone.now()).distinct().order_by('id')
|
||||
|
||||
|
||||
class NewsListView(CanViewMixin, ListView):
|
||||
model = News
|
||||
template_name = 'com/news_list.jinja'
|
||||
@ -255,6 +270,7 @@ class NewsListView(CanViewMixin, ListView):
|
||||
kwargs['timedelta'] = timedelta
|
||||
return kwargs
|
||||
|
||||
|
||||
class NewsDetailView(CanViewMixin, DetailView):
|
||||
model = News
|
||||
template_name = 'com/news_detail.jinja'
|
||||
@ -262,6 +278,7 @@ class NewsDetailView(CanViewMixin, DetailView):
|
||||
|
||||
# Weekmail
|
||||
|
||||
|
||||
class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
|
||||
model = Weekmail
|
||||
template_name = 'com/weekmail_preview.jinja'
|
||||
@ -274,7 +291,8 @@ class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
|
||||
if request.POST['send'] == "validate":
|
||||
self.object.send()
|
||||
return HttpResponseRedirect(reverse('com:weekmail') + "?qn_weekmail_send_success")
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return super(WeekmailEditView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
@ -286,6 +304,7 @@ class WeekmailPreviewView(ComTabsMixin, CanEditPropMixin, DetailView):
|
||||
kwargs['weekmail_rendered'] = self.object.render_html()
|
||||
return kwargs
|
||||
|
||||
|
||||
class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateView):
|
||||
model = Weekmail
|
||||
template_name = 'com/weekmail.jinja'
|
||||
@ -341,6 +360,7 @@ class WeekmailEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateVi
|
||||
kwargs['orphans'] = WeekmailArticle.objects.filter(weekmail=None)
|
||||
return kwargs
|
||||
|
||||
|
||||
class WeekmailArticleEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, UpdateView):
|
||||
"""Edit an article"""
|
||||
model = WeekmailArticle
|
||||
@ -351,6 +371,7 @@ class WeekmailArticleEditView(ComTabsMixin, QuickNotifMixin, CanEditPropMixin, U
|
||||
quick_notif_url_arg = "qn_weekmail_article_edit"
|
||||
current_tab = "weekmail"
|
||||
|
||||
|
||||
class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
|
||||
"""Post an article"""
|
||||
model = WeekmailArticle
|
||||
@ -363,7 +384,8 @@ class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
|
||||
init = {}
|
||||
try:
|
||||
init['club'] = Club.objects.filter(id=self.request.GET['club']).first()
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return init
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
@ -385,14 +407,10 @@ class WeekmailArticleCreateView(QuickNotifMixin, CreateView):
|
||||
form.instance.author = self.request.user
|
||||
return super(WeekmailArticleCreateView, self).form_valid(form)
|
||||
|
||||
|
||||
class WeekmailArticleDeleteView(CanEditPropMixin, DeleteView):
|
||||
"""Delete an article"""
|
||||
model = WeekmailArticle
|
||||
template_name = 'core/delete_confirm.jinja'
|
||||
success_url = reverse_lazy('com:weekmail')
|
||||
pk_url_kwarg = "article_id"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -33,6 +33,7 @@ admin.site.unregister(AuthGroup)
|
||||
admin.site.register(RealGroup)
|
||||
admin.site.register(Page)
|
||||
|
||||
|
||||
@admin.register(SithFile)
|
||||
class SithFileAdmin(admin.ModelAdmin):
|
||||
form = make_ajax_form(SithFile, {
|
||||
|
@ -23,9 +23,9 @@
|
||||
#
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.dispatch import receiver
|
||||
from django.core.signals import request_started
|
||||
|
||||
|
||||
class SithConfig(AppConfig):
|
||||
name = 'core'
|
||||
verbose_name = "Core app of the Sith"
|
||||
@ -48,4 +48,3 @@ class SithConfig(AppConfig):
|
||||
request_started.connect(clear_cached_groups, weak=False, dispatch_uid="clear_cached_groups")
|
||||
request_started.connect(clear_cached_memberships, weak=False, dispatch_uid="clear_cached_memberships")
|
||||
# TODO: there may be a need to add more cache clearing
|
||||
|
||||
|
@ -31,16 +31,19 @@ from club.models import Club
|
||||
from counter.models import Product, Counter
|
||||
from accounting.models import ClubAccount, Company
|
||||
|
||||
|
||||
def check_token(request):
|
||||
return ('counter_token' in request.session.keys() and
|
||||
request.session['counter_token'] and
|
||||
Counter.objects.filter(token=request.session['counter_token']).exists())
|
||||
|
||||
|
||||
class RightManagedLookupChannel(LookupChannel):
|
||||
def check_auth(self, request):
|
||||
if not request.user.was_subscribed and not check_token(request):
|
||||
raise PermissionDenied
|
||||
|
||||
|
||||
@register('users')
|
||||
class UsersLookup(RightManagedLookupChannel):
|
||||
model = User
|
||||
@ -54,6 +57,7 @@ class UsersLookup(RightManagedLookupChannel):
|
||||
def format_item_display(self, item):
|
||||
return item.get_display_name()
|
||||
|
||||
|
||||
@register('groups')
|
||||
class GroupsLookup(RightManagedLookupChannel):
|
||||
model = Group
|
||||
@ -67,6 +71,7 @@ class GroupsLookup(RightManagedLookupChannel):
|
||||
def format_item_display(self, item):
|
||||
return item.name
|
||||
|
||||
|
||||
@register('clubs')
|
||||
class ClubLookup(RightManagedLookupChannel):
|
||||
model = Club
|
||||
@ -80,6 +85,7 @@ class ClubLookup(RightManagedLookupChannel):
|
||||
def format_item_display(self, item):
|
||||
return item.name
|
||||
|
||||
|
||||
@register('counters')
|
||||
class CountersLookup(RightManagedLookupChannel):
|
||||
model = Counter
|
||||
@ -90,6 +96,7 @@ class CountersLookup(RightManagedLookupChannel):
|
||||
def format_item_display(self, item):
|
||||
return item.name
|
||||
|
||||
|
||||
@register('products')
|
||||
class ProductsLookup(RightManagedLookupChannel):
|
||||
model = Product
|
||||
@ -101,6 +108,7 @@ class ProductsLookup(RightManagedLookupChannel):
|
||||
def format_item_display(self, item):
|
||||
return "%s (%s)" % (item.name, item.code)
|
||||
|
||||
|
||||
@register('files')
|
||||
class SithFileLookup(RightManagedLookupChannel):
|
||||
model = SithFile
|
||||
@ -108,6 +116,7 @@ class SithFileLookup(RightManagedLookupChannel):
|
||||
def get_query(self, q, request):
|
||||
return self.model.objects.filter(name__icontains=q)[:50]
|
||||
|
||||
|
||||
@register('club_accounts')
|
||||
class ClubAccountLookup(RightManagedLookupChannel):
|
||||
model = ClubAccount
|
||||
@ -118,6 +127,7 @@ class ClubAccountLookup(RightManagedLookupChannel):
|
||||
def format_item_display(self, item):
|
||||
return item.name
|
||||
|
||||
|
||||
@register('companies')
|
||||
class CompaniesLookup(RightManagedLookupChannel):
|
||||
model = Company
|
||||
|
@ -21,4 +21,3 @@
|
||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
#
|
||||
|
||||
|
@ -44,7 +44,6 @@ class Command(BaseCommand):
|
||||
args['precision'] = settings.SASS_PRECISION
|
||||
return sass.compile(**args)
|
||||
|
||||
|
||||
def is_compilable(self, file, ext_list):
|
||||
path, ext = os.path.splitext(file)
|
||||
return ext in ext_list
|
||||
|
@ -26,7 +26,7 @@ import os
|
||||
from datetime import date, datetime
|
||||
from io import StringIO, BytesIO
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.core.management import call_command
|
||||
from django.conf import settings
|
||||
from django.db import connection
|
||||
@ -42,7 +42,7 @@ from subscription.models import Subscription
|
||||
from counter.models import Customer, ProductType, Product, Counter
|
||||
from com.models import Sith, Weekmail
|
||||
from election.models import Election, Role, Candidature, ElectionList
|
||||
from forum.models import Forum, ForumMessage, ForumTopic
|
||||
from forum.models import Forum, ForumTopic
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
@ -258,7 +258,7 @@ Welcome to the wiki page!
|
||||
PageRev(page=p, title="README", author=skia, content=rm.read()).save()
|
||||
|
||||
# Subscription
|
||||
## Root
|
||||
# Root
|
||||
s = Subscription(member=User.objects.filter(pk=root.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -266,7 +266,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## Skia
|
||||
# Skia
|
||||
s = Subscription(member=User.objects.filter(pk=skia.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -274,7 +274,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## Counter admin
|
||||
# Counter admin
|
||||
s = Subscription(member=User.objects.filter(pk=counter.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -282,7 +282,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## Comptable
|
||||
# Comptable
|
||||
s = Subscription(member=User.objects.filter(pk=comptable.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -290,7 +290,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## Richard
|
||||
# Richard
|
||||
s = Subscription(member=User.objects.filter(pk=r.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -298,7 +298,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## User
|
||||
# User
|
||||
s = Subscription(member=User.objects.filter(pk=subscriber.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -306,7 +306,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## Old subscriber
|
||||
# Old subscriber
|
||||
s = Subscription(member=User.objects.filter(pk=old_subscriber.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start(datetime(year=2012, month=9, day=4))
|
||||
@ -456,7 +456,7 @@ Welcome to the wiki page!
|
||||
krophil_profile.save()
|
||||
krophil.profile_pict = krophil_profile
|
||||
krophil.save()
|
||||
## Adding subscription for sli
|
||||
# Adding subscription for sli
|
||||
s = Subscription(member=User.objects.filter(pk=sli.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -464,7 +464,7 @@ Welcome to the wiki page!
|
||||
duration=settings.SITH_SUBSCRIPTIONS[s.subscription_type]['duration'],
|
||||
start=s.subscription_start)
|
||||
s.save()
|
||||
## Adding subscription for Krophil
|
||||
# Adding subscription for Krophil
|
||||
s = Subscription(member=User.objects.filter(pk=krophil.pk).first(), subscription_type=list(settings.SITH_SUBSCRIPTIONS.keys())[0],
|
||||
payment_method=settings.SITH_SUBSCRIPTION_PAYMENT_METHOD[0])
|
||||
s.subscription_start = s.compute_start()
|
||||
@ -519,4 +519,3 @@ Welcome to the wiki page!
|
||||
various.save()
|
||||
Forum(name="Promos", description="Réservé aux Promos", parent=various).save()
|
||||
ForumTopic(forum=hall)
|
||||
|
||||
|
@ -23,9 +23,8 @@
|
||||
#
|
||||
|
||||
import os
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.core.management import call_command
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
import re
|
||||
from mistune import Renderer, InlineGrammar, InlineLexer, Markdown, escape, escape_link
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
|
||||
class SithRenderer(Renderer):
|
||||
@ -54,13 +54,16 @@ class SithRenderer(Renderer):
|
||||
src = original_src
|
||||
else:
|
||||
width = m.group(1)
|
||||
if not width.endswith('%'): width += "px"
|
||||
if not width.endswith('%'):
|
||||
width += "px"
|
||||
style = "width: %s; " % width
|
||||
try:
|
||||
height = m.group(3)
|
||||
if not height.endswith('%'): height += "px"
|
||||
if not height.endswith('%'):
|
||||
height += "px"
|
||||
style += "height: %s; " % height
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
params = None
|
||||
src = original_src
|
||||
@ -77,6 +80,7 @@ class SithRenderer(Renderer):
|
||||
return '%s />' % html
|
||||
return '%s>' % html
|
||||
|
||||
|
||||
class SithInlineGrammar(InlineGrammar):
|
||||
double_emphasis = re.compile(
|
||||
r'^\*{2}([\s\S]+?)\*{2}(?!\*)' # **word**
|
||||
@ -94,6 +98,7 @@ class SithInlineGrammar(InlineGrammar):
|
||||
r'^<sub>([\s\S]+?)</sub>' # <sub>text</sub>
|
||||
)
|
||||
|
||||
|
||||
class SithInlineLexer(InlineLexer):
|
||||
grammar_class = SithInlineGrammar
|
||||
|
||||
@ -166,7 +171,8 @@ class SithInlineLexer(InlineLexer):
|
||||
match = page.search(link)
|
||||
page = match.group(1) or ""
|
||||
link = reverse('core:page', kwargs={'page_name': page})
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
try: # Add file:// support for links
|
||||
file_link = re.compile(
|
||||
r'^file://(\d*)/?(\S*)?' # file://4000/download
|
||||
@ -175,9 +181,11 @@ class SithInlineLexer(InlineLexer):
|
||||
id = match.group(1)
|
||||
suffix = match.group(2) or ""
|
||||
link = reverse('core:file_detail', kwargs={'file_id': id}) + suffix
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return super(SithInlineLexer, self)._process_link(m, link, title)
|
||||
|
||||
|
||||
renderer = SithRenderer(escape=True)
|
||||
inline = SithInlineLexer(renderer)
|
||||
|
||||
@ -222,4 +230,3 @@ Petit *test* _sur_ ^une^ **seule** ^ligne pour voir^
|
||||
|
||||
"""
|
||||
print(markdown(text))
|
||||
|
||||
|
@ -52,4 +52,3 @@ class AuthenticationMiddleware(DjangoAuthenticationMiddleware):
|
||||
"'account.middleware.AuthenticationMiddleware'."
|
||||
)
|
||||
request.user = SimpleLazyObject(lambda: get_cached_user(request))
|
||||
|
||||
|
@ -44,14 +44,17 @@ from datetime import datetime, timedelta, date
|
||||
|
||||
import unicodedata
|
||||
|
||||
|
||||
class RealGroupManager(AuthGroupManager):
|
||||
def get_queryset(self):
|
||||
return super(RealGroupManager, self).get_queryset().filter(is_meta=False)
|
||||
|
||||
|
||||
class MetaGroupManager(AuthGroupManager):
|
||||
def get_queryset(self):
|
||||
return super(MetaGroupManager, self).get_queryset().filter(is_meta=True)
|
||||
|
||||
|
||||
class Group(AuthGroup):
|
||||
is_meta = models.BooleanField(
|
||||
_('meta group status'),
|
||||
@ -69,8 +72,10 @@ class Group(AuthGroup):
|
||||
"""
|
||||
return reverse('core:group_list')
|
||||
|
||||
|
||||
class MetaGroup(Group):
|
||||
objects = MetaGroupManager()
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
@ -78,11 +83,14 @@ class MetaGroup(Group):
|
||||
super(MetaGroup, self).__init__(*args, **kwargs)
|
||||
self.is_meta = True
|
||||
|
||||
|
||||
class RealGroup(Group):
|
||||
objects = RealGroupManager()
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
|
||||
def validate_promo(value):
|
||||
start_year = settings.SITH_SCHOOL_START_YEAR
|
||||
delta = (date.today() + timedelta(days=180)).year - start_year
|
||||
@ -92,6 +100,7 @@ def validate_promo(value):
|
||||
params={'value': value, 'end': delta},
|
||||
)
|
||||
|
||||
|
||||
class User(AbstractBaseUser):
|
||||
"""
|
||||
Defines the base user class, useable in every app
|
||||
@ -226,6 +235,7 @@ class User(AbstractBaseUser):
|
||||
_club_memberships = {}
|
||||
_group_names = {}
|
||||
_group_ids = {}
|
||||
|
||||
def is_in_group(self, group_name):
|
||||
"""If the user is in the group passed in argument (as string or by id)"""
|
||||
group_id = 0
|
||||
@ -401,7 +411,7 @@ class User(AbstractBaseUser):
|
||||
Returns the generated username
|
||||
"""
|
||||
def remove_accents(data):
|
||||
return ''.join(x for x in unicodedata.normalize('NFKD', data) if \
|
||||
return ''.join(x for x in unicodedata.normalize('NFKD', data) if
|
||||
unicodedata.category(x)[0] == 'L').lower()
|
||||
user_name = remove_accents(self.first_name[0] + self.last_name).encode('ascii', 'ignore').decode('utf-8')
|
||||
un_set = [u.username for u in User.objects.all()]
|
||||
@ -489,6 +499,7 @@ class User(AbstractBaseUser):
|
||||
infos.save()
|
||||
return infos
|
||||
|
||||
|
||||
class AnonymousUser(AuthAnonymousUser):
|
||||
def __init__(self, request):
|
||||
super(AnonymousUser, self).__init__()
|
||||
@ -557,6 +568,7 @@ class AnonymousUser(AuthAnonymousUser):
|
||||
def get_display_name(self):
|
||||
return _("Visitor")
|
||||
|
||||
|
||||
class Preferences(models.Model):
|
||||
user = models.OneToOneField(User, related_name="preferences")
|
||||
receive_weekmail = models.BooleanField(
|
||||
@ -576,15 +588,19 @@ class Preferences(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return self.user.get_absolute_url()
|
||||
|
||||
|
||||
def get_directory(instance, filename):
|
||||
return '.{0}/{1}'.format(instance.get_parent_path(), filename)
|
||||
|
||||
|
||||
def get_compressed_directory(instance, filename):
|
||||
return '.{0}/compressed/{1}'.format(instance.get_parent_path(), filename)
|
||||
|
||||
|
||||
def get_thumbnail_directory(instance, filename):
|
||||
return '.{0}/thumbnail/{1}'.format(instance.get_parent_path(), filename)
|
||||
|
||||
|
||||
class SithFile(models.Model):
|
||||
name = models.CharField(_('file name'), max_length=256, blank=False)
|
||||
parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True)
|
||||
@ -763,18 +779,22 @@ class SithFile(models.Model):
|
||||
def __str__(self):
|
||||
return self.get_parent_path() + "/" + self.name
|
||||
|
||||
|
||||
class LockError(Exception):
|
||||
"""There was a lock error on the object"""
|
||||
pass
|
||||
|
||||
|
||||
class AlreadyLocked(LockError):
|
||||
"""The object is already locked"""
|
||||
pass
|
||||
|
||||
|
||||
class NotLocked(LockError):
|
||||
"""The object is not locked"""
|
||||
pass
|
||||
|
||||
|
||||
class Page(models.Model):
|
||||
"""
|
||||
The page class to build a Wiki
|
||||
@ -792,8 +812,7 @@ class Page(models.Model):
|
||||
r'^[A-z.+-]+$',
|
||||
_('Enter a valid page name. This value may contain only '
|
||||
'unaccented letters, numbers ' 'and ./+/-/_ characters.')
|
||||
),
|
||||
],
|
||||
), ],
|
||||
blank=False)
|
||||
parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True, on_delete=models.SET_NULL)
|
||||
# Attention: this field may not be valid until you call save(). It's made for fast query, but don't rely on it when
|
||||
@ -854,7 +873,8 @@ class Page(models.Model):
|
||||
Performs some needed actions before and after saving a page in database
|
||||
"""
|
||||
locked = kwargs.pop('force_lock', False)
|
||||
if not locked: locked = self.is_locked()
|
||||
if not locked:
|
||||
locked = self.is_locked()
|
||||
if not locked:
|
||||
raise NotLocked("The page is not locked and thus can not be saved")
|
||||
self.full_clean()
|
||||
@ -1003,6 +1023,7 @@ class PageRev(models.Model):
|
||||
# Don't forget to unlock, otherwise, people will have to wait for the page's timeout
|
||||
self.page.unset_lock()
|
||||
|
||||
|
||||
class Notification(models.Model):
|
||||
user = models.ForeignKey(User, related_name='notifications')
|
||||
url = models.CharField(_("url"), max_length=255)
|
||||
@ -1015,4 +1036,3 @@ class Notification(models.Model):
|
||||
if self.param:
|
||||
return self.get_type_display() % self.param
|
||||
return self.get_type_display()
|
||||
|
||||
|
@ -27,17 +27,18 @@ from django import template
|
||||
from django.template.defaultfilters import stringfilter
|
||||
from django.utils.safestring import mark_safe
|
||||
from core.scss.processor import ScssProcessor
|
||||
from django.utils.html import escape
|
||||
|
||||
from core.markdown import markdown as md
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
@stringfilter
|
||||
def markdown(text):
|
||||
return mark_safe("<div class=\"markdown\">%s</div>" % md(text))
|
||||
|
||||
|
||||
@register.filter()
|
||||
@stringfilter
|
||||
def datetime_format_python_to_PHP(python_format_string):
|
||||
@ -51,6 +52,7 @@ def datetime_format_python_to_PHP(python_format_string):
|
||||
php_format_string = php_format_string.replace(py, php)
|
||||
return php_format_string
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def scss(path):
|
||||
"""
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
from django.test import Client, TestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.management import call_command
|
||||
|
||||
from core.models import User, Group, Page
|
||||
@ -34,6 +33,7 @@ to run these tests :
|
||||
python3 manage.py test
|
||||
"""
|
||||
|
||||
|
||||
class UserRegistrationTest(TestCase):
|
||||
def setUp(self):
|
||||
try:
|
||||
@ -185,6 +185,7 @@ class UserRegistrationTest(TestCase):
|
||||
self.assertTrue(response.status_code == 200)
|
||||
self.assertTrue("""<p>Votre nom d\\'utilisateur et votre mot de passe ne correspondent pas. Merci de r\\xc3\\xa9essayer.</p>""" in str(response.content))
|
||||
|
||||
|
||||
class PageHandlingTest(TestCase):
|
||||
def setUp(self):
|
||||
try:
|
||||
@ -260,7 +261,7 @@ class PageHandlingTest(TestCase):
|
||||
'name': 'guy',
|
||||
'owner_group': '1',
|
||||
})
|
||||
r = self.client.post(reverse('core:page_edit', kwargs={'page_name': 'guy'}), {
|
||||
self.client.post(reverse('core:page_edit', kwargs={'page_name': 'guy'}), {
|
||||
'title': 'Bibou',
|
||||
'content':
|
||||
'''Guy *bibou*
|
||||
@ -285,6 +286,7 @@ http://git.an
|
||||
# - changing a page's parent --> check that page's children's full_name
|
||||
# - changing the different groups of the page
|
||||
|
||||
|
||||
class FileHandlingTest(TestCase):
|
||||
def setUp(self):
|
||||
try:
|
||||
@ -310,4 +312,3 @@ class FileHandlingTest(TestCase):
|
||||
response = self.client.get(reverse("core:file_detail", kwargs={"file_id": self.subscriber.home.id}))
|
||||
self.assertTrue(response.status_code == 200)
|
||||
self.assertTrue("ls</a>" in str(response.content))
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from core.views import *
|
||||
|
||||
|
@ -27,9 +27,10 @@ import re
|
||||
# Image utils
|
||||
|
||||
from io import BytesIO
|
||||
from datetime import datetime, timezone, date
|
||||
from datetime import date
|
||||
|
||||
from PIL import Image, ExifTags
|
||||
from PIL import ExifTags
|
||||
# from exceptions import IOError
|
||||
import PIL
|
||||
|
||||
from django.conf import settings
|
||||
@ -59,6 +60,7 @@ def get_start_of_semester(d=date.today()):
|
||||
else:
|
||||
return start2
|
||||
|
||||
|
||||
def get_semester(d=date.today()):
|
||||
start = get_start_of_semester(d)
|
||||
if start.month <= 6:
|
||||
@ -66,6 +68,7 @@ def get_semester(d=date.today()):
|
||||
else:
|
||||
return "A" + str(start.year)[-2:]
|
||||
|
||||
|
||||
def scale_dimension(width, height, long_edge):
|
||||
if width > height:
|
||||
ratio = long_edge * 1. / width
|
||||
@ -73,6 +76,7 @@ def scale_dimension(width, height, long_edge):
|
||||
ratio = long_edge * 1. / height
|
||||
return int(width * ratio), int(height * ratio)
|
||||
|
||||
|
||||
def resize_image(im, edge, format):
|
||||
(w, h) = im.size
|
||||
(width, height) = scale_dimension(w, h, long_edge=edge)
|
||||
@ -85,9 +89,11 @@ def resize_image(im, edge, format):
|
||||
im.save(fp=content, format=format.upper(), quality=90, optimize=True, progressive=True)
|
||||
return ContentFile(content.getvalue())
|
||||
|
||||
|
||||
def exif_auto_rotate(image):
|
||||
for orientation in ExifTags.TAGS.keys():
|
||||
if ExifTags.TAGS[orientation]=='Orientation' : break
|
||||
if ExifTags.TAGS[orientation] == 'Orientation':
|
||||
break
|
||||
exif = dict(image._getexif().items())
|
||||
|
||||
if exif[orientation] == 3:
|
||||
@ -99,6 +105,7 @@ def exif_auto_rotate(image):
|
||||
|
||||
return image
|
||||
|
||||
|
||||
def doku_to_markdown(text):
|
||||
"""This is a quite correct doku translator"""
|
||||
text = re.sub(r'([^:]|^)\/\/(.*?)\/\/', r'*\2*', text) # Italic (prevents protocol:// conflict)
|
||||
@ -164,12 +171,14 @@ def doku_to_markdown(text):
|
||||
quote_level -= 1
|
||||
final_newline = True
|
||||
new_text.append("> " * final_quote_level + line) # Finally append the line
|
||||
if final_newline: new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
|
||||
if final_newline:
|
||||
new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
|
||||
else:
|
||||
new_text.append(line)
|
||||
|
||||
return "\n".join(new_text)
|
||||
|
||||
|
||||
def bbcode_to_markdown(text):
|
||||
"""This is a very basic BBcode translator"""
|
||||
text = re.sub(r'\[b\](.*?)\[\/b\]', r'**\1**', text, flags=re.DOTALL) # Bold
|
||||
@ -205,9 +214,9 @@ def bbcode_to_markdown(text):
|
||||
quote_level -= 1
|
||||
final_newline = True
|
||||
new_text.append("> " * final_quote_level + line) # Finally append the line
|
||||
if final_newline: new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
|
||||
if final_newline:
|
||||
new_text.append("\n") # Add a new line to ensure the separation between the quote and the following text
|
||||
else:
|
||||
new_text.append(line)
|
||||
|
||||
return "\n".join(new_text)
|
||||
|
||||
|
@ -23,29 +23,28 @@
|
||||
#
|
||||
|
||||
# This file contains all the views that concern the page model
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.shortcuts import redirect
|
||||
from django.views.generic import ListView, DetailView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, CreateView, FormMixin, DeleteView
|
||||
from django.views.generic.edit import UpdateView, FormMixin, DeleteView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.forms.models import modelform_factory
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.http import HttpResponse
|
||||
from django.core.servers.basehttp import FileWrapper
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django import forms
|
||||
|
||||
import os
|
||||
|
||||
from ajax_select import make_ajax_form, make_ajax_field
|
||||
from ajax_select import make_ajax_field
|
||||
|
||||
from core.models import SithFile, RealGroup, Notification
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, can_view, not_found
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, can_view, not_found
|
||||
from counter.models import Counter
|
||||
|
||||
|
||||
def send_file(request, file_id, file_class=SithFile, file_attr="file"):
|
||||
"""
|
||||
Send a file through Django without loading the whole file into
|
||||
@ -70,6 +69,7 @@ def send_file(request, file_id, file_class=SithFile, file_attr="file"):
|
||||
response['Content-Disposition'] = ('inline; filename="%s"' % f.name).encode('utf-8')
|
||||
return response
|
||||
|
||||
|
||||
class AddFilesForm(forms.Form):
|
||||
folder_name = forms.CharField(label=_("Add a new folder"), max_length=30, required=False)
|
||||
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}), label=_("Files"),
|
||||
@ -100,6 +100,7 @@ class AddFilesForm(forms.Form):
|
||||
if not u.notifications.filter(type="FILE_MODERATION", viewed=False).exists():
|
||||
Notification(user=u, url=reverse("core:file_moderation"), type="FILE_MODERATION").save()
|
||||
|
||||
|
||||
class FileListView(ListView):
|
||||
template_name = 'core/file_list.jinja'
|
||||
context_object_name = "file_list"
|
||||
@ -114,6 +115,7 @@ class FileListView(ListView):
|
||||
kwargs['popup'] = 'popup'
|
||||
return kwargs
|
||||
|
||||
|
||||
class FileEditView(CanEditMixin, UpdateView):
|
||||
model = SithFile
|
||||
pk_url_kwarg = "file_id"
|
||||
@ -138,6 +140,7 @@ class FileEditView(CanEditMixin, UpdateView):
|
||||
kwargs['popup'] = 'popup'
|
||||
return kwargs
|
||||
|
||||
|
||||
class FileEditPropForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = SithFile
|
||||
@ -147,6 +150,7 @@ class FileEditPropForm(forms.ModelForm):
|
||||
view_groups = make_ajax_field(SithFile, 'view_groups', 'groups', help_text="", label=_("view group"))
|
||||
recursive = forms.BooleanField(label=_("Apply rights recursively"), required=False)
|
||||
|
||||
|
||||
class FileEditPropView(CanEditPropMixin, UpdateView):
|
||||
model = SithFile
|
||||
pk_url_kwarg = "file_id"
|
||||
@ -175,6 +179,7 @@ class FileEditPropView(CanEditPropMixin, UpdateView):
|
||||
kwargs['popup'] = 'popup'
|
||||
return kwargs
|
||||
|
||||
|
||||
class FileView(CanViewMixin, DetailView, FormMixin):
|
||||
"""This class handle the upload of new files into a folder"""
|
||||
model = SithFile
|
||||
@ -237,6 +242,7 @@ class FileView(CanViewMixin, DetailView, FormMixin):
|
||||
kwargs['clipboard'] = SithFile.objects.filter(id__in=self.request.session['clipboard'])
|
||||
return kwargs
|
||||
|
||||
|
||||
class FileDeleteView(CanEditPropMixin, DeleteView):
|
||||
model = SithFile
|
||||
pk_url_kwarg = "file_id"
|
||||
@ -258,6 +264,7 @@ class FileDeleteView(CanEditPropMixin, DeleteView):
|
||||
kwargs['popup'] = 'popup'
|
||||
return kwargs
|
||||
|
||||
|
||||
class FileModerationView(TemplateView):
|
||||
template_name = "core/file_moderation.jinja"
|
||||
|
||||
@ -266,6 +273,7 @@ class FileModerationView(TemplateView):
|
||||
kwargs['files'] = SithFile.objects.filter(is_moderated=False)[:100]
|
||||
return kwargs
|
||||
|
||||
|
||||
class FileModerateView(CanEditPropMixin, SingleObjectMixin):
|
||||
model = SithFile
|
||||
pk_url_kwarg = "file_id"
|
||||
@ -278,4 +286,3 @@ class FileModerateView(CanEditPropMixin, SingleObjectMixin):
|
||||
if 'next' in self.request.GET.keys():
|
||||
return redirect(self.request.GET['next'])
|
||||
return redirect('core:file_moderation')
|
||||
|
||||
|
@ -22,22 +22,24 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm, UserChangeForm
|
||||
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.contrib.auth import logout, login, authenticate
|
||||
from django.forms import CheckboxSelectMultiple, Select, DateInput, TextInput, DateTimeInput, Textarea
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext
|
||||
from phonenumber_field.widgets import PhoneNumberInternationalFallbackWidget
|
||||
from ajax_select.fields import AutoCompleteSelectField
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
from core.models import User, Page, RealGroup, SithFile
|
||||
from core.models import User, Page, SithFile
|
||||
|
||||
from core.utils import resize_image
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
|
||||
|
||||
# Widgets
|
||||
@ -50,6 +52,7 @@ class SelectSingle(Select):
|
||||
attrs = {'class': "select_single"}
|
||||
return super(SelectSingle, self).render(name, value, attrs)
|
||||
|
||||
|
||||
class SelectMultiple(Select):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
@ -58,6 +61,7 @@ class SelectMultiple(Select):
|
||||
attrs = {'class': "select_multiple"}
|
||||
return super(SelectMultiple, self).render(name, value, attrs)
|
||||
|
||||
|
||||
class SelectDateTime(DateTimeInput):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
@ -66,6 +70,7 @@ class SelectDateTime(DateTimeInput):
|
||||
attrs = {'class': "select_datetime"}
|
||||
return super(SelectDateTime, self).render(name, value, attrs)
|
||||
|
||||
|
||||
class SelectDate(DateInput):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
@ -74,6 +79,7 @@ class SelectDate(DateInput):
|
||||
attrs = {'class': "select_date"}
|
||||
return super(SelectDate, self).render(name, value, attrs)
|
||||
|
||||
|
||||
class MarkdownInput(Textarea):
|
||||
def render(self, name, value, attrs=None):
|
||||
output = '<p><a href="%(syntax_url)s">%(help_text)s</a></p>'\
|
||||
@ -84,6 +90,7 @@ class MarkdownInput(Textarea):
|
||||
}
|
||||
return output
|
||||
|
||||
|
||||
class SelectFile(TextInput):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
@ -98,6 +105,7 @@ class SelectFile(TextInput):
|
||||
output += '<span name="' + name + '" class="choose_file_button">' + ugettext("Choose file") + '</span>'
|
||||
return output
|
||||
|
||||
|
||||
class SelectUser(TextInput):
|
||||
def render(self, name, value, attrs=None):
|
||||
if attrs:
|
||||
@ -114,6 +122,7 @@ class SelectUser(TextInput):
|
||||
|
||||
# Forms
|
||||
|
||||
|
||||
class LoginForm(AuthenticationForm):
|
||||
def __init__(self, *arg, **kwargs):
|
||||
if 'data' in kwargs.keys():
|
||||
@ -128,14 +137,17 @@ class LoginForm(AuthenticationForm):
|
||||
else:
|
||||
user = User.objects.filter(username=data['username']).first()
|
||||
data['username'] = user.username
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
kwargs['data'] = data
|
||||
super(LoginForm, self).__init__(*arg, **kwargs)
|
||||
self.fields['username'].label = _("Username, email, or account number")
|
||||
|
||||
|
||||
class RegisteringForm(UserCreationForm):
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('first_name', 'last_name', 'email')
|
||||
@ -148,9 +160,6 @@ class RegisteringForm(UserCreationForm):
|
||||
user.save()
|
||||
return user
|
||||
|
||||
from core.utils import resize_image
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
|
||||
class UserProfileForm(forms.ModelForm):
|
||||
"""
|
||||
@ -223,9 +232,11 @@ class UserProfileForm(forms.ModelForm):
|
||||
{'file_name': f, 'msg': _("Bad image format, only jpeg, png, and gif are accepted")})
|
||||
self._post_clean()
|
||||
|
||||
|
||||
class UserPropForm(forms.ModelForm):
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['groups']
|
||||
@ -236,13 +247,16 @@ class UserPropForm(forms.ModelForm):
|
||||
'groups': CheckboxSelectMultiple,
|
||||
}
|
||||
|
||||
|
||||
class UserGodfathersForm(forms.Form):
|
||||
type = forms.ChoiceField(choices=[('godfather', _("Godfather")), ('godchild', _("Godchild"))], label=_("Add"))
|
||||
user = AutoCompleteSelectField('users', required=True, label=_("Select user"), help_text=None)
|
||||
|
||||
|
||||
class PagePropForm(forms.ModelForm):
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
|
||||
class Meta:
|
||||
model = Page
|
||||
fields = ['parent', 'name', 'owner_group', 'edit_groups', 'view_groups', ]
|
||||
@ -255,4 +269,3 @@ class PagePropForm(forms.ModelForm):
|
||||
super(PagePropForm, self).__init__(*arg, **kwargs)
|
||||
self.fields['edit_groups'].required = False
|
||||
self.fields['view_groups'].required = False
|
||||
|
||||
|
@ -29,6 +29,7 @@ from django.core.urlresolvers import reverse_lazy
|
||||
from core.models import RealGroup
|
||||
from core.views import CanEditMixin
|
||||
|
||||
|
||||
class GroupListView(CanEditMixin, ListView):
|
||||
"""
|
||||
Displays the group list
|
||||
@ -36,17 +37,20 @@ class GroupListView(CanEditMixin, ListView):
|
||||
model = RealGroup
|
||||
template_name = "core/group_list.jinja"
|
||||
|
||||
|
||||
class GroupEditView(CanEditMixin, UpdateView):
|
||||
model = RealGroup
|
||||
pk_url_kwarg = "group_id"
|
||||
template_name = "core/group_edit.jinja"
|
||||
fields = ['name', 'description']
|
||||
|
||||
|
||||
class GroupCreateView(CanEditMixin, CreateView):
|
||||
model = RealGroup
|
||||
template_name = "core/group_edit.jinja"
|
||||
fields = ['name', 'description']
|
||||
|
||||
|
||||
class GroupDeleteView(CanEditMixin, DeleteView):
|
||||
model = RealGroup
|
||||
pk_url_kwarg = "group_id"
|
||||
|
@ -23,23 +23,22 @@
|
||||
#
|
||||
|
||||
# This file contains all the views that concern the page model
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.views.generic import ListView, DetailView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.forms.models import modelform_factory
|
||||
from django.forms import CheckboxSelectMultiple, modelform_factory
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
|
||||
from core.models import Page, PageRev, LockError
|
||||
from core.views.forms import PagePropForm, MarkdownInput
|
||||
from core.views.forms import MarkdownInput
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
|
||||
|
||||
|
||||
class PageListView(CanViewMixin, ListView):
|
||||
model = Page
|
||||
template_name = 'core/page_list.jinja'
|
||||
|
||||
|
||||
class PageView(CanViewMixin, DetailView):
|
||||
model = Page
|
||||
template_name = 'core/page_detail.jinja'
|
||||
@ -54,6 +53,7 @@ class PageView(CanViewMixin, DetailView):
|
||||
context['new_page'] = self.kwargs['page_name']
|
||||
return context
|
||||
|
||||
|
||||
class PageHistView(CanViewMixin, DetailView):
|
||||
model = Page
|
||||
template_name = 'core/page_hist.jinja'
|
||||
@ -62,6 +62,7 @@ class PageHistView(CanViewMixin, DetailView):
|
||||
self.page = Page.get_page_by_full_name(self.kwargs['page_name'])
|
||||
return self.page
|
||||
|
||||
|
||||
class PageRevView(CanViewMixin, DetailView):
|
||||
model = Page
|
||||
template_name = 'core/page_detail.jinja'
|
||||
@ -84,6 +85,7 @@ class PageRevView(CanViewMixin, DetailView):
|
||||
context['new_page'] = self.kwargs['page_name']
|
||||
return context
|
||||
|
||||
|
||||
class PageCreateView(CanCreateMixin, CreateView):
|
||||
model = Page
|
||||
form_class = modelform_factory(Page,
|
||||
@ -115,6 +117,7 @@ class PageCreateView(CanCreateMixin, CreateView):
|
||||
ret = super(PageCreateView, self).form_valid(form)
|
||||
return ret
|
||||
|
||||
|
||||
class PagePropView(CanEditPropMixin, UpdateView):
|
||||
model = Page
|
||||
form_class = modelform_factory(Page,
|
||||
@ -145,6 +148,7 @@ class PagePropView(CanEditPropMixin, UpdateView):
|
||||
raise e
|
||||
return self.page
|
||||
|
||||
|
||||
class PageEditView(CanEditMixin, UpdateView):
|
||||
model = PageRev
|
||||
form_class = modelform_factory(model=PageRev, fields=['title', 'content', ], widgets={'content': MarkdownInput})
|
||||
|
@ -22,17 +22,13 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.db import models
|
||||
from django.shortcuts import render, redirect
|
||||
from django.http import JsonResponse
|
||||
from django.core import serializers
|
||||
from django.db.models import Q
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.views.generic import ListView, TemplateView
|
||||
|
||||
import os
|
||||
import json
|
||||
from itertools import chain
|
||||
|
||||
from haystack.query import SearchQuerySet
|
||||
|
||||
@ -40,9 +36,11 @@ from core.models import User, Notification
|
||||
from core.utils import doku_to_markdown, bbcode_to_markdown
|
||||
from club.models import Club
|
||||
|
||||
|
||||
def index(request, context=None):
|
||||
return render(request, "core/index.jinja")
|
||||
|
||||
|
||||
class NotificationList(ListView):
|
||||
model = Notification
|
||||
template_name = "core/notification_list.jinja"
|
||||
@ -52,6 +50,7 @@ class NotificationList(ListView):
|
||||
self.request.user.notifications.update(viewed=True)
|
||||
return self.request.user.notifications.order_by('-id')[:20]
|
||||
|
||||
|
||||
def notification(request, notif_id):
|
||||
notif = Notification.objects.filter(id=notif_id).first()
|
||||
if notif:
|
||||
@ -60,10 +59,12 @@ def notification(request, notif_id):
|
||||
return redirect(notif.url)
|
||||
return redirect("/")
|
||||
|
||||
|
||||
def search_user(query, as_json=False):
|
||||
res = SearchQuerySet().models(User).filter(text=query).filter_or(text__contains=query)[:20]
|
||||
return [r.object for r in res]
|
||||
|
||||
|
||||
def search_club(query, as_json=False):
|
||||
clubs = []
|
||||
if query:
|
||||
@ -75,6 +76,7 @@ def search_club(query, as_json=False):
|
||||
clubs = list(clubs)
|
||||
return clubs
|
||||
|
||||
|
||||
@login_required
|
||||
def search_view(request):
|
||||
result = {
|
||||
@ -83,6 +85,7 @@ def search_view(request):
|
||||
}
|
||||
return render(request, "core/search.jinja", context={'result': result})
|
||||
|
||||
|
||||
@login_required
|
||||
def search_user_json(request):
|
||||
result = {
|
||||
@ -90,6 +93,7 @@ def search_user_json(request):
|
||||
}
|
||||
return JsonResponse(result)
|
||||
|
||||
|
||||
@login_required
|
||||
def search_json(request):
|
||||
result = {
|
||||
@ -98,6 +102,7 @@ def search_json(request):
|
||||
}
|
||||
return JsonResponse(result)
|
||||
|
||||
|
||||
class ToMarkdownView(TemplateView):
|
||||
template_name = "core/to_markdown.jinja"
|
||||
|
||||
@ -119,4 +124,3 @@ class ToMarkdownView(TemplateView):
|
||||
kwargs['text'] = ""
|
||||
kwargs['text_md'] = ""
|
||||
return kwargs
|
||||
|
||||
|
@ -24,30 +24,29 @@
|
||||
|
||||
# This file contains all the views that concern the user model
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.contrib.auth import logout as auth_logout, views
|
||||
from django.contrib.auth import views
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist, ValidationError
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.http import Http404
|
||||
from django.views.generic.edit import UpdateView
|
||||
from django.views.generic import ListView, DetailView, TemplateView, DeleteView
|
||||
from django.views.generic import ListView, DetailView, TemplateView
|
||||
from django.forms.models import modelform_factory
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.template.response import TemplateResponse
|
||||
from django.conf import settings
|
||||
from django.views.generic.dates import YearMixin, MonthMixin
|
||||
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta, datetime, date
|
||||
from datetime import timedelta, date
|
||||
import logging
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, QuickNotifMixin
|
||||
from core.views.forms import RegisteringForm, UserPropForm, UserProfileForm, LoginForm, UserGodfathersForm
|
||||
from core.views.forms import RegisteringForm, UserProfileForm, LoginForm, UserGodfathersForm
|
||||
from core.models import User, SithFile, Preferences
|
||||
from club.models import Club
|
||||
from subscription.models import Subscription
|
||||
from trombi.views import UserTrombiForm
|
||||
|
||||
|
||||
def login(request):
|
||||
"""
|
||||
The login view
|
||||
@ -56,24 +55,28 @@ def login(request):
|
||||
"""
|
||||
return views.login(request, template_name="core/login.jinja", authentication_form=LoginForm)
|
||||
|
||||
|
||||
def logout(request):
|
||||
"""
|
||||
The logout view
|
||||
"""
|
||||
return views.logout_then_login(request)
|
||||
|
||||
|
||||
def password_change(request):
|
||||
"""
|
||||
Allows a user to change its password
|
||||
"""
|
||||
return views.password_change(request, template_name="core/password_change.jinja", post_change_redirect=reverse("core:password_change_done"))
|
||||
|
||||
|
||||
def password_change_done(request):
|
||||
"""
|
||||
Allows a user to change its password
|
||||
"""
|
||||
return views.password_change_done(request, template_name="core/password_change_done.jinja")
|
||||
|
||||
|
||||
def password_root_change(request, user_id):
|
||||
"""
|
||||
Allows a root user to change someone's password
|
||||
@ -92,6 +95,7 @@ def password_root_change(request, user_id):
|
||||
form = views.SetPasswordForm(user=user)
|
||||
return TemplateResponse(request, "core/password_change.jinja", {'form': form, 'target': user})
|
||||
|
||||
|
||||
def password_reset(request):
|
||||
"""
|
||||
Allows someone to enter an email adresse for resetting password
|
||||
@ -102,12 +106,14 @@ def password_reset(request):
|
||||
post_reset_redirect="core:password_reset_done",
|
||||
)
|
||||
|
||||
|
||||
def password_reset_done(request):
|
||||
"""
|
||||
Confirm that the reset email has been sent
|
||||
"""
|
||||
return views.password_reset_done(request, template_name="core/password_reset_done.jinja")
|
||||
|
||||
|
||||
def password_reset_confirm(request, uidb64=None, token=None):
|
||||
"""
|
||||
Provide a reset password formular
|
||||
@ -117,6 +123,7 @@ def password_reset_confirm(request, uidb64=None, token=None):
|
||||
template_name="core/password_reset_confirm.jinja",
|
||||
)
|
||||
|
||||
|
||||
def password_reset_complete(request):
|
||||
"""
|
||||
Confirm the password has sucessfully been reset
|
||||
@ -125,6 +132,7 @@ def password_reset_complete(request):
|
||||
template_name="core/password_reset_complete.jinja",
|
||||
)
|
||||
|
||||
|
||||
def register(request):
|
||||
context = {}
|
||||
if request.method == 'POST':
|
||||
@ -143,6 +151,7 @@ def register(request):
|
||||
context['form'] = form.as_p()
|
||||
return render(request, "core/register.jinja", context)
|
||||
|
||||
|
||||
class UserTabsMixin(TabedViewMixin):
|
||||
def get_tabs_title(self):
|
||||
return self.object.get_display_name()
|
||||
@ -208,9 +217,11 @@ class UserTabsMixin(TabedViewMixin):
|
||||
'slug': 'account',
|
||||
'name': _("Account") + " (%s €)" % self.object.customer.amount,
|
||||
})
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return tab_list
|
||||
|
||||
|
||||
class UserView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a user's profile
|
||||
@ -236,6 +247,7 @@ def DeleteUserGodfathers(request, user_id, godfather_id, is_father):
|
||||
raise PermissionDenied
|
||||
return redirect('core:user_godfathers', user_id=user_id)
|
||||
|
||||
|
||||
class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a user's pictures
|
||||
@ -246,6 +258,7 @@ class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
template_name = "core/user_pictures.jinja"
|
||||
current_tab = 'pictures'
|
||||
|
||||
|
||||
class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a user's godfathers
|
||||
@ -277,6 +290,7 @@ class UserGodfathersView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['form'] = UserGodfathersForm()
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a user's stats
|
||||
@ -303,7 +317,7 @@ class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super(UserStatsView, self).get_context_data(**kwargs)
|
||||
from counter.models import Counter, Product, Selling
|
||||
from counter.models import Counter
|
||||
from django.db.models import Sum
|
||||
foyer = Counter.objects.filter(name="Foyer").first()
|
||||
mde = Counter.objects.filter(name="MDE").first()
|
||||
@ -323,6 +337,7 @@ class UserStatsView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
product_sum=Sum('quantity')).exclude(product_sum=None).order_by('-product_sum').all()[:10]
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserMiniView(CanViewMixin, DetailView):
|
||||
"""
|
||||
Display a user's profile
|
||||
@ -332,6 +347,7 @@ class UserMiniView(CanViewMixin, DetailView):
|
||||
context_object_name = "profile"
|
||||
template_name = "core/user_mini.jinja"
|
||||
|
||||
|
||||
class UserListView(ListView, CanEditPropMixin):
|
||||
"""
|
||||
Displays the user list
|
||||
@ -339,6 +355,7 @@ class UserListView(ListView, CanEditPropMixin):
|
||||
model = User
|
||||
template_name = "core/user_list.jinja"
|
||||
|
||||
|
||||
class UserUploadProfilePictView(CanEditMixin, DetailView):
|
||||
"""
|
||||
Handle the upload of the profile picture taken with webcam in navigator
|
||||
@ -367,6 +384,7 @@ class UserUploadProfilePictView(CanEditMixin, DetailView):
|
||||
self.object.save()
|
||||
return redirect("core:user_edit", user_id=self.object.id)
|
||||
|
||||
|
||||
class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||
"""
|
||||
Edit a user's profile
|
||||
@ -412,6 +430,7 @@ class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||
kwargs['form'] = self.form
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserClubView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Display the user's club(s)
|
||||
@ -422,6 +441,7 @@ class UserClubView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
template_name = "core/user_clubs.jinja"
|
||||
current_tab = "clubs"
|
||||
|
||||
|
||||
class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||
"""
|
||||
Edit a user's preferences
|
||||
@ -453,6 +473,7 @@ class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||
kwargs['trombi_form'] = UserTrombiForm()
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserUpdateGroupView(UserTabsMixin, CanEditPropMixin, UpdateView):
|
||||
"""
|
||||
Edit a user's groups
|
||||
@ -465,6 +486,7 @@ class UserUpdateGroupView(UserTabsMixin, CanEditPropMixin, UpdateView):
|
||||
context_object_name = "profile"
|
||||
current_tab = "groups"
|
||||
|
||||
|
||||
class UserToolsView(QuickNotifMixin, UserTabsMixin, TemplateView):
|
||||
"""
|
||||
Displays the logged user's tools
|
||||
@ -481,6 +503,7 @@ class UserToolsView(QuickNotifMixin, UserTabsMixin, TemplateView):
|
||||
kwargs['object'] = self.request.user
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserAccountBase(UserTabsMixin, DetailView):
|
||||
"""
|
||||
Base class for UserAccount
|
||||
@ -498,6 +521,7 @@ class UserAccountBase(UserTabsMixin, DetailView):
|
||||
return res
|
||||
raise PermissionDenied
|
||||
|
||||
|
||||
class UserAccountView(UserAccountBase):
|
||||
"""
|
||||
Display a user's account
|
||||
@ -551,6 +575,7 @@ class UserAccountView(UserAccountBase):
|
||||
print(repr(e))
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserAccountDetailView(UserAccountBase, YearMixin, MonthMixin):
|
||||
"""
|
||||
Display a user's account for month
|
||||
@ -568,4 +593,3 @@ class UserAccountDetailView(UserAccountBase, YearMixin, MonthMixin):
|
||||
pass
|
||||
kwargs['tab'] = "account"
|
||||
return kwargs
|
||||
|
||||
|
@ -36,4 +36,3 @@ admin.site.register(Selling)
|
||||
admin.site.register(Permanency)
|
||||
admin.site.register(CashRegisterSummary)
|
||||
admin.site.register(Eticket)
|
||||
|
||||
|
@ -22,13 +22,12 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.db import models, DataError
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.forms import ValidationError
|
||||
from django.contrib.sites.shortcuts import get_current_site
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
from datetime import timedelta, date
|
||||
@ -43,6 +42,7 @@ from accounting.models import CurrencyField
|
||||
from core.models import Group, User, Notification
|
||||
from subscription.models import Subscription
|
||||
|
||||
|
||||
class Customer(models.Model):
|
||||
"""
|
||||
This class extends a user to make a customer. It adds some basic customers informations, such as the accound ID, and
|
||||
@ -118,6 +118,7 @@ class ProductType(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse('counter:producttype_list')
|
||||
|
||||
|
||||
class Product(models.Model):
|
||||
"""
|
||||
This describes a product, with all its related informations
|
||||
@ -156,6 +157,7 @@ class Product(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse('counter:product_list')
|
||||
|
||||
|
||||
class Counter(models.Model):
|
||||
name = models.CharField(_('name'), max_length=30)
|
||||
club = models.ForeignKey(Club, related_name="counters", verbose_name=_("club"))
|
||||
@ -265,6 +267,7 @@ class Counter(models.Model):
|
||||
"""
|
||||
return [b.id for b in self.get_barmen_list()]
|
||||
|
||||
|
||||
class Refilling(models.Model):
|
||||
"""
|
||||
Handle the refilling
|
||||
@ -309,6 +312,7 @@ class Refilling(models.Model):
|
||||
).save()
|
||||
super(Refilling, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
class Selling(models.Model):
|
||||
"""
|
||||
Handle the sellings
|
||||
@ -414,7 +418,8 @@ class Selling(models.Model):
|
||||
try:
|
||||
if self.product.eticket:
|
||||
self.send_mail_customer()
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
Notification(
|
||||
user=self.customer.user,
|
||||
url=reverse('core:user_account_detail',
|
||||
@ -424,6 +429,7 @@ class Selling(models.Model):
|
||||
).save()
|
||||
super(Selling, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
class Permanency(models.Model):
|
||||
"""
|
||||
This class aims at storing a traceability of who was barman where and when
|
||||
@ -444,6 +450,7 @@ class Permanency(models.Model):
|
||||
self.end.strftime("%Y-%m-%d %H:%M:%S") if self.end else "",
|
||||
)
|
||||
|
||||
|
||||
class CashRegisterSummary(models.Model):
|
||||
user = models.ForeignKey(User, related_name="cash_summaries", verbose_name=_("user"))
|
||||
counter = models.ForeignKey(Counter, related_name="cash_summaries", verbose_name=_("counter"))
|
||||
@ -515,6 +522,7 @@ class CashRegisterSummary(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse('counter:cash_summary_list')
|
||||
|
||||
|
||||
class CashRegisterSummaryItem(models.Model):
|
||||
cash_summary = models.ForeignKey(CashRegisterSummary, related_name="items", verbose_name=_("cash summary"))
|
||||
value = CurrencyField(_("value"))
|
||||
@ -524,6 +532,7 @@ class CashRegisterSummaryItem(models.Model):
|
||||
class Meta:
|
||||
verbose_name = _("cash register summary item")
|
||||
|
||||
|
||||
class Eticket(models.Model):
|
||||
"""
|
||||
Eticket can be linked to a product an allows PDF generation
|
||||
@ -552,6 +561,6 @@ class Eticket(models.Model):
|
||||
return user.is_in_group(settings.SITH_GROUP_COUNTER_ADMIN_ID)
|
||||
|
||||
def get_hash(self, string):
|
||||
import hashlib, hmac
|
||||
import hashlib
|
||||
import hmac
|
||||
return hmac.new(bytes(self.secret, 'utf-8'), bytes(string, 'utf-8'), hashlib.sha1).hexdigest()
|
||||
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
import re
|
||||
|
||||
from pprint import pprint
|
||||
from django.test import TestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.management import call_command
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from counter.views import *
|
||||
|
||||
|
104
counter/views.py
104
counter/views.py
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.http import Http404
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
|
||||
@ -31,7 +31,6 @@ from django.views.generic.edit import UpdateView, CreateView, DeleteView, Proces
|
||||
from django.forms.models import modelform_factory
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.utils import timezone
|
||||
from django import forms
|
||||
@ -43,16 +42,17 @@ import re
|
||||
import pytz
|
||||
from datetime import date, timedelta, datetime
|
||||
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
|
||||
from ajax_select import make_ajax_form, make_ajax_field
|
||||
from ajax_select import make_ajax_field
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
|
||||
from core.views.forms import SelectUser, LoginForm, SelectDate, SelectDateTime
|
||||
from core.views import CanViewMixin, TabedViewMixin
|
||||
from core.views.forms import LoginForm, SelectDate, SelectDateTime
|
||||
from core.models import User
|
||||
from subscription.models import Subscription
|
||||
from counter.models import Counter, Customer, Product, Selling, Refilling, ProductType, \
|
||||
CashRegisterSummary, CashRegisterSummaryItem, Eticket, Permanency
|
||||
from accounting.models import CurrencyField
|
||||
|
||||
|
||||
class CounterAdminMixin(View):
|
||||
"""
|
||||
This view is made to protect counter admin section
|
||||
@ -72,13 +72,13 @@ class CounterAdminMixin(View):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not (request.user.is_root or self._test_group(request.user)
|
||||
or self._test_club(request.user)):
|
||||
raise PermissionDenied
|
||||
return super(CounterAdminMixin, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class GetUserForm(forms.Form):
|
||||
"""
|
||||
The Form class aims at providing a valid user_id field in its cleaned data, in order to pass it to some view,
|
||||
@ -107,20 +107,24 @@ class GetUserForm(forms.Form):
|
||||
cleaned_data['user'] = cus.user
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class RefillForm(forms.ModelForm):
|
||||
error_css_class = 'error'
|
||||
required_css_class = 'required'
|
||||
amount = forms.FloatField(min_value=0, widget=forms.NumberInput(attrs={'class': 'focus'}))
|
||||
|
||||
class Meta:
|
||||
model = Refilling
|
||||
fields = ['amount', 'payment_method', 'bank']
|
||||
|
||||
|
||||
class CounterTabsMixin(TabedViewMixin):
|
||||
def get_tabs_title(self):
|
||||
if hasattr(self.object, 'stock_owner'):
|
||||
return self.object.stock_owner.counter
|
||||
else:
|
||||
return self.object
|
||||
|
||||
def get_list_of_tabs(self):
|
||||
tab_list = []
|
||||
tab_list.append({
|
||||
@ -149,9 +153,11 @@ class CounterTabsMixin(TabedViewMixin):
|
||||
'slug': 'take_items_from_stock',
|
||||
'name': _("Take items from stock"),
|
||||
})
|
||||
except: pass # The counter just have no stock
|
||||
except:
|
||||
pass # The counter just have no stock
|
||||
return tab_list
|
||||
|
||||
|
||||
class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, FormMixin):
|
||||
"""
|
||||
The public (barman) view
|
||||
@ -210,6 +216,7 @@ class CounterMain(CounterTabsMixin, CanViewMixin, DetailView, ProcessFormView, F
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('counter:click', args=self.args, kwargs=self.kwargs)
|
||||
|
||||
|
||||
class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
The click view
|
||||
@ -470,6 +477,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['categories'] = ProductType.objects.all()
|
||||
return kwargs
|
||||
|
||||
|
||||
class CounterLogin(RedirectView):
|
||||
"""
|
||||
Handle the login of a barman
|
||||
@ -477,6 +485,7 @@ class CounterLogin(RedirectView):
|
||||
Logged barmen are stored in the Permanency model
|
||||
"""
|
||||
permanent = False
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Register the logged user as barman for this counter
|
||||
@ -501,8 +510,10 @@ class CounterLogin(RedirectView):
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
return reverse_lazy('counter:details', args=args, kwargs=kwargs) + "?" + '&'.join(self.errors)
|
||||
|
||||
|
||||
class CounterLogout(RedirectView):
|
||||
permanent = False
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
Unregister the user from the barman
|
||||
@ -515,7 +526,8 @@ class CounterLogout(RedirectView):
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
return reverse_lazy('counter:details', args=args, kwargs=kwargs)
|
||||
|
||||
## Counter admin views
|
||||
# Counter admin views
|
||||
|
||||
|
||||
class CounterAdminTabsMixin(TabedViewMixin):
|
||||
tabs_title = _("Counter administration")
|
||||
@ -562,6 +574,7 @@ class CounterAdminTabsMixin(TabedViewMixin):
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class CounterListView(CounterAdminTabsMixin, CanViewMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
@ -570,6 +583,7 @@ class CounterListView(CounterAdminTabsMixin, CanViewMixin, ListView):
|
||||
template_name = 'counter/counter_list.jinja'
|
||||
current_tab = "counters"
|
||||
|
||||
|
||||
class CounterEditForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Counter
|
||||
@ -577,6 +591,7 @@ class CounterEditForm(forms.ModelForm):
|
||||
sellers = make_ajax_field(Counter, 'sellers', 'users', help_text="")
|
||||
products = make_ajax_field(Counter, 'products', 'products', help_text="")
|
||||
|
||||
|
||||
class CounterEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
Edit a counter's main informations (for the counter's manager)
|
||||
@ -595,6 +610,7 @@ class CounterEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('counter:admin', kwargs={'counter_id': self.object.id})
|
||||
|
||||
|
||||
class CounterEditPropView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
Edit a counter's main informations (for the counter's admin)
|
||||
@ -605,6 +621,7 @@ class CounterEditPropView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
template_name = 'core/edit.jinja'
|
||||
current_tab = "counters"
|
||||
|
||||
|
||||
class CounterCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
"""
|
||||
Create a counter (for the admins)
|
||||
@ -615,6 +632,7 @@ class CounterCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
template_name = 'core/create.jinja'
|
||||
current_tab = "counters"
|
||||
|
||||
|
||||
class CounterDeleteView(CounterAdminTabsMixin, CounterAdminMixin, DeleteView):
|
||||
"""
|
||||
Delete a counter (for the admins)
|
||||
@ -627,6 +645,7 @@ class CounterDeleteView(CounterAdminTabsMixin, CounterAdminMixin, DeleteView):
|
||||
|
||||
# Product management
|
||||
|
||||
|
||||
class ProductTypeListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
@ -635,6 +654,7 @@ class ProductTypeListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
template_name = 'counter/producttype_list.jinja'
|
||||
current_tab = "product_types"
|
||||
|
||||
|
||||
class ProductTypeCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
"""
|
||||
A create view for the admins
|
||||
@ -644,6 +664,7 @@ class ProductTypeCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView
|
||||
template_name = 'core/create.jinja'
|
||||
current_tab = "products"
|
||||
|
||||
|
||||
class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
An edit view for the admins
|
||||
@ -654,6 +675,7 @@ class ProductTypeEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
pk_url_kwarg = "type_id"
|
||||
current_tab = "products"
|
||||
|
||||
|
||||
class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
@ -664,6 +686,7 @@ class ProductArchivedListView(CounterAdminTabsMixin, CounterAdminMixin, ListView
|
||||
ordering = ['name']
|
||||
current_tab = "archive"
|
||||
|
||||
|
||||
class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
@ -674,6 +697,7 @@ class ProductListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
ordering = ['name']
|
||||
current_tab = "products"
|
||||
|
||||
|
||||
class ProductEditForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Product
|
||||
@ -702,6 +726,7 @@ class ProductEditForm(forms.ModelForm):
|
||||
c.save()
|
||||
return ret
|
||||
|
||||
|
||||
class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
"""
|
||||
A create view for the admins
|
||||
@ -711,6 +736,7 @@ class ProductCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
template_name = 'core/create.jinja'
|
||||
current_tab = "products"
|
||||
|
||||
|
||||
class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
An edit view for the admins
|
||||
@ -721,6 +747,7 @@ class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
template_name = 'core/edit.jinja'
|
||||
current_tab = "products"
|
||||
|
||||
|
||||
class RefillingDeleteView(DeleteView):
|
||||
"""
|
||||
Delete a refilling (for the admins)
|
||||
@ -745,6 +772,7 @@ class RefillingDeleteView(DeleteView):
|
||||
return super(RefillingDeleteView, self).dispatch(request, *args, **kwargs)
|
||||
raise PermissionDenied
|
||||
|
||||
|
||||
class SellingDeleteView(DeleteView):
|
||||
"""
|
||||
Delete a selling (for the admins)
|
||||
@ -771,6 +799,7 @@ class SellingDeleteView(DeleteView):
|
||||
|
||||
# Cash register summaries
|
||||
|
||||
|
||||
class CashRegisterSummaryForm(forms.Form):
|
||||
"""
|
||||
Provide the cash summary form
|
||||
@ -839,30 +868,46 @@ class CashRegisterSummaryForm(forms.Form):
|
||||
summary.save()
|
||||
summary.items.all().delete()
|
||||
# Cash
|
||||
if cd['ten_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.1, quantity=cd['ten_cents']).save()
|
||||
if cd['twenty_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.2, quantity=cd['twenty_cents']).save()
|
||||
if cd['fifty_cents']: CashRegisterSummaryItem(cash_summary=summary, value=0.5, quantity=cd['fifty_cents']).save()
|
||||
if cd['one_euro']: CashRegisterSummaryItem(cash_summary=summary, value=1, quantity=cd['one_euro']).save()
|
||||
if cd['two_euros']: CashRegisterSummaryItem(cash_summary=summary, value=2, quantity=cd['two_euros']).save()
|
||||
if cd['five_euros']: CashRegisterSummaryItem(cash_summary=summary, value=5, quantity=cd['five_euros']).save()
|
||||
if cd['ten_euros']: CashRegisterSummaryItem(cash_summary=summary, value=10, quantity=cd['ten_euros']).save()
|
||||
if cd['twenty_euros']: CashRegisterSummaryItem(cash_summary=summary, value=20, quantity=cd['twenty_euros']).save()
|
||||
if cd['fifty_euros']: CashRegisterSummaryItem(cash_summary=summary, value=50, quantity=cd['fifty_euros']).save()
|
||||
if cd['hundred_euros']: CashRegisterSummaryItem(cash_summary=summary, value=100, quantity=cd['hundred_euros']).save()
|
||||
if cd['ten_cents']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=0.1, quantity=cd['ten_cents']).save()
|
||||
if cd['twenty_cents']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=0.2, quantity=cd['twenty_cents']).save()
|
||||
if cd['fifty_cents']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=0.5, quantity=cd['fifty_cents']).save()
|
||||
if cd['one_euro']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=1, quantity=cd['one_euro']).save()
|
||||
if cd['two_euros']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=2, quantity=cd['two_euros']).save()
|
||||
if cd['five_euros']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=5, quantity=cd['five_euros']).save()
|
||||
if cd['ten_euros']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=10, quantity=cd['ten_euros']).save()
|
||||
if cd['twenty_euros']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=20, quantity=cd['twenty_euros']).save()
|
||||
if cd['fifty_euros']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=50, quantity=cd['fifty_euros']).save()
|
||||
if cd['hundred_euros']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=100, quantity=cd['hundred_euros']).save()
|
||||
# Checks
|
||||
if cd['check_1_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_1_value'],
|
||||
if cd['check_1_quantity']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_1_value'],
|
||||
quantity=cd['check_1_quantity'], check=True).save()
|
||||
if cd['check_2_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_2_value'],
|
||||
if cd['check_2_quantity']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_2_value'],
|
||||
quantity=cd['check_2_quantity'], check=True).save()
|
||||
if cd['check_3_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_3_value'],
|
||||
if cd['check_3_quantity']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_3_value'],
|
||||
quantity=cd['check_3_quantity'], check=True).save()
|
||||
if cd['check_4_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_4_value'],
|
||||
if cd['check_4_quantity']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_4_value'],
|
||||
quantity=cd['check_4_quantity'], check=True).save()
|
||||
if cd['check_5_quantity']: CashRegisterSummaryItem(cash_summary=summary, value=cd['check_5_value'],
|
||||
if cd['check_5_quantity']:
|
||||
CashRegisterSummaryItem(cash_summary=summary, value=cd['check_5_value'],
|
||||
quantity=cd['check_5_quantity'], check=True).save()
|
||||
if summary.items.count() < 1:
|
||||
summary.delete()
|
||||
|
||||
|
||||
class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Provide the last operations to allow barmen to delete them
|
||||
@ -891,6 +936,7 @@ class CounterLastOperationsView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['last_sellings'] = self.object.sellings.filter(date__gte=threshold).order_by('-id')[:20]
|
||||
return kwargs
|
||||
|
||||
|
||||
class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
"""
|
||||
Provide the cash summary form
|
||||
@ -933,6 +979,7 @@ class CounterCashSummaryView(CounterTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs['form'] = self.form
|
||||
return kwargs
|
||||
|
||||
|
||||
class CounterActivityView(DetailView):
|
||||
"""
|
||||
Show the bar activity
|
||||
@ -941,6 +988,7 @@ class CounterActivityView(DetailView):
|
||||
pk_url_kwarg = "counter_id"
|
||||
template_name = 'counter/activity.jinja'
|
||||
|
||||
|
||||
class CounterStatView(DetailView, CounterAdminMixin):
|
||||
"""
|
||||
Show the bar stats
|
||||
@ -1003,6 +1051,7 @@ class CounterStatView(DetailView, CounterAdminMixin):
|
||||
return super(CanEditMixin, self).dispatch(request, *args, **kwargs)
|
||||
raise PermissionDenied
|
||||
|
||||
|
||||
class CashSummaryEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""Edit cash summaries"""
|
||||
model = CashRegisterSummary
|
||||
@ -1015,10 +1064,12 @@ class CashSummaryEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView)
|
||||
def get_success_url(self):
|
||||
return reverse('counter:cash_summary_list')
|
||||
|
||||
|
||||
class CashSummaryFormBase(forms.Form):
|
||||
begin_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("Begin date"), required=False, widget=SelectDateTime)
|
||||
end_date = forms.DateTimeField(['%Y-%m-%d %H:%M:%S'], label=_("End date"), required=False, widget=SelectDateTime)
|
||||
|
||||
|
||||
class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""Display a list of cash summaries"""
|
||||
model = CashRegisterSummary
|
||||
@ -1056,6 +1107,7 @@ class CashSummaryListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
kwargs['refilling_sums'][c.name] = sum([s.amount for s in refillings.all()])
|
||||
return kwargs
|
||||
|
||||
|
||||
class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
|
||||
template_name = 'counter/invoices_call.jinja'
|
||||
current_tab = 'invoices_call'
|
||||
@ -1087,6 +1139,7 @@ class InvoiceCallView(CounterAdminTabsMixin, CounterAdminMixin, TemplateView):
|
||||
)).exclude(selling_sum=None).order_by('-selling_sum')
|
||||
return kwargs
|
||||
|
||||
|
||||
class EticketListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
"""
|
||||
A list view for the admins
|
||||
@ -1096,6 +1149,7 @@ class EticketListView(CounterAdminTabsMixin, CounterAdminMixin, ListView):
|
||||
ordering = ['id']
|
||||
current_tab = "etickets"
|
||||
|
||||
|
||||
class EticketForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Eticket
|
||||
@ -1105,6 +1159,7 @@ class EticketForm(forms.ModelForm):
|
||||
}
|
||||
product = AutoCompleteSelectField('products', show_help_text=False, label=_("Product"), required=True)
|
||||
|
||||
|
||||
class EticketCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
"""
|
||||
Create an eticket
|
||||
@ -1114,6 +1169,7 @@ class EticketCreateView(CounterAdminTabsMixin, CounterAdminMixin, CreateView):
|
||||
form_class = EticketForm
|
||||
current_tab = "etickets"
|
||||
|
||||
|
||||
class EticketEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
"""
|
||||
Edit an eticket
|
||||
@ -1124,6 +1180,7 @@ class EticketEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
pk_url_kwarg = "eticket_id"
|
||||
current_tab = "etickets"
|
||||
|
||||
|
||||
class EticketPDFView(CanViewMixin, DetailView):
|
||||
"""
|
||||
Display the PDF of an eticket
|
||||
@ -1195,4 +1252,3 @@ class EticketPDFView(CanViewMixin, DetailView):
|
||||
p.showPage()
|
||||
p.save()
|
||||
return response
|
||||
|
||||
|
@ -27,9 +27,9 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
|
||||
from accounting.models import CurrencyField
|
||||
from counter.models import Counter, Product, Customer, Selling, Refilling
|
||||
from counter.models import Counter, Product, Selling, Refilling
|
||||
from core.models import User
|
||||
from subscription.models import Subscription
|
||||
|
||||
|
||||
class Basket(models.Model):
|
||||
"""
|
||||
@ -64,6 +64,7 @@ class Basket(models.Model):
|
||||
def __str__(self):
|
||||
return "%s's basket (%d items)" % (self.user, self.items.all().count())
|
||||
|
||||
|
||||
class Invoice(models.Model):
|
||||
"""
|
||||
Invoices are generated once the payment has been validated
|
||||
@ -120,6 +121,7 @@ class Invoice(models.Model):
|
||||
self.validated = True
|
||||
self.save()
|
||||
|
||||
|
||||
class AbstractBaseItem(models.Model):
|
||||
product_id = models.IntegerField(_('product id'))
|
||||
product_name = models.CharField(_('product name'), max_length=255)
|
||||
@ -133,8 +135,10 @@ class AbstractBaseItem(models.Model):
|
||||
def __str__(self):
|
||||
return "Item: %s (%s) x%d" % (self.product_name, self.product_unit_price, self.quantity)
|
||||
|
||||
|
||||
class BasketItem(AbstractBaseItem):
|
||||
basket = models.ForeignKey(Basket, related_name='items', verbose_name=_('basket'))
|
||||
|
||||
|
||||
class InvoiceItem(AbstractBaseItem):
|
||||
invoice = models.ForeignKey(Invoice, related_name='items', verbose_name=_('invoice'))
|
||||
|
@ -34,7 +34,8 @@ from django.core.management import call_command
|
||||
from django.conf import settings
|
||||
|
||||
from core.models import User
|
||||
from counter.models import Customer, ProductType, Product, Counter, Refilling
|
||||
from counter.models import Product, Counter, Refilling
|
||||
|
||||
|
||||
class EbouticTest(TestCase):
|
||||
def setUp(self):
|
||||
@ -93,7 +94,6 @@ class EbouticTest(TestCase):
|
||||
" <td>Barbar</td>\\n <td>1</td>\\n <td>1.70 \\xe2\\x82\\xac</td>\\n"
|
||||
" <td>Compte utilisateur</td>" in str(response.content))
|
||||
|
||||
|
||||
def test_buy_simple_product_with_credit_card(self):
|
||||
self.client.login(username='subscriber', password='plop')
|
||||
response = self.client.post(reverse("eboutic:main"), {
|
||||
@ -171,6 +171,3 @@ class EbouticTest(TestCase):
|
||||
" <td>15.00 \\xe2\\x82\\xac</td>" in str(response.content))
|
||||
response = self.client.get(reverse("core:user_profile", kwargs={"user_id": self.old_subscriber.id}))
|
||||
self.assertTrue("Cotisant jusqu\\'au" in str(response.content))
|
||||
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from eboutic.views import *
|
||||
|
||||
@ -33,6 +33,3 @@ urlpatterns = [
|
||||
url(r'^pay$', EbouticPayWithSith.as_view(), name='pay_with_sith'),
|
||||
url(r'^et_autoanswer$', EtransactionAutoAnswer.as_view(), name='etransation_autoanswer'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
@ -24,23 +24,21 @@
|
||||
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime
|
||||
import pytz
|
||||
import hmac
|
||||
import base64
|
||||
from OpenSSL import crypto
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.views.generic import TemplateView, View
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
from django.shortcuts import render
|
||||
from django.db import transaction, DataError
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.conf import settings
|
||||
|
||||
from counter.models import Customer, Counter, ProductType, Selling
|
||||
from eboutic.models import Basket, Invoice, BasketItem, InvoiceItem
|
||||
from eboutic.models import Basket, Invoice, InvoiceItem
|
||||
|
||||
|
||||
class EbouticMain(TemplateView):
|
||||
template_name = 'eboutic/eboutic_main.jinja'
|
||||
@ -77,7 +75,6 @@ class EbouticMain(TemplateView):
|
||||
self.del_product(request)
|
||||
return self.render_to_response(self.get_context_data(**kwargs))
|
||||
|
||||
|
||||
def add_product(self, request):
|
||||
""" Add a product to the basket """
|
||||
try:
|
||||
@ -108,6 +105,7 @@ class EbouticMain(TemplateView):
|
||||
kwargs['categories'] = kwargs['categories'].exclude(id=settings.SITH_PRODUCTTYPE_SUBSCRIPTION)
|
||||
return kwargs
|
||||
|
||||
|
||||
class EbouticCommand(TemplateView):
|
||||
template_name = 'eboutic/eboutic_makecommand.jinja'
|
||||
|
||||
@ -150,6 +148,7 @@ class EbouticCommand(TemplateView):
|
||||
"sha512").hexdigest().upper()
|
||||
return kwargs
|
||||
|
||||
|
||||
class EbouticPayWithSith(TemplateView):
|
||||
template_name = 'eboutic/eboutic_payment_result.jinja'
|
||||
|
||||
@ -189,6 +188,7 @@ class EbouticPayWithSith(TemplateView):
|
||||
kwargs['not_enough'] = True
|
||||
return self.render_to_response(self.get_context_data(**kwargs))
|
||||
|
||||
|
||||
class EtransactionAutoAnswer(View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
if (not 'Amount' in request.GET.keys() or
|
||||
@ -225,4 +225,3 @@ class EtransactionAutoAnswer(View):
|
||||
return HttpResponse()
|
||||
else:
|
||||
return HttpResponse("Payment failed with error: " + request.GET['Error'], status=400)
|
||||
|
||||
|
@ -23,11 +23,9 @@
|
||||
#
|
||||
|
||||
from django.db import models
|
||||
from django.core import validators
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.functional import cached_property
|
||||
@ -35,9 +33,10 @@ from django.utils.functional import cached_property
|
||||
from datetime import datetime
|
||||
import pytz
|
||||
|
||||
from core.models import User, MetaGroup, Group, SithFile
|
||||
from core.models import User, Group
|
||||
from club.models import Club
|
||||
|
||||
|
||||
class Forum(models.Model):
|
||||
"""
|
||||
The Forum class, made as a tree to allow nice tidy organization
|
||||
@ -118,10 +117,12 @@ class Forum(models.Model):
|
||||
def is_owned_by(self, user):
|
||||
if user.is_in_group(settings.SITH_GROUP_FORUM_ADMIN_ID):
|
||||
return True
|
||||
try: m = Forum._club_memberships[self.id][user.id]
|
||||
try:
|
||||
m = Forum._club_memberships[self.id][user.id]
|
||||
except:
|
||||
m = self.owner_club.get_membership_for(user)
|
||||
try: Forum._club_memberships[self.id][user.id] = m
|
||||
try:
|
||||
Forum._club_memberships[self.id][user.id] = m
|
||||
except:
|
||||
Forum._club_memberships[self.id] = {}
|
||||
Forum._club_memberships[self.id][user.id] = m
|
||||
@ -178,6 +179,7 @@ class Forum(models.Model):
|
||||
l += c.get_children_list()
|
||||
return l
|
||||
|
||||
|
||||
class ForumTopic(models.Model):
|
||||
forum = models.ForeignKey(Forum, related_name='topics')
|
||||
author = models.ForeignKey(User, related_name='forum_topics')
|
||||
@ -225,6 +227,7 @@ class ForumTopic(models.Model):
|
||||
def title(self):
|
||||
return self._title
|
||||
|
||||
|
||||
class ForumMessage(models.Model):
|
||||
"""
|
||||
"A ForumMessage object represents a message in the forum" -- Cpt. Obvious
|
||||
@ -282,10 +285,11 @@ class ForumMessage(models.Model):
|
||||
return int(self.topic.messages.filter(id__lt=self.id).count() / settings.SITH_FORUM_PAGE_LENGTH) + 1
|
||||
|
||||
def mark_as_read(self, user):
|
||||
try: # Need the try/except because of AnonymousUser
|
||||
try: # Need the try/except because of AnonymousUser
|
||||
if not self.is_read(user):
|
||||
self.readers.add(user)
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
|
||||
def is_read(self, user):
|
||||
return (self.date < user.forum_infos.last_read_date) or (user in self.readers.all())
|
||||
@ -296,12 +300,14 @@ class ForumMessage(models.Model):
|
||||
return meta.action == "DELETE"
|
||||
return False
|
||||
|
||||
|
||||
MESSAGE_META_ACTIONS = [
|
||||
('EDIT', _("Message edited by")),
|
||||
('DELETE', _("Message deleted by")),
|
||||
('UNDELETE', _("Message undeleted by")),
|
||||
]
|
||||
|
||||
|
||||
class ForumMessageMeta(models.Model):
|
||||
user = models.ForeignKey(User, related_name="forum_message_metas")
|
||||
message = models.ForeignKey(ForumMessage, related_name="metas")
|
||||
@ -326,4 +332,3 @@ class ForumUserInfo(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return str(self.user)
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from forum.views import *
|
||||
|
||||
|
@ -22,30 +22,30 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.views.generic import ListView, DetailView, RedirectView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.urlresolvers import reverse, reverse_lazy
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from django import forms
|
||||
from django.db import models
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
|
||||
from ajax_select import make_ajax_form, make_ajax_field
|
||||
from ajax_select import make_ajax_field
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
|
||||
from core.views.forms import MarkdownInput
|
||||
from core.models import Page
|
||||
from forum.models import Forum, ForumMessage, ForumTopic, ForumMessageMeta
|
||||
|
||||
|
||||
class ForumMainView(ListView):
|
||||
queryset = Forum.objects.filter(parent=None).prefetch_related("children___last_message__author", "children___last_message__topic")
|
||||
template_name = "forum/main.jinja"
|
||||
|
||||
|
||||
class ForumMarkAllAsRead(RedirectView):
|
||||
permanent = False
|
||||
url = reverse_lazy('forum:last_unread')
|
||||
@ -57,9 +57,11 @@ class ForumMarkAllAsRead(RedirectView):
|
||||
fi.save()
|
||||
for m in request.user.read_messages.filter(date__lt=fi.last_read_date):
|
||||
m.readers.remove(request.user) # Clean up to keep table low in data
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return super(ForumMarkAllAsRead, self).get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class ForumLastUnread(ListView):
|
||||
model = ForumTopic
|
||||
template_name = "forum/last_unread.jinja"
|
||||
@ -73,6 +75,7 @@ class ForumLastUnread(ListView):
|
||||
.prefetch_related('forum__edit_groups')
|
||||
return topic_list
|
||||
|
||||
|
||||
class ForumForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Forum
|
||||
@ -80,6 +83,7 @@ class ForumForm(forms.ModelForm):
|
||||
edit_groups = make_ajax_field(Forum, 'edit_groups', 'groups', help_text="")
|
||||
view_groups = make_ajax_field(Forum, 'view_groups', 'groups', help_text="")
|
||||
|
||||
|
||||
class ForumCreateView(CanCreateMixin, CreateView):
|
||||
model = Forum
|
||||
form_class = ForumForm
|
||||
@ -93,12 +97,15 @@ class ForumCreateView(CanCreateMixin, CreateView):
|
||||
init['owner_club'] = parent.owner_club
|
||||
init['edit_groups'] = parent.edit_groups.all()
|
||||
init['view_groups'] = parent.view_groups.all()
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return init
|
||||
|
||||
|
||||
class ForumEditForm(ForumForm):
|
||||
recursive = forms.BooleanField(label=_("Apply rights and club owner recursively"), required=False)
|
||||
|
||||
|
||||
class ForumEditView(CanEditPropMixin, UpdateView):
|
||||
model = Forum
|
||||
pk_url_kwarg = "forum_id"
|
||||
@ -112,12 +119,14 @@ class ForumEditView(CanEditPropMixin, UpdateView):
|
||||
self.object.apply_rights_recursively()
|
||||
return ret
|
||||
|
||||
|
||||
class ForumDeleteView(CanEditPropMixin, DeleteView):
|
||||
model = Forum
|
||||
pk_url_kwarg = "forum_id"
|
||||
template_name = "core/delete_confirm.jinja"
|
||||
success_url = reverse_lazy('forum:main')
|
||||
|
||||
|
||||
class ForumDetailView(CanViewMixin, DetailView):
|
||||
model = Forum
|
||||
template_name = "forum/forum.jinja"
|
||||
@ -139,6 +148,7 @@ class ForumDetailView(CanViewMixin, DetailView):
|
||||
kwargs["topics"] = paginator.page(paginator.num_pages)
|
||||
return kwargs
|
||||
|
||||
|
||||
class TopicForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = ForumMessage
|
||||
@ -148,6 +158,7 @@ class TopicForm(forms.ModelForm):
|
||||
}
|
||||
title = forms.CharField(required=True, label=_("Title"))
|
||||
|
||||
|
||||
class ForumTopicCreateView(CanCreateMixin, CreateView):
|
||||
model = ForumMessage
|
||||
form_class = TopicForm
|
||||
@ -166,12 +177,14 @@ class ForumTopicCreateView(CanCreateMixin, CreateView):
|
||||
form.instance.author = self.request.user
|
||||
return super(ForumTopicCreateView, self).form_valid(form)
|
||||
|
||||
|
||||
class ForumTopicEditView(CanEditMixin, UpdateView):
|
||||
model = ForumTopic
|
||||
fields = ['forum']
|
||||
pk_url_kwarg = "topic_id"
|
||||
template_name = "core/edit.jinja"
|
||||
|
||||
|
||||
class ForumTopicDetailView(CanViewMixin, DetailView):
|
||||
model = ForumTopic
|
||||
pk_url_kwarg = "topic_id"
|
||||
@ -186,7 +199,7 @@ class ForumTopicDetailView(CanViewMixin, DetailView):
|
||||
kwargs['first_unread_message_id'] = msg.id
|
||||
except:
|
||||
kwargs['first_unread_message_id'] = float("inf")
|
||||
paginator = Paginator(self.object.messages.select_related('author__avatar_pict')\
|
||||
paginator = Paginator(self.object.messages.select_related('author__avatar_pict')
|
||||
.prefetch_related('topic__forum__edit_groups', 'readers').order_by('date'),
|
||||
settings.SITH_FORUM_PAGE_LENGTH)
|
||||
page = self.request.GET.get('page')
|
||||
@ -198,6 +211,7 @@ class ForumTopicDetailView(CanViewMixin, DetailView):
|
||||
kwargs["msgs"] = paginator.page(paginator.num_pages)
|
||||
return kwargs
|
||||
|
||||
|
||||
class ForumMessageView(SingleObjectMixin, RedirectView):
|
||||
model = ForumMessage
|
||||
pk_url_kwarg = "message_id"
|
||||
@ -207,6 +221,7 @@ class ForumMessageView(SingleObjectMixin, RedirectView):
|
||||
self.object = self.get_object()
|
||||
return self.object.get_url()
|
||||
|
||||
|
||||
class ForumMessageEditView(CanEditMixin, UpdateView):
|
||||
model = ForumMessage
|
||||
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message', ], widgets={'message': MarkdownInput})
|
||||
@ -222,6 +237,7 @@ class ForumMessageEditView(CanEditMixin, UpdateView):
|
||||
kwargs['topic'] = self.object.topic
|
||||
return kwargs
|
||||
|
||||
|
||||
class ForumMessageDeleteView(SingleObjectMixin, RedirectView):
|
||||
model = ForumMessage
|
||||
pk_url_kwarg = "message_id"
|
||||
@ -233,6 +249,7 @@ class ForumMessageDeleteView(SingleObjectMixin, RedirectView):
|
||||
ForumMessageMeta(message=self.object, user=self.request.user, action="DELETE").save()
|
||||
return self.object.get_absolute_url()
|
||||
|
||||
|
||||
class ForumMessageUndeleteView(SingleObjectMixin, RedirectView):
|
||||
model = ForumMessage
|
||||
pk_url_kwarg = "message_id"
|
||||
@ -244,6 +261,7 @@ class ForumMessageUndeleteView(SingleObjectMixin, RedirectView):
|
||||
ForumMessageMeta(message=self.object, user=self.request.user, action="UNDELETE").save()
|
||||
return self.object.get_absolute_url()
|
||||
|
||||
|
||||
class ForumMessageCreateView(CanCreateMixin, CreateView):
|
||||
model = ForumMessage
|
||||
form_class = forms.modelform_factory(model=ForumMessage, fields=['title', 'message', ], widgets={'message': MarkdownInput})
|
||||
@ -277,4 +295,3 @@ class ForumMessageCreateView(CanCreateMixin, CreateView):
|
||||
kwargs = super(ForumMessageCreateView, self).get_context_data(**kwargs)
|
||||
kwargs['topic'] = self.topic
|
||||
return kwargs
|
||||
|
||||
|
@ -27,12 +27,13 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from counter.models import Counter, Product
|
||||
from counter.models import Counter
|
||||
from core.models import User
|
||||
from club.models import Club
|
||||
|
||||
# Create your models here.
|
||||
|
||||
|
||||
class Launderette(models.Model):
|
||||
name = models.CharField(_('name'), max_length=30)
|
||||
counter = models.OneToOneField(Counter, verbose_name=_('counter'), related_name='launderette')
|
||||
@ -78,6 +79,7 @@ class Launderette(models.Model):
|
||||
def token_list(self):
|
||||
return [t.id for t in self.get_token_list()]
|
||||
|
||||
|
||||
class Machine(models.Model):
|
||||
name = models.CharField(_('name'), max_length=30)
|
||||
launderette = models.ForeignKey(Launderette, related_name='machines', verbose_name=_('launderette'))
|
||||
@ -103,6 +105,7 @@ class Machine(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse('launderette:launderette_admin', kwargs={"launderette_id": self.launderette.id})
|
||||
|
||||
|
||||
class Token(models.Model):
|
||||
name = models.CharField(_('name'), max_length=5)
|
||||
launderette = models.ForeignKey(Launderette, related_name='tokens', verbose_name=_('launderette'))
|
||||
@ -140,6 +143,7 @@ class Token(models.Model):
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class Slot(models.Model):
|
||||
start_date = models.DateTimeField(_('start date'))
|
||||
type = models.CharField(_('type'), max_length=10, choices=settings.SITH_LAUNDERETTE_MACHINE_TYPES)
|
||||
@ -157,5 +161,3 @@ class Slot(models.Model):
|
||||
def __str__(self):
|
||||
return "User: %s - Date: %s - Type: %s - Machine: %s - Token: %s" % (self.user, self.start_date, self.get_type_display(),
|
||||
self.machine.name, self.token)
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from launderette.views import *
|
||||
|
||||
|
@ -26,11 +26,8 @@ from datetime import datetime, timedelta
|
||||
from collections import OrderedDict
|
||||
import pytz
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
|
||||
from django.views.generic import ListView, DetailView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView, BaseFormView
|
||||
from django.forms.models import modelform_factory
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils import dateparse, timezone
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
@ -38,7 +35,6 @@ from django.conf import settings
|
||||
from django.db import transaction, DataError
|
||||
from django import forms
|
||||
from django.template import defaultfilters
|
||||
from django.utils import formats
|
||||
|
||||
from core.models import Page, User
|
||||
from club.models import Club
|
||||
@ -49,6 +45,7 @@ from counter.views import GetUserForm
|
||||
|
||||
# For users
|
||||
|
||||
|
||||
class LaunderetteMainView(TemplateView):
|
||||
"""Main presentation view"""
|
||||
template_name = 'launderette/launderette_main.jinja'
|
||||
@ -59,11 +56,13 @@ class LaunderetteMainView(TemplateView):
|
||||
kwargs['page'] = Page.objects.filter(name='launderette').first()
|
||||
return kwargs
|
||||
|
||||
|
||||
class LaunderetteBookMainView(CanViewMixin, ListView):
|
||||
"""Choose which launderette to book"""
|
||||
model = Launderette
|
||||
template_name = 'launderette/launderette_book_choose.jinja'
|
||||
|
||||
|
||||
class LaunderetteBookView(CanViewMixin, DetailView):
|
||||
"""Display the launderette schedule"""
|
||||
model = Launderette
|
||||
@ -100,7 +99,8 @@ class LaunderetteBookView(CanViewMixin, DetailView):
|
||||
return super(LaunderetteBookView, self).get(request, *args, **kwargs)
|
||||
|
||||
def check_slot(self, type, date=None):
|
||||
if date is None: date = self.date
|
||||
if date is None:
|
||||
date = self.date
|
||||
for m in self.object.machines.filter(is_working=True, type=type).all():
|
||||
slot = Slot.objects.filter(start_date=date, machine=m).first()
|
||||
if slot is None:
|
||||
@ -137,6 +137,7 @@ class LaunderetteBookView(CanViewMixin, DetailView):
|
||||
kwargs['planning'][date].append(None)
|
||||
return kwargs
|
||||
|
||||
|
||||
class SlotDeleteView(CanEditPropMixin, DeleteView):
|
||||
"""Delete a slot"""
|
||||
model = Slot
|
||||
@ -154,6 +155,7 @@ class LaunderetteListView(CanEditPropMixin, ListView):
|
||||
model = Launderette
|
||||
template_name = 'launderette/launderette_list.jinja'
|
||||
|
||||
|
||||
class LaunderetteEditView(CanEditPropMixin, UpdateView):
|
||||
"""Edit a launderette"""
|
||||
model = Launderette
|
||||
@ -161,6 +163,7 @@ class LaunderetteEditView(CanEditPropMixin, UpdateView):
|
||||
fields = ['name']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class LaunderetteCreateView(CanCreateMixin, CreateView):
|
||||
"""Create a new launderette"""
|
||||
model = Launderette
|
||||
@ -174,6 +177,7 @@ class LaunderetteCreateView(CanCreateMixin, CreateView):
|
||||
form.instance.counter = c
|
||||
return super(LaunderetteCreateView, self).form_valid(form)
|
||||
|
||||
|
||||
class ManageTokenForm(forms.Form):
|
||||
action = forms.ChoiceField(choices=[("BACK", _("Back")), ("ADD", _("Add")), ("DEL", _("Delete"))], initial="BACK",
|
||||
label=_("Action"), widget=forms.RadioSelect)
|
||||
@ -210,6 +214,7 @@ class ManageTokenForm(forms.Form):
|
||||
except:
|
||||
self.add_error(None, _("Token %(token_name)s does not exists") % {'token_name': t})
|
||||
|
||||
|
||||
class LaunderetteAdminView(CanEditPropMixin, BaseFormView, DetailView):
|
||||
"""The admin page of the launderette"""
|
||||
model = Launderette
|
||||
@ -253,6 +258,7 @@ class LaunderetteAdminView(CanEditPropMixin, BaseFormView, DetailView):
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('launderette:launderette_admin', args=self.args, kwargs=self.kwargs)
|
||||
|
||||
|
||||
class GetLaunderetteUserForm(GetUserForm):
|
||||
def clean(self):
|
||||
cleaned_data = super(GetLaunderetteUserForm, self).clean()
|
||||
@ -261,6 +267,7 @@ class GetLaunderetteUserForm(GetUserForm):
|
||||
raise forms.ValidationError(_("User has booked no slot"))
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class LaunderetteMainClickView(CanEditMixin, BaseFormView, DetailView):
|
||||
"""The click page of the launderette"""
|
||||
model = Launderette
|
||||
@ -301,6 +308,7 @@ class LaunderetteMainClickView(CanEditMixin, BaseFormView, DetailView):
|
||||
def get_success_url(self):
|
||||
return reverse_lazy('launderette:click', args=self.args, kwargs=self.kwargs)
|
||||
|
||||
|
||||
class ClickTokenForm(forms.BaseForm):
|
||||
def clean(self):
|
||||
with transaction.atomic():
|
||||
@ -332,6 +340,7 @@ class ClickTokenForm(forms.BaseForm):
|
||||
self.last_basket['last_total'] = str(total)
|
||||
return self.cleaned_data
|
||||
|
||||
|
||||
class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
|
||||
"""The click page of the launderette"""
|
||||
model = Launderette
|
||||
@ -401,7 +410,6 @@ class LaunderetteClickView(CanEditMixin, DetailView, BaseFormView):
|
||||
return reverse_lazy('launderette:main_click', args=self.args, kwargs=self.kwargs)
|
||||
|
||||
|
||||
|
||||
class MachineEditView(CanEditPropMixin, UpdateView):
|
||||
"""Edit a machine"""
|
||||
model = Machine
|
||||
@ -409,6 +417,7 @@ class MachineEditView(CanEditPropMixin, UpdateView):
|
||||
fields = ['name', 'launderette', 'type', 'is_working']
|
||||
template_name = 'core/edit.jinja'
|
||||
|
||||
|
||||
class MachineDeleteView(CanEditPropMixin, DeleteView):
|
||||
"""Edit a machine"""
|
||||
model = Machine
|
||||
@ -416,6 +425,7 @@ class MachineDeleteView(CanEditPropMixin, DeleteView):
|
||||
template_name = 'core/delete_confirm.jinja'
|
||||
success_url = reverse_lazy('launderette:launderette_list')
|
||||
|
||||
|
||||
class MachineCreateView(CanCreateMixin, CreateView):
|
||||
"""Create a new machine"""
|
||||
model = Machine
|
||||
@ -429,6 +439,3 @@ class MachineCreateView(CanCreateMixin, CreateView):
|
||||
if obj is not None:
|
||||
ret['launderette'] = obj.id
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
|
@ -30,5 +30,3 @@ from sas.models import *
|
||||
admin.site.register(Album)
|
||||
# admin.site.register(Picture)
|
||||
admin.site.register(PeoplePictureRelation)
|
||||
|
||||
|
||||
|
@ -23,11 +23,9 @@
|
||||
#
|
||||
|
||||
from django.db import models
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.files.base import ContentFile
|
||||
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
@ -36,6 +34,7 @@ import os
|
||||
from core.models import SithFile, User
|
||||
from core.utils import resize_image, exif_auto_rotate
|
||||
|
||||
|
||||
class Picture(SithFile):
|
||||
class Meta:
|
||||
proxy = True
|
||||
@ -73,7 +72,8 @@ class Picture(SithFile):
|
||||
im = Image.open(BytesIO(self.file.read()))
|
||||
try:
|
||||
im = exif_auto_rotate(im)
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
file = resize_image(im, max(im.size), self.mime_type.split('/')[-1])
|
||||
thumb = resize_image(im, 200, self.mime_type.split('/')[-1])
|
||||
compressed = resize_image(im, 1200, self.mime_type.split('/')[-1])
|
||||
@ -113,6 +113,7 @@ class Picture(SithFile):
|
||||
else:
|
||||
return Picture.objects.filter(id__lt=self.id, is_moderated=False, is_in_sas=True).order_by('-id').first()
|
||||
|
||||
|
||||
class Album(SithFile):
|
||||
class Meta:
|
||||
proxy = True
|
||||
@ -148,6 +149,7 @@ class Album(SithFile):
|
||||
self.file.name = self.name + '/thumb.jpg'
|
||||
self.save()
|
||||
|
||||
|
||||
class PeoplePictureRelation(models.Model):
|
||||
"""
|
||||
The PeoplePictureRelation class makes the connection between User and Picture
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from sas.views import *
|
||||
|
||||
@ -40,4 +40,3 @@ urlpatterns = [
|
||||
# url(r'^album/new$', AlbumCreateView.as_view(), name='album_new'),
|
||||
# url(r'^(?P<club_id>[0-9]+)/$', ClubView.as_view(), name='club_view'),
|
||||
]
|
||||
|
||||
|
43
sas/views.py
43
sas/views.py
@ -22,31 +22,27 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.shortcuts import render, redirect
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.shortcuts import redirect
|
||||
from django.http import HttpResponse
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView, ProcessFormView, FormMixin, FormView
|
||||
from core.views.forms import SelectDate
|
||||
from django.views.generic import DetailView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, FormMixin, FormView
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from django.forms.models import modelform_factory
|
||||
from django import forms
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
from ajax_select import make_ajax_form, make_ajax_field
|
||||
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
|
||||
from ajax_select import make_ajax_field
|
||||
from ajax_select.fields import AutoCompleteSelectMultipleField
|
||||
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin, TabedViewMixin
|
||||
from core.views.forms import SelectUser, LoginForm, SelectDate, SelectDateTime
|
||||
from core.views import CanViewMixin, CanEditMixin
|
||||
from core.views.files import send_file, FileView
|
||||
from core.models import SithFile, User, Notification, RealGroup
|
||||
|
||||
from sas.models import Picture, Album, PeoplePictureRelation
|
||||
|
||||
|
||||
class SASForm(forms.Form):
|
||||
album_name = forms.CharField(label=_("Add a new album"), max_length=30, required=False)
|
||||
images = forms.ImageField(widget=forms.ClearableFileInput(attrs={'multiple': True}), label=_("Upload images"),
|
||||
@ -80,6 +76,7 @@ class SASForm(forms.Form):
|
||||
if not u.notifications.filter(type="SAS_MODERATION", viewed=False).exists():
|
||||
Notification(user=u, url=reverse("sas:moderation"), type="SAS_MODERATION").save()
|
||||
|
||||
|
||||
class RelationForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = PeoplePictureRelation
|
||||
@ -87,6 +84,7 @@ class RelationForm(forms.ModelForm):
|
||||
widgets = {'picture': forms.HiddenInput}
|
||||
users = AutoCompleteSelectMultipleField('users', show_help_text=False, help_text="", label=_("Add user"), required=False)
|
||||
|
||||
|
||||
class SASMainView(FormView):
|
||||
form_class = SASForm
|
||||
template_name = "sas/main.jinja"
|
||||
@ -112,6 +110,7 @@ class SASMainView(FormView):
|
||||
kwargs['latest'] = SithFile.objects.filter(is_in_sas=True, is_folder=True, is_moderated=True).order_by('-id')[:5]
|
||||
return kwargs
|
||||
|
||||
|
||||
class PictureView(CanViewMixin, DetailView, FormMixin):
|
||||
model = Picture
|
||||
form_class = RelationForm
|
||||
@ -132,8 +131,9 @@ class PictureView(CanViewMixin, DetailView, FormMixin):
|
||||
try:
|
||||
user = User.objects.filter(id=int(request.GET['remove_user'])).first()
|
||||
if user.id == request.user.id or request.user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID):
|
||||
r = PeoplePictureRelation.objects.filter(user=user, picture=self.object).delete()
|
||||
except: pass
|
||||
PeoplePictureRelation.objects.filter(user=user, picture=self.object).delete()
|
||||
except:
|
||||
pass
|
||||
if 'ask_removal' in request.GET.keys():
|
||||
self.object.is_moderated = False
|
||||
self.object.asked_for_removal = True
|
||||
@ -165,15 +165,19 @@ class PictureView(CanViewMixin, DetailView, FormMixin):
|
||||
def get_success_url(self):
|
||||
return reverse('sas:picture', kwargs={'picture_id': self.object.id})
|
||||
|
||||
|
||||
def send_pict(request, picture_id):
|
||||
return send_file(request, picture_id, Picture)
|
||||
|
||||
|
||||
def send_compressed(request, picture_id):
|
||||
return send_file(request, picture_id, Picture, "compressed")
|
||||
|
||||
|
||||
def send_thumb(request, picture_id):
|
||||
return send_file(request, picture_id, Picture, "thumbnail")
|
||||
|
||||
|
||||
class AlbumUploadView(CanViewMixin, DetailView, FormMixin):
|
||||
model = Album
|
||||
form_class = SASForm
|
||||
@ -239,6 +243,7 @@ class AlbumView(CanViewMixin, DetailView, FormMixin):
|
||||
|
||||
# Admin views
|
||||
|
||||
|
||||
class ModerationView(TemplateView):
|
||||
template_name = "sas/moderation.jinja"
|
||||
|
||||
@ -257,7 +262,8 @@ class ModerationView(TemplateView):
|
||||
a.save()
|
||||
elif 'delete' in request.POST.keys():
|
||||
a.delete()
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return super(ModerationView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@ -268,12 +274,14 @@ class ModerationView(TemplateView):
|
||||
kwargs['albums'] = Album.objects.filter(id__in=kwargs['pictures'].values('parent').distinct('parent'))
|
||||
return kwargs
|
||||
|
||||
|
||||
class PictureEditForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Picture
|
||||
fields = ['name', 'parent']
|
||||
parent = make_ajax_field(Picture, 'parent', 'files', help_text="")
|
||||
|
||||
|
||||
class AlbumEditForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Album
|
||||
@ -283,12 +291,14 @@ class AlbumEditForm(forms.ModelForm):
|
||||
edit_groups = make_ajax_field(Album, 'edit_groups', 'groups', help_text="")
|
||||
recursive = forms.BooleanField(label=_("Apply rights recursively"), required=False)
|
||||
|
||||
|
||||
class PictureEditView(CanEditMixin, UpdateView):
|
||||
model = Picture
|
||||
form_class = PictureEditForm
|
||||
template_name = 'core/edit.jinja'
|
||||
pk_url_kwarg = "picture_id"
|
||||
|
||||
|
||||
class AlbumEditView(CanEditMixin, UpdateView):
|
||||
model = Album
|
||||
form_class = AlbumEditForm
|
||||
@ -300,4 +310,3 @@ class AlbumEditView(CanEditMixin, UpdateView):
|
||||
if form.cleaned_data['recursive']:
|
||||
self.object.apply_rights_recursively(True)
|
||||
return ret
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
from debug_toolbar.panels.templates import TemplatesPanel as BaseTemplatesPanel
|
||||
|
||||
|
||||
class TemplatesPanel(BaseTemplatesPanel):
|
||||
def generate_stats(self, *args):
|
||||
template = self.templates[0]['template']
|
||||
|
@ -80,4 +80,3 @@ if settings.DEBUG:
|
||||
urlpatterns += [
|
||||
url(r'^__debug__/', include(debug_toolbar.urls)),
|
||||
]
|
||||
|
||||
|
@ -26,6 +26,4 @@ from django.contrib import admin
|
||||
|
||||
from subscription.models import Subscription
|
||||
|
||||
|
||||
|
||||
admin.site.register(Subscription)
|
||||
|
@ -35,15 +35,16 @@ from core.models import User
|
||||
from core.utils import get_start_of_semester
|
||||
|
||||
|
||||
|
||||
def validate_type(value):
|
||||
if value not in settings.SITH_SUBSCRIPTIONS.keys():
|
||||
raise ValidationError(_('Bad subscription type'))
|
||||
|
||||
|
||||
def validate_payment(value):
|
||||
if value not in settings.SITH_SUBSCRIPTION_PAYMENT_METHOD:
|
||||
raise ValidationError(_('Bad payment method'))
|
||||
|
||||
|
||||
class Subscription(models.Model):
|
||||
member = models.ForeignKey(User, related_name='subscriptions')
|
||||
subscription_type = models.CharField(_('subscription type'),
|
||||
@ -134,7 +135,6 @@ class Subscription(models.Model):
|
||||
else:
|
||||
return 'No user - ' + str(self.pk)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def compute_start(d=date.today(), duration=1):
|
||||
"""
|
||||
@ -171,17 +171,21 @@ class Subscription(models.Model):
|
||||
return start.replace(day=1, month=(start.month + 6 * duration) % 12 + 1,
|
||||
year=start.year + int(duration / 2) + (1 if start.month > 6 and duration % 2 == 1 else 0))
|
||||
|
||||
|
||||
def can_be_edited_by(self, user):
|
||||
return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user.is_root
|
||||
|
||||
def is_valid_now(self):
|
||||
return self.subscription_start <= date.today() and date.today() <= self.subscription_end
|
||||
|
||||
|
||||
def guy_test(date, duration=4):
|
||||
print(str(date) + " - " + str(duration) + " -> " + str(Subscription.compute_start(date, duration)))
|
||||
|
||||
|
||||
def bibou_test(duration, date=date.today()):
|
||||
print(str(date) + " - " + str(duration) + " -> " + str(Subscription.compute_end(duration, Subscription.compute_start(date, duration))))
|
||||
|
||||
|
||||
def guy():
|
||||
guy_test(date(2015, 7, 11))
|
||||
guy_test(date(2015, 8, 11))
|
||||
@ -219,5 +223,6 @@ def guy():
|
||||
bibou_test(3)
|
||||
bibou_test(4)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
guy()
|
||||
|
@ -31,6 +31,3 @@ urlpatterns = [
|
||||
url(r'^$', NewSubscription.as_view(), name='subscription'),
|
||||
url(r'stats', SubscriptionsStatsView.as_view(), name='stats'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
@ -26,16 +26,13 @@ from django.views.generic.edit import CreateView, FormView
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.db import IntegrityError
|
||||
from django import forms
|
||||
from django.forms import Select
|
||||
from django.conf import settings
|
||||
|
||||
from ajax_select.fields import AutoCompleteSelectField
|
||||
import random
|
||||
|
||||
from subscription.models import Subscription
|
||||
from core.views import CanEditMixin, CanEditPropMixin, CanViewMixin
|
||||
from core.views.forms import SelectDateTime
|
||||
from core.models import User
|
||||
|
||||
@ -102,6 +99,7 @@ class SubscriptionForm(forms.ModelForm):
|
||||
raise ValidationError(_("You must either choose an existing user or create a new one properly"))
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class NewSubscription(CreateView):
|
||||
template_name = 'subscription/subscription.jinja'
|
||||
form_class = SubscriptionForm
|
||||
|
@ -34,15 +34,18 @@ from core.models import User
|
||||
from core.utils import get_start_of_semester, get_semester
|
||||
from club.models import Club
|
||||
|
||||
|
||||
class TrombiManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super(TrombiManager, self).get_queryset()
|
||||
|
||||
|
||||
class AvailableTrombiManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super(AvailableTrombiManager,
|
||||
self).get_queryset().filter(subscription_deadline__gte=date.today())
|
||||
|
||||
|
||||
class Trombi(models.Model):
|
||||
"""
|
||||
This is the main class, the Trombi itself.
|
||||
@ -81,6 +84,7 @@ class Trombi(models.Model):
|
||||
def can_be_viewed_by(self, user):
|
||||
return user.id in [u.user.id for u in self.users.all()]
|
||||
|
||||
|
||||
class TrombiUser(models.Model):
|
||||
"""
|
||||
This class is only here to avoid cross references between the core, club,
|
||||
@ -120,6 +124,7 @@ class TrombiUser(models.Model):
|
||||
end=end_date,
|
||||
).save()
|
||||
|
||||
|
||||
class TrombiComment(models.Model):
|
||||
"""
|
||||
This represent a comment given by someone to someone else in the same Trombi
|
||||
@ -135,6 +140,7 @@ class TrombiComment(models.Model):
|
||||
return False
|
||||
return user.id == self.author.user.id or user.can_edit(self.author.trombi)
|
||||
|
||||
|
||||
class TrombiClubMembership(models.Model):
|
||||
"""
|
||||
This represent a membership to a club
|
||||
@ -156,4 +162,3 @@ class TrombiClubMembership(models.Model):
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('trombi:profile')
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
#
|
||||
#
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.conf.urls import url
|
||||
|
||||
from trombi.views import *
|
||||
|
||||
@ -43,4 +43,3 @@ urlpatterns = [
|
||||
url(r'^membership/(?P<membership_id>[0-9]+)/edit$', UserTrombiEditMembershipView.as_view(), name='edit_membership'),
|
||||
url(r'^membership/(?P<membership_id>[0-9]+)/delete$', UserTrombiDeleteMembershipView.as_view(), name='delete_membership'),
|
||||
]
|
||||
|
||||
|
@ -23,10 +23,10 @@
|
||||
#
|
||||
|
||||
from django.http import Http404
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.core.urlresolvers import reverse_lazy, reverse
|
||||
from django.views.generic import ListView, DetailView, RedirectView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView, FormView, SingleObjectMixin
|
||||
from django.views.generic import DetailView, RedirectView, TemplateView
|
||||
from django.views.generic.edit import UpdateView, CreateView, DeleteView
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
@ -35,11 +35,12 @@ from django.forms.models import modelform_factory
|
||||
from datetime import date
|
||||
|
||||
from trombi.models import Trombi, TrombiUser, TrombiComment, TrombiClubMembership
|
||||
from core.views.forms import SelectFile, SelectDate
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, CanCreateMixin, QuickNotifMixin
|
||||
from core.views.forms import SelectDate
|
||||
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, QuickNotifMixin
|
||||
from core.models import User
|
||||
from club.models import Club
|
||||
|
||||
|
||||
class TrombiTabsMixin(TabedViewMixin):
|
||||
def get_tabs_title(self):
|
||||
return _("Trombi")
|
||||
@ -69,9 +70,11 @@ class TrombiTabsMixin(TabedViewMixin):
|
||||
'slug': 'admin_tools',
|
||||
'name': _("Admin tools"),
|
||||
})
|
||||
except: pass
|
||||
except:
|
||||
pass
|
||||
return tab_list
|
||||
|
||||
|
||||
class TrombiForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Trombi
|
||||
@ -81,6 +84,7 @@ class TrombiForm(forms.ModelForm):
|
||||
'comments_deadline': SelectDate,
|
||||
}
|
||||
|
||||
|
||||
class TrombiCreateView(CanCreateMixin, CreateView):
|
||||
"""
|
||||
Create a trombi for a club
|
||||
@ -102,6 +106,7 @@ class TrombiCreateView(CanCreateMixin, CreateView):
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
|
||||
|
||||
class TrombiEditView(CanEditPropMixin, TrombiTabsMixin, UpdateView):
|
||||
model = Trombi
|
||||
form_class = TrombiForm
|
||||
@ -112,12 +117,14 @@ class TrombiEditView(CanEditPropMixin, TrombiTabsMixin, UpdateView):
|
||||
def get_success_url(self):
|
||||
return super(TrombiEditView, self).get_success_url() + "?qn_success"
|
||||
|
||||
|
||||
class TrombiDetailView(CanEditMixin, QuickNotifMixin, TrombiTabsMixin, DetailView):
|
||||
model = Trombi
|
||||
template_name = 'trombi/detail.jinja'
|
||||
pk_url_kwarg = 'trombi_id'
|
||||
current_tab = "admin_tools"
|
||||
|
||||
|
||||
class TrombiDeleteUserView(CanEditPropMixin, TrombiTabsMixin, DeleteView):
|
||||
model = TrombiUser
|
||||
pk_url_kwarg = 'user_id'
|
||||
@ -127,6 +134,7 @@ class TrombiDeleteUserView(CanEditPropMixin, TrombiTabsMixin, DeleteView):
|
||||
def get_success_url(self):
|
||||
return reverse('trombi:detail', kwargs={'trombi_id': self.object.trombi.id}) + "?qn_success"
|
||||
|
||||
|
||||
class TrombiModerateCommentsView(CanEditPropMixin, QuickNotifMixin, TrombiTabsMixin, DetailView):
|
||||
model = Trombi
|
||||
template_name = 'trombi/comment_moderation.jinja'
|
||||
@ -139,10 +147,12 @@ class TrombiModerateCommentsView(CanEditPropMixin, QuickNotifMixin, TrombiTabsMi
|
||||
author__trombi__id=self.object.id).exclude(target__user__id=self.request.user.id)
|
||||
return kwargs
|
||||
|
||||
|
||||
class TrombiModerateForm(forms.Form):
|
||||
reason = forms.CharField(help_text=_("Explain why you rejected the comment"))
|
||||
action = forms.CharField(initial="delete", widget=forms.widgets.HiddenInput)
|
||||
|
||||
|
||||
class TrombiModerateCommentView(DetailView):
|
||||
model = TrombiComment
|
||||
template_name = 'core/edit.jinja'
|
||||
@ -179,23 +189,26 @@ class TrombiModerateCommentView(DetailView):
|
||||
return redirect(reverse('trombi:moderate_comments', kwargs={'trombi_id': self.object.author.trombi.id}) + "?qn_success")
|
||||
raise Http404
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super(TrombiModerateCommentView, self).get_context_data(**kwargs)
|
||||
kwargs['form'] = TrombiModerateForm()
|
||||
return kwargs
|
||||
|
||||
# User side
|
||||
|
||||
|
||||
class TrombiModelChoiceField(forms.ModelChoiceField):
|
||||
def label_from_instance(self, obj):
|
||||
return _("%(name)s (deadline: %(date)s)") % {'name': str(obj), 'date': str(obj.subscription_deadline)}
|
||||
|
||||
|
||||
class UserTrombiForm(forms.Form):
|
||||
trombi = TrombiModelChoiceField(Trombi.availables.all(), required=False, label=_("Select trombi"),
|
||||
help_text=_("This allows you to subscribe to a Trombi. "
|
||||
"Be aware that you can subscribe only once, so don't play with that, "
|
||||
"or you will expose yourself to the admins' wrath!"))
|
||||
|
||||
|
||||
class UserTrombiToolsView(QuickNotifMixin, TrombiTabsMixin, TemplateView):
|
||||
"""
|
||||
Display a user's trombi tools
|
||||
@ -222,6 +235,7 @@ class UserTrombiToolsView(QuickNotifMixin, TrombiTabsMixin, TemplateView):
|
||||
kwargs['date'] = date
|
||||
return kwargs
|
||||
|
||||
|
||||
class UserTrombiEditPicturesView(TrombiTabsMixin, UpdateView):
|
||||
model = TrombiUser
|
||||
fields = ['profile_pict', 'scrub_pict']
|
||||
@ -234,6 +248,7 @@ class UserTrombiEditPicturesView(TrombiTabsMixin, UpdateView):
|
||||
def get_success_url(self):
|
||||
return reverse('trombi:user_tools') + "?qn_success"
|
||||
|
||||
|
||||
class UserTrombiEditProfileView(QuickNotifMixin, TrombiTabsMixin, UpdateView):
|
||||
model = User
|
||||
form_class = modelform_factory(User,
|
||||
@ -253,6 +268,7 @@ class UserTrombiEditProfileView(QuickNotifMixin, TrombiTabsMixin, UpdateView):
|
||||
def get_success_url(self):
|
||||
return reverse('trombi:user_tools') + "?qn_success"
|
||||
|
||||
|
||||
class UserTrombiResetClubMembershipsView(RedirectView):
|
||||
permanent = False
|
||||
|
||||
@ -264,6 +280,7 @@ class UserTrombiResetClubMembershipsView(RedirectView):
|
||||
def get_success_url(self):
|
||||
return reverse('trombi:profile') + "?qn_success"
|
||||
|
||||
|
||||
class UserTrombiDeleteMembershipView(TrombiTabsMixin, CanEditMixin, DeleteView):
|
||||
model = TrombiClubMembership
|
||||
pk_url_kwarg = "membership_id"
|
||||
@ -274,6 +291,7 @@ class UserTrombiDeleteMembershipView(TrombiTabsMixin, CanEditMixin, DeleteView):
|
||||
def get_success_url(self):
|
||||
return super(UserTrombiDeleteMembershipView, self).get_success_url() + "?qn_success"
|
||||
|
||||
|
||||
class UserTrombiEditMembershipView(CanEditMixin, TrombiTabsMixin, UpdateView):
|
||||
model = TrombiClubMembership
|
||||
pk_url_kwarg = "membership_id"
|
||||
@ -300,6 +318,7 @@ class UserTrombiProfileView(TrombiTabsMixin, DetailView):
|
||||
raise Http404()
|
||||
return super(UserTrombiProfileView, self).get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class TrombiCommentFormView():
|
||||
"""
|
||||
Create/edit a trombi comment
|
||||
@ -335,6 +354,7 @@ class TrombiCommentFormView():
|
||||
kwargs['target'] = self.object.target
|
||||
return kwargs
|
||||
|
||||
|
||||
class TrombiCommentCreateView(TrombiCommentFormView, CreateView):
|
||||
def form_valid(self, form):
|
||||
target = get_object_or_404(TrombiUser, id=self.kwargs['user_id'])
|
||||
@ -342,11 +362,10 @@ class TrombiCommentCreateView(TrombiCommentFormView, CreateView):
|
||||
form.instance.target = target
|
||||
return super(TrombiCommentCreateView, self).form_valid(form)
|
||||
|
||||
|
||||
class TrombiCommentEditView(TrombiCommentFormView, CanViewMixin, UpdateView):
|
||||
pk_url_kwarg = "comment_id"
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.is_moderated = False
|
||||
return super(TrombiCommentEditView, self).form_valid(form)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user