",options:{disabled:!1,create:null},_createWidget:function(t,i){i=e(i||this.defaultElement||this)[0],this.element=e(i),this.uuid=s++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=e(),this.hoverable=e(),this.focusable=e(),i!==this&&(e.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===i&&this.destroy()}}),this.document=e(i.style?i.ownerDocument:i.document||i),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(t,i){var s,n,a,o=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(o={},s=t.split("."),t=s.shift(),s.length){for(n=o[t]=e.widget.extend({},this.options[t]),a=0;s.length-1>a;a++)n[s[a]]=n[s[a]]||{},n=n[s[a]];if(t=s.pop(),1===arguments.length)return void 0===n[t]?null:n[t];n[t]=i}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];o[t]=i}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!t),t&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(t,i,s){var n,a=this;"boolean"!=typeof t&&(s=i,i=t,t=!1),s?(i=n=e(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),e.each(s,function(s,o){function r(){return t||a.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?a[o]:o).apply(a,arguments):void 0}"string"!=typeof o&&(r.guid=o.guid=o.guid||r.guid||e.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+a.eventNamespace,u=h[2];u?n.delegate(u,l,r):i.bind(l,r)})},_off:function(t,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.unbind(i).undelegate(i),this.bindings=e(this.bindings.not(t).get()),this.focusable=e(this.focusable.not(t).get()),this.hoverable=e(this.hoverable.not(t).get())},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,o=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(o)&&o.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var o,r=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),o=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),o&&e.effects&&e.effects.effect[r]?s[t](n):r!==t&&s[r]?s[r](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}}),e.widget,e.widget("ui.tabs",{version:"1.11.4",delay:300,options:{active:null,collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_isLocal:function(){var e=/#.*$/;return function(t){var i,s;t=t.cloneNode(!1),i=t.href.replace(e,""),s=location.href.replace(e,"");try{i=decodeURIComponent(i)}catch(n){}try{s=decodeURIComponent(s)}catch(n){}return t.hash.length>1&&i===s}}(),_create:function(){var t=this,i=this.options;this.running=!1,this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all").toggleClass("ui-tabs-collapsible",i.collapsible),this._processTabs(),i.active=this._initialActive(),e.isArray(i.disabled)&&(i.disabled=e.unique(i.disabled.concat(e.map(this.tabs.filter(".ui-state-disabled"),function(e){return t.tabs.index(e)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(i.active):e(),this._refresh(),this.active.length&&this.load(i.active)},_initialActive:function(){var t=this.options.active,i=this.options.collapsible,s=location.hash.substring(1);return null===t&&(s&&this.tabs.each(function(i,n){return e(n).attr("aria-controls")===s?(t=i,!1):void 0}),null===t&&(t=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===t||-1===t)&&(t=this.tabs.length?0:!1)),t!==!1&&(t=this.tabs.index(this.tabs.eq(t)),-1===t&&(t=i?!1:0)),!i&&t===!1&&this.anchors.length&&(t=0),t},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):e()}},_tabKeydown:function(t){var i=e(this.document[0].activeElement).closest("li"),s=this.tabs.index(i),n=!0;if(!this._handlePageNav(t)){switch(t.keyCode){case e.ui.keyCode.RIGHT:case e.ui.keyCode.DOWN:s++;break;case e.ui.keyCode.UP:case e.ui.keyCode.LEFT:n=!1,s--;break;case e.ui.keyCode.END:s=this.anchors.length-1;break;case e.ui.keyCode.HOME:s=0;break;case e.ui.keyCode.SPACE:return t.preventDefault(),clearTimeout(this.activating),this._activate(s),void 0;case e.ui.keyCode.ENTER:return t.preventDefault(),clearTimeout(this.activating),this._activate(s===this.options.active?!1:s),void 0;default:return}t.preventDefault(),clearTimeout(this.activating),s=this._focusNextTab(s,n),t.ctrlKey||t.metaKey||(i.attr("aria-selected","false"),this.tabs.eq(s).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",s)},this.delay))}},_panelKeydown:function(t){this._handlePageNav(t)||t.ctrlKey&&t.keyCode===e.ui.keyCode.UP&&(t.preventDefault(),this.active.focus())},_handlePageNav:function(t){return t.altKey&&t.keyCode===e.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):t.altKey&&t.keyCode===e.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):void 0},_findNextTab:function(t,i){function s(){return t>n&&(t=0),0>t&&(t=n),t}for(var n=this.tabs.length-1;-1!==e.inArray(s(),this.options.disabled);)t=i?t+1:t-1;return t},_focusNextTab:function(e,t){return e=this._findNextTab(e,t),this.tabs.eq(e).focus(),e},_setOption:function(e,t){return"active"===e?(this._activate(t),void 0):"disabled"===e?(this._setupDisabled(t),void 0):(this._super(e,t),"collapsible"===e&&(this.element.toggleClass("ui-tabs-collapsible",t),t||this.options.active!==!1||this._activate(0)),"event"===e&&this._setupEvents(t),"heightStyle"===e&&this._setupHeightStyle(t),void 0)},_sanitizeSelector:function(e){return e?e.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var t=this.options,i=this.tablist.children(":has(a[href])");t.disabled=e.map(i.filter(".ui-state-disabled"),function(e){return i.index(e)}),this._processTabs(),t.active!==!1&&this.anchors.length?this.active.length&&!e.contains(this.tablist[0],this.active[0])?this.tabs.length===t.disabled.length?(t.active=!1,this.active=e()):this._activate(this._findNextTab(Math.max(0,t.active-1),!1)):t.active=this.tabs.index(this.active):(t.active=!1,this.active=e()),this._refresh()},_refresh:function(){this._setupDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-hidden":"true"}),this.active.length?(this.active.addClass("ui-tabs-active ui-state-active").attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}),this._getPanelForTab(this.active).show().attr({"aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var t=this,i=this.tabs,s=this.anchors,n=this.panels;this.tablist=this._getList().addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").attr("role","tablist").delegate("> li","mousedown"+this.eventNamespace,function(t){e(this).is(".ui-state-disabled")&&t.preventDefault()}).delegate(".ui-tabs-anchor","focus"+this.eventNamespace,function(){e(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this.tabs=this.tablist.find("> li:has(a[href])").addClass("ui-state-default ui-corner-top").attr({role:"tab",tabIndex:-1}),this.anchors=this.tabs.map(function(){return e("a",this)[0]}).addClass("ui-tabs-anchor").attr({role:"presentation",tabIndex:-1}),this.panels=e(),this.anchors.each(function(i,s){var n,a,o,r=e(s).uniqueId().attr("id"),h=e(s).closest("li"),l=h.attr("aria-controls");t._isLocal(s)?(n=s.hash,o=n.substring(1),a=t.element.find(t._sanitizeSelector(n))):(o=h.attr("aria-controls")||e({}).uniqueId()[0].id,n="#"+o,a=t.element.find(n),a.length||(a=t._createPanel(o),a.insertAfter(t.panels[i-1]||t.tablist)),a.attr("aria-live","polite")),a.length&&(t.panels=t.panels.add(a)),l&&h.data("ui-tabs-aria-controls",l),h.attr({"aria-controls":o,"aria-labelledby":r}),a.attr("aria-labelledby",r)}),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").attr("role","tabpanel"),i&&(this._off(i.not(this.tabs)),this._off(s.not(this.anchors)),this._off(n.not(this.panels)))},_getList:function(){return this.tablist||this.element.find("ol,ul").eq(0)},_createPanel:function(t){return e("
").attr("id",t).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)},_setupDisabled:function(t){e.isArray(t)&&(t.length?t.length===this.anchors.length&&(t=!0):t=!1);for(var i,s=0;i=this.tabs[s];s++)t===!0||-1!==e.inArray(s,t)?e(i).addClass("ui-state-disabled").attr("aria-disabled","true"):e(i).removeClass("ui-state-disabled").removeAttr("aria-disabled");this.options.disabled=t},_setupEvents:function(t){var i={};t&&e.each(t.split(" "),function(e,t){i[t]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(!0,this.anchors,{click:function(e){e.preventDefault()}}),this._on(this.anchors,i),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(t){var i,s=this.element.parent();"fill"===t?(i=s.height(),i-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var t=e(this),s=t.css("position");"absolute"!==s&&"fixed"!==s&&(i-=t.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){i-=e(this).outerHeight(!0)}),this.panels.each(function(){e(this).height(Math.max(0,i-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===t&&(i=0,this.panels.each(function(){i=Math.max(i,e(this).height("").height())}).height(i))},_eventHandler:function(t){var i=this.options,s=this.active,n=e(t.currentTarget),a=n.closest("li"),o=a[0]===s[0],r=o&&i.collapsible,h=r?e():this._getPanelForTab(a),l=s.length?this._getPanelForTab(s):e(),u={oldTab:s,oldPanel:l,newTab:r?e():a,newPanel:h};t.preventDefault(),a.hasClass("ui-state-disabled")||a.hasClass("ui-tabs-loading")||this.running||o&&!i.collapsible||this._trigger("beforeActivate",t,u)===!1||(i.active=r?!1:this.tabs.index(a),this.active=o?e():a,this.xhr&&this.xhr.abort(),l.length||h.length||e.error("jQuery UI Tabs: Mismatching fragment identifier."),h.length&&this.load(this.tabs.index(a),t),this._toggle(t,u))},_toggle:function(t,i){function s(){a.running=!1,a._trigger("activate",t,i)}function n(){i.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),o.length&&a.options.show?a._show(o,a.options.show,s):(o.show(),s())}var a=this,o=i.newPanel,r=i.oldPanel;this.running=!0,r.length&&this.options.hide?this._hide(r,this.options.hide,function(){i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),n()}):(i.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),r.hide(),n()),r.attr("aria-hidden","true"),i.oldTab.attr({"aria-selected":"false","aria-expanded":"false"}),o.length&&r.length?i.oldTab.attr("tabIndex",-1):o.length&&this.tabs.filter(function(){return 0===e(this).attr("tabIndex")}).attr("tabIndex",-1),o.attr("aria-hidden","false"),i.newTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_activate:function(t){var i,s=this._findActive(t);s[0]!==this.active[0]&&(s.length||(s=this.active),i=s.find(".ui-tabs-anchor")[0],this._eventHandler({target:i,currentTarget:i,preventDefault:e.noop}))},_findActive:function(t){return t===!1?e():this.tabs.eq(t)},_getIndex:function(e){return"string"==typeof e&&(e=this.anchors.index(this.anchors.filter("[href$='"+e+"']"))),e},_destroy:function(){this.xhr&&this.xhr.abort(),this.element.removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible"),this.tablist.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").removeAttr("role"),this.anchors.removeClass("ui-tabs-anchor").removeAttr("role").removeAttr("tabIndex").removeUniqueId(),this.tablist.unbind(this.eventNamespace),this.tabs.add(this.panels).each(function(){e.data(this,"ui-tabs-destroy")?e(this).remove():e(this).removeClass("ui-state-default ui-state-active ui-state-disabled ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel").removeAttr("tabIndex").removeAttr("aria-live").removeAttr("aria-busy").removeAttr("aria-selected").removeAttr("aria-labelledby").removeAttr("aria-hidden").removeAttr("aria-expanded").removeAttr("role")}),this.tabs.each(function(){var t=e(this),i=t.data("ui-tabs-aria-controls");i?t.attr("aria-controls",i).removeData("ui-tabs-aria-controls"):t.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(t){var i=this.options.disabled;i!==!1&&(void 0===t?i=!1:(t=this._getIndex(t),i=e.isArray(i)?e.map(i,function(e){return e!==t?e:null}):e.map(this.tabs,function(e,i){return i!==t?i:null})),this._setupDisabled(i))},disable:function(t){var i=this.options.disabled;if(i!==!0){if(void 0===t)i=!0;else{if(t=this._getIndex(t),-1!==e.inArray(t,i))return;i=e.isArray(i)?e.merge([t],i).sort():[t]}this._setupDisabled(i)}},load:function(t,i){t=this._getIndex(t);var s=this,n=this.tabs.eq(t),a=n.find(".ui-tabs-anchor"),o=this._getPanelForTab(n),r={tab:n,panel:o},h=function(e,t){"abort"===t&&s.panels.stop(!1,!0),n.removeClass("ui-tabs-loading"),o.removeAttr("aria-busy"),e===s.xhr&&delete s.xhr};this._isLocal(a[0])||(this.xhr=e.ajax(this._ajaxSettings(a,i,r)),this.xhr&&"canceled"!==this.xhr.statusText&&(n.addClass("ui-tabs-loading"),o.attr("aria-busy","true"),this.xhr.done(function(e,t,n){setTimeout(function(){o.html(e),s._trigger("load",i,r),h(n,t)},1)}).fail(function(e,t){setTimeout(function(){h(e,t)},1)})))},_ajaxSettings:function(t,i,s){var n=this;return{url:t.attr("href"),beforeSend:function(t,a){return n._trigger("beforeLoad",i,e.extend({jqXHR:t,ajaxSettings:a},s))}}},_getPanelForTab:function(t){var i=e(t).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+i))}})});
diff --git a/tabbed_admin/templates/tabbed_admin/change_form.html b/tabbed_admin/templates/tabbed_admin/change_form.html
new file mode 100644
index 0000000..d7d6be4
--- /dev/null
+++ b/tabbed_admin/templates/tabbed_admin/change_form.html
@@ -0,0 +1,104 @@
+{% extends "admin/change_form.html" %}
+{% load i18n admin_modify admin_urls tabbed_admin_tags %}
+
+{% block content %}
+
+
+ {% block object-tools %}{{ block.super }}{% endblock %}
+
+
+{% endblock %}
diff --git a/db/tournamentpages/migrations/__init__.py b/tabbed_admin/templatetags/__init__.py
similarity index 100%
rename from db/tournamentpages/migrations/__init__.py
rename to tabbed_admin/templatetags/__init__.py
diff --git a/tabbed_admin/templatetags/tabbed_admin_tags.py b/tabbed_admin/templatetags/tabbed_admin_tags.py
new file mode 100644
index 0000000..4c9e48c
--- /dev/null
+++ b/tabbed_admin/templatetags/tabbed_admin_tags.py
@@ -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 ''
diff --git a/tabbed_admin/tests/__init__.py b/tabbed_admin/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tabbed_admin/tests/admin.py b/tabbed_admin/tests/admin.py
new file mode 100644
index 0000000..fa3b1c7
--- /dev/null
+++ b/tabbed_admin/tests/admin.py
@@ -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)
+ ]
diff --git a/tabbed_admin/tests/models.py b/tabbed_admin/tests/models.py
new file mode 100644
index 0000000..6122b01
--- /dev/null
+++ b/tabbed_admin/tests/models.py
@@ -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()
diff --git a/tabbed_admin/tests/runtests.py b/tabbed_admin/tests/runtests.py
new file mode 100644
index 0000000..c080424
--- /dev/null
+++ b/tabbed_admin/tests/runtests.py
@@ -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)
diff --git a/tabbed_admin/tests/tests.py b/tabbed_admin/tests/tests.py
new file mode 100644
index 0000000..1006560
--- /dev/null
+++ b/tabbed_admin/tests/tests.py
@@ -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)
diff --git a/tabbed_admin/tests/urls.py b/tabbed_admin/tests/urls.py
new file mode 100644
index 0000000..eb91d28
--- /dev/null
+++ b/tabbed_admin/tests/urls.py
@@ -0,0 +1,6 @@
+from django.conf.urls import include, url
+from django.contrib import admin
+
+urlpatterns = [
+ url(r'^admin/', include(admin.site.urls)),
+]
diff --git a/theme/static/css/dist/styles.css b/theme/static/css/dist/styles.css
index 4e6b3c1..dc30e55 100644
--- a/theme/static/css/dist/styles.css
+++ b/theme/static/css/dist/styles.css
@@ -1233,6 +1233,21 @@ select {
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 {
position: absolute;
width: 1px;
@@ -1406,6 +1421,10 @@ select {
resize: both;
}
+.flex-row {
+ flex-direction: row;
+}
+
.flex-col {
flex-direction: column;
}
@@ -1505,6 +1524,11 @@ select {
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 {
--tw-bg-opacity: 1;
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
@@ -1795,19 +1819,6 @@ h4 {
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 {
--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);
@@ -1818,6 +1829,10 @@ h4 {
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"] *))) {
margin-bottom: 0.5rem;
}
diff --git a/theme/static_src/src/styles.css b/theme/static_src/src/styles.css
index 347b6d7..cd4f408 100644
--- a/theme/static_src/src/styles.css
+++ b/theme/static_src/src/styles.css
@@ -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 {
@@ -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;
-}
diff --git a/tournamentpages/__init__.py b/tournamentpages/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tournamentpages/admin.py b/tournamentpages/admin.py
new file mode 100644
index 0000000..41e8d3e
--- /dev/null
+++ b/tournamentpages/admin.py
@@ -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),
+ ]
diff --git a/db/tournamentpages/apps.py b/tournamentpages/apps.py
similarity index 80%
rename from db/tournamentpages/apps.py
rename to tournamentpages/apps.py
index 119fa77..a7a2241 100644
--- a/db/tournamentpages/apps.py
+++ b/tournamentpages/apps.py
@@ -4,3 +4,4 @@ from django.apps import AppConfig
class TournamentpagesConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'tournamentpages'
+ verbose_name = 'Strony turniejów'
diff --git a/tournamentpages/migrations/0001_initial.py b/tournamentpages/migrations/0001_initial.py
new file mode 100644
index 0000000..34b4535
--- /dev/null
+++ b/tournamentpages/migrations/0001_initial.py
@@ -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',
+ },
+ ),
+ ]
diff --git a/tournamentpages/migrations/__init__.py b/tournamentpages/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tournamentpages/models.py b/tournamentpages/models.py
new file mode 100644
index 0000000..03c203e
--- /dev/null
+++ b/tournamentpages/models.py
@@ -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'
{href}')
+
+ def __str__(self):
+ return self.name
diff --git a/tournamentpages/templates/tournament.html b/tournamentpages/templates/tournament.html
new file mode 100644
index 0000000..dfb4c82
--- /dev/null
+++ b/tournamentpages/templates/tournament.html
@@ -0,0 +1,76 @@
+
+
+{% load static tailwind_tags %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{% block title %}{{ t.name }}{% endblock %}
+
+
+ {% tailwind_css %}
+
+
+
+
+
+
+ {{ content | safe }}
+
+
+
+
diff --git a/tournamentpages/urls.py b/tournamentpages/urls.py
new file mode 100644
index 0000000..59fe5e7
--- /dev/null
+++ b/tournamentpages/urls.py
@@ -0,0 +1,14 @@
+from django.urls import path
+from .views import *
+
+
+urlpatterns = [
+ path('
', HomeView.as_view(), name='homepage'),
+ path('/wyniki', ScheduleAndResultsView.as_view(),
+ name='schedule_and_results'),
+ path('/rejestracja', RegistrationView.as_view(), name='registration'),
+ path('/regulamin', RulesView.as_view(), name='rules'),
+ path('/nagrody', FeeAndPrizesView.as_view(), name='fee_and_prizes'),
+ path('/noclegi', AccomodationView.as_view(), name='accomodation'),
+ path('/kontakt', ContactView.as_view(), name='contact'),
+]
diff --git a/tournamentpages/views.py b/tournamentpages/views.py
new file mode 100644
index 0000000..97cc9ca
--- /dev/null
+++ b/tournamentpages/views.py
@@ -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}