[func] tournament pages
|
|
@ -1,3 +0,0 @@
|
||||||
from django.contrib import admin
|
|
||||||
|
|
||||||
# Register your models here.
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
# Create your models here.
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
from django.shortcuts import render
|
|
||||||
|
|
||||||
# Create your views here.
|
|
||||||
|
|
@ -55,12 +55,14 @@ INSTALLED_APPS = [
|
||||||
'db.atu',
|
'db.atu',
|
||||||
'db.mbkb',
|
'db.mbkb',
|
||||||
'theme',
|
'theme',
|
||||||
'core'
|
'core',
|
||||||
|
'tournamentpages'
|
||||||
] + [
|
] + [
|
||||||
'tailwind',
|
'tailwind',
|
||||||
'tinymce',
|
'tinymce',
|
||||||
'filebrowser',
|
'filebrowser',
|
||||||
'admin_ordering',
|
'admin_ordering',
|
||||||
|
'tabbed_admin',
|
||||||
'django_browser_reload'
|
'django_browser_reload'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -169,3 +171,5 @@ TINYMCE_SPELLCHECKER = True
|
||||||
|
|
||||||
FILEBROWSER_DIRECTORY = ''
|
FILEBROWSER_DIRECTORY = ''
|
||||||
FILEBROWSER_EXCLUDE = [r'^_versions']
|
FILEBROWSER_EXCLUDE = [r'^_versions']
|
||||||
|
|
||||||
|
TABBED_ADMIN_USE_JQUERY_UI = True
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ from filebrowser.sites import site
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', include('core.urls')),
|
path('', include('core.urls')),
|
||||||
|
path('turnieje/', include('tournamentpages.urls')),
|
||||||
path('admin/filebrowser/', site.urls),
|
path('admin/filebrowser/', site.urls),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path('tinymce/', include('tinymce.urls')),
|
path('tinymce/', include('tinymce.urls')),
|
||||||
|
|
|
||||||
|
|
@ -1233,6 +1233,21 @@ select {
|
||||||
padding-bottom: 7px;
|
padding-bottom: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tpage-nav ul li:hover {
|
||||||
|
font-weight: 700;
|
||||||
|
-webkit-text-decoration-line: underline;
|
||||||
|
text-decoration-line: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tpage-nav ul li.active {
|
||||||
|
-webkit-text-decoration-line: none;
|
||||||
|
text-decoration-line: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tpage-nav ul li.active a {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
.sr-only {
|
.sr-only {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
|
|
@ -1406,6 +1421,10 @@ select {
|
||||||
resize: both;
|
resize: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flex-row {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
.flex-col {
|
.flex-col {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
@ -1505,6 +1524,11 @@ select {
|
||||||
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
|
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-green-100 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(220 252 231 / var(--tw-bg-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
.bg-gray-50 {
|
.bg-gray-50 {
|
||||||
--tw-bg-opacity: 1;
|
--tw-bg-opacity: 1;
|
||||||
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
|
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
|
||||||
|
|
@ -1795,19 +1819,6 @@ h4 {
|
||||||
font-size: 1.25em;
|
font-size: 1.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#atu {
|
|
||||||
transition: all 0.2s ease, visibility 0s;
|
|
||||||
border-radius: 0px;
|
|
||||||
background: repeat padding-box border-box 0% 0% / auto auto scroll
|
|
||||||
linear-gradient(180deg, rgba(8, 50, 4, 0.5) 0%, rgba(8, 50, 4, 0.5) 100%),
|
|
||||||
no-repeat padding-box border-box 78% 59%/200% scroll url("/static/atu.jpg");
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.target\:block:target {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active\:ring-2:active {
|
.active\:ring-2:active {
|
||||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||||
|
|
@ -1818,6 +1829,10 @@ h4 {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prose-h1\:font-medium :is(:where(h1):not(:where([class~="not-prose"] *))) {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.prose-h2\:mb-2 :is(:where(h2):not(:where([class~="not-prose"] *))) {
|
.prose-h2\:mb-2 :is(:where(h2):not(:where([class~="not-prose"] *))) {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 212 B |
|
After Width: | Height: | Size: 220 B |
|
After Width: | Height: | Size: 206 B |
|
After Width: | Height: | Size: 220 B |
|
After Width: | Height: | Size: 262 B |
|
After Width: | Height: | Size: 262 B |
|
After Width: | Height: | Size: 262 B |
|
After Width: | Height: | Size: 253 B |
|
After Width: | Height: | Size: 281 B |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
#tabs{clear:both}.ui-state-default a.errortab{background-color:red;color:white}.ui-tabs .ui-tabs-panel{overflow:hidden}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
.ui-tabs{background-color:#fff;border:0;min-width:960px}.ui-widget textarea,.ui-widget input,.ui-widget select{font-family:Arial,sans-serif}.ui-widget{font-family:Arial,sans-serif;font-size:1em}.ui-tabs .ui-tabs-panel{background-color:#fff;padding:5px 4px 0}.ui-tabs .ui-tabs-anchor{font-size:9pt}.ui-widget-content{background:none}.ui-tabs .ui-widget-content a{color:#309BBF}.ui-tabs .ui-widget-content a:hover{color:#444}.ui-tabs .ui-widget-header{background-image:linear-gradient(#E5E5E5,#DBDBDB)}.ui-tabs .ui-state-default{background-image:linear-gradient(#CEE9F2,#E1F0F5)}.ui-tabs .ui-state-active{background-image:linear-gradient(#CEE9F2,#fff);border-color:#BBB}.ui-tabs .ui-state-active a{color:#444}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2015, Guillaume Pousseo
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the <ORGANIZATION> nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
from .admin import TabbedModelAdmin
|
||||||
|
|
||||||
|
__author__ = 'Guillaume Pousseo'
|
||||||
|
__all__ = [
|
||||||
|
"TabbedModelAdmin"
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.admin.options import ModelAdmin
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from .settings import USE_JQUERY_UI, JQUERY_UI_CSS, JQUERY_UI_JS
|
||||||
|
|
||||||
|
|
||||||
|
class TabbedModelAdmin(ModelAdmin):
|
||||||
|
tabs = None
|
||||||
|
formatted_tabs = {}
|
||||||
|
|
||||||
|
# Needs a specific template to display the tabs
|
||||||
|
change_form_template = 'tabbed_admin/change_form.html'
|
||||||
|
|
||||||
|
def get_fieldsets(self, request, obj=None):
|
||||||
|
"""
|
||||||
|
Overwrites BaseModelAdmin fieldsets to add fieldsets passed by the
|
||||||
|
tabs.
|
||||||
|
If the tabs attribute is not set, use the default ModelAdmin method.
|
||||||
|
"""
|
||||||
|
tabs_fieldsets = self.get_formatted_tabs(request, obj)['fieldsets']
|
||||||
|
if self.tabs is not None:
|
||||||
|
self.fieldsets = ()
|
||||||
|
self.fieldsets = self.add_tabbed_item(tabs_fieldsets, self.fieldsets)
|
||||||
|
return super(TabbedModelAdmin, self).get_fieldsets(request, obj)
|
||||||
|
|
||||||
|
def get_inline_instances(self, request, obj=None):
|
||||||
|
"""
|
||||||
|
Overwrites BaseModelAdmin fieldsets to add fieldsets passed by the
|
||||||
|
tabs.
|
||||||
|
If the tabs attribute is not set, use the default ModelAdmin method.
|
||||||
|
"""
|
||||||
|
if self.tabs is not None:
|
||||||
|
self.inlines = ()
|
||||||
|
tabs_inlines = self.get_formatted_tabs(request, obj)['inlines']
|
||||||
|
self.inlines = self.add_tabbed_item(tabs_inlines, self.inlines)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# django >=1.7
|
||||||
|
return super(TabbedModelAdmin, self)\
|
||||||
|
.get_inline_instances(request, obj)
|
||||||
|
except TypeError:
|
||||||
|
return super(TabbedModelAdmin, self).get_inline_instances(request)
|
||||||
|
|
||||||
|
def add_tabbed_item(self, items_to_add, collection):
|
||||||
|
"""
|
||||||
|
Adds tabbed items (inlines or fieldsets) to their corresponding
|
||||||
|
attribute.
|
||||||
|
"""
|
||||||
|
if items_to_add:
|
||||||
|
for item in items_to_add:
|
||||||
|
if item not in collection:
|
||||||
|
if type(collection) == tuple:
|
||||||
|
collection = collection + (item, )
|
||||||
|
elif type(collection) == list:
|
||||||
|
collection.append(item)
|
||||||
|
return collection
|
||||||
|
|
||||||
|
def get_tabs(self, request, obj=None):
|
||||||
|
"""
|
||||||
|
Returns the tabs attribute.
|
||||||
|
"""
|
||||||
|
return self.tabs
|
||||||
|
|
||||||
|
def get_formatted_tabs(self, request, obj=None):
|
||||||
|
"""
|
||||||
|
Returns the formated tabs attribute.
|
||||||
|
"""
|
||||||
|
if self.tabs:
|
||||||
|
self.parse_fieldsets_inlines_from_tabs(request, obj)
|
||||||
|
return self.formatted_tabs
|
||||||
|
|
||||||
|
def parse_fieldsets_inlines_from_tabs(self, request, obj=None):
|
||||||
|
"""
|
||||||
|
Parses the self.tabs attribute. Checks its integrity and populates
|
||||||
|
self._tabs_fieldsets and self._tabs_inlines attributes.
|
||||||
|
"""
|
||||||
|
tabs_fieldsets = ()
|
||||||
|
tabs_inlines = ()
|
||||||
|
self.formatted_tabs['fields'] = []
|
||||||
|
for tab in self.get_tabs(request, obj):
|
||||||
|
# Checks that each tab if formated with 2 arguments, verbose name
|
||||||
|
# of the tab and the tab configuration.
|
||||||
|
if type(tab) not in [tuple, list]:
|
||||||
|
raise TypeError(
|
||||||
|
_(u'Each tab entry must be either a list or a tuple'))
|
||||||
|
if len(tab) != 2:
|
||||||
|
raise ValueError(
|
||||||
|
_(u'Each tabs entry must contain 2 arguments: a verbose name and the tab setup.'))
|
||||||
|
if type(tab[1]) not in [tuple, list]:
|
||||||
|
raise TypeError(
|
||||||
|
_(u'A tab definition must be either a list or a tuple'))
|
||||||
|
# So far all went well, lets parse the tab configuration, we go
|
||||||
|
# through each item. If its a tuple or a list we consider its a
|
||||||
|
# fieldset, otherwise its a tuple.
|
||||||
|
formatted_tab = {'name': tab[0], 'entries': []}
|
||||||
|
for tab_entry in tab[1]:
|
||||||
|
formatted_tab_entry = {}
|
||||||
|
if type(tab_entry) in [tuple, list]:
|
||||||
|
tabs_fieldsets = tabs_fieldsets + (tab_entry, )
|
||||||
|
formatted_tab_entry = {'type': 'fieldset',
|
||||||
|
'name': tab_entry[0],
|
||||||
|
'config': tab_entry[1]}
|
||||||
|
else:
|
||||||
|
tabs_inlines = tabs_inlines + (tab_entry, )
|
||||||
|
formatted_tab_entry = {'type': 'inline',
|
||||||
|
'name': tab_entry.__name__}
|
||||||
|
formatted_tab['entries'].append(formatted_tab_entry)
|
||||||
|
self.formatted_tabs['fields'].append(formatted_tab)
|
||||||
|
self.formatted_tabs['fieldsets'] = tabs_fieldsets
|
||||||
|
self.formatted_tabs['inlines'] = tabs_inlines
|
||||||
|
|
||||||
|
def add_view(self, request, form_url='', extra_context=None):
|
||||||
|
"""
|
||||||
|
Overwrites add_view to insert the tabs config for the template.
|
||||||
|
"""
|
||||||
|
if extra_context is None:
|
||||||
|
extra_context = {}
|
||||||
|
extra_context.update({'tabs': self.get_formatted_tabs(request)})
|
||||||
|
return super(TabbedModelAdmin, self)\
|
||||||
|
.add_view(request, form_url=form_url, extra_context=extra_context)
|
||||||
|
|
||||||
|
def change_view(self, request, object_id, form_url='', extra_context=None):
|
||||||
|
"""
|
||||||
|
Overwrites change_view to insert the tabs config for the template.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# django 1.4
|
||||||
|
change_view = super(TabbedModelAdmin, self)\
|
||||||
|
.change_view(request, object_id, form_url=form_url,
|
||||||
|
extra_context=extra_context)
|
||||||
|
except TypeError:
|
||||||
|
# django 1.3
|
||||||
|
change_view = super(TabbedModelAdmin, self)\
|
||||||
|
.change_view(request, object_id, extra_context=extra_context)
|
||||||
|
if hasattr(change_view, 'context_data'):
|
||||||
|
change_view.context_data.update(
|
||||||
|
{'tabs': self.get_formatted_tabs(request,
|
||||||
|
change_view.context_data.get('original'))}
|
||||||
|
)
|
||||||
|
return change_view
|
||||||
|
|
||||||
|
class Media:
|
||||||
|
"""
|
||||||
|
Extends media class to add custom jquery ui if
|
||||||
|
TABBED_ADMIN_USE_JQUERY_UI is set to True.
|
||||||
|
"""
|
||||||
|
if 'grappelli' in settings.INSTALLED_APPS:
|
||||||
|
css = {'all': ("tabbed_admin/css/tabbed_grappelli_admin.css", )}
|
||||||
|
|
||||||
|
if USE_JQUERY_UI:
|
||||||
|
css = {
|
||||||
|
'all': (JQUERY_UI_CSS, 'tabbed_admin/css/tabbed_admin.css', )}
|
||||||
|
js = (JQUERY_UI_JS,)
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
# No particular version to test anything
|
||||||
|
|
||||||
|
Django
|
||||||
|
django-grappelli
|
||||||
|
django-tabbed-admin
|
||||||
|
django-gipsy
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
# DJANGO IMPORTS
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
# Activate the library jquery ui
|
||||||
|
USE_JQUERY_UI = getattr(settings, "TABBED_ADMIN_USE_JQUERY_UI", False)
|
||||||
|
USE_GRAPPELLI = getattr(settings, "TABBED_ADMIN_USE_GRAPPELLI", False)
|
||||||
|
|
||||||
|
# Default jquery ui css and js
|
||||||
|
DEFAULT_JQUERY_UI_CSS = 'tabbed_admin/css/jquery-ui-1.11.4.min.css'
|
||||||
|
DEFAULT_JQUERY_UI_JS = 'tabbed_admin/js/jquery-ui-1.11.4.min.js'
|
||||||
|
|
||||||
|
# User ability to override the default css and js
|
||||||
|
JQUERY_UI_CSS = getattr(
|
||||||
|
settings, "TABBED_ADMIN_JQUERY_UI_CSS", DEFAULT_JQUERY_UI_CSS)
|
||||||
|
JQUERY_UI_JS = getattr(
|
||||||
|
settings, "TABBED_ADMIN_JQUERY_UI_JS", DEFAULT_JQUERY_UI_JS)
|
||||||
|
After Width: | Height: | Size: 212 B |
|
After Width: | Height: | Size: 220 B |
|
After Width: | Height: | Size: 206 B |
|
After Width: | Height: | Size: 220 B |
|
After Width: | Height: | Size: 262 B |
|
After Width: | Height: | Size: 262 B |
|
After Width: | Height: | Size: 262 B |
|
After Width: | Height: | Size: 253 B |
|
After Width: | Height: | Size: 281 B |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
#tabs{clear:both}.ui-state-default a.errortab{background-color:red;color:white}.ui-tabs .ui-tabs-panel{overflow:hidden}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
.ui-tabs{background-color:#fff;border:0;min-width:960px}.ui-widget textarea,.ui-widget input,.ui-widget select{font-family:Arial,sans-serif}.ui-widget{font-family:Arial,sans-serif;font-size:1em}.ui-tabs .ui-tabs-panel{background-color:#fff;padding:5px 4px 0}.ui-tabs .ui-tabs-anchor{font-size:9pt}.ui-widget-content{background:none}.ui-tabs .ui-widget-content a{color:#309BBF}.ui-tabs .ui-widget-content a:hover{color:#444}.ui-tabs .ui-widget-header{background-image:linear-gradient(#E5E5E5,#DBDBDB)}.ui-tabs .ui-state-default{background-image:linear-gradient(#CEE9F2,#E1F0F5)}.ui-tabs .ui-state-active{background-image:linear-gradient(#CEE9F2,#fff);border-color:#BBB}.ui-tabs .ui-state-active a{color:#444}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
{% extends "admin/change_form.html" %}
|
||||||
|
{% load i18n admin_modify admin_urls tabbed_admin_tags %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="content-main">
|
||||||
|
{% block object-tools %}{{ block.super }}{% endblock %}
|
||||||
|
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.model_name }}_form" novalidate>{% csrf_token %}{% block form_top %}{% endblock %}
|
||||||
|
<div>
|
||||||
|
{% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1" />{% endif %}
|
||||||
|
{% if to_field %}<input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}" />{% endif %}
|
||||||
|
{% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %}
|
||||||
|
{% if errors %}
|
||||||
|
<p class="errornote">
|
||||||
|
{% if errors|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %}
|
||||||
|
</p>
|
||||||
|
{{ adminform.form.non_field_errors }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- start admin_tabs stuff -->
|
||||||
|
<div id="tabs">
|
||||||
|
<ul>
|
||||||
|
{% for tab in tabs.fields %}
|
||||||
|
<li><a href="#tabs-{{ forloop.counter }}" id="for_tabs-{{ forloop.counter }}">{{ tab.name }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% for tab in tabs.fields %}
|
||||||
|
<div id="tabs-{{ forloop.counter }}" class="{{ tab.name }}">
|
||||||
|
{% for entry in tab.entries %}
|
||||||
|
{% render_tab_fieldsets_inlines entry %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
(function($) {
|
||||||
|
$(window).scrollTop()
|
||||||
|
$('#tabs').tabs({
|
||||||
|
{% if add %}
|
||||||
|
// when adding, don't select a tab by default, we'll do it ourselves
|
||||||
|
// by finding the first available tab.
|
||||||
|
selected: -1
|
||||||
|
{% endif %}
|
||||||
|
});
|
||||||
|
|
||||||
|
// disable tabs marked as such in page_config
|
||||||
|
var enabled_tabs = [];
|
||||||
|
var disabled_tabs = [];
|
||||||
|
|
||||||
|
{% for tab in page_config %}
|
||||||
|
{% if tab.enabled %}
|
||||||
|
enabled_tabs.push({{ forloop.counter0 }});
|
||||||
|
{% else %}
|
||||||
|
disabled_tabs.push({{ forloop.counter0 }});
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
for (var i = 0; i < disabled_tabs.length; i++) {
|
||||||
|
$('#tabs').tabs("disable", disabled_tabs[i]);
|
||||||
|
}
|
||||||
|
// enable the first non-disabled tab in add view
|
||||||
|
{% if add %}
|
||||||
|
$('#tabs').tabs("option", "active", enabled_tabs[0]);
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
// Hightlight tabs with errors inside
|
||||||
|
$('#tabs > div').each(function() {
|
||||||
|
if($(this).find('.errorlist').length) {
|
||||||
|
$('#tabs #for_' + this.id).addClass("errortab");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#tabs").on('tabsactivate', function(event, ui) {
|
||||||
|
var scrollPos = $(window).scrollTop();
|
||||||
|
var hash = ui.newTab.children("li a").attr("href");
|
||||||
|
window.location.hash = hash;
|
||||||
|
$(window).scrollTop(scrollPos);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($('.errornote').length) {
|
||||||
|
$('.errornote').addClass('tabbed-errornote');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
})(django.jQuery);
|
||||||
|
</script>
|
||||||
|
<!-- end admin_tabs stuff -->
|
||||||
|
|
||||||
|
{% block after_field_sets %}{% endblock %}
|
||||||
|
|
||||||
|
{% block after_related_objects %}{% endblock %}
|
||||||
|
|
||||||
|
{% block submit_buttons_bottom %}{% submit_row %}{% endblock %}
|
||||||
|
|
||||||
|
{% if adminform and add and adminform.first_field and adminform.first_field.id_for_label %}
|
||||||
|
<script type="text/javascript">document.getElementById("{{ adminform.first_field.id_for_label }}").focus();</script>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% prepopulated_fields_js %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from django import template
|
||||||
|
from django.contrib.admin.helpers import Fieldset
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag(takes_context=True)
|
||||||
|
def render_tab_fieldsets_inlines(context, entry):
|
||||||
|
"""
|
||||||
|
Render the fieldsets and inlines for a tab.
|
||||||
|
"""
|
||||||
|
template = "admin/includes/fieldset.html"
|
||||||
|
admin_form = context['adminform']
|
||||||
|
if 'request' not in context:
|
||||||
|
raise ImproperlyConfigured(
|
||||||
|
'"request" missing from context. Add django.core.context'
|
||||||
|
'_processors.request to your'
|
||||||
|
'TEMPLATE_CONTEXT_PROCESSORS')
|
||||||
|
request = context['request']
|
||||||
|
obj = context.get('original', None)
|
||||||
|
readonly_fields = admin_form.model_admin.get_readonly_fields(request, obj)
|
||||||
|
inline_matching = {}
|
||||||
|
if "inline_admin_formsets" in context:
|
||||||
|
inline_matching = dict((inline.opts.__class__.__name__, inline)
|
||||||
|
for inline in context["inline_admin_formsets"])
|
||||||
|
|
||||||
|
if entry['type'] == 'fieldset':
|
||||||
|
name = entry['name']
|
||||||
|
f = Fieldset(
|
||||||
|
admin_form.form,
|
||||||
|
name,
|
||||||
|
readonly_fields=readonly_fields,
|
||||||
|
model_admin=admin_form.model_admin,
|
||||||
|
**entry['config']
|
||||||
|
)
|
||||||
|
context["fieldset"] = f
|
||||||
|
return render_to_string(template, context.flatten(), request=request)
|
||||||
|
elif entry['type'] == 'inline':
|
||||||
|
try:
|
||||||
|
inline_admin_formset = inline_matching[entry["name"]]
|
||||||
|
context["inline_admin_formset"] = inline_admin_formset
|
||||||
|
return render_to_string(inline_admin_formset.opts.template,
|
||||||
|
context.flatten(), request=request)
|
||||||
|
except KeyError: # The user does not have the permission
|
||||||
|
pass
|
||||||
|
return ''
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from tabbed_admin import TabbedModelAdmin
|
||||||
|
from tabbed_admin.tests.models import Band, Musician, Concert, Album, Interview
|
||||||
|
|
||||||
|
|
||||||
|
class MusicianInline(admin.StackedInline):
|
||||||
|
model = Musician
|
||||||
|
|
||||||
|
|
||||||
|
class ConcertInline(admin.TabularInline):
|
||||||
|
model = Concert
|
||||||
|
|
||||||
|
|
||||||
|
class AlbumInline(admin.TabularInline):
|
||||||
|
model = Album
|
||||||
|
|
||||||
|
|
||||||
|
class InterviewInline(admin.TabularInline):
|
||||||
|
model = Interview
|
||||||
|
|
||||||
|
|
||||||
|
class BandAdmin(TabbedModelAdmin):
|
||||||
|
model = Band
|
||||||
|
|
||||||
|
tab_overview = (
|
||||||
|
(None, {
|
||||||
|
'fields': ('name', 'bio', 'style')
|
||||||
|
}),
|
||||||
|
MusicianInline,
|
||||||
|
('Contact', {
|
||||||
|
'fields': ('agent', 'phone', 'email')
|
||||||
|
})
|
||||||
|
)
|
||||||
|
tab_ressources = (
|
||||||
|
ConcertInline,
|
||||||
|
AlbumInline
|
||||||
|
)
|
||||||
|
tabs = [
|
||||||
|
('Overview', tab_overview),
|
||||||
|
('Ressources', tab_ressources)
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Band(models.Model):
|
||||||
|
STYLE_ROCK = 1
|
||||||
|
STYLE_FUNK = 2
|
||||||
|
STYLE_JAZZ = 3
|
||||||
|
STYLE_OVERRIDE = 4
|
||||||
|
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
bio = models.TextField(blank=True, null=True)
|
||||||
|
style = models.CharField(max_length=100, choices=(
|
||||||
|
(STYLE_ROCK, 'Rock'),
|
||||||
|
(STYLE_FUNK, 'Funk'),
|
||||||
|
(STYLE_JAZZ, 'Jazz'),
|
||||||
|
(STYLE_OVERRIDE, 'Override')
|
||||||
|
))
|
||||||
|
agent = models.CharField(max_length=100, blank=True, null=True)
|
||||||
|
phone = models.CharField(max_length=100, blank=True, null=True)
|
||||||
|
email = models.CharField(max_length=100, blank=True, null=True)
|
||||||
|
address = models.CharField(max_length=100, blank=True, null=True)
|
||||||
|
website = models.CharField(max_length=100, blank=True, null=True)
|
||||||
|
twitter = models.CharField(max_length=100, blank=True, null=True)
|
||||||
|
facebook = models.CharField(max_length=100, blank=True, null=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Musician(models.Model):
|
||||||
|
band = models.ForeignKey(Band)
|
||||||
|
first_name = models.CharField(max_length=100)
|
||||||
|
last_name = models.CharField(max_length=100)
|
||||||
|
specialty = models.CharField(max_length=100, choices=(
|
||||||
|
(1, 'Vocal'),
|
||||||
|
(2, 'Guitar'),
|
||||||
|
(3, 'Bass'),
|
||||||
|
(4, 'Drums')
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class Concert(models.Model):
|
||||||
|
band = models.ForeignKey(Band)
|
||||||
|
location = models.CharField(max_length=100)
|
||||||
|
date = models.DateField()
|
||||||
|
|
||||||
|
|
||||||
|
class Album(models.Model):
|
||||||
|
band = models.ForeignKey(Band)
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
date = models.DateField()
|
||||||
|
|
||||||
|
|
||||||
|
class Interview(models.Model):
|
||||||
|
band = models.ForeignKey(Band)
|
||||||
|
media_name = models.CharField(max_length=100)
|
||||||
|
date = models.DateField()
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
DIRNAME = os.path.dirname(__file__)
|
||||||
|
settings.configure(
|
||||||
|
DEBUG=True,
|
||||||
|
DATABASE_ENGINE='sqlite3',
|
||||||
|
DATABASE_NAME=os.path.join(DIRNAME, 'database.db'),
|
||||||
|
DATABASES={
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ROOT_URLCONF='tabbed_admin.tests.urls',
|
||||||
|
MIDDLEWARE_CLASSES=(),
|
||||||
|
TEMPLATE_CONTEXT_PROCESSORS=[
|
||||||
|
'django.template.context_processors.request'
|
||||||
|
],
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth'
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
INSTALLED_APPS=(
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.admin',
|
||||||
|
|
||||||
|
'tabbed_admin',
|
||||||
|
'tabbed_admin.tests'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Django < 1.8
|
||||||
|
from django.test.simple import DjangoTestSuiteRunner
|
||||||
|
test_runner = DjangoTestSuiteRunner(verbosity=1)
|
||||||
|
except ImportError:
|
||||||
|
# Django >= 1.8
|
||||||
|
from django.test.runner import DiscoverRunner
|
||||||
|
test_runner = DiscoverRunner(verbosity=1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Django < 1.7
|
||||||
|
from django.core.management import setup_environ
|
||||||
|
setup_environ(settings)
|
||||||
|
failures = test_runner.run_tests(['tabbed_admin'])
|
||||||
|
except:
|
||||||
|
# Django >= 1.7
|
||||||
|
import django
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from django.test.utils import get_runner
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'tabbed_admin.runtests'
|
||||||
|
TestRunner = get_runner(settings)
|
||||||
|
test_runner = TestRunner()
|
||||||
|
failures = test_runner.run_tests(['tabbed_admin'])
|
||||||
|
|
||||||
|
if failures:
|
||||||
|
sys.exit(failures)
|
||||||
|
|
@ -0,0 +1,240 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.admin.sites import AdminSite
|
||||||
|
from django.template import Context
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.test.client import RequestFactory, Client
|
||||||
|
from django.test.utils import override_settings
|
||||||
|
|
||||||
|
from tabbed_admin.settings import USE_JQUERY_UI
|
||||||
|
from tabbed_admin.templatetags.tabbed_admin_tags import render_tab_fieldsets_inlines
|
||||||
|
from tabbed_admin.tests.admin import BandAdmin, InterviewInline
|
||||||
|
from tabbed_admin.tests.models import Band
|
||||||
|
|
||||||
|
|
||||||
|
class MockRequest(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MockSuperUser(object):
|
||||||
|
is_active = True
|
||||||
|
is_staff = True
|
||||||
|
def has_perm(self, perm):
|
||||||
|
return True
|
||||||
|
|
||||||
|
request = RequestFactory()
|
||||||
|
request.user = MockSuperUser()
|
||||||
|
request.csrf_processing_done = True
|
||||||
|
|
||||||
|
|
||||||
|
class TabbedModelAdminTest(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.site = AdminSite()
|
||||||
|
|
||||||
|
def test_fieldsets_inline_attribute_populated(self):
|
||||||
|
"""
|
||||||
|
Tests if self.inlines and self.fieldsets are correcly populated from
|
||||||
|
the self.tabs attribute.
|
||||||
|
"""
|
||||||
|
admin = BandAdmin(Band, self.site)
|
||||||
|
self.assertIsNone(admin.fieldsets)
|
||||||
|
self.assertEqual(0, len(admin.inlines))
|
||||||
|
fieldsets = admin.get_fieldsets(request)
|
||||||
|
inlines = admin.get_inline_instances(request)
|
||||||
|
self.assertNotEqual(0, len(fieldsets))
|
||||||
|
self.assertNotEqual(0, len(inlines))
|
||||||
|
self.assertNotEqual(0, len(admin.fieldsets))
|
||||||
|
self.assertNotEqual(0, len(admin.inlines))
|
||||||
|
|
||||||
|
def test_fieldsets_inlines_overriden_by_tabs(self):
|
||||||
|
"""
|
||||||
|
Tests if when set by default, fieldsets and inlines are properly
|
||||||
|
overriden.
|
||||||
|
"""
|
||||||
|
class TestBandAdmin(BandAdmin):
|
||||||
|
fieldsets = (
|
||||||
|
('Social', {
|
||||||
|
'fields': ('website', 'twitter', 'facebook')
|
||||||
|
})
|
||||||
|
)
|
||||||
|
inlines = (
|
||||||
|
InterviewInline,
|
||||||
|
)
|
||||||
|
admin = TestBandAdmin(Band, self.site)
|
||||||
|
self.assertEqual(admin.get_fieldsets(request),
|
||||||
|
admin.formatted_tabs['fieldsets'])
|
||||||
|
inlines = admin.get_inline_instances(request)
|
||||||
|
inlines = admin.inlines
|
||||||
|
for inline in inlines:
|
||||||
|
self.assertIn(inline, admin.formatted_tabs['inlines'])
|
||||||
|
|
||||||
|
def test_get_tabs_overrides_tabs_attribute(self):
|
||||||
|
"""
|
||||||
|
Tests if get_tabs method successfully overrides the self.tabs and returns it.
|
||||||
|
"""
|
||||||
|
single_tab = [('Overview', BandAdmin.tab_overview)]
|
||||||
|
class TestBandAdmin(BandAdmin):
|
||||||
|
def get_tabs(self, request, obj=None):
|
||||||
|
"""
|
||||||
|
Returns the tabs attribute.
|
||||||
|
"""
|
||||||
|
tabs = self.tabs
|
||||||
|
if obj is not None and obj.style == Band.STYLE_OVERRIDE:
|
||||||
|
tabs = single_tab
|
||||||
|
self.tabs = tabs
|
||||||
|
return super(TestBandAdmin, self).get_tabs(request, obj)
|
||||||
|
|
||||||
|
admin = TestBandAdmin(Band, self.site)
|
||||||
|
band = Band.objects.create(name="Test band", style=Band.STYLE_JAZZ)
|
||||||
|
tabs = admin.get_tabs(request, band)
|
||||||
|
self.assertEqual(len(tabs), 2)
|
||||||
|
self.assertNotEqual(tabs, single_tab)
|
||||||
|
band.style = Band.STYLE_OVERRIDE
|
||||||
|
tabs = admin.get_tabs(request, band)
|
||||||
|
self.assertEqual(len(tabs), 1)
|
||||||
|
self.assertEqual(tabs, single_tab)
|
||||||
|
|
||||||
|
def test_dynamically_add_fieldsets_inlines_to_tabs(self):
|
||||||
|
"""
|
||||||
|
Tests overriding dynamically tabs via get_tabs.
|
||||||
|
"""
|
||||||
|
added_fieldset = ('Social', {
|
||||||
|
'fields': ('website', 'twitter', 'facebook')
|
||||||
|
})
|
||||||
|
added_inline = InterviewInline
|
||||||
|
|
||||||
|
class TestBandAdmin(BandAdmin):
|
||||||
|
def get_tabs(self, request, obj=None):
|
||||||
|
tabs = self.tabs
|
||||||
|
tab_overview = self.tab_overview + (added_fieldset, )
|
||||||
|
tab_ressources = self.tab_ressources + (added_inline, )
|
||||||
|
tabs = [
|
||||||
|
('Overview', tab_overview),
|
||||||
|
('Ressources', tab_ressources)
|
||||||
|
]
|
||||||
|
self.tabs = tabs
|
||||||
|
return super(TestBandAdmin, self).get_tabs(request, obj)
|
||||||
|
|
||||||
|
original_admin = BandAdmin(Band, self.site)
|
||||||
|
self.assertNotIn(added_fieldset, original_admin.get_fieldsets(request))
|
||||||
|
self.assertNotIn(added_inline, original_admin.tab_ressources)
|
||||||
|
admin = TestBandAdmin(Band, self.site)
|
||||||
|
inlines_classes = []
|
||||||
|
inlines = admin.get_inline_instances(request)
|
||||||
|
for inline in inlines:
|
||||||
|
inlines_classes.append(inline.__class__)
|
||||||
|
self.assertIn(added_inline, inlines_classes)
|
||||||
|
|
||||||
|
def test_version_previous_to_django(self):
|
||||||
|
"""
|
||||||
|
Tests overriding dynamically tabs via get_tabs.
|
||||||
|
"""
|
||||||
|
added_fieldset = ('Social', {
|
||||||
|
'fields': ('website', 'twitter', 'facebook')
|
||||||
|
})
|
||||||
|
added_inline = InterviewInline
|
||||||
|
|
||||||
|
class TestBandAdmin(BandAdmin):
|
||||||
|
def get_tabs(self, request, obj=None):
|
||||||
|
tabs = self.tabs
|
||||||
|
tab_overview = self.tab_overview + (added_fieldset, )
|
||||||
|
tab_ressources = self.tab_ressources + (added_inline, )
|
||||||
|
tabs = [
|
||||||
|
('Overview', tab_overview),
|
||||||
|
('Ressources', tab_ressources)
|
||||||
|
]
|
||||||
|
self.tabs = tabs
|
||||||
|
return super(TestBandAdmin, self).get_tabs(request, obj)
|
||||||
|
|
||||||
|
original_admin = BandAdmin(Band, self.site)
|
||||||
|
self.assertNotIn(added_fieldset, original_admin.get_fieldsets(request))
|
||||||
|
self.assertNotIn(added_inline, original_admin.tab_ressources)
|
||||||
|
admin = TestBandAdmin(Band, self.site)
|
||||||
|
inlines_classes = []
|
||||||
|
inlines = admin.get_inline_instances(request)
|
||||||
|
for inline in inlines:
|
||||||
|
inlines_classes.append(inline.__class__)
|
||||||
|
self.assertIn(added_inline, inlines_classes)
|
||||||
|
self.assertIn(added_fieldset, admin.get_fieldsets(request))
|
||||||
|
self.assertIn(added_fieldset, admin.get_fieldsets(request))
|
||||||
|
|
||||||
|
def test_medias_method_with_default_settings(self):
|
||||||
|
"""
|
||||||
|
Tests that the media method is retrning the proper static files when settings.TABBED_ADMIN_USE_JQUERY_UI
|
||||||
|
is True or False.
|
||||||
|
"""
|
||||||
|
self.assertEqual(False, USE_JQUERY_UI)
|
||||||
|
admin = BandAdmin(Band, self.site)
|
||||||
|
medias = admin.media
|
||||||
|
self.assertEqual({}, medias._css)
|
||||||
|
for js in medias._js:
|
||||||
|
self.assertNotIn(js, 'tabbed_admin')
|
||||||
|
|
||||||
|
def test_medias_method_with_grappelli(self):
|
||||||
|
"""
|
||||||
|
Tests if the right css ile is triggered when grappelli is installed.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
import grappelli
|
||||||
|
except ImportError:
|
||||||
|
return
|
||||||
|
settings.INSTALLED_APPS += ('grappelli', )
|
||||||
|
self.assertIn('grappelli', settings.INSTALLED_APPS)
|
||||||
|
admin = BandAdmin(Band, self.site)
|
||||||
|
medias = admin.media
|
||||||
|
self.assertTrue(len(medias._css) > 0)
|
||||||
|
self.assertIn('all', medias._css)
|
||||||
|
self.assertTrue(len(medias._css['all']) == 1)
|
||||||
|
self.assertIn('grappelli', medias._css['all'][0])
|
||||||
|
|
||||||
|
|
||||||
|
class TabbedAdminTagsTest(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.site = AdminSite()
|
||||||
|
self.admin = BandAdmin(Band, self.site)
|
||||||
|
self.req = request.get('/admin/tabbed_admin/tab/')
|
||||||
|
self.req.user = request.user
|
||||||
|
self.view = self.admin.add_view(self.req)
|
||||||
|
self.context = Context(self.view)
|
||||||
|
self.context.push()
|
||||||
|
self.context['adminform'] = self.view.context_data['adminform']
|
||||||
|
self.context['request'] = self.req
|
||||||
|
self.context['inline_admin_formsets'] = self.view.context_data['inline_admin_formsets']
|
||||||
|
|
||||||
|
def test_request_not_in_context_raising_improperly_configured(self):
|
||||||
|
"""
|
||||||
|
Tests if an exception is thrown when no request is passed.
|
||||||
|
"""
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
context = self.context
|
||||||
|
del context['request']
|
||||||
|
self.assertRaises(ImproperlyConfigured, render_tab_fieldsets_inlines, self.context, [])
|
||||||
|
|
||||||
|
def test_fieldset_passed_returns_fieldset_templated(self):
|
||||||
|
"""
|
||||||
|
Tests if the fieldset html is correctly generated when a fieldset is passed
|
||||||
|
"""
|
||||||
|
fieldset = self.view.context_data['tabs']['fields'][0]['entries'][0]
|
||||||
|
self.assertEqual('fieldset', fieldset['type'])
|
||||||
|
#tag = render_tab_fieldsets_inlines(self.context, fieldset)
|
||||||
|
#self.assertIn('fieldset', tag)
|
||||||
|
|
||||||
|
def test_inline_passed_returns_inline_templated(self):
|
||||||
|
"""
|
||||||
|
Tests if an inline html is correctly generated when an inline is passed.
|
||||||
|
"""
|
||||||
|
inline = self.view.context_data['tabs']['fields'][0]['entries'][1]
|
||||||
|
self.assertEqual('inline', inline['type'])
|
||||||
|
#tag = render_tab_fieldsets_inlines(self.context, inline)
|
||||||
|
#self.assertIn('inline', tag)
|
||||||
|
|
||||||
|
def test_wrong_inline_key_returns_nothing(self):
|
||||||
|
"""
|
||||||
|
Tests if a worng inline naming returns nothing.
|
||||||
|
"""
|
||||||
|
inline = self.view.context_data['tabs']['fields'][0]['entries'][1]
|
||||||
|
self.assertEqual('inline', inline['type'])
|
||||||
|
inline['name'] = 'Not exists'
|
||||||
|
tag = render_tab_fieldsets_inlines(self.context, inline)
|
||||||
|
self.assertEqual('', tag)
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
from django.conf.urls import include, url
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^admin/', include(admin.site.urls)),
|
||||||
|
]
|
||||||
|
|
@ -1233,6 +1233,21 @@ select {
|
||||||
padding-bottom: 7px;
|
padding-bottom: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tpage-nav ul li:hover {
|
||||||
|
font-weight: 700;
|
||||||
|
-webkit-text-decoration-line: underline;
|
||||||
|
text-decoration-line: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tpage-nav ul li.active {
|
||||||
|
-webkit-text-decoration-line: none;
|
||||||
|
text-decoration-line: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tpage-nav ul li.active a {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
.sr-only {
|
.sr-only {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
|
|
@ -1406,6 +1421,10 @@ select {
|
||||||
resize: both;
|
resize: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flex-row {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
.flex-col {
|
.flex-col {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
@ -1505,6 +1524,11 @@ select {
|
||||||
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
|
background-color: rgb(250 250 249 / var(--tw-bg-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-green-100 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(220 252 231 / var(--tw-bg-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
.bg-gray-50 {
|
.bg-gray-50 {
|
||||||
--tw-bg-opacity: 1;
|
--tw-bg-opacity: 1;
|
||||||
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
|
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
|
||||||
|
|
@ -1795,19 +1819,6 @@ h4 {
|
||||||
font-size: 1.25em;
|
font-size: 1.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#atu {
|
|
||||||
transition: all 0.2s ease, visibility 0s;
|
|
||||||
border-radius: 0px;
|
|
||||||
background: repeat padding-box border-box 0% 0% / auto auto scroll
|
|
||||||
linear-gradient(180deg, rgba(8, 50, 4, 0.5) 0%, rgba(8, 50, 4, 0.5) 100%),
|
|
||||||
no-repeat padding-box border-box 78% 59%/200% scroll url("/static/atu.jpg");
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.target\:block:target {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active\:ring-2:active {
|
.active\:ring-2:active {
|
||||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||||
|
|
@ -1818,6 +1829,10 @@ h4 {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prose-h1\:font-medium :is(:where(h1):not(:where([class~="not-prose"] *))) {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.prose-h2\:mb-2 :is(:where(h2):not(:where([class~="not-prose"] *))) {
|
.prose-h2\:mb-2 :is(:where(h2):not(:where([class~="not-prose"] *))) {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,17 @@ h4 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tpage-nav ul li {
|
||||||
|
@apply hover:font-bold hover:underline;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
@apply no-underline;
|
||||||
|
a {
|
||||||
|
@apply font-semibold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
|
|
@ -77,12 +88,3 @@ h4 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#atu {
|
|
||||||
transition: all 0.2s ease, visibility 0s;
|
|
||||||
border-radius: 0px;
|
|
||||||
background: repeat padding-box border-box 0% 0% / auto auto scroll
|
|
||||||
linear-gradient(180deg, rgba(8, 50, 4, 0.5) 0%, rgba(8, 50, 4, 0.5) 100%),
|
|
||||||
no-repeat padding-box border-box 78% 59%/200% scroll url("/static/atu.jpg");
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from tabbed_admin import TabbedModelAdmin
|
||||||
|
from .models import TournamentPage
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(TournamentPage)
|
||||||
|
class BandAdmin(TabbedModelAdmin):
|
||||||
|
model = TournamentPage
|
||||||
|
|
||||||
|
list_display = ['name', 'link']
|
||||||
|
|
||||||
|
tab_general = (
|
||||||
|
(None, {
|
||||||
|
'fields': ('name', 'published', 'header', 'footer')
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
tab_homepage = (
|
||||||
|
(None, {
|
||||||
|
'fields': ('homepage',)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
tab_schedule_and_results = (
|
||||||
|
(None, {
|
||||||
|
'fields': ('schedule_and_results', 'schedule_and_results_enabled')
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
tab_registration = (
|
||||||
|
(None, {
|
||||||
|
'fields': ('registration', 'registration_enabled')
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
tab_rules = (
|
||||||
|
(None, {
|
||||||
|
'fields': ('rules', 'rules_enabled')
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
tab_fee_and_prizes = (
|
||||||
|
(None, {
|
||||||
|
'fields': ('fee_and_prizes', 'fee_and_prizes_enabled')
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
tab_accomodation = (
|
||||||
|
(None, {
|
||||||
|
'fields': ('accomodation', 'accomodation_enabled')
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
tab_contact = (
|
||||||
|
(None, {
|
||||||
|
'fields': ('contact', 'contact_enabled')
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
tabs = [
|
||||||
|
('Ogólne ustawienia', tab_general),
|
||||||
|
('Strona główna', tab_homepage),
|
||||||
|
('Harmonogram i wyniki', tab_schedule_and_results),
|
||||||
|
('Rejestracja', tab_registration),
|
||||||
|
('Regulamin', tab_rules),
|
||||||
|
('Wpisowe i nagrody', tab_fee_and_prizes),
|
||||||
|
('Noclegi', tab_accomodation),
|
||||||
|
('Kontakt', tab_contact),
|
||||||
|
]
|
||||||
|
|
@ -4,3 +4,4 @@ from django.apps import AppConfig
|
||||||
class TournamentpagesConfig(AppConfig):
|
class TournamentpagesConfig(AppConfig):
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
name = 'tournamentpages'
|
name = 'tournamentpages'
|
||||||
|
verbose_name = 'Strony turniejów'
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
# Generated by Django 4.0.5 on 2022-10-05 18:29
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import tinymce.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TournamentPage',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=250, verbose_name='Nazwa')),
|
||||||
|
('published', models.BooleanField(default=False, verbose_name='Turniej opublikowany')),
|
||||||
|
('header', tinymce.models.HTMLField(blank=True, default='', verbose_name='Nagłówek')),
|
||||||
|
('footer', tinymce.models.HTMLField(blank=True, default='', verbose_name='Stopka')),
|
||||||
|
('homepage', tinymce.models.HTMLField(blank=True, default='', verbose_name='Strona główna')),
|
||||||
|
('schedule_and_results', tinymce.models.HTMLField(blank=True, default='', verbose_name='Harmonogram i wyniki')),
|
||||||
|
('schedule_and_results_enabled', models.BooleanField(default=False, verbose_name='Strona włączona')),
|
||||||
|
('registration', tinymce.models.HTMLField(blank=True, default='', verbose_name='Rejestracja')),
|
||||||
|
('registration_enabled', models.BooleanField(default=False, verbose_name='Strona włączona')),
|
||||||
|
('rules', tinymce.models.HTMLField(blank=True, default='', verbose_name='Regulamin')),
|
||||||
|
('rules_enabled', models.BooleanField(default=False, verbose_name='Strona włączona')),
|
||||||
|
('fee_and_prizes', tinymce.models.HTMLField(blank=True, default='', verbose_name='Wpisowe i nagrody')),
|
||||||
|
('fee_and_prizes_enabled', models.BooleanField(default=False, verbose_name='Strona włączona')),
|
||||||
|
('accomodation', tinymce.models.HTMLField(blank=True, default='', verbose_name='Noclegi')),
|
||||||
|
('accomodation_enabled', models.BooleanField(default=False, verbose_name='Strona włączona')),
|
||||||
|
('contact', tinymce.models.HTMLField(blank=True, default='', verbose_name='Kontakt')),
|
||||||
|
('contact_enabled', models.BooleanField(default=False, verbose_name='Strona włączona')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Strona turnieju',
|
||||||
|
'verbose_name_plural': 'Strony turniejów',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.urls.base import reverse_lazy
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
from tinymce.models import HTMLField
|
||||||
|
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
|
|
||||||
|
|
||||||
|
class TournamentPage(models.Model):
|
||||||
|
name = models.CharField('Nazwa', max_length=250)
|
||||||
|
published = models.BooleanField('Turniej opublikowany', default=False)
|
||||||
|
|
||||||
|
header = HTMLField('Nagłówek', default='', blank=True)
|
||||||
|
footer = HTMLField('Stopka', default='', blank=True)
|
||||||
|
|
||||||
|
homepage = HTMLField('Strona główna', default='', blank=True)
|
||||||
|
|
||||||
|
schedule_and_results = HTMLField(
|
||||||
|
'Harmonogram i wyniki', default='', blank=True)
|
||||||
|
schedule_and_results_enabled = models.BooleanField(
|
||||||
|
'Strona włączona', default=False)
|
||||||
|
|
||||||
|
registration = HTMLField('Rejestracja', default='', blank=True)
|
||||||
|
registration_enabled = models.BooleanField(
|
||||||
|
'Strona włączona', default=False)
|
||||||
|
|
||||||
|
rules = HTMLField('Regulamin', default='', blank=True)
|
||||||
|
rules_enabled = models.BooleanField('Strona włączona', default=False)
|
||||||
|
|
||||||
|
fee_and_prizes = HTMLField('Wpisowe i nagrody', default='', blank=True)
|
||||||
|
fee_and_prizes_enabled = models.BooleanField(
|
||||||
|
'Strona włączona', default=False)
|
||||||
|
|
||||||
|
accomodation = HTMLField('Noclegi', default='', blank=True)
|
||||||
|
accomodation_enabled = models.BooleanField(
|
||||||
|
'Strona włączona', default=False)
|
||||||
|
|
||||||
|
contact = HTMLField('Kontakt', default='', blank=True)
|
||||||
|
contact_enabled = models.BooleanField('Strona włączona', default=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'Strona turnieju'
|
||||||
|
verbose_name_plural = 'Strony turniejów'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def link(self):
|
||||||
|
href = reverse_lazy('homepage', args=[self.id])
|
||||||
|
return mark_safe(f'<a href="{href}" target="_blank">{href}</a>')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
{% load static tailwind_tags %}
|
||||||
|
|
||||||
|
<html lang="pl" dir="ltr">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="description" content="Podlaski Związek Brydża Sportowego" />
|
||||||
|
<meta name="author" content="Mikołaj Kubiczek" />
|
||||||
|
|
||||||
|
<link rel="icon" href="{% static 'favicon.ico' %}" type="image/x-icon" />
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="192x192"
|
||||||
|
href="{% static 'icon-192x192.png' %}"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="512x512"
|
||||||
|
href="{% static 'icon-512x512.png' %}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<title>{% block title %}{{ t.name }}{% endblock %}</title>
|
||||||
|
<base href="/" />
|
||||||
|
|
||||||
|
{% tailwind_css %}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="antialiased flex flex-col gap-4 items-center prose max-w-full">
|
||||||
|
<header>{{ t.header | safe}}</header>
|
||||||
|
<nav class="not-prose bg-green-100 p-2 tpage-nav">
|
||||||
|
<ul class="flex flex-row flex-wrap gap-4 text-xl xl:max-w-screen-xl">
|
||||||
|
<li class="{% if homepage %}active{% endif %}">
|
||||||
|
<a href="{% url 'homepage' t.id %}">Strona główna</a>
|
||||||
|
</li>
|
||||||
|
{% if t.schedule_and_results_enabled %}
|
||||||
|
<li class="{% if schedule_and_results %}active{% endif %}">
|
||||||
|
<a href="{% url 'schedule_and_results' t.id %}"
|
||||||
|
>Harmonogram i wyniki</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
{% endif %} {% if t.registration_enabled %}
|
||||||
|
<li class="{% if registration %}active{% endif %}">
|
||||||
|
<a href="{% url 'registration' t.id %}">Rejestracja</a>
|
||||||
|
</li>
|
||||||
|
{% endif %} {% if t.rules_enabled %}
|
||||||
|
<li class="{% if rules %}active{% endif %}">
|
||||||
|
<a href="{% url 'rules' t.id %}">Regulamin</a>
|
||||||
|
</li>
|
||||||
|
{% endif %} {% if t.fee_and_prizes_enabled %}
|
||||||
|
<li class="{% if fee_and_prizes %}active{% endif %}">
|
||||||
|
<a href="{% url 'fee_and_prizes' t.id %}">Wpisowe i nagrody</a>
|
||||||
|
</li>
|
||||||
|
{% endif %} {% if t.accomodation_enabled %}
|
||||||
|
<li class="{% if accomodation %}active{% endif %}">
|
||||||
|
<a href="{% url 'accomodation' t.id %}">Noclegi</a>
|
||||||
|
</li>
|
||||||
|
{% endif %} {% if t.contact_enabled %}
|
||||||
|
<li class="{% if contact %}active{% endif %}">
|
||||||
|
<a href="{% url 'contact' t.id %}">Kontakt</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main
|
||||||
|
class="container xl:max-w-screen-xl mx-auto p-4 prose-h1:font-medium prose-img:my-1 prose-p:my-[0.3em]"
|
||||||
|
>
|
||||||
|
{{ content | safe }}
|
||||||
|
</main>
|
||||||
|
<footer>{{ t.footer | safe}}</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
from django.urls import path
|
||||||
|
from .views import *
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('<int:id>', HomeView.as_view(), name='homepage'),
|
||||||
|
path('<int:id>/wyniki', ScheduleAndResultsView.as_view(),
|
||||||
|
name='schedule_and_results'),
|
||||||
|
path('<int:id>/rejestracja', RegistrationView.as_view(), name='registration'),
|
||||||
|
path('<int:id>/regulamin', RulesView.as_view(), name='rules'),
|
||||||
|
path('<int:id>/nagrody', FeeAndPrizesView.as_view(), name='fee_and_prizes'),
|
||||||
|
path('<int:id>/noclegi', AccomodationView.as_view(), name='accomodation'),
|
||||||
|
path('<int:id>/kontakt', ContactView.as_view(), name='contact'),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.views.generic import TemplateView
|
||||||
|
from .models import TournamentPage
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
|
|
||||||
|
|
||||||
|
class HomeView(TemplateView):
|
||||||
|
template_name = "tournament.html"
|
||||||
|
|
||||||
|
def get_context_data(self, id, **kwargs):
|
||||||
|
t = TournamentPage.objects.get(id=id, published=True)
|
||||||
|
return {'t': t, 'content': t.homepage, 'homepage': True}
|
||||||
|
|
||||||
|
|
||||||
|
class ScheduleAndResultsView(TemplateView):
|
||||||
|
template_name = "tournament.html"
|
||||||
|
|
||||||
|
def get_context_data(self, id, **kwargs):
|
||||||
|
t = TournamentPage.objects.get(
|
||||||
|
id=id, published=True, schedule_and_results_enabled=True)
|
||||||
|
return {'t': t, 'content': t.schedule_and_results, 'schedule_and_results': True}
|
||||||
|
|
||||||
|
|
||||||
|
class RegistrationView(TemplateView):
|
||||||
|
template_name = "tournament.html"
|
||||||
|
|
||||||
|
def get_context_data(self, id, **kwargs):
|
||||||
|
t = TournamentPage.objects.get(
|
||||||
|
id=id, published=True, registration_enabled=True)
|
||||||
|
return {'t': t, 'content': t.registration, 'registration': True}
|
||||||
|
|
||||||
|
|
||||||
|
class RulesView(TemplateView):
|
||||||
|
template_name = "tournament.html"
|
||||||
|
|
||||||
|
def get_context_data(self, id, **kwargs):
|
||||||
|
t = TournamentPage.objects.get(
|
||||||
|
id=id, published=True, rules_enabled=True)
|
||||||
|
return {'t': t, 'content': t.rules, 'rules': True}
|
||||||
|
|
||||||
|
|
||||||
|
class FeeAndPrizesView(TemplateView):
|
||||||
|
template_name = "tournament.html"
|
||||||
|
|
||||||
|
def get_context_data(self, id, **kwargs):
|
||||||
|
t = TournamentPage.objects.get(
|
||||||
|
id=id, published=True, fee_and_prizes_enabled=True)
|
||||||
|
return {'t': t, 'content': t.fee_and_prizes, 'fee_and_prizes': True}
|
||||||
|
|
||||||
|
|
||||||
|
class AccomodationView(TemplateView):
|
||||||
|
template_name = "tournament.html"
|
||||||
|
|
||||||
|
def get_context_data(self, id, **kwargs):
|
||||||
|
t = TournamentPage.objects.get(
|
||||||
|
id=id, published=True, accomodation_enabled=True)
|
||||||
|
return {'t': t, 'content': t.accomodation, 'accomodation': True}
|
||||||
|
|
||||||
|
|
||||||
|
class ContactView(TemplateView):
|
||||||
|
template_name = "tournament.html"
|
||||||
|
|
||||||
|
def get_context_data(self, id, **kwargs):
|
||||||
|
t = TournamentPage.objects.get(
|
||||||
|
id=id, published=True, contact_enabled=True)
|
||||||
|
return {'t': t, 'content': t.contact, 'contact': True}
|
||||||