parent
01a7f5c16f
commit
e2d98d9962
Binary file not shown.
|
@ -153,7 +153,7 @@ INSTALLED_APPS = (
|
|||
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',),
|
||||
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
|
||||
'PAGINATE_BY': 10
|
||||
}
|
||||
|
||||
|
|
|
@ -16,15 +16,9 @@ from django.conf.urls.static import static
|
|||
admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# Examples:
|
||||
# url(r'^$', 'blechreiz.views.home', name='home'),
|
||||
# url(r'^blechreiz/', include('blechreiz.foo.urls')),
|
||||
|
||||
# Uncomment the admin/doc line below to enable admin documentation:
|
||||
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
|
||||
# Uncomment the next line to enable the admin:
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
url(r'^events/', include( eventplanner.views.urls) ),
|
||||
url(r'^admin/', include(admin.site.urls) ),
|
||||
url(r'^admin/doc/', include('django.contrib.admindocs.urls') ),
|
||||
|
||||
url(r'^musicians/$', MusicianList.as_view() ),
|
||||
url(r'^musicians/(?P<username>[\w]+)$', user_edit ),
|
||||
|
@ -32,10 +26,4 @@ urlpatterns = patterns('',
|
|||
url(r'^website/$', website.views.home_view),
|
||||
url(r'^login/$', website.views.login_view),
|
||||
url(r'^logout/$', website.views.logout_view),
|
||||
url(r'^events/$', eventplanner.views.eventplanning_view),
|
||||
url(r'^eventParticipation/$', eventplanner.views.event_participation_detail ),
|
||||
url(r'^eventParticipation/(\w+)/$', eventplanner.views.event_participation_detail ),
|
||||
url(r'^eventParticipation/(\w+)/(\d+)$', eventplanner.views.event_participation_detail ),
|
||||
url(r'^events/grid$', eventplanner.views.events_grid ),
|
||||
url(r'^events/(?P<pk>\d+)$', eventplanner.views.EventUpdate.as_view() ),
|
||||
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2013-06-30 10:55+0200\n"
|
||||
"POT-Creation-Date: 2013-06-30 16:20+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -18,18 +18,22 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: models.py:11
|
||||
#: models.py:10
|
||||
msgid "Rehearsal"
|
||||
msgstr "Probe"
|
||||
|
||||
#: models.py:12
|
||||
#: models.py:11
|
||||
msgid "Concert"
|
||||
msgstr "Konzert"
|
||||
|
||||
#: models.py:13
|
||||
#: models.py:12
|
||||
msgid "General Rehearsal"
|
||||
msgstr "Generalprobe"
|
||||
|
||||
#: models.py:13
|
||||
msgid "Party"
|
||||
msgstr "Feier"
|
||||
|
||||
#: models.py:16
|
||||
msgid "title"
|
||||
msgstr "Titel"
|
||||
|
@ -39,52 +43,70 @@ msgid "type"
|
|||
msgstr "Typ"
|
||||
|
||||
#: models.py:18
|
||||
msgid "date"
|
||||
msgstr "Datum"
|
||||
|
||||
#: models.py:19
|
||||
msgid "time"
|
||||
msgstr "Uhrzeit"
|
||||
|
||||
#: models.py:20
|
||||
msgid "participants"
|
||||
msgstr "Teilnehmer"
|
||||
|
||||
#: models.py:22
|
||||
msgid "description"
|
||||
msgstr "Beschreibung"
|
||||
|
||||
#: models.py:23
|
||||
msgid "location"
|
||||
msgstr "Ort"
|
||||
|
||||
#: models.py:44
|
||||
#: models.py:19
|
||||
msgid "description"
|
||||
msgstr "Beschreibung"
|
||||
|
||||
#: models.py:21
|
||||
msgid "date"
|
||||
msgstr "Datum"
|
||||
|
||||
#: models.py:22
|
||||
msgid "time"
|
||||
msgstr "Uhrzeit"
|
||||
|
||||
#: models.py:23
|
||||
msgid "meeting_time"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:25
|
||||
msgid "participants"
|
||||
msgstr "Teilnehmer"
|
||||
|
||||
#: models.py:45
|
||||
msgid "?"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:45
|
||||
#: models.py:46
|
||||
msgid "Yes"
|
||||
msgstr "Ja"
|
||||
|
||||
#: models.py:46
|
||||
#: models.py:47
|
||||
msgid "No"
|
||||
msgstr "Nein"
|
||||
|
||||
#: models.py:49
|
||||
#: models.py:50
|
||||
msgid "event"
|
||||
msgstr "Termin"
|
||||
|
||||
#: models.py:50
|
||||
#: models.py:51
|
||||
msgid "musician"
|
||||
msgstr "Musiker"
|
||||
|
||||
#: models.py:51
|
||||
#: models.py:52
|
||||
msgid "status"
|
||||
msgstr "Status"
|
||||
|
||||
#: models.py:52
|
||||
#: models.py:53
|
||||
msgid "comment"
|
||||
msgstr "Kommentar"
|
||||
|
||||
#~ msgid "Delete?"
|
||||
#~ msgstr "Löschen"
|
||||
#: models.py:72
|
||||
msgid "Can modify participation status of other users"
|
||||
msgstr ""
|
||||
|
||||
#: templates/custom_tabular.html:17
|
||||
msgid "Delete?"
|
||||
msgstr "Löschen"
|
||||
|
||||
#: templates/custom_tabular.html:74
|
||||
#, python-format
|
||||
msgid "Add another %(verbose_name)s"
|
||||
msgstr ""
|
||||
|
||||
#: templates/custom_tabular.html:75
|
||||
msgid "Remove"
|
||||
msgstr ""
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import ugettext as _
|
||||
from locpick.field import LocationField
|
||||
|
||||
from musicians.models import Musician
|
||||
|
||||
|
@ -11,18 +10,20 @@ class Event ( models.Model ):
|
|||
( 'Reh', _('Rehearsal') ),
|
||||
( 'Conc', _('Concert') ),
|
||||
( 'GenReh', _('General Rehearsal') ),
|
||||
( 'Party', _('Party') ),
|
||||
)
|
||||
|
||||
title = models.CharField( max_length=40, verbose_name = _("title") )
|
||||
type = models.CharField( max_length=6, choices=EVENT_TYPES, default='Reh', verbose_name= _("type") )
|
||||
location = models.TextField( blank=False, verbose_name=_("location") )
|
||||
desc = models.TextField( blank=True, verbose_name=_("description"))
|
||||
|
||||
date = models.DateField( verbose_name= _("date") )
|
||||
time = models.TimeField( null=True, blank=True, verbose_name = _("time") )
|
||||
meeting_time = models.TimeField( null=True, blank=True, verbose_name = _("meeting_time") )
|
||||
|
||||
participants = models.ManyToManyField( Musician, through='EventParticipation', verbose_name=_("participants") )
|
||||
|
||||
desc = models.TextField( blank=True, verbose_name=_("description"))
|
||||
location = LocationField( verbose_name=_("location") )
|
||||
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title + " ( " + self.get_type_display() + " ) "
|
||||
|
||||
|
@ -51,10 +52,25 @@ class EventParticipation( models.Model ):
|
|||
status = models.CharField ( max_length=3, choices = OPTIONS, default='?', verbose_name=_("status") )
|
||||
comment = models.CharField ( max_length=64, blank=True, verbose_name=_("comment") )
|
||||
|
||||
|
||||
def get_musician_username(self):
|
||||
return self.musician.user.username
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_or_create( musician , event ):
|
||||
try:
|
||||
result = EventParticipation.objects.get( event = event, musician = musician )
|
||||
except EventParticipation.DoesNotExist:
|
||||
result = EventParticipation.objects.create( event = event, musician = musician, status='?', comment = '' )
|
||||
|
||||
return result
|
||||
|
||||
class Meta:
|
||||
unique_together = ("event", "musician")
|
||||
permissions = (
|
||||
("change_others_participation", _("Can modify participation status of other users") ),
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
{% load i18n admin_static admin_modify %}
|
||||
<div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group">
|
||||
<div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
|
||||
{{ inline_admin_formset.formset.management_form }}
|
||||
<fieldset class="module">
|
||||
<h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
|
||||
{{ inline_admin_formset.formset.non_form_errors }}
|
||||
<table>
|
||||
<thead><tr>
|
||||
{% for field in inline_admin_formset.fields %}
|
||||
{% if not field.widget.is_hidden %}
|
||||
<th{% if forloop.first %} colspan="2"{% endif %}{% if field.required %} class="required"{% endif %}>{{ field.label|capfirst }}
|
||||
{% if field.help_text %} <img src="{% static "admin/img/icon-unknown.gif" %}" class="help help-tooltip" width="10" height="10" alt="({{ field.help_text|striptags }})" title="{{ field.help_text|striptags }}" />{% endif %}
|
||||
</th>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if inline_admin_formset.formset.can_delete %}<th>{% trans "Delete?" %}</th>{% endif %}
|
||||
</tr></thead>
|
||||
|
||||
<tbody>
|
||||
{% for inline_admin_form in inline_admin_formset %}
|
||||
{% if inline_admin_form.form.non_field_errors %}
|
||||
<tr><td colspan="{{ inline_admin_form|cell_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
|
||||
{% endif %}
|
||||
<tr class="form-row {% cycle "row1" "row2" %} {% if forloop.last %} empty-form{% endif %}"
|
||||
id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
|
||||
<td class="original">
|
||||
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
|
||||
|
||||
</p>{% endif %}
|
||||
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
|
||||
{{ inline_admin_form.fk_field.field }}
|
||||
{% spaceless %}
|
||||
{% for fieldset in inline_admin_form %}
|
||||
{% for line in fieldset %}
|
||||
{% for field in line %}
|
||||
{% if field.is_hidden %} {{ field.field }} {% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endspaceless %}
|
||||
</td>
|
||||
{% for fieldset in inline_admin_form %}
|
||||
{% for line in fieldset %}
|
||||
{% for field in line %}
|
||||
<td{% if field.field.name %} class="field-{{ field.field.name }}"{% endif %}>
|
||||
{% if field.is_readonly %}
|
||||
<p>{{ field.contents|linebreaksbr }}</p>
|
||||
{% else %}
|
||||
{{ field.field.errors.as_ul }}
|
||||
{{ field.field }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% if inline_admin_formset.formset.can_delete %}
|
||||
<td class="delete">{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
(function($) {
|
||||
$("#{{ inline_admin_formset.formset.prefix }}-group .tabular.inline-related tbody tr").tabularFormset({
|
||||
prefix: "{{ inline_admin_formset.formset.prefix }}",
|
||||
adminStaticPrefix: '{% static "admin/" %}',
|
||||
addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
|
||||
deleteText: "{% trans 'Remove' %}"
|
||||
});
|
||||
})(django.jQuery);
|
||||
</script>
|
|
@ -51,7 +51,7 @@
|
|||
|
||||
request = $.ajax( {
|
||||
type: "PUT",
|
||||
url: "/eventParticipation/" + p.data("username") + "/" + p.data("event-id"),
|
||||
url: "{% url 'event_api' %}",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(putObject)
|
||||
});
|
||||
|
@ -73,7 +73,7 @@
|
|||
|
||||
$.ajax( {
|
||||
type: "PUT",
|
||||
url: "/eventParticipation/" + $(this).data("username") + "/" + $(this).data("event-id"),
|
||||
url: "{% url 'event_api' %}",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(putObject)
|
||||
});
|
||||
|
|
|
@ -29,9 +29,14 @@
|
|||
{% addtoblock "js" %}
|
||||
<script>
|
||||
|
||||
{% if not perms.eventplanner.change_others_participation %}
|
||||
$(".eventButton").attr('disabled', 'disabled');
|
||||
{% endif %}
|
||||
|
||||
$('.eventButton').tooltip()
|
||||
|
||||
$(".eventButton").click( function () {
|
||||
$(".eventButton").click( function (e) {
|
||||
e.preventDefault();
|
||||
$(this).removeClass("btn-danger")
|
||||
.removeClass("btn-warning")
|
||||
.removeClass("btn-success");
|
||||
|
@ -57,8 +62,10 @@
|
|||
$('#saveButton').removeAttr('disabled');
|
||||
});
|
||||
|
||||
$("#saveButton").click( function() {
|
||||
|
||||
|
||||
$("#saveButton").click( function(e) {
|
||||
e.preventDefault();
|
||||
arr = [];
|
||||
|
||||
$('.userEventTableData').each( function() {
|
||||
|
@ -74,7 +81,7 @@
|
|||
|
||||
$.ajax( {
|
||||
type: "PUT",
|
||||
url: "/eventParticipation/",
|
||||
url: "{% url 'event_api' %}",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify(arr)
|
||||
});
|
||||
|
@ -146,11 +153,16 @@
|
|||
|
||||
</div><!--/row-->
|
||||
|
||||
|
||||
|
||||
{% if perms.eventplanner.change_others_participation %}
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<button id="saveButton" class="btn btn-primary" disabled="true" >Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
|
@ -16,15 +16,19 @@ from rest_framework import status
|
|||
from django.forms.models import ModelForm
|
||||
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.decorators import method_decorator
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------- API ---------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
@api_view( ['GET', 'PUT'] )
|
||||
def event_participation_detail( request, username = None, eventId = None ):
|
||||
# TODO Permissions!
|
||||
def event_api( request, username = None, eventId = None ):
|
||||
try:
|
||||
participationQs = EventParticipation.objects.filter( event__date__gte = datetime.date.today() )
|
||||
if username:
|
||||
|
@ -33,7 +37,7 @@ def event_participation_detail( request, username = None, eventId = None ):
|
|||
participationQs = participationQs.filter( event__pk = eventId )
|
||||
|
||||
except EventParticipation.DoesNotExist:
|
||||
return HttpResponse( status=404)
|
||||
return HttpResponse( status=404 )
|
||||
|
||||
|
||||
if request.method == 'GET':
|
||||
|
@ -43,6 +47,11 @@ def event_participation_detail( request, username = None, eventId = None ):
|
|||
elif request.method == 'PUT':
|
||||
serializer = ParticipationSerializer ( participationQs, data = request.DATA, many=True )
|
||||
if serializer.is_valid():
|
||||
for serializedObject in serializer.object:
|
||||
if serializedObject.musician.user != request.user:
|
||||
if not request.user.has_perm('change_others_participation') :
|
||||
return Response( status = status.HTTP_403_FORBIDDEN )
|
||||
|
||||
serializer.save()
|
||||
return Response( serializer.data )
|
||||
else:
|
||||
|
@ -50,16 +59,17 @@ def event_participation_detail( request, username = None, eventId = None ):
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ------------------------------------ Normal Views ----------------------------------------------------
|
||||
|
||||
@login_required
|
||||
def main_view( request ):
|
||||
if request.user.has_perm( 'eventplanner.change_others_participation'):
|
||||
return events_grid( request )
|
||||
else:
|
||||
return eventplanning( request )
|
||||
|
||||
|
||||
|
||||
def eventplanning_view( request ):
|
||||
@login_required
|
||||
def eventplanning( request ):
|
||||
"""
|
||||
View for a specific user, to edit his events
|
||||
"""
|
||||
|
@ -70,14 +80,14 @@ def eventplanning_view( request ):
|
|||
musician = get_object_or_404( Musician, user=request.user )
|
||||
|
||||
for e in all_future_events:
|
||||
e.participation = EventParticipation.objects.get( event = e, musician = musician )
|
||||
e.participation = EventParticipation.get_or_create( event = e, musician = musician )
|
||||
|
||||
context = { 'events' : all_future_events }
|
||||
return render ( request, 'eventplanner/eventplanning_view.html', context )
|
||||
|
||||
|
||||
|
||||
|
||||
@login_required
|
||||
def events_grid( request ):
|
||||
|
||||
musicians = Musician.objects.all()
|
||||
|
@ -87,17 +97,7 @@ def events_grid( request ):
|
|||
all_future_events = list ( Event.objects.filter( date__gte = datetime.date.today() ) )
|
||||
|
||||
for e in all_future_events:
|
||||
e.participation = [ EventParticipation.objects.get( event = e, musician = m ) for m in musicians ]
|
||||
|
||||
# Sort the participations in a dict
|
||||
#for p in participationQs:
|
||||
# username = p.musician.user.username
|
||||
# eventId = p.event.pk
|
||||
# if not eventId in grid:
|
||||
# grid[eventId] = {}
|
||||
#
|
||||
# grid[eventId][username] = p
|
||||
|
||||
e.participation = [ EventParticipation.get_or_create( event = e, musician = m ) for m in musicians ]
|
||||
|
||||
context = { 'events': all_future_events,
|
||||
'musicianNames' : musicianNames }
|
||||
|
@ -107,6 +107,9 @@ def events_grid( request ):
|
|||
|
||||
|
||||
|
||||
# ------------------------------------ Detail Views ----------------------------------------------------
|
||||
|
||||
|
||||
from django.views.generic.edit import UpdateView
|
||||
|
||||
|
||||
|
@ -121,8 +124,27 @@ class EventUpdate( UpdateView ):
|
|||
template_name_suffix = "_update_form"
|
||||
success_url = '/events/grid'
|
||||
|
||||
@method_decorator(login_required)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
return super(EventUpdate, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ------------------------------------ URLS ----------------------------------------------------
|
||||
|
||||
|
||||
urls = patterns('',
|
||||
url(r'^$', main_view ),
|
||||
url(r'^grid$', events_grid ),
|
||||
url(r'^planning$', eventplanning ),
|
||||
url(r'^(?P<pk>\d+)$', EventUpdate.as_view() ),
|
||||
url(r'^api/', event_api, name="event_api" ),
|
||||
url(r'^api/(\w+)/$', event_api, name="event_api_per_user" ),
|
||||
url(r'^api/(\w+)/(\d+)$', event_api, name="event_api_per_user_event"),
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -25,11 +25,19 @@ class Musician( models.Model ):
|
|||
image = models.ImageField( upload_to = musicianPictureName, verbose_name=_("image") )
|
||||
|
||||
instrument = models.CharField( max_length=4, choices=INSTRUMENTS, blank=True, verbose_name=_("instrument") )
|
||||
|
||||
birthday = models.DateField( null=True, verbose_name=_("birthday") )
|
||||
|
||||
street = models.CharField( max_length=80, blank=True, verbose_name=_("street") )
|
||||
city = models.CharField( max_length=40, blank=True, verbose_name=_("city") )
|
||||
zip_code = models.IntegerField( null=True, verbose_name=_("zip_code") )
|
||||
|
||||
phone_home = models.CharField( max_length=18, blank=True, verbose_name=_("phone_home") )
|
||||
phone_mobile = models.CharField( max_length=18, blank=True, verbose_name=_("phone_mobile") )
|
||||
phone_work = models.CharField( max_length=18, blank=True, verbose_name=_("phone_work") )
|
||||
|
||||
|
||||
|
||||
public_description = models.TextField( blank=True, verbose_name=_("public_description") )
|
||||
|
||||
def __unicode__(self):
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
|
||||
------------------ Outline ------------------------------------------
|
||||
|
||||
|
||||
Internal / Normal User
|
||||
|
||||
- / MainPage - Either login view, or Main
|
||||
- feature slider ( Terminplanung - Nachrichten )
|
||||
- next event countdown
|
||||
- /events Normale Planung
|
||||
/events/api/... API
|
||||
/events/<pk> ReadOnly view of event
|
||||
/musicians/profile Own profile
|
||||
/musicians All Contact information
|
||||
|
||||
|
||||
|
||||
Internal / Managing User
|
||||
|
||||
/ MainPage ( or login )
|
||||
- feature slider ( new event, event grid )
|
||||
- event grid
|
||||
|
||||
/events Displays Event Grid
|
||||
|
||||
/musicians
|
||||
/musicians/create
|
||||
( /musicians/<username> edit profile information )
|
||||
|
||||
|
||||
|
||||
|
||||
------------------ TODO ------------------------------------------
|
||||
|
||||
- URL Conf into modules
|
||||
- Access Rights
|
||||
- new user type
|
||||
- bootstrap customize: everything red
|
||||
|
Loading…
Reference in New Issue