mirror of
https://github.com/pretix/pretix.git
synced 2026-05-09 15:54:03 +00:00
Merge branch 'master' of github.com:tixl/tixl
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +1,6 @@
|
|||||||
[submodule "src/tixlbase/static/bootstrap"]
|
[submodule "src/tixlbase/static/bootstrap"]
|
||||||
path = src/tixlbase/static/bootstrap
|
path = src/tixlbase/static/bootstrap
|
||||||
url = https://github.com/twbs/bootstrap.git
|
url = https://github.com/twbs/bootstrap.git
|
||||||
|
[submodule "src/tixlbase/static/fontawesome"]
|
||||||
|
path = src/tixlbase/static/fontawesome
|
||||||
|
url = https://github.com/FortAwesome/Font-Awesome.git
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ Implementation Concepts
|
|||||||
Basic terminology
|
Basic terminology
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
Tixl is a sofware selling **Items**, an abstract thing which is related to an **Event**. Every Event is managed by the **Organizer**, who runs the event.
|
Users and events
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Tixl know two types of **Users**:
|
Tixl is all about **events**, which are defined as something happening somewhere. Every Event is managed by the **organizer**, an abstract entity running the event.
|
||||||
|
|
||||||
|
Tixl is used by **users**, of which it knows two types:
|
||||||
|
|
||||||
**Local users**
|
**Local users**
|
||||||
Local users do only exist inside the scope of one event. They are identified by usernames, which are only valid for exactly one event.
|
Local users do only exist inside the scope of one event. They are identified by usernames, which are only valid for exactly one event.
|
||||||
@@ -16,3 +19,31 @@ Tixl know two types of **Users**:
|
|||||||
|
|
||||||
For more information about this user concept and reasons behind it, see the docstring of the ``tixlbase.models.User`` class.
|
For more information about this user concept and reasons behind it, see the docstring of the ``tixlbase.models.User`` class.
|
||||||
|
|
||||||
|
Items and flavors
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The purpose of tixl is to sell **items** (which belong to **events**) to **users**. An **item** is a abstract thing, popular examples being event tickets or a piece of merchandise, like 'T-Shirt'. An **item** can have multiple **properties** with multiple **values** each. For example, the **item** 'T-Shirt' could have the **property** 'Size' with **values** 'S', 'M' and 'L' and the **property** 'Color' with **values** 'black' and 'blue'.
|
||||||
|
|
||||||
|
Any combination of those **values** is called a **flavor**. Using the examples from above, a possible **flavor** would be 'T-Shirt S blue'.
|
||||||
|
|
||||||
|
Restrictions
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The probably most powerful concepts of tixl is the very abstract concept of **restricitons**. We already know that **items** can come in very different **flavors**, but a **restriction** decides whether an item is available for sale and assign **prices** to **flavors**. There are **restriction types**, which are pieces of code implementing the restrictions and **restriction instances**, which are configurations made by the **organzier**. Although **restrictions** are a very abstract concept which can be used to do nearly anything, there are a few obvious examples:
|
||||||
|
|
||||||
|
* One easy example is the time restriction, which allows the sale of certain item flavors only within a certain time frame. As restrictions can also assign a price to a flavor, this can also be used to implement something like 'early-bird prices' for your tickets by using multiple time restrictions with different prices.
|
||||||
|
* The most obvious example is the number restriction, which limits the sale of the tickets to a maximum number. You can use this either to stop selling tickets completely when your house is full or for creating limited 'VIP tickets'.
|
||||||
|
* A more advanced example is a restriction by user, for example reduced ticket prices for members who are members of a special group.
|
||||||
|
* Arbitrary sophisticated features like coupon codes are also possible to be implemented using this feature.
|
||||||
|
|
||||||
|
Any number of **restrictions** can be applied to the whole of a **item** or to a specific **flavor**. The processing of the restriction follows the following set of rules:
|
||||||
|
|
||||||
|
* **Flavor**-specific rules have precedence over **item**-specific rules.
|
||||||
|
* The restrictions are being processed in random order (there may not be any assumptions about the evaluation order).
|
||||||
|
* Multiple restriction instances of **different restriction types** are linked with *and*, so if both a time frame and a number restriction are applied to an item, the item is only avaliable for sale within the given time frame *and* only as long as items are available.
|
||||||
|
* Multiple restriction instances of the **same restriction type** are linked with *or*, so if two time frames are applied to an item, the item is available for sale in both of the time frames. (This behaviour is actually a decision of the restriction type itself, so this rule is not enforced but rather a general rule of thumb).
|
||||||
|
* If multiple restrictions apply which set the price, the *cheapest* price determines the final price.
|
||||||
|
|
||||||
|
Restriction types can be introduced by 3rd-party code and do not require changes to the tixl codebase.
|
||||||
|
|
||||||
|
.. note:: This pluggability of restrictions is implemented using the 'signal and receiver' pattern provided by Django. Restrictions can therefore live in seperate Django apps.
|
||||||
|
|||||||
0
src/helpers/__init__.py
Normal file
0
src/helpers/__init__.py
Normal file
11
src/helpers/lessabsolutefilter.py
Normal file
11
src/helpers/lessabsolutefilter.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from compressor.filters.base import CompilerFilter
|
||||||
|
from compressor.filters.css_default import CssAbsoluteFilter
|
||||||
|
|
||||||
|
|
||||||
|
class LessFilter(CompilerFilter):
|
||||||
|
def __init__(self, content, attrs, **kwargs):
|
||||||
|
super(LessFilter, self).__init__(content, command='lessc {infile} {outfile}', **kwargs)
|
||||||
|
|
||||||
|
def input(self, **kwargs):
|
||||||
|
content = super(LessFilter, self).input(**kwargs)
|
||||||
|
return CssAbsoluteFilter(content).input(**kwargs)
|
||||||
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: 1\n"
|
"Project-Id-Version: 1\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2014-09-11 11:16+0200\n"
|
"POT-Creation-Date: 2014-09-11 21:09+0200\n"
|
||||||
"PO-Revision-Date: 2014-09-11 11:05+200\n"
|
"PO-Revision-Date: 2014-09-11 11:05+200\n"
|
||||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||||
"Language-Team: Raphael Michel <michel@rami.io>\n"
|
"Language-Team: Raphael Michel <michel@rami.io>\n"
|
||||||
@@ -18,11 +18,11 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: tixl/settings.py:92
|
#: tixl/settings.py:104
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr "Deutsch"
|
msgstr "Deutsch"
|
||||||
|
|
||||||
#: tixl/settings.py:93
|
#: tixl/settings.py:105
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr "Englisch"
|
msgstr "Englisch"
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ msgstr "Englisch"
|
|||||||
msgid "The two password fields didn't match."
|
msgid "The two password fields didn't match."
|
||||||
msgstr "Die beiden eingegebenen Passwörter stimmen nicht überein."
|
msgstr "Die beiden eingegebenen Passwörter stimmen nicht überein."
|
||||||
|
|
||||||
#: tixlbase/admin.py:18 tixlcontrol/views/auth.py:14
|
#: tixlbase/admin.py:18 tixlcontrol/views/auth.py:15
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr "Passwort"
|
msgstr "Passwort"
|
||||||
|
|
||||||
@@ -70,15 +70,27 @@ msgstr "Nachname"
|
|||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr "Anmelden"
|
msgstr "Anmelden"
|
||||||
|
|
||||||
#: tixlcontrol/views/auth.py:13
|
#: tixlcontrol/templates/tixlcontrol/base.html:17
|
||||||
|
msgid "Toggle navigation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: tixlcontrol/templates/tixlcontrol/base.html:26
|
||||||
|
msgid "Dashboard"
|
||||||
|
msgstr "Übersicht"
|
||||||
|
|
||||||
|
#: tixlcontrol/templates/tixlcontrol/base.html:30
|
||||||
|
msgid "Log out"
|
||||||
|
msgstr "Abmelden"
|
||||||
|
|
||||||
|
#: tixlcontrol/views/auth.py:14
|
||||||
msgid "E-mail address"
|
msgid "E-mail address"
|
||||||
msgstr "E-Mail-Adresse"
|
msgstr "E-Mail-Adresse"
|
||||||
|
|
||||||
#: tixlcontrol/views/auth.py:18
|
#: tixlcontrol/views/auth.py:19
|
||||||
msgid "Please enter a correct e-mail address and password."
|
msgid "Please enter a correct e-mail address and password."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Bitte geben Sie eine gültige Kombination aus E-Mail-Adresse und Passwort ein."
|
"Bitte geben Sie eine gültige Kombination aus E-Mail-Adresse und Passwort ein."
|
||||||
|
|
||||||
#: tixlcontrol/views/auth.py:19
|
#: tixlcontrol/views/auth.py:20
|
||||||
msgid "This account is inactive."
|
msgid "This account is inactive."
|
||||||
msgstr "Dieses Konto ist deaktiviert."
|
msgstr "Dieses Konto ist deaktiviert."
|
||||||
|
|||||||
@@ -55,6 +55,18 @@ MIDDLEWARE_CLASSES = (
|
|||||||
'tixlcontrol.middleware.LoginRequiredMiddleware',
|
'tixlcontrol.middleware.LoginRequiredMiddleware',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||||
|
"django.contrib.auth.context_processors.auth",
|
||||||
|
"django.core.context_processors.debug",
|
||||||
|
"django.core.context_processors.i18n",
|
||||||
|
"django.core.context_processors.media",
|
||||||
|
"django.core.context_processors.request",
|
||||||
|
"django.core.context_processors.static",
|
||||||
|
"django.core.context_processors.tz",
|
||||||
|
"django.contrib.messages.context_processors.messages",
|
||||||
|
'tixlcontrol.context.contextprocessor',
|
||||||
|
)
|
||||||
|
|
||||||
ROOT_URLCONF = 'tixl.urls'
|
ROOT_URLCONF = 'tixl.urls'
|
||||||
|
|
||||||
WSGI_APPLICATION = 'tixl.wsgi.application'
|
WSGI_APPLICATION = 'tixl.wsgi.application'
|
||||||
@@ -114,7 +126,7 @@ STATICFILES_FINDERS = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
COMPRESS_PRECOMPILERS = (
|
COMPRESS_PRECOMPILERS = (
|
||||||
('text/less', 'lessc {infile} {outfile}'),
|
('text/less', 'helpers.lessabsolutefilter.LessFilter'),
|
||||||
)
|
)
|
||||||
|
|
||||||
COMPRESS_CSS_FILTERS = (
|
COMPRESS_CSS_FILTERS = (
|
||||||
@@ -122,6 +134,10 @@ COMPRESS_CSS_FILTERS = (
|
|||||||
'compressor.filters.cssmin.CSSMinFilter',
|
'compressor.filters.cssmin.CSSMinFilter',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Tixl specific settings
|
||||||
|
|
||||||
|
TIXL_INSTANCE_NAME = 'tixl.de'
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from local_settings import *
|
from local_settings import *
|
||||||
|
|||||||
61
src/tixlbase/migrations/0004_auto_20140911_2037.py
Normal file
61
src/tixlbase/migrations/0004_auto_20140911_2037.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
from django.conf import settings
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('tixlbase', '0003_auto_20140910_1649'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OrganizerPermission',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)),
|
||||||
|
('can_create_events', models.BooleanField(default=True)),
|
||||||
|
('organizer', models.ForeignKey(to='tixlbase.Organizer', related_name='perms')),
|
||||||
|
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='organizer_perms')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='organizerpermission',
|
||||||
|
unique_together=set([('organizer', 'user')]),
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='organizer',
|
||||||
|
name='owner',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='event',
|
||||||
|
name='organizer',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='events', to='tixlbase.Organizer'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='email',
|
||||||
|
field=models.EmailField(null=True, blank=True, db_index=True, verbose_name='E-mail', max_length=75),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='event',
|
||||||
|
field=models.ForeignKey(null=True, blank=True, on_delete=django.db.models.deletion.PROTECT, related_name='users', to='tixlbase.Event'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='familyname',
|
||||||
|
field=models.CharField(null=True, blank=True, verbose_name='Family name', max_length=255),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='givenname',
|
||||||
|
field=models.CharField(null=True, blank=True, verbose_name='Given name', max_length=255),
|
||||||
|
),
|
||||||
|
]
|
||||||
48
src/tixlbase/migrations/0005_auto_20140911_2052.py
Normal file
48
src/tixlbase/migrations/0005_auto_20140911_2052.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models, migrations
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('tixlbase', '0004_auto_20140911_2037'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='EventPermission',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')),
|
||||||
|
('can_change_settings', models.BooleanField(default=True)),
|
||||||
|
('organizer', models.ForeignKey(to='tixlbase.Event')),
|
||||||
|
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_perms')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='eventpermission',
|
||||||
|
unique_together=set([('organizer', 'user')]),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='event',
|
||||||
|
name='permitted',
|
||||||
|
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, related_name='events', through='tixlbase.EventPermission'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='organizer',
|
||||||
|
name='permitted',
|
||||||
|
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, related_name='organizers', through='tixlbase.OrganizerPermission'),
|
||||||
|
preserve_default=True,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='organizerpermission',
|
||||||
|
name='organizer',
|
||||||
|
field=models.ForeignKey(to='tixlbase.Organizer'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -124,24 +124,35 @@ class User(AbstractBaseUser, PermissionsMixin):
|
|||||||
|
|
||||||
class Organizer(models.Model):
|
class Organizer(models.Model):
|
||||||
"""
|
"""
|
||||||
This model represents an entity organizing events, like a company,
|
This model represents an entity organizing events, like a company.
|
||||||
an organization or a person. It has one user as owner (who has
|
Any organizer has a unique slug, which is a short name (alphanumeric,
|
||||||
registered it) and can have any number of users with admin
|
all lowercase) being used in URLs.
|
||||||
authorization. Any organizer has a unique slug, which is a short
|
|
||||||
name (alphanumeric, all lowercase) being used in URLs.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
slug = models.CharField(max_length=50,
|
slug = models.CharField(max_length=50,
|
||||||
unique=True,
|
unique=True, db_index=True)
|
||||||
db_index=True)
|
permitted = models.ManyToManyField(User, through='OrganizerPermission',
|
||||||
owner = models.ForeignKey(User, null=True, blank=True,
|
related_name="organizers")
|
||||||
on_delete=models.PROTECT)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ("name",)
|
ordering = ("name",)
|
||||||
|
|
||||||
|
|
||||||
|
class OrganizerPermission(models.Model):
|
||||||
|
"""
|
||||||
|
The relation between an Organizer and an User who has permissions to
|
||||||
|
access an organizer profile.
|
||||||
|
"""
|
||||||
|
|
||||||
|
organizer = models.ForeignKey(Organizer)
|
||||||
|
user = models.ForeignKey(User, related_name="organizer_perms")
|
||||||
|
can_create_events = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = (("organizer", "user"),)
|
||||||
|
|
||||||
|
|
||||||
class Event(models.Model):
|
class Event(models.Model):
|
||||||
"""
|
"""
|
||||||
This model represents an event. An event is anything you can buy
|
This model represents an event. An event is anything you can buy
|
||||||
@@ -171,8 +182,9 @@ class Event(models.Model):
|
|||||||
organizer = models.ForeignKey(Organizer, related_name="events",
|
organizer = models.ForeignKey(Organizer, related_name="events",
|
||||||
on_delete=models.PROTECT)
|
on_delete=models.PROTECT)
|
||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
slug = models.CharField(max_length=50,
|
slug = models.CharField(max_length=50, db_index=True)
|
||||||
db_index=True)
|
permitted = models.ManyToManyField(User, through='EventPermission',
|
||||||
|
related_name="events")
|
||||||
locale = models.CharField(max_length=10)
|
locale = models.CharField(max_length=10)
|
||||||
currency = models.CharField(max_length=10)
|
currency = models.CharField(max_length=10)
|
||||||
date_from = models.DateTimeField()
|
date_from = models.DateTimeField()
|
||||||
@@ -187,3 +199,17 @@ class Event(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
unique_together = (("organizer", "slug"),)
|
unique_together = (("organizer", "slug"),)
|
||||||
ordering = ("date_from", "name")
|
ordering = ("date_from", "name")
|
||||||
|
|
||||||
|
|
||||||
|
class EventPermission(models.Model):
|
||||||
|
"""
|
||||||
|
The relation between an Event and an User who has permissions to
|
||||||
|
access an event.
|
||||||
|
"""
|
||||||
|
|
||||||
|
organizer = models.ForeignKey(Event)
|
||||||
|
user = models.ForeignKey(User, related_name="event_perms")
|
||||||
|
can_change_settings = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = (("organizer", "user"),)
|
||||||
|
|||||||
Submodule src/tixlbase/static/bootstrap updated: 97027a2f6f...c068162161
1
src/tixlbase/static/fontawesome
Submodule
1
src/tixlbase/static/fontawesome
Submodule
Submodule src/tixlbase/static/fontawesome added at a65bd93d81
7
src/tixlcontrol/context.py
Normal file
7
src/tixlcontrol/context.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
def contextprocessor(request):
|
||||||
|
return {
|
||||||
|
'settings': settings,
|
||||||
|
}
|
||||||
3
src/tixlcontrol/static/tixlcontrol/less/main.less
Normal file
3
src/tixlcontrol/static/tixlcontrol/less/main.less
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
@import "../../../../tixlbase/static/bootstrap/less/bootstrap.less";
|
||||||
|
@import "../../../../tixlbase/static/fontawesome/less/font-awesome.less";
|
||||||
|
@fa-font-path: "../../fontawesome/fonts";
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title></title>
|
<title>{{ settings.TIXL_INSTANCE_NAME }}</title>
|
||||||
{% compress css %}
|
{% compress css %}
|
||||||
<link rel="stylesheet" type="text/less" href="{% static "tixlcontrol/less/auth.less" %}" />
|
<link rel="stylesheet" type="text/less" href="{% static "tixlcontrol/less/auth.less" %}" />
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
|
|||||||
40
src/tixlcontrol/templates/tixlcontrol/base.html
Normal file
40
src/tixlcontrol/templates/tixlcontrol/base.html
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{% load compress %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load i18n %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{{ settings.TIXL_INSTANCE_NAME }}</title>
|
||||||
|
{% compress css %}
|
||||||
|
<link rel="stylesheet" type="text/less" href="{% static "tixlcontrol/less/main.less" %}" />
|
||||||
|
{% endcompress %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="navbar navbar-default navbar-static-top" role="navigation">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
|
||||||
|
<span class="sr-only">{% trans "Toggle navigation" %}</span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
</button>
|
||||||
|
<a class="navbar-brand" href="#">{{ settings.TIXL_INSTANCE_NAME }}</a>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-collapse collapse">
|
||||||
|
<ul class="nav navbar-nav">
|
||||||
|
<li class="active"><a href="{% url 'control:index' %}">{% trans "Dashboard" %}</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul class="nav navbar-nav navbar-right">
|
||||||
|
<li><a href="#"><i class="fa fa-user"></i> {{ request.user.get_full_name }}</a></li>
|
||||||
|
<li><a href="{% url 'control:auth.logout' %}" title="{% trans "Log out" %}"><i class="fa fa-sign-out"></i></a></li>
|
||||||
|
</ul>
|
||||||
|
</div><!--/.nav-collapse -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -2,5 +2,6 @@ from django.conf.urls import patterns, url
|
|||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^$', 'tixlcontrol.views.main.index', name='index'),
|
url(r'^$', 'tixlcontrol.views.main.index', name='index'),
|
||||||
|
url(r'^logout$', 'tixlcontrol.views.auth.logout', name='auth.logout'),
|
||||||
url(r'^login$', 'tixlcontrol.views.auth.login', name='auth.login'),
|
url(r'^login$', 'tixlcontrol.views.auth.login', name='auth.login'),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from django import forms
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
from django.contrib.auth import login as auth_login
|
from django.contrib.auth import login as auth_login
|
||||||
|
from django.contrib.auth import logout as auth_logout
|
||||||
|
|
||||||
|
|
||||||
class AuthenticationForm(BaseAuthenticationForm):
|
class AuthenticationForm(BaseAuthenticationForm):
|
||||||
@@ -64,3 +65,8 @@ def login(request):
|
|||||||
form = AuthenticationForm()
|
form = AuthenticationForm()
|
||||||
ctx['form'] = form
|
ctx['form'] = form
|
||||||
return render(request, 'tixlcontrol/auth/login.html', ctx)
|
return render(request, 'tixlcontrol/auth/login.html', ctx)
|
||||||
|
|
||||||
|
|
||||||
|
def logout(request):
|
||||||
|
auth_logout(request)
|
||||||
|
return redirect('control:auth.login')
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
return HttpResponse('Coming soon.')
|
return render(request, 'tixlcontrol/base.html', {})
|
||||||
|
|||||||
Reference in New Issue
Block a user