Changeset - 252697b842c0
[Not reviewed]
! ! !
Joel Addison - 4 years ago 2020-11-22 13:58:14
joel@addison.net.au
Update to Django 2.2

Upgrade site and modules to Django 2.2. Remove and replace obsolete
functionality with current equivalents. Update requirements to latest
versions where possible. Remove unused dependencies.
65 files changed:
Changeset was too big and was cut off... Show full diff anyway
0 comments (0 inline, 0 general)
constraints.txt
Show inline comments
 
django<1.12,>=1.11
 
pysaml2==4.8.0
 
django<3.0,>=2.2
 
pysaml2>=5.3.0
docker/Dockerfile
Show inline comments
 
FROM python:3.6-stretch as symposion_base
 
FROM python:3.8-buster as symposion_base
 

	
 
RUN set -ex \
 
    && apt-get update
 
RUN set -ex \
 
    && buildDeps=' \
 
        libffi-dev \
 
        libfreetype6-dev \
 
        libjpeg-dev \
 
        libwebp-dev \
 
        libpng-dev \
 
        liblcms2-dev \
 
        zlib1g-dev \
 
        libmemcached-dev \
 
        libsasl2-dev \
 
        inkscape \
 
    ' \
 
    && apt-get install -y git xmlsec1 \
 
    && apt-get install -y $buildDeps --no-install-recommends \
 
    && rm -rf /var/lib/apt/lists/*
 

	
 
RUN set -ex \
 
    && pip install uwsgi
 

	
 
COPY constraints.txt requirements.txt /reqs/
 

	
 
RUN set -ex \
 
    && pip install -U pip \
 
    && pip install --no-cache-dir -r /reqs/requirements.txt -c /reqs/constraints.txt \
 
    && apt-get purge -y --auto-remove $buildDeps \
 
    && rm -rf /usr/src/python ~/.cache
 

	
 
COPY . /app/symposion_app
 

	
 
WORKDIR /app/symposion_app
 
RUN set -x \
 
    && pip install -r vendored_requirements.txt -c /reqs/constraints.txt
 
RUN set -x \
 
    && DJANGO_SECRET_KEY=1234 STRIPE_PUBLIC_KEY=1234 STRIPE_SECRET_KEY=1234 \
 
       DATABASE_URL="sqlite:////dev/null" python manage.py compilescss
 
RUN set -x \
 
    && DJANGO_SECRET_KEY=1234 STRIPE_PUBLIC_KEY=1234 STRIPE_SECRET_KEY=1234 \
 
       DATABASE_URL="sqlite:////dev/null" \
 
       python manage.py collectstatic --noinput -l -v 0
 
RUN set -ex \
 
    && cp static/build/fonts/*.ttf /usr/local/share/fonts/ \
 
    && fc-cache \
 
    && fc-list
 

	
 
FROM symposion_base as symposion_dev
 
VOLUME /app/symposion_app
 
CMD ["./manage.py", "runserver", "-v3", "0.0.0.0:8000"]
 

	
 
FROM symposion_base as symposion_prod
 
CMD ["/usr/local/bin/uwsgi", "--http-socket", "0.0.0.0:8000", "-b", "8192", "--wsgi-file", "pinaxcon/wsgi.py"]
docker/laptop-mode-env
Show inline comments
 
DJANGO_SECRET_KEY=5CEA51A5-A613-4AEF-A9FB-D0A57D77C13B
 
STRIPE_PUBLIC_KEY=5CEA51A5-A613-4AEF-A9FB-D0A57D77C13B
 
STRIPE_SECRET_KEY=5CEA51A5-A613-4AEF-A9FB-D0A57D77C13B
 
GCS_BUCKET=5CEA51A5-A613-4AEF-A9FB-D0A57D77C13B
 
GOOGLE_APPLICATION_CREDENTIALS=/dev/null
 
DATABASE_URL=sqlite:////tmp/symposion.sqlite
 
SYMPOSION_DEV_MODE=LAPTOP
 
SYMPOSION_APP_DEBUG=1
...
 
\ No newline at end of file
 
SYMPOSION_APP_DEBUG=1
pinaxcon/devmode_settings.py
Show inline comments
 
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 
AUTHENTICATION_BACKENDS = [
 
    'symposion.teams.backends.TeamPermissionsBackend',
 
    'django.contrib.auth.backends.ModelBackend',
 
]
 
LOGIN_URL='/accounts/login'
 

	
 
ROOT_URLCONF = "pinaxcon.devmode_urls"
 

	
 
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
 

	
 
INTERNAL_IPS = ['*']
pinaxcon/devmode_urls.py
Show inline comments
 
from django.conf.urls import include, url
 
from django.contrib.auth.views import login, logout
 
    
 
from django.contrib.auth.views import LoginView, LogoutView
 
from django.urls import include, path
 

	
 
from pinaxcon import urls
 

	
 
urlpatterns = [
 
    url(r'^accounts/logout', logout, {'template_name': 'admin/logout.html'}),
 
    url(r'^accounts/login', login, {'template_name': 'admin/login.html'}),
 
    path('accounts/logout', LogoutView.as_view(template_name='admin/logout.html')),
 
    path('accounts/login', LoginView.as_view(template_name='admin/login.html')),
 
]
 

	
 
urlpatterns += urls.urlpatterns
pinaxcon/monkey_patch.py
Show inline comments
 
from functools import wraps
 

	
 

	
 
class MonkeyPatchMiddleware(object):
 
    ''' Ensures that our monkey patching only gets called after it is safe to do so.'''
 

	
 
    def process_request(self, request):
 
    def __init__(self, get_response):
 
        self.get_response = get_response
 

	
 
    def __call__(self, request):
 
        do_monkey_patch()
 
        response = self.get_response(request)
 
        return response
 

	
 

	
 
def do_monkey_patch():
 
    patch_stripe_payment_form()
 

	
 
    # Remove this function from existence
 
    global do_monkey_patch
 
    do_monkey_patch = lambda: None  # noqa: E731
 

	
 

	
 
def patch_stripe_payment_form():  # noqa: C901
 

	
 
    import inspect  # Oh no.
 
    from django.http.request import HttpRequest
 
    from registripe.forms import CreditCardForm
 
    from pinaxcon.registrasion import models
 

	
 
    old_init = CreditCardForm.__init__
 

	
 
    @wraps(old_init)
 
    def new_init(self, *a, **k):
 

	
 
        # Map the names from our attendee profile model
 
        # To the values expected in the Stripe card model
 
        mappings = (
 
            ("address_line_1", "address_line1"),
 
            ("address_line_2", "address_line2"),
 
            ("address_suburb", "address_city"),
 
            ("address_postcode", "address_zip"),
 
            ("state", "address_state"),
 
            ("country", "address_country"),
 
        )
 

	
 
        initial = "initial"
 
        if initial not in k:
 
            k[initial] = {}
 
        initial = k[initial]
 

	
 
        # Find request context maybe?
 
        frame = inspect.currentframe()
 
        attendee_profile = None
 
        if frame:
 
            context = frame.f_back.f_locals
 
            for name, value in (context.items() or {}):
 
                if not isinstance(value, HttpRequest):
 
                    continue
 
                user = value.user
 
                if not user.is_authenticated():
 
                if not user.is_authenticated:
 
                    break
 
                try:
 
                    attendee_profile = models.AttendeeProfile.objects.get(
 
                        attendee__user=user
 
                    )
 
                except models.AttendeeProfile.DoesNotExist:
 
                    # Profile is still none.
 
                    pass
 
                break
 

	
 
        if attendee_profile:
 
            for us, stripe in mappings:
 
                i = getattr(attendee_profile, us, None)
 
                if i:
 
                    initial[stripe] = i
 

	
 
        old_init(self, *a, **k)
 

	
 
    CreditCardForm.__init__ = new_init
pinaxcon/raffle/models.py
Show inline comments
 
from django.conf import settings
 
from django.db import models
 

	
 
from pinaxcon.raffle.mixins import PrizeMixin, RaffleMixin
 

	
 

	
 
class Raffle(RaffleMixin, models.Model):
 
    """
 
    Stores a single Raffle object, related to one or many
 
    :model:`pinaxcon_registrasion.Product`, which  is usually a raffle ticket,
 
    but can be set to tickets or other products for door prizes.
 
    """
 
    description = models.CharField(max_length=255)
 
    products = models.ManyToManyField('registrasion.Product')
 
    hidden = models.BooleanField(default=True)
 

	
 
    def __str__(self):
 
        return self.description
 

	
 

	
 
class Prize(PrizeMixin, models.Model):
 
    """
 
    Stores a Prize for a given :model:`pinaxcon_raffle.Raffle`.
 

	
 
    Once `winning_ticket` has been set to a :model:`pinaxcon_raffle.DrawnTicket`
 
    object, no further changes are permitted unless the object is explicitely
 
    unlocked.
 
    """
 
    description = models.CharField(max_length=255)
 
    raffle = models.ForeignKey('pinaxcon_raffle.Raffle', related_name='prizes')
 
    raffle = models.ForeignKey(
 
        'pinaxcon_raffle.Raffle',
 
        related_name='prizes',
 
        on_delete=models.CASCADE,
 
    )
 
    order = models.PositiveIntegerField()
 
    winning_ticket = models.OneToOneField(
 
        'pinaxcon_raffle.DrawnTicket', null=True,
 
        blank=True, related_name='+', on_delete=models.PROTECT
 
        'pinaxcon_raffle.DrawnTicket',
 
        null=True,
 
        blank=True,
 
        related_name='+',
 
        on_delete=models.PROTECT,
 
    )
 

	
 
    class Meta:
 
        unique_together = ('raffle', 'order')
 

	
 
    def __str__(self):
 
        return f"{self.order}. Prize: {self.description}"
 

	
 

	
 
class PrizeAudit(models.Model):
 
    """
 
    Stores an audit event for changes to a particular :model:`pinaxcon_raffle.Prize`.
 
    """
 
    reason = models.CharField(max_length=255)
 
    prize = models.ForeignKey('pinaxcon_raffle.Prize', related_name='audit_events')
 
    prize = models.ForeignKey(
 
        'pinaxcon_raffle.Prize',
 
        related_name='audit_events',
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    user = models.ForeignKey('auth.User')
 
    user = models.ForeignKey(
 
        settings.AUTH_USER_MODEL,
 
        on_delete=models.CASCADE,
 
    )
 
    timestamp = models.DateTimeField(auto_now_add=True)
 

	
 
    class Meta:
 
        ordering = ('-timestamp',)
 

	
 
    def __str__(self):
 
        return self.reason
 

	
 

	
 
class Draw(models.Model):
 
    """
 
    Stores a draw for a given :model:`pinaxcon_raffle.Raffle`, along with audit fields
 
    for the creating :model:`auth.User` and the creation timestamp.
 
    """
 
    raffle = models.ForeignKey('pinaxcon_raffle.Raffle', related_name='draws')
 
    drawn_by = models.ForeignKey('auth.User')
 
    raffle = models.ForeignKey(
 
        'pinaxcon_raffle.Raffle',
 
        related_name='draws',
 
        on_delete=models.CASCADE,
 
    )
 
    drawn_by = models.ForeignKey(
 
        settings.AUTH_USER_MODEL,
 
        on_delete=models.CASCADE,
 
    )
 
    drawn_time = models.DateTimeField(auto_now_add=True)
 

	
 
    def __str__(self):
 
        return f"{self.raffle}: {self.drawn_time}"
 

	
 

	
 
class DrawnTicket(models.Model):
 
    """
 
    Stores the result of a ticket draw, along with the corresponding
 
    :model:`pinaxcon_raffle.Draw`, :model:`pinaxcon_raffle.Prize` and the
 
    :model:`registrasion.commerce.LineItem` from which it was generated.
 
    """
 
    ticket = models.CharField(max_length=255)
 

	
 
    draw = models.ForeignKey('pinaxcon_raffle.Draw')
 
    prize = models.ForeignKey('pinaxcon_raffle.Prize')
 
    lineitem = models.ForeignKey('registrasion.LineItem')
 
    draw = models.ForeignKey(
 
        'pinaxcon_raffle.Draw',
 
        on_delete=models.CASCADE,
 
    )
 
    prize = models.ForeignKey(
 
        'pinaxcon_raffle.Prize',
 
        on_delete=models.CASCADE,
 
    )
 
    lineitem = models.ForeignKey(
 
        'registrasion.LineItem',
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    def __str__(self):
 
        return f"{self.ticket}: {self.draw.raffle}"
...
 
\ No newline at end of file
 
        return f"{self.ticket}: {self.draw.raffle}"
pinaxcon/registrasion/management/commands/dummy_presentations.py
Show inline comments
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
from django.core.management.base import BaseCommand
 

	
 
from symposion.conference.models import Section, current_conference
 

	
 
from symposion.speakers.models import Speaker
 
from symposion.schedule.models import Presentation
 
from symposion.proposals.models import ProposalKind
 
from pinaxcon.proposals.models import TalkProposal
 

	
 
User = get_user_model()
 

	
 

	
 
class Command(BaseCommand):
 

	
 
    help = "Creates a bunch of dummy presentations to play around with."
 

	
 
    def handle(self, *args, **options):
 
        conf = current_conference()
 
        section = Section.objects.filter(conference=conf, slug="main").all().first()
 

	
 
        user = User.objects.first()
 
        speaker = Speaker.objects.first()
 
        if not speaker:
 
            speaker, _ = Speaker.objects.get_or_create(name="Dummy Speaker",
 
                                                       defaults={"user": user})
 
        talk_kind = ProposalKind.objects.first()
 
        target_audience = TalkProposal.TARGET_USER
 

	
 
        for i in range(1000, 1020):
 
            prop, _created = TalkProposal.objects.get_or_create(
 
                pk=i, kind=talk_kind, speaker=speaker, target_audience=target_audience,
 
                title=f"dummy title {i}", abstract=f"dummy abstract {i}")
 

	
 
            pres, _created = Presentation.objects.get_or_create(
 
                proposal_base=prop, section=section, speaker=speaker,
 
                title=f"dummy title {i}", abstract=f"dummy abstract {i}")
pinaxcon/settings.py
Show inline comments
 
from decimal import Decimal
 
import os
 
import sys
 

	
 
import django
 
import dj_database_url
 
import saml2
 
import saml2.saml
 

	
 
from datetime import date, datetime, timedelta
 
import pytz
 

	
 
from dataclasses import dataclass
 

	
 
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
 
PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__))
 
DJANGO_ROOT = os.path.abspath(os.path.dirname(django.__file__))
 
BASE_DIR = PACKAGE_ROOT
 
sys.path.append(os.path.join(PROJECT_ROOT, 'vendor'))
 

	
 

	
 
### USER SETTINGS
 
DEV_MODE = os.environ.get("SYMPOSION_DEV_MODE", None)
 
DEBUG = os.environ.get('SYMPOSION_APP_DEBUG', '0')
 
if isinstance(DEBUG, str):
 
    try:
 
        i = int(DEBUG)
 
        if not i in [0, 1]:
 
            raise ValueError("not 0 or 1")
 
        DEBUG = bool(i)
 
    except ValueError:
 
        sys.exit('DEBUG env var must be set to string value of a 0 or 1')
 
else:
 
    sys.exit('DEBUG env var is in unexpected format.  Should be a string'
 
             'containing either a 0 or a 1 - Got type %s' % type(DEBUG))
 

	
 
DATABASES = {}
 
DATABASES['default'] = dj_database_url.config(conn_max_age=600)
 
if DATABASES['default']['ENGINE'] == 'django.db.backends.mysql':
 
    DATABASES['default']['OPTIONS'] = {'charset': 'utf8mb4'}
 

	
 
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
 
EMAIL_HOST = os.environ.get('EMAIL_HOST', None)
 
EMAIL_PORT = os.environ.get('EMAIL_PORT', 25)
 
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER', None)
 
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD', None)
 
DEFAULT_FROM_EMAIL = os.environ.get('DEFAULT_FROM_EMAIL', 'webmaster@localhost')
 
EMAIL_USE_SSL = False
 
EMAIL_USE_TLS = False
 
_EMAIL_SSL_FLAVOR=os.environ.get('EMAIL_SSL_FLAVOR', None)
 
if _EMAIL_SSL_FLAVOR == "TLS":
 
    EMAIL_USE_TLS = True
 
elif _EMAIL_SSL_FLAVOR == "SSL":
 
    EMAIL_USE_SSL = True
 

	
 
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', None)
 

	
 
PINAX_STRIPE_PUBLIC_KEY = os.environ.get('STRIPE_PUBLIC_KEY', None)
 
PINAX_STRIPE_SECRET_KEY = os.environ.get('STRIPE_SECRET_KEY', None)
 
PINAX_STRIPE_SEND_EMAIL_RECEIPTS = False
 

	
 
ANALYTICS_KEY = os.environ.get('ANALYTICS_KEY', None)
 

	
 
saml2_entityid = os.environ.get('SAML2_ENTITYID', None)
 
saml2_sp_name = os.environ.get('SAML2_SP_NAME', None)
 
saml2_sp_assertion_service = os.environ.get('SAML2_SP_ASSERTION_SERVICE', None)
 
saml2_sp_slo_rdir = os.environ.get('SAML2_SP_SLO_RDIR', None)
 
saml2_sp_slo_post = os.environ.get('SAML2_SP_SLO_POST', None)
 

	
 
saml2_idp_metadata = {
 
    'local': [os.environ.get('SAML2_IDP_METADATA_FILE', None)],
 
    }
 
saml2_signing_key = os.environ.get('SAML2_SIGNING_KEY', None)
 
saml2_signing_crt = os.environ.get('SAML2_SIGNING_CRT', None)
 
saml2_encr_key = os.environ.get('SAML2_ENCRYPTION_KEY', None)
 
saml2_encr_crt = os.environ.get('SAML2_ENCRYPTION_CRT', None)
 
saml2_contact = {
 
    'given_name': os.environ.get("META_GIVEN_NAME", 'Bastard'),
 
    'sur_name': os.environ.get('META_FAM_NAME', 'Operator'),
 
    'company': os.environ.get('META_COMPANY', 'Corp1'),
 
    'email_address': os.environ.get('META_EMAIL', 'op@example.com'),
 
    'contact_type': 'technical'},
 

	
 
fail = False
 

	
 
BADGER_DEFAULT_SVG = 'registrasion/badge.svg'
 
BADGER_DEFAULT_FORM = "registrasion/badge_form.html"
 

	
 
if SECRET_KEY is None:
 
    print("FAILURE: You need to supply a DJANGO_SECRET_KEY "
 
          "environment variable")
 
    fail = True
 

	
 
if PINAX_STRIPE_PUBLIC_KEY is None:
 
    print("FAILURE: You need to supply a STRIPE_PUBLIC_KEY "
 
          "environment variable")
 
    fail = True
 

	
 
if PINAX_STRIPE_SECRET_KEY is None:
 
    print("FAILURE: You need to supply a STRIPE_SECRET_KEY "
 
          "environment variable")
 
    fail = True
 

	
 
if fail:
 
    sys.exit('FAILURE: Missing environment variables.')
 

	
 
### Standard settings
 

	
 
ADMIN_USERNAMES = []
 

	
 
if DEV_MODE and DEV_MODE == "LAPTOP":
 
    CACHES = {
 
        'default': {
 
            'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
 
        }
 
    }
 
else:
 
    CACHES = {
 
        'default': {
 
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
 
            'LOCATION': 'unique-snowflake',
 
        }
 
    }
 

	
 

	
 
ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '*']
 

	
 
TIME_ZONE = "Australia/Brisbane"
 
TIME_ZONE = "Australia/Melbourne"
 
DATE_FORMAT = "j F Y"
 
LANGUAGE_CODE = "en-au"
 

	
 
SITE_ID = int(os.environ.get("SITE_ID", 1))
 
USE_I18N = True
 
USE_L10N = True
 
USE_TZ = True
 

	
 
MEDIA_ROOT = os.environ.get("MEDIA_ROOT", os.path.join(PACKAGE_ROOT, "site_media", "media"))
 
MEDIA_URL = "/site_media/media/"
 

	
 
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static', 'build')
 
STATIC_URL = '/static/build/'
 

	
 
STATICFILES_DIRS = [
 
    os.path.join(PROJECT_ROOT, 'static', 'src'),
 
]
 

	
 
STATICFILES_FINDERS = [
 
    "django.contrib.staticfiles.finders.FileSystemFinder",
 
    "django.contrib.staticfiles.finders.AppDirectoriesFinder",
 
    "sass_processor.finders.CssFinder",
 
]
 

	
 
TEMPLATES = [
 
    {
 
        "BACKEND": "django.template.backends.django.DjangoTemplates",
 
        "DIRS": [
 
            os.path.join(PACKAGE_ROOT, "templates"),
 
            os.path.join(DJANGO_ROOT, 'forms/templates')
 
        ],
 
        "APP_DIRS": True,
 
        "OPTIONS": {
 
            "debug": DEBUG,
 
            "context_processors": [
 
                "django.contrib.auth.context_processors.auth",
 
                "django.template.context_processors.debug",
 
                "django.template.context_processors.i18n",
 
                "django.template.context_processors.media",
 
                "django.template.context_processors.static",
 
                "django.template.context_processors.tz",
 
                "django.template.context_processors.request",
 
                "django.contrib.messages.context_processors.messages",
 
                "pinax_theme_bootstrap.context_processors.theme",
 
                "symposion.reviews.context_processors.reviews",
 
                "django_settings_export.settings_export",
 
            ],
 
        },
 
    },
 
]
 

	
 
MIDDLEWARE_CLASSES = [
 
MIDDLEWARE = [
 
    "whitenoise.middleware.WhiteNoiseMiddleware",
 
    "django.contrib.sessions.middleware.SessionMiddleware",
 
    "django.middleware.common.CommonMiddleware",
 
    "django.middleware.csrf.CsrfViewMiddleware",
 
    "django.contrib.auth.middleware.AuthenticationMiddleware",
 
    "django.contrib.auth.middleware.SessionAuthenticationMiddleware",
 
    "django.contrib.messages.middleware.MessageMiddleware",
 
    "debug_toolbar.middleware.DebugToolbarMiddleware",
 
    "reversion.middleware.RevisionMiddleware",
 
    "waffle.middleware.WaffleMiddleware",
 
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
 
    "django.contrib.flatpages.middleware.FlatpageFallbackMiddleware",
 
    'pinaxcon.monkey_patch.MonkeyPatchMiddleware',
 
]
 

	
 
if DEV_MODE and DEV_MODE == "LAPTOP":
 
    ROOT_URLCONF = "pinaxcon.devmode_urls"
 
else:
 
    ROOT_URLCONF = "pinaxcon.urls"
 

	
 
# Python dotted path to the WSGI application used by Django's runserver.
 
WSGI_APPLICATION = "pinaxcon.wsgi.application"
 

	
 
INSTALLED_APPS = [
 
    "whitenoise.runserver_nostatic",
 
    "django.contrib.admin",
 
    "django.contrib.auth",
 
    "django.contrib.contenttypes",
 
    "django.contrib.flatpages",
 
    "django.contrib.messages",
 
    "django.contrib.sessions",
 
    "django.contrib.sites",
 
    "django.contrib.staticfiles",
 
    "django.contrib.humanize",
 
    "debug_toolbar",
 

	
 
    'djangosaml2',
 

	
 
    # theme
 
    "bootstrapform",
 
    "pinax_theme_bootstrap",
 
    "sass_processor",
 
    "capture_tag",
 

	
 
    # external
 
    "easy_thumbnails",
 
    "taggit",
 
    "reversion",
 
    "sitetree",
 
    "django_jsonfield_backport",
 
    "pinax.eventlog",
 

	
 
    # symposion
 
    "symposion",
 
    "symposion.conference",
 
    "symposion.proposals",
 
    "symposion.reviews",
 
    "symposion.schedule",
 
    "symposion.speakers",
 
    "symposion.teams",
 

	
 
    # Registrasion
 
    "registrasion",
 

	
 
    # Registrasion-stripe
 
    "pinax.stripe",
 
    "django_countries",
 
    "registripe",
 

	
 
    #registrasion-desk
 
    "regidesk",
 

	
 
    # admin - required by registrasion ??
 
    "nested_admin",
 

	
 
    # project
 
    "pinaxcon",
 
    "pinaxcon.proposals",
 
    "pinaxcon.registrasion",
 
    "pinaxcon.raffle",
 
    "jquery",
 
    "djangoformsetjs",
 

	
 
    # testing and rollout
 
    "django_nose",
 
    "waffle",
 

	
 
    "crispy_forms",
 
]
 

	
 
CRISPY_TEMPLATE_PACK = "bootstrap4"
 

	
 
DEBUG_TOOLBAR_PANELS = [
 
    'debug_toolbar.panels.versions.VersionsPanel',
 
    'debug_toolbar.panels.timer.TimerPanel',
 
    'debug_toolbar.panels.settings.SettingsPanel',
 
    'debug_toolbar.panels.headers.HeadersPanel',
 
    'debug_toolbar.panels.request.RequestPanel',
 
    'debug_toolbar.panels.sql.SQLPanel',
 
    'debug_toolbar.panels.staticfiles.StaticFilesPanel',
 
    'debug_toolbar.panels.cache.CachePanel',
 
    'debug_toolbar.panels.signals.SignalsPanel',
 
    'debug_toolbar.panels.logging.LoggingPanel',
 
    'debug_toolbar.panels.templates.TemplatesPanel',
 
    'debug_toolbar.panels.redirects.RedirectsPanel',
 
]
 

	
 
DEBUG_TOOLBAR_CONFIG = {
 
    'INTERCEPT_REDIRECTS': False,
 
    'SHOW_TOOLBAR_CALLBACK': lambda x: DEBUG,
 
}
 

	
 
INTERNAL_IPS = [
 
    '127.0.0.1',
 
]
 

	
 
from debug_toolbar.panels.logging import collector
 
LOGGING = {
 
    'version': 1,
 
    'disable_existing_loggers': False,
 
    'formatters': {
 
        'verbose': {
 
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
 
        },
 
        'simple': {
 
            'format': '%(asctime)s %(levelname)s $(module)s %(message)s'
 
        },
 
    },
 
    'filters': {
 
        'require_debug_false': {
 
            '()': 'django.utils.log.RequireDebugFalse'
 
        }
 
    },
 
    'handlers': {
 
        'console': {
 
            'level': 'DEBUG',
 
            'class': 'logging.StreamHandler',
 
            'formatter': 'simple'
 
        },
 
        'mail_admins': {
 
            'level': 'ERROR',
 
            'filters': ['require_debug_false'],
 
            'class': 'django.utils.log.AdminEmailHandler',
 
            'include_html': True,
 
        },
 
        'djdt_log': {
 
            'level': 'DEBUG',
 
            'class': 'debug_toolbar.panels.logging.ThreadTrackingHandler',
 
            'collector': collector,
 
        },
 
    },
 
    'loggers': {
 
        'django.request': {
 
            'handlers': ['mail_admins'],
 
            'level': 'DEBUG',
 
            'propagate': True,
 
        },
 
        'symposion.request': {
 
            'handlers': ['mail_admins'],
 
            'level': 'DEBUG',
 
            'propagate': True,
 
        },
 
    },
 
    'root': {
 
        'handlers': ['console', 'djdt_log'],
 
        'level': 'DEBUG'
 
    },
 
}
 
FIXTURE_DIRS = [
 
    os.path.join(PROJECT_ROOT, "fixtures"),
 
]
 

	
 
AUTHENTICATION_BACKENDS = [
 
    'symposion.teams.backends.TeamPermissionsBackend',
 
    'django.contrib.auth.backends.ModelBackend',
 
    'djangosaml2.backends.Saml2Backend',
 
]
 

	
 
LOGIN_URL = '/saml2/login/'
 
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
...
 
@@ -441,122 +447,123 @@ GAPC_STORAGE = {
 
SETTINGS_EXPORT = [
 
    'DEBUG',
 
    'ANALYTICS_KEY',
 
]
 

	
 
if DEV_MODE and DEV_MODE == "LAPTOP":
 
    print("ENABLING LAPTOP MODE")
 
    from .devmode_settings import *
 

	
 

	
 
class Category(object):
 
    tickets = []
 

	
 
    @classmethod
 
    def order(cls, ticket) -> int:
 
        return (cls.tickets.index(ticket) + 1) * 10
 

	
 

	
 
@dataclass(frozen=True)
 
class Ticket:
 
    name: str
 
    regular_price: Decimal
 
    earlybird_price: Decimal
 

	
 
    def earlybird_discount(self):
 
        return self.regular_price - self.earlybird_price
 

	
 

	
 
@dataclass(frozen=True)
 
class DinnerTicket:
 
    name: str
 
    price: Decimal
 
    description: str
 
    reservation: timedelta
 
    cat: Category
 

	
 
    def order(self):
 
        return self.cat.order(self)
 

	
 

	
 
class PenguinDinnerTicket(DinnerTicket):
 
    pass
 

	
 

	
 
class SpeakersDinnerTicket(DinnerTicket):
 
    pass
 

	
 

	
 
class SpeakersDinnerCat(Category):
 
    @classmethod
 
    def create(cls, name: str, price: Decimal, description: str, reservation: timedelta) -> SpeakersDinnerTicket:
 
        t = SpeakersDinnerTicket(name, price, description, reservation, cls)
 
        cls.tickets.append(t)
 
        return t
 

	
 

	
 
class PenguinDinnerCat(Category):
 
    @classmethod
 
    def create(cls, name: str, price: Decimal, description: str, reservation: timedelta) -> PenguinDinnerTicket:
 
        t = PenguinDinnerTicket(name, price, description, reservation, cls)
 
        cls.tickets.append(t)
 
        return t
 

	
 

	
 
LCA_START = datetime(2021, 1, 23)
 
LCA_END = datetime(2021, 1, 25)
 
EARLY_BIRD_DEADLINE = datetime(2020, 12, 1)
 
_TZINFO = pytz.timezone(TIME_ZONE)
 
LCA_START = datetime(2021, 1, 23, tzinfo=_TZINFO)
 
LCA_END = datetime(2021, 1, 25, tzinfo=_TZINFO)
 
EARLY_BIRD_DEADLINE = datetime(2020, 12, 1, tzinfo=_TZINFO)
 
PENGUIN_DINNER_TICKET_DATE = date(2021, 1, 23)
 
SPEAKER_DINNER_TICKET_DATE = date(2021, 1, 25)
 
PDNS_TICKET_DATE = date(2021, 1, 24)
 

	
 
TSHIRT_PRICE = Decimal("25.00")
 

	
 
CONTRIBUTOR = Ticket("Contributor", Decimal("1999.00"), Decimal("1849.00"))
 
PROFESSIONAL = Ticket("Professional", Decimal("1099.00"), Decimal("949.00"))
 
HOBBYIST = Ticket("Hobbyist", Decimal("549.00"), Decimal("399.00"))
 
STUDENT = Ticket("Student", Decimal("199.00"), None)
 

	
 
MINICONF_MT = Ticket("Monday and Tuesday Only", Decimal("198.00"), None)
 
MINICONF_M = Ticket("Monday Only", Decimal("99.00"), None)
 
MINICONF_T = Ticket("Tuesday Only", Decimal("99.00"), None)
 

	
 
MEDIA = Ticket("Media", Decimal("0.0"), None)
 
SPEAKER = Ticket("Speaker", Decimal("0.0"), None)
 
SPONSOR = Ticket("Sponsor", Decimal("0.0"), None)
 

	
 
CONFERENCE_ORG = Ticket("Conference Organiser", Decimal("0.0"), None)
 
CONFERENCE_VOL = Ticket("Conference Volunteer", Decimal("0.0"), None)
 

	
 
PENGUIN_DINNER = PenguinDinnerCat
 
PENGUIN_DINNER_ADULT = PenguinDinnerCat.create(
 
    "Adult", Decimal("95.00"),
 
    "Includes an adult's meal and full beverage service.",
 
    timedelta(hours=1))
 
PENGUIN_DINNER_CHILD = PenguinDinnerCat.create(
 
    "Child", Decimal("50.00"),
 
    "Children 14 and under. "
 
    "Includes a child's meal and soft drink service.",
 
    timedelta(hours=1))
 
PENGUIN_DINNER_INFANT = PenguinDinnerCat.create(
 
    "Infant", Decimal("0.0"),
 
    "Includes no food or beverage service.",
 
    timedelta(hours=1))
 

	
 
SPEAKERS_DINNER = SpeakersDinnerCat
 

	
 
SPEAKERS_DINNER_ADULT = SpeakersDinnerCat.create(
 
    "Adult", Decimal("100.00"),
 
    "Includes an adult's meal and full beverage service.",
 
    timedelta(hours=1))
 

	
 
# SPEAKERS_DINNER_CHILD = SpeakersDinnerCat.create(
 
#     "Child", Decimal("60.00"),
 
#     "Children 14 and under. "
 
#     "Includes a child's meal and soft drink service.",
 
#     timedelta(hours=1))
 

	
 
# SPEAKERS_DINNER_INFANT = SpeakersDinnerCat.create(
 
#     "Infant", Decimal("00.00"),
 
#     "Infant must be seated in an adult's lap. "
 
#     "No food or beverage service.",
 
#     timedelta(hours=1))
pinaxcon/templates/403_csrf.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% load i18n %}
 

	
 
{% block body_class %}template-blogpage{% endblock %}
 

	
 
{% block head_title %}{{ page.title }}{% endblock %}
 

	
 
{% block body %}
 
  {% block content %}
 
    <div class="l-content-page">
 
      <div class="l-content-page--richtext">
 
        <h2>{{ title }} <span>(403)</span></h2>
 

	
 
        <p>{{ main }}</p>
 

	
 
        {% if bad_token and request.user.is_authenticated %}
 
          <p>You are already logged in. If you saw this issue whilst attempting
 
            to log in, you can to go to the
 
            <a href='{% url "dashboard" %}'>Dashboard</a> and continue using
 
            the site.</p>
 
        {% endif %}
 

	
 
        {% if no_referer %}
 
          <p>{{ no_referer1 }}</p>
 
          <p>{{ no_referer2 }}</p>
 
        {% endif %}
 
        {% if no_cookie %}
 
          <p>{{ no_cookie1 }}</p>
 
          <p>{{ no_cookie2 }}</p>
 
        {% endif %}
 

	
 
        {% if DEBUG %}
 
          <h2>Help</h2>
 
            {% if reason %}
 
            <p>Reason given for failure:</p>
 
            <pre>
 
            {{ reason }}
 
            </pre>
 
            {% endif %}
 
          <p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when
 
          <a
 
          href="https://docs.djangoproject.com/en/{{ docs_version }}/ref/csrf/">Django's
 
          CSRF mechanism</a> has not been used correctly.  For POST forms, you need to
 
          ensure:</p>
 
          <ul>
 
            <li>Your browser is accepting cookies.</li>
 
            <li>The view function passes a <code>request</code> to the template's <a
 
            href="https://docs.djangoproject.com/en/dev/topics/templates/#django.template.backends.base.Template.render"><code>render</code></a>
 
            method.</li>
 
            <li>In the template, there is a <code>{% templatetag openblock %} csrf_token
 
            {% templatetag closeblock %}</code> template tag inside each POST form that
 
            targets an internal URL.</li>
 
            <li>If you are not using <code>CsrfViewMiddleware</code>, then you must use
 
            <code>csrf_protect</code> on any views that use the <code>csrf_token</code>
 
            template tag, as well as those that accept the POST data.</li>
 
          </ul>
 
          <p>You're seeing the help section of this page because you have <code>DEBUG =
 
          True</code> in your Django settings file. Change that to <code>False</code>,
 
          and only the initial error message will be displayed.  </p>
 
          <p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p>
 
        {% else %}
 
          <p><small>{{ more }}</small></p>
 
        {% endif %}
 
      </div>
pinaxcon/templates/404.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% load i18n %}
 

	
 
{% block body_class %}template-blogpage{% endblock %}
 

	
 
{% block head_title %}{{ page.title }}{% endblock %}
 

	
 
{% block body %}
 
  {% block content %}
 
    <div class="l-content-page">
 
      <div class="l-content-page--richtext ooops-hack">
 
        <h2>Ooops</h2>
 

	
 
        <p>The page you're looking for doesn't exist. Sorry!</p>
 
      </div>
 
    </div>
 
  {% endblock %}
 
{% endblock %}
pinaxcon/templates/dashboard.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 
{% load i18n %}
 

	
 
{% load review_tags %}
 
{% load teams_tags %}
 
{% load registrasion_tags %}
 
{% load lca2018_tags %}
 
{% load staticfiles %}
 

	
 

	
 
{% block head_title %}Dashboard{% endblock %}
 
{% block page_title %}User Dashboard{% endblock %}
 

	
 
{% block alert %}
 
{% endblock %}
 

	
 
{% block content %}
 

	
 
{% available_categories as categories %}
 
{% if categories %}
 
  {% include "symposion/dashboard/_categories.html" %}
 
{% endif %}
 

	
 
<div class="mb-4">
 
  {% include "symposion/dashboard/speaking.html" %}
 
</div>
 

	
 
{% if review_sections %}
 
<div class="mb-4">
 
  <div class="row">
 
    <h2 class="col-12 mb-3">{% trans "Reviews" %}</h2>
 
  </div>
 
  <div class="row">
 
    {% for section in review_sections %}
 
    <div class="col-md-6 col-lg-4">
 
    <div class="card card-default mb-3">
 
      <div class="card-header">
 
          <h3 class="card-title">{{ section }}</h3>
 
      </div>
 
      <div class="card-body">
 
        <ul class="list-unstyled">
 
            <li><a href="{% url "review_section" section.section.slug %}">All Reviews</a></li>
 
            {% comment %}
 
            <li><a href="{% url "review_section_assignments" section.section.slug %}">Your Assignments</a></li>
 
            {% endcomment %}
 
            <li><a href="{% url "user_reviewed" section.section.slug %}">Reviewed by you</a></li>
 
            <li><a href="{% url "user_not_reviewed" section.section.slug %}">Not Reviewed by you</a></li>
 
            <li><a href="{% url "user_random" section.section.slug %}">Random unreviewed proposal</a></li>
 
          </ul>
 

	
 
          <ul class="list-unstyled">
 
            <li><a href="{% url "review_status" section.section.slug %}">Voting Status</a></li>
 
            {% if section in manage_sections %}
 
            <li><a href="{% url "review_bulk_update" section.section.slug %}">Bulk Update</a></li>
 
            <li><a href="{% url "result_notification" section.section.slug "accepted" %}">Result notifications</a></li>
 
            <li><a href="{% url "review_admin" section.section.slug %}">Reviewer Stats</a></li>
 
            {% endif %}
 
          </ul>
 
      </div>
 
    </div>
 
    </div>
 
    {% endfor %}
 
  </div>
 
</div>
 
{% endif %}
 

	
 
{% available_teams as available_teams %}
 
{% if user.memberships.exists or available_teams %}
 
<div class="mb-4">
 
  <div class="row">
 
    <h2 class="col-12 mb-3">{% trans "Teams "%}</h2>
 
  </div>
pinaxcon/templates/raffle.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load registrasion_tags %}
 
{% load lca2018_tags %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% block head_title %}Raffle Tickets{% endblock %}
 
{% block page_title %}Raffle Tickets{% endblock %}
 

	
 
{% block content %}
 
<p>
 
  All of the raffles you are entered into and the tickets you have for each of them.
 
  If available, you will be able to purchase additional raffle tickets from the <a href="{% url "dashboard" %}">Dashboard</a>.
 
</p>
 

	
 
<h2>Your Raffle Tickets</h2>
 
{% for raffle in raffles %}
 
{% if raffle.tickets %}
 
  <h3 class="mt-3">{{ raffle }}</h3>
 
  <ul>
 
   {% for id, numbers in raffle.tickets %}
 
    <li>
 
      <strong>Ticket {{ id }}</strong>
 
      <p>{% for number in numbers %}{{ number }}{% if not forloop.last %}, {% endif %}{% endfor %}</p>
 
    </li>
 
  {% endfor %}
 
  </ul>
 
{% endif %}
 
{% empty %}
 
<p>You do not have tickets in any raffles.</p>
 
{% endfor %}
 
{% endblock %}
pinaxcon/templates/raffle_draw.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load registrasion_tags %}
 
{% load lca2018_tags %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% block head_title %}Raffle Winners{% endblock %}
 
{% block page_title %}Raffle Winners{% endblock %}
 

	
 
{% block content %}
 
{% for raffle in raffles %}
 
{% if raffle.hidden %}
 
{% else %}
 
<h2 class="mt-3"><a href="{% url "raffle-draw" raffle.id %}">{{ raffle }}</a></h2>
 

	
 
<dl class="row my-4">
 
  {% for prize in raffle.prizes.all %}
 
  <dt class="col-sm-3 text-truncate">{{ prize }}</dt>
 
  <dd class="col-sm-9">
 
    {% if prize.winning_ticket %}
 
    {% with prize.winning_ticket as winner %}
 
    {# this should be attendee name #}
 
    {% with winner.lineitem.invoice.user.attendee.attendeeprofilebase as profile %}
 
    <p><strong>Winning ticket {{ winner.ticket }}, <a href="{% url "attendee" winner.lineitem.invoice.user.id %}">{{ profile.attendee_name }}</a></strong><br />
 
      Drawn by {{ winner.draw.drawn_by }}, {{ winner.draw.drawn_time}}
 
    </p>
 
    {% endwith %}
 
    <div class="alert alert-danger">
 
      <form method="POST" action="{% url 'raffle-redraw' winner.id %}">
 
        {% csrf_token %}
 
        {# This should have a `reason` field that can be passed through to the Audit log #}
 
        <p>
 
          Re-draw <em>{{ prize }}</em>
 
          <button type="submit" class="btn btn-danger float-right">Re-draw</button>
 
        </p>
 
        <div class="clearfix"></div>
 
      </form>
 
    </div>
 
    {% endwith %}
 
    {% else %}
 
    Not drawn
 
    {% endif %}
 
  </dd>
 
  {% endfor %}
 
</dl>
 

	
 
{% if raffle.is_open %}
 
<form method="POST" action="{% url 'raffle-draw' raffle_id=raffle.id %}">
 
    {% csrf_token %}
 
    <button type="submit" class="btn btn-success">Draw tickets</button>
 
    <div class="clearfix"></div>
 
  </form>
 
{% endif %}
 
{% if not forloop.last %}<hr>{% endif %}
 
{% endif %}
 
{% endfor %}
 

	
 
{% endblock %}
pinaxcon/templates/registrasion/base.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 
{% load lca2018_tags %}
 
{% load i18n %}
 

	
 
{% block head_title %}{% block page_title %}{% endblock %}{% endblock %}
 

	
 
{% block content_base %}
 
{% block content %}
 
  <div class="jumbotron rego-content">
 
    {% block proposals_body %}
 
    {% endblock %}
 
  </div>
 
{% endblock content %}
 
{% endblock %}
pinaxcon/templates/registrasion/invoice.html
Show inline comments
 
{% extends "registrasion/base.html" %}
 
{% load registrasion_tags %}
 
{% load lca2018_tags %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% block head_title %}Tax Invoice/Statement #{{ invoice.id }}{% endblock %}
 
{% block page_title %}{% conference_name %}{% endblock %}
 

	
 
{% block proposals_body %}
 
{% include "registrasion/_invoice_details.html" %}
 
<div class="d-print-none mb-4 pb-4">
 
  {% if invoice.is_unpaid %}
 
  <p>
 
    <strong>NOTICE:</strong> The above invoice is automatically generated, and
 
    will be voided if you amend your selections before payment, or if discounts
 
    or products contained in the invoice become unavailable. The products and
 
    discounts are only reserved until the invoice due time, please pay before then
 
    to guarantee your selection. Late payments are accepted only if the products
 
    and discounts are still available.</p>
 

	
 
  {% url "invoice_access" invoice.user.attendee.access_code as access_url %}
 
  <p>Your most recent unpaid invoice will be available at
 
    <a href="{{ access_url }}">{{ request.scheme }}://{{ request.get_host }}{{ access_url }}</a>
 
    You can print that page for your records, or give this URL to your accounts department to pay for this invoice
 
  </p>
 

	
 
  <a class="btn btn-primary" href='{% url "registripe_card" invoice.id invoice.user.attendee.access_code %}'>Pay this invoice by card</a>
 

	
 
  {% if user.is_staff %}
 
  <a class="btn btn-secondary" href="{% url "manual_payment" invoice.id %}">Apply manual payment</a>
 
  {% endif %}
 

	
 
  {% elif invoice.is_paid %}
 
  {% if user.is_staff %}
 
  <a class="btn btn-primary" href="{% url "manual_payment" invoice.id %}">Apply manual payment/refund</a>
 
  <a class="btn btn-secondary" href="{% url "refund" invoice.id %}">Refund by issuing credit note</a>
 
  {% endif %}
 
  {% endif %}
 

	
 
  {% if user.is_staff %}
 
  <a class="btn btn-info" href="{% url "attendee" invoice.user.id %}">View attendee</a>
 
  <a class="btn btn-light" href="{% url "invoice_update" invoice.id %}">Refresh recipient</a>
 
  {% endif %}
 
</div>
 

	
 
{% endblock %}
pinaxcon/templates/site_base.html
Show inline comments
 
{% load staticfiles %}
 
{% load static %}
 
{% load i18n %}
 
{% load sitetree %}
 
{% load sass_tags %}
 
{% load capture_tags %}
 

	
 
{% capture as head_title silent %}{% block head_title_base %}{% if SITE_NAME %}{{ SITE_NAME }} | {% endif %}{% block head_title %}{% endblock %}{% endblock %}{% endcapture %}
 

	
 
<!DOCTYPE html>
 

	
 
<html lang="en">
 
<head>
 
  <meta charset="utf-8">
 
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
 
  <meta name="description" content="">
 
  <meta name="viewport" content="width=device-width, initial-scale=1">
 

	
 
  <title>{{ head_title }}</title>
 

	
 
  <meta property="og:type" content="website" />
 

	
 
  <!-- Cards -->
 
  <meta property="og:title" content="{{ head_title }}">
 
  <meta property="og:description" content="linux.conf.au 2021 - Jan 23-25 2021, Online, Worldwide" />
 
  <meta property="og:url" content="{{ request.scheme }}://{{ request.get_host }}{{ request.path }}">
 
  <meta name="twitter:site" content="@linuxconfau">
 
  <meta name="twitter:image:alt" content="{{ head_title }}" />
 
  <meta name="twitter:card" content="summary">
 
  <meta name="twitter:image" content="{{ request.scheme }}://{{ request.get_host }}/media/img/card/lca_badge.0e10614e.png" />
 
  <meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}/media/img/card/lca_badge.0e10614e.png" />
 
  <meta property="og:image:width" content="400" />
 
  <meta property="og:image:height" content="400" />
 

	
 
  {% block styles %}
 
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
 
  <link href="{% sass_src 'scss/app.scss' %}" rel="stylesheet" type="text/css" />
 
  {% block extra_style %}{% endblock %}
 
  {% endblock %}
 

	
 
  {% block extra_head_base %}
 
  {% block extra_head %}{% endblock %}
 
  {% endblock %}
 
</head>
 
<body class="{% block body_class %}{% endblock %}">
 
  {% block template_overrides %}{% endblock %}
 
  <header class="clearfix d-print-none">
 
    {% block alert %}{% endblock %}
 
    {% block navbar %}{% include 'nav.html' %}{% endblock %}
 
  </header>
 

	
 
  {% if messages %}
 
  <div class="container my-5 alert alert-primary d-print-none">
 
    <ul class="messagelist list-unstyled">
 
      {% for message in messages %}
 
      <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
 
      {% endfor %}
 
    </ul>
 
  </div>
 
  {% endif %}
 

	
 
  <main role="main" class="{% block main_class %}container{% endblock %}">
 
    <div class="row">
 
      <div class="col page-header">
 
        <h1 class="page-title">{% block page_title %}{% endblock %}</h1>
 
        <p class="lead">{% block page_lead %}{% endblock %}</p>
pinaxcon/templates/symposion/dashboard/_categories.html
Show inline comments
 
{% load i18n %}
 
{% load proposal_tags %}
 
{% load review_tags %}
 
{% load teams_tags %}
 
{% load registrasion_tags %}
 
{% load lca2018_tags %}
 
{% load lca2019_tags %}
 
{% load staticfiles %}
 
{% load static %}
 
{% load waffle_tags %}
 

	
 
{% if user.is_staff %}
 
<div class="mb-4">
 
  <div class="row">
 
    <div class="col-12">
 
      <h2>{% trans "Administration" %}</h2>
 
      <p>The following administrative tools are available to you:
 
        <ul class="list-unstyled">
 
          <li><a href="{% url "reports_list" %}">Reports</a></li>
 
        </ul>
 
      </p>
 
    </div>
 
  </div>
 
</div>
 
{% endif %}
 

	
 
<div class="mb-4">
 
  <div class="row">
 
    <div class="col-12">
 
      <h2>{% trans "Attend" %} {% conference_name %}</h2>
 
    </div>
 
  </div>
 

	
 
  {% if not user.attendee.completed_registration %}
 
  <div class="row">
 
    <div class="col-12">
 
      <h3>Register</h3>
 
      <p>To attend the conference, you must create an attendee profile and purchase your ticket</p>
 
      <div>
 
        <a class="btn btn-lg btn-primary" role="button" href="{% url "guided_registration" %}">Get your ticket</a>
 
      </div>
 
    </div>
 
  </div>
 
  {% else %}
 
  <div class="row">
 
    <div class="col-md-6 mb-3 mb-md-0">
 
      <h3>Attendee Profile</h3>
 
      <p>If you would like to change the details on your badge or your attendee statistics, you may edit your attendee profile here.</p>
 
      <div>
 
        <a class="btn btn-primary" role="button" href="{% url "attendee_edit" %}">Edit attendee profile</a>
 
        {% flag "badge_preview" %}
 
        <a class="btn btn-info" role="button" href="{% url "user_badge" %}">Preview my badge</a>
 
        {% endflag %}
 
      </div>
 
    </div>
 
    <div class="col-md-6 mb-3 mb-md-0">
 
      <h3>Account Management</h3>
 
      <p>If you would like to change your registered email address or password, you can use our self-service account management portal</p>
 
      <div>
 
        <a class="btn btn-primary" role="button" href="https://login.linux.conf.au/manage/">Account Management</a>
 
      </div>
 
    </div>
 
  </div>
 

	
 
  {% items_pending as pending %}
 
  <div class="row">
 
    <div class="col-12">
 
      <h3 class="my-3">Account</h3>
 
    </div>
 
  </div>
 

	
 
  <div class="row">
 
    {% if pending %}
pinaxcon/templates/symposion/proposals/base.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 

	
 
{% block body_outer %}
 
  {% block proposals_body %}
 
  {% endblock %}
 
{% endblock %}
 

	
pinaxcon/templates/symposion/reviews/base.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% load i18n %}
 

	
 
{% block body_class %}reviews{% endblock %}
 
{% block main_class %}container-fluid{% endblock %}
 

	
 
{% block body_outer %}
 
<div class="container-fluid">
 
    <div class="row">
 
        <div class="col-md-2 col-sm-4">
 
            {% block sidebar %}
 
            {% for section in review_sections %}
 

	
 
            <div class="card card-default mb-3">
 
                <div class="card-header">
 
                    <h3 class="card-title">{{ section }}</h3>
 
                </div>
 
                <div class="list-group">
 
                    <a class="list-group-item review-list" href="{% url "review_section" section.section.slug %}">
 
                        {% trans "All Reviews" %}
 
                    </a>
 
                    {% comment %}
 
                    <a class="list-group-item" href="{% url "review_section_assignments" section.section.slug %}">
 
                        {% trans "Your Assignments" %}
 
                    </a>
 
                    {% endcomment %}
 
                    <a class="list-group-item user-reviewed" href="{% url "user_reviewed" section.section.slug %}">
 
                        {% trans "Reviewed by you" %}
 
                    </a>
 
                    <a class="list-group-item user-not-reviewed" href="{% url "user_not_reviewed" section.section.slug %}">
 
                        {% trans "Not Reviewed by you" %}
 
                    </a>
 
                    <a class="list-group-item user-random" href="{% url "user_random" section.section.slug %}">
 
                        {% trans "Random unreviewed proposal" %}
 
                    </a>
 
                    <a class="list-group-item voting-status" href="{% url "review_status" section.section.slug %}">
 
                        {% trans "Voting Status" %}
 
                    </a>
 
                    {% if section in manage_sections %}
 
                    <a class="list-group-item" href="{% url "review_bulk_update" section.section.slug %}">
 
                        Bulk Update
 
                    </a>
 
                    <a class="list-group-item review-results" href="{% url "result_notification" section.section.slug 'accepted' %}">
 
                        Result Notifications
 
                    </a>
 
                    <a class="list-group-item" href="{% url "review_admin" section.section.slug %}">
 
                        Reviewer Stats
 
                    </a>
 
                    {% endif %}
 
                </div>
 
            </div>
 
            {% endfor %}
 
            {% endblock %}
 
        </div>
 
        <div class="col-md-10 col-sm-8">
 
            {% block body %}
 
            {% endblock %}
 
        </div>
 
    </div>
 
</div>
 
{% endblock %}
 

	
 
{% block extra_style %}
 
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/bs4/jszip-2.5.0/dt-1.10.18/b-1.5.6/b-colvis-1.5.6/b-html5-1.5.6/b-print-1.5.6/cr-1.5.0/fc-3.2.5/fh-3.1.4/kt-2.5.0/r-2.2.2/rg-1.1.0/datatables.min.css"/>
pinaxcon/templates/symposion/reviews/review_list.html
Show inline comments
 
{% extends "symposion/reviews/base.html" %}
 

	
 
{% block head_title %}Reviews - {{ section }}{% endblock %}
 

	
 
{% block body_class %}{{ block.super }}
 
	{% if reviewed == "all_reviews" %}
 
		review-list
 
	{% endif %}
 
{% endblock %}
 

	
 
{% block body %}
 
	<h3>{{ section }}</h3>
 
	{% if reviewed == 'all_reviews' %}
 
    	<h4>All proposals</h4>
 
    {% elif reviewed == 'user_reviewed' %}
 
    	<h4>Proposals you have reviewed</h4>
 
		<h4>Proposals you have reviewed</h4>
 
	{% elif reviewed == 'user_not_reviewed' %}
 
		<h4>Proposals you have not reviewed</h4>
 
    {% else %}
 
    	<h4>Proposals you have not yet reviewed</h4>
 
    	<h4>Proposals reviewed by selected reviewer</h4>
 
    {% endif %}
 

	
 
    {% include "symposion/reviews/_review_table.html" %}
 
{% endblock %}
pinaxcon/templates/symposion/reviews/review_review.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% load bootstrap %}
 

	
 
{% block body_class %}review{% endblock %}
 

	
 
{% block body %}
 
<div class="l-content-page">
 
<div class="l-content-page--richtext">
 
<div class="rich-text">
 
    <h1>Proposal Review</h1>
 

	
 
    <div class="proposal">
 
        <h2>{{ proposal.title }}</h2>
 

	
 
        <p>
 
            {% if proposal.cancelled %}
 
                Cancelled
 
            {% endif %}
 
        </p>
 

	
 
        <div>
 
            {{ proposal.description }}
 
        </div>
 

	
 
        <p><b>Type</b>: {{ proposal.get_session_type_display }}</p>
 

	
 
        <h3>Abstract</h3>
 
        <div class="abstract">
 
            {{ proposal.abstract_html|safe }}
 
        </div>
 

	
 
        <p><b>Audience level</b>: {{ proposal.get_audience_level_display }}</p>
 

	
 
        <p><b>Submitting speaker</b>: {{ proposal.speaker }}</p> {# @@@ bio? #}
 

	
 
        {% if proposal.additional_speakers.all %}
 
            <p><b>Additional speakers</b>:</p>
 
            <ul>
 
            {% for speaker in proposal.additional_speakers.all %}
 
                {% if speaker.user %}
 
                    <li><b>{{ speaker.name }}</b> &mdash; {{ speaker.email }}</li>
 
                {% else %}
 
                    <li>{{ speaker.email }} &mdash; pending invitation</li>
 
                {% endif %}
 
            {% endfor %}
 
            </ul>
 
        {% endif %}
 

	
 
        <h3>Additional Notes (private from submitter)</h3>
 
        <div class="additional_notes">
 
            {{ proposal.additional_notes }}
 
        </div>
 
    </div>
 

	
 
    <h2>Review</h2>
 

	
 
    <form method="POST" action="{% url "review_review" proposal.pk %}" class="form form-horizontal">
 
        {% csrf_token %}
 
        <fieldset class="inlineLabels">
 
            {{ review_form|bootstrap }}
 
            <div class="form_block">
 
                <input type="submit" value="Submit" />
 
            </div>
 
        </fieldset>
pinaxcon/templates/symposion/schedule/presentation_detail.html
Show inline comments
 
{% extends "site_base.html" %}
 

	
 
{% load lca2018_tags %}
 
{% load lca2019_tags %}
 
{% load sitetree %}
 
{% load staticfiles %}
 
{% load static %}
 
{% load thumbnail %}
 

	
 
{% block head_title %}Presentation: {{ presentation.title }}{% endblock %}
 
{% block page_title %}{{ presentation.title }}{% endblock %}
 
{% block page_lead %}
 
{% if presentation.slot %}
 
{{ presentation.slot.rooms.0 }} | {{ presentation.slot.day.date|date:"D d M" }} | {{ presentation.slot.start }}&ndash;{{ presentation.slot.end }}
 
{% else %}
 
<em>Not currently scheduled.</em>
 
{% endif %}
 
{% endblock %}
 

	
 
{% block content %}
 
  {% if presentation.unpublish %}
 
  <div class="row">
 
    <div class="col">
 
      <p><strong>Presentation not published.</strong></p>
 
    </div>
 
  </div>
 
  {% endif %}
 

	
 
  <div class="row presentation-details">
 
    <div class="col-md-3">
 
      <h2 class="mt-4">Presented by</h4>
 
      <ul class="list-unstyled">
 
        {% for speaker in presentation.speakers %}
 
        <li class="mb-4 pb-2">
 
          {% speaker_photo speaker 120 as speaker_photo_url %}
 
          <img src="{{ speaker_photo_url }}" alt="{{ speaker }}" class="rounded-circle img-fluid">
 
          <p>
 
            <strong><a href="{% url "speaker_profile" speaker.pk %}">{{ speaker }}</a></strong><br />
 
            {% if speaker.twitter_username %}
 
              <a href="https://twitter.com/{{ speaker.twitter_username }}">{{ speaker.twitter_username|twitter_handle }}</a><br />
 
            {% endif %}
 
            {% if speaker.homepage %}
 
              <a href="{{ speaker.homepage }}">{{ speaker.homepage }}</a>
 
            {% endif %}
 
          </p>
 
          <div class="bio">{{ speaker.biography_html|safe}}</div>
 
          </p>
 
        </li>
 
        {% endfor %}
 
      </ul>
 
    </div>
 

	
 
    <div class="col-md-9 presentation-abstract">
 
      <h2 class="mt-4">Abstract</h4>
 
      {% autoescape off %}
 
      <div class="abstract pb-4"><p>{{ presentation.abstract_html|safe|clean_text|urlize }}</p></div>
 
      {% endautoescape %}
 
    </div>
 
  </div>
 
{% endblock %}
pinaxcon/templates/symposion/schedule/session_detail.html
Show inline comments
 
{% extends "site_base.html" %}
 

	
 
{% load lca2018_tags %}
 
{% load sitetree %}
 
{% load staticfiles %}
 
{% load static %}
 
{% load thumbnail %}
 
{% load i18n %}
 

	
 

	
 
{% block head_title %}Session: {{ session }}{% endblock %}
 

	
 
{% block breadcrumbs %}{% sitetree_breadcrumbs from "main" %}{% endblock %}
 

	
 

	
 
{% block header_title %}Session: {{ session }}{% endblock %}
 

	
 
{% block header_paragraph %}
 

	
 
{% endblock %}
 

	
 
{% block content %}
 

	
 
  <dl class="dl-horizontal">
 
    <dt>{% trans "Session Chair" %}</dt>
 
    <dd>
 
        {% if chair %}
 
            {{ chair.attendee.attendeeprofilebase.attendeeprofile.name }}
 
            {% if request.user == chair %}
 
                <form method="POST">
 
                    {% csrf_token %}
 
                    <input type="hidden" name="role" value="un-chair" />
 
                    <input type="submit" value="Opt out" class="btn btn-info"/>
 
                </form>
 
            {% endif %}
 
        {% else %}
 
            {% if request.user.is_authenticated %}
 
                {% if request.user.attendee.completed_registration %}
 
                    {% if not chair_denied %}
 
                        <form method="POST">
 
                            {% csrf_token %}
 
                            <input type="hidden" name="role" value="chair" />
 
                            <input type="submit" class="btn btn-success" value="Volunteer to be session chair"/>
 
                        </form>
 
                    {% endif %}
 
                {% else %}
 
                    {% url 'guided_registration' as guided_registration %}
 
                    <a href="{{ guided_registration }}">Click here to get a ticket for the conference,</a> and enable volunteering for session roles.
 
                {% endif %}
 
            {% else %}
 
              Sign up and <a href="/saml2/login/?next={{ request.path }}">log in</a> to volunteer to be session chair.
 
            {% endif %}
 
        {% endif %}
 
    </dd>
 
    <dt>{% trans "Session Runner" %}</dt>
 
    <dd>
 
        {% if runner %}
 
          {{ runner.profile.display_name }}
 
        {% else %}
 
          {% blocktrans %}Session runner not assigned.{% endblocktrans %}
 
        {% endif %}
 
    </dd>
 
  </dl>
 

	
 
  <h2>Slots</h2>
 

	
 
  <table class="table">
 
    {% for slot in session.slots.all %}
 
      <tr>
 
          <td>{{ slot }}</td>
pinaxcon/templates/symposion/schedule/session_list.html
Show inline comments
 
{% extends "site_base.html" %}
 

	
 
{% load lca2018_tags %}
 
{% load sitetree %}
 
{% load staticfiles %}
 
{% load static %}
 
{% load thumbnail %}
 
{% load i18n %}
 

	
 

	
 
{% block head_title %}Sessions{% endblock %}
 

	
 
{% block breadcrumbs %}{% sitetree_breadcrumbs from "main" %}{% endblock %}
 

	
 
{% block header_title %}Sessions{% endblock %}
 

	
 
{% block header_paragraph %}
 

	
 
{% endblock %}
 

	
 
{% block content %}
 
  <ul class="unstyled">
 
    {% for session in sessions %}
 
      {% if session.sorted_slots %}
 
        <li>
 
          <a href="{% url 'schedule_session_detail' session.pk %}">{% blocktrans %}Session #{% endblocktrans %}{{ forloop.counter }}</a>
 
          <div class="well">
 
            <ul class="unstyled">
 
              {% for role in session.sessionrole_set.all %}
 
                <li>
 
                  <b>{{ role.get_role_display }}</b>: {{ role.user.attendee.attendeeprofilebase.attendeeprofile.name }}
 
                </li>
 
              {% empty %}
 
                <li>
 
                  <a href="{% url 'schedule_session_detail' session.pk %}">{% blocktrans %}No volunteers signed up. Sign up!{% endblocktrans %}</a>
 
                </li>
 
              {% endfor %}
 
            </ul>
 
            <h4>{% trans "Slots" %}</h4>
 
            <table class="table">
 
              {% for slot in session.sorted_slots %}
 
                <tr>
 
                  <td>{{ slot }}</td>
 
                  <td>
 
                    {% if slot.content %}<a href="{% url 'schedule_presentation_detail' slot.content.pk %}">{{ slot.content }}</a>{% endif %}
 
                  </td>
 
                </tr>
 
              {% empty %}
 
                  <tr>{% trans "No slots in session." %}</tr>
 
              {% endfor %}
 
            </table>
 
          </div>
 
        </li>
 
      {% endif %}
 
    {% empty %}
 
      <li>{% trans "No sessions defined." %}</li>
 
    {% endfor %}
 
  </ul>
 

	
 
{% endblock %}
pinaxcon/templatetags/lca2018_tags.py
Show inline comments
 
import hashlib
 

	
 
from decimal import Decimal
 
from django import template
 
from django.conf import settings
 
from django.contrib.staticfiles.templatetags import staticfiles
 
from easy_thumbnails.files import get_thumbnailer
 
from registrasion.templatetags import registrasion_tags
 
from symposion.conference import models as conference_models
 
from symposion.schedule.models import Track
 

	
 
CONFERENCE_ID = settings.CONFERENCE_ID
 
GST_RATE = settings.GST_RATE
 

	
 
register = template.Library()
 

	
 

	
 
@register.assignment_tag()
 
@register.simple_tag()
 
def classname(ob):
 
    return ob.__class__.__name__
 

	
 

	
 
@register.simple_tag(takes_context=True)
 
def can_manage(context, proposal):
 
    return proposal_permission(context, "manage", proposal)
 

	
 

	
 
@register.simple_tag(takes_context=True)
 
def can_review(context, proposal):
 
    return proposal_permission(context, "review", proposal)
 

	
 

	
 
def proposal_permission(context, permname, proposal):
 
    slug = proposal.kind.section.slug
 
    perm = "reviews.can_%s_%s" % (permname, slug)
 
    return context.request.user.has_perm(perm)
 

	
 

	
 
@register.simple_tag(takes_context=True)
 
def speaker_photo(context, speaker, size):
 
    ''' Provides the speaker profile, or else fall back to libravatar or gravatar. '''
 

	
 
    if speaker.photo:
 
        thumbnailer = get_thumbnailer(speaker.photo)
 
        thumbnail_options = {'crop': True, 'size': (size, size)}
 
        thumbnail = thumbnailer.get_thumbnail(thumbnail_options)
 
        return thumbnail.url
 
    else:
 
        email = speaker.user.email.encode("utf-8")
 
        md5sum = hashlib.md5(email.strip().lower()).hexdigest()
 
        fallback_image = ("https://linux.conf.au/site_media/static/lca2017"
 
                          "/images/speaker-fallback-devil.jpg")
 
        url = "https://secure.gravatar.com/avatar/%s?s=%d&d=%s" % (md5sum, size, "mp")
 

	
 
        return url
 

	
 

	
 
@register.simple_tag()
 
def define(value):
 
    return value
 

	
 

	
 
@register.simple_tag()
 
def presentation_bg_number(presentation, count):
 
    return sum(ord(i) for i in presentation.title) % count