wizyta oraz widok księdza
This commit is contained in:
0
backend/__init__.py
Normal file
0
backend/__init__.py
Normal file
59
backend/admin.py
Normal file
59
backend/admin.py
Normal file
@ -0,0 +1,59 @@
|
||||
from django.contrib import admin
|
||||
from .models import Parish, Submission, Priest
|
||||
from authtools.models import User
|
||||
|
||||
# Register your models here.
|
||||
|
||||
|
||||
@admin.register(Priest)
|
||||
class PriestAdmin(admin.ModelAdmin):
|
||||
list_display = ["priest", "parish"]
|
||||
|
||||
def get_form(self, request, obj=None, **kwargs):
|
||||
form = super(PriestAdmin, self).get_form(request, obj, **kwargs)
|
||||
form.base_fields["priest"].queryset = User.objects.filter(is_staff=True)
|
||||
return form
|
||||
|
||||
|
||||
@admin.register(Submission)
|
||||
class SubmissionAdmin(admin.ModelAdmin):
|
||||
list_display = ["id", "parish", "user", "name", "phone", "adres"]
|
||||
|
||||
def get_exclude(self, request, obj=None):
|
||||
if not request.user.is_superuser:
|
||||
return ["parish", "user"]
|
||||
|
||||
return []
|
||||
|
||||
def get_queryset(self, request):
|
||||
qs = super().get_queryset(request)
|
||||
|
||||
if not request.user.is_superuser:
|
||||
p = request.user.priest_rel.parish if request.user.priest_rel else None
|
||||
return qs.filter(parish=p)
|
||||
|
||||
return qs
|
||||
|
||||
def get_changeform_initial_data(self, request):
|
||||
initial = super(SubmissionAdmin, self).get_changeform_initial_data(request)
|
||||
if not request.user.is_superuser:
|
||||
p = request.user.priest_rel.parish if request.user.priest_rel else None
|
||||
return {"parish": p, **initial}
|
||||
|
||||
return initial
|
||||
|
||||
def get_form(self, request, obj=None, **kwargs):
|
||||
form = super(SubmissionAdmin, self).get_form(request, obj, **kwargs)
|
||||
|
||||
return form
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
if not request.user.is_superuser:
|
||||
p = request.user.priest_rel.parish if request.user.priest_rel else None
|
||||
obj.parish = p
|
||||
super().save_model(request, obj, form, change)
|
||||
|
||||
|
||||
@admin.register(Parish)
|
||||
class ParishAdmin(admin.ModelAdmin):
|
||||
list_display = ["slug", "name", "visible", "submissions"]
|
6
backend/apps.py
Normal file
6
backend/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BackendConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'backend'
|
30
backend/migrations/0001_initial.py
Normal file
30
backend/migrations/0001_initial.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Generated by Django 5.1.6 on 2025-02-14 18:36
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Parish',
|
||||
fields=[
|
||||
('slug', models.SlugField(primary_key=True, serialize=False, verbose_name='Identyfikator')),
|
||||
('name', models.CharField(max_length=512, verbose_name='Nazwa')),
|
||||
('submissions', models.BooleanField(default=False, verbose_name='Kolęda aktywna')),
|
||||
('announcements', models.TextField(blank=True, null=True, verbose_name='Skrypt - Ogłoszenia')),
|
||||
('intentions', models.TextField(blank=True, null=True, verbose_name='Skrypt - Intencje')),
|
||||
('streets', models.JSONField(default=list, verbose_name='Ulice')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Parafia',
|
||||
'verbose_name_plural': 'Parafie',
|
||||
'ordering': ['slug'],
|
||||
},
|
||||
),
|
||||
]
|
18
backend/migrations/0002_parish_channel.py
Normal file
18
backend/migrations/0002_parish_channel.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.6 on 2025-02-14 18:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('backend', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='parish',
|
||||
name='channel',
|
||||
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='Identyfikator kanału'),
|
||||
),
|
||||
]
|
@ -0,0 +1,29 @@
|
||||
# Generated by Django 5.1.6 on 2025-02-14 19:33
|
||||
|
||||
import pictures.models
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('backend', '0002_parish_channel'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='parish',
|
||||
name='icon',
|
||||
field=pictures.models.PictureField(aspect_ratios=[None], breakpoints={'l': 1200, 'm': 992, 's': 768, 'xl': 1400, 'xs': 576}, container_width=1200, file_types=['WEBP', 'PNG'], grid_columns=12, height_field='icon_height', null=True, pixel_densities=[1, 2], upload_to='icons', verbose_name='Ikona', width_field='icon_width'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='parish',
|
||||
name='icon_height',
|
||||
field=models.PositiveIntegerField(null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='parish',
|
||||
name='icon_width',
|
||||
field=models.PositiveIntegerField(null=True),
|
||||
),
|
||||
]
|
18
backend/migrations/0004_parish_visible.py
Normal file
18
backend/migrations/0004_parish_visible.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.6 on 2025-02-14 21:07
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('backend', '0003_parish_icon_parish_icon_height_parish_icon_width'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='parish',
|
||||
name='visible',
|
||||
field=models.BooleanField(default=False, verbose_name='Parafia widoczna'),
|
||||
),
|
||||
]
|
33
backend/migrations/0005_submission.py
Normal file
33
backend/migrations/0005_submission.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Generated by Django 5.1.6 on 2025-02-14 23:37
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('backend', '0004_parish_visible'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Submission',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=256, verbose_name='Imię i nazwisko')),
|
||||
('phone', models.CharField(max_length=32, verbose_name='Numer telefonu')),
|
||||
('street', models.CharField(max_length=128, verbose_name='Ulica')),
|
||||
('street_number', models.PositiveIntegerField(verbose_name='Numer domu')),
|
||||
('apartement_number', models.PositiveIntegerField(blank=True, null=True, verbose_name='Numer mieszkania')),
|
||||
('parish', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submission', to='backend.parish', verbose_name='Parafia')),
|
||||
('user', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='submission', to=settings.AUTH_USER_MODEL, verbose_name='Użytkownik')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Zgłoszenie',
|
||||
'verbose_name_plural': 'Zgłoszenia',
|
||||
},
|
||||
),
|
||||
]
|
@ -0,0 +1,50 @@
|
||||
# Generated by Django 5.1.6 on 2025-06-26 13:19
|
||||
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
import pictures.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('backend', '0005_submission'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='parish',
|
||||
name='icon',
|
||||
field=pictures.models.PictureField(aspect_ratios=[None], blank=True, breakpoints={'l': 1200, 'm': 992, 's': 768, 'xl': 1400, 'xs': 576}, container_width=1200, file_types=['WEBP', 'PNG'], grid_columns=12, height_field='icon_height', null=True, pixel_densities=[1, 2], upload_to='icons', verbose_name='Ikona', width_field='icon_width'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='parish',
|
||||
name='streets',
|
||||
field=models.JSONField(blank=True, default=list, null=True, verbose_name='Ulice'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='submission',
|
||||
name='phone',
|
||||
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator(message="Numer telefonu musi być postaci: '(+48)123456789'", regex='^\\+?1?\\d{9,15}$')], verbose_name='Numer telefonu'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='submission',
|
||||
name='user',
|
||||
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='submission', to=settings.AUTH_USER_MODEL, verbose_name='Użytkownik'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Priest',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('parish', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='priest', to='backend.parish', verbose_name='Parafia')),
|
||||
('priest', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Ksiądz')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Ksiądz',
|
||||
'verbose_name_plural': 'Księża',
|
||||
},
|
||||
),
|
||||
]
|
0
backend/migrations/__init__.py
Normal file
0
backend/migrations/__init__.py
Normal file
65
backend/models.py
Normal file
65
backend/models.py
Normal file
@ -0,0 +1,65 @@
|
||||
from django.db import models
|
||||
from pictures.models import PictureField
|
||||
from authtools.models import User
|
||||
from django.core.validators import RegexValidator
|
||||
|
||||
# Create your models here.
|
||||
|
||||
|
||||
class Parish(models.Model):
|
||||
slug = models.SlugField("Identyfikator", primary_key=True)
|
||||
name = models.CharField("Nazwa", max_length=512)
|
||||
visible = models.BooleanField("Parafia widoczna", default=False)
|
||||
|
||||
icon = PictureField(
|
||||
"Ikona", upload_to="icons", height_field="icon_height", width_field="icon_width", null=True, blank=True
|
||||
)
|
||||
icon_width = models.PositiveIntegerField(null=True)
|
||||
icon_height = models.PositiveIntegerField(null=True)
|
||||
|
||||
channel = models.CharField("Identyfikator kanału", max_length=64, blank=True, null=True)
|
||||
submissions = models.BooleanField("Kolęda aktywna", default=False)
|
||||
announcements = models.TextField("Skrypt - Ogłoszenia", blank=True, null=True)
|
||||
intentions = models.TextField("Skrypt - Intencje", blank=True, null=True)
|
||||
streets = models.JSONField("Ulice", default=list, null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Parafia"
|
||||
verbose_name_plural = "Parafie"
|
||||
ordering = ["slug"]
|
||||
|
||||
|
||||
class Submission(models.Model):
|
||||
user = models.OneToOneField(
|
||||
User, on_delete=models.CASCADE, related_name="submission", verbose_name="Użytkownik", null=True, blank=True
|
||||
)
|
||||
parish = models.ForeignKey(Parish, on_delete=models.CASCADE, related_name="submission", verbose_name="Parafia")
|
||||
|
||||
name = models.CharField("Imię i nazwisko", max_length=256)
|
||||
phone_regex = RegexValidator(regex=r"^\+?1?\d{9,15}$", message="Numer telefonu musi być postaci: '(+48)123456789'")
|
||||
phone = models.CharField("Numer telefonu", max_length=32, validators=[phone_regex])
|
||||
street = models.CharField("Ulica", max_length=128)
|
||||
street_number = models.PositiveIntegerField("Numer domu")
|
||||
apartement_number = models.PositiveIntegerField("Numer mieszkania", null=True, blank=True)
|
||||
|
||||
@property
|
||||
def adres(self):
|
||||
return (
|
||||
f"{self.street} {self.street_number}{'/' + str(self.apartement_number) if self.apartement_number else ''}"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Zgłoszenie"
|
||||
verbose_name_plural = "Zgłoszenia"
|
||||
|
||||
|
||||
class Priest(models.Model):
|
||||
priest = models.OneToOneField(User, on_delete=models.CASCADE, related_name="priest_rel", verbose_name="Ksiądz")
|
||||
parish = models.ForeignKey(Parish, on_delete=models.CASCADE, related_name="priest_rel", verbose_name="Parafia")
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Ksiądz"
|
||||
verbose_name_plural = "Księża"
|
94
backend/templates/base.html
Normal file
94
backend/templates/base.html
Normal file
@ -0,0 +1,94 @@
|
||||
<!DOCTYPE html>
|
||||
{% load static i18n %}
|
||||
|
||||
<html lang="pl" dir="ltr" data-theme="light">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="author" content="Nikola Kubiczek" />
|
||||
|
||||
<title>eParafia</title>
|
||||
<base href="/" />
|
||||
|
||||
{% block head %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="x5-page-mode" content="app" />
|
||||
<meta name="browsermode" content="application" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.2/css/bulma.min.css" />
|
||||
{% comment %} <link rel="stylesheet" href="/background.css" /> {% endcomment %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Get all "navbar-burger" elements
|
||||
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0)
|
||||
|
||||
// Add a click event on each of them
|
||||
$navbarBurgers.forEach((el) => {
|
||||
el.addEventListener('click', () => {
|
||||
// Get the target from the "data-target" attribute
|
||||
const target = el.dataset.target
|
||||
const $target = document.getElementById(target)
|
||||
|
||||
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
|
||||
el.classList.toggle('is-active')
|
||||
$target.classList.toggle('is-active')
|
||||
})
|
||||
})
|
||||
|
||||
// main.js
|
||||
|
||||
let installPrompt = null
|
||||
const installButton = document.querySelector('#install')
|
||||
|
||||
window.addEventListener('beforeinstallprompt', (event) => {
|
||||
event.preventDefault()
|
||||
installPrompt = event
|
||||
installButton.classList.remove('is-hidden')
|
||||
})
|
||||
|
||||
installButton.addEventListener('click', async () => {
|
||||
if (!installPrompt) {
|
||||
return
|
||||
}
|
||||
const result = await installPrompt.prompt()
|
||||
console.log(`Install prompt was: ${result.outcome}`)
|
||||
disableInAppInstallPrompt()
|
||||
})
|
||||
|
||||
function disableInAppInstallPrompt() {
|
||||
installPrompt = null
|
||||
installButton.classList.add('is-hidden')
|
||||
}
|
||||
|
||||
window.addEventListener('appinstalled', disableInAppInstallPrompt)
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
.navbar-link.is-active,
|
||||
a.navbar-item.is-active {
|
||||
color: #0a0a0a;
|
||||
}
|
||||
.navbar-link.is-active:not(:focus):not(:hover),
|
||||
a.navbar-item.is-active:not(:focus):not(:hover) {
|
||||
background-color: initial;
|
||||
}
|
||||
.navbar-item.has-dropdown.is-active .navbar-link,
|
||||
.navbar-item.has-dropdown:focus .navbar-link,
|
||||
.navbar-item.has-dropdown:hover .navbar-link {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="container section">
|
||||
{% block layout %}
|
||||
<main class="section container">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
16
backend/templates/index.html
Normal file
16
backend/templates/index.html
Normal file
@ -0,0 +1,16 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="has-text-centered is-flex is-flex-direction-column">
|
||||
<p class="is-size-2 has-text-weight-semibold is-family-monospace">eParafia</p>
|
||||
|
||||
<p class="is-family-monospace">Dziękujemy za korzystanie z naszej aplikacji! Poniżej znajduje się lista odnośników do wszystkich zarejestrowanych z nami parafii. Klikając w przycisk zostaniecie państwo bezpośrednio przekierowani do aplikacji danej parafii</p>
|
||||
|
||||
<p class="is-size-4 has-text-weight-light">Zapraszamy!</p>
|
||||
<div class="is-flex is-flex-direction-column mt-6">
|
||||
{% for parish in parishes %}
|
||||
<a class="button has-text-weight-semibold mx-auto my-2" href="{% url 'parish' parish.slug %}">{{ parish.name }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
5
backend/templates/parish/announcements.html
Normal file
5
backend/templates/parish/announcements.html
Normal file
@ -0,0 +1,5 @@
|
||||
{% extends 'parish/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content">{{res | safe}}</div>
|
||||
{% endblock %}
|
45
backend/templates/parish/base.html
Normal file
45
backend/templates/parish/base.html
Normal file
@ -0,0 +1,45 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load pictures %}
|
||||
|
||||
{% block head %}
|
||||
<link href="{% img_url parish.icon file_type='png' width=512 %}" rel="icon" type="image/png" sizes="512x512" />
|
||||
<link href="{% img_url parish.icon file_type='png' width=192 %}" rel="icon" type="image/png" sizes="192x192" />
|
||||
<link rel="manifest" href="{% url 'manifest' parish.slug %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block layout %}
|
||||
<div class="container">
|
||||
<nav class="navbar is-transparent" role="navigation" aria-label="menu">
|
||||
<div class="navbar-brand is-flex is-flex-wrap-wrap is-justify-content-space-between is-flex-shrink-1">
|
||||
<span class="navbar-item is-is-active is-flex-shrink-1"><span class="title is-4 is-unselectable">{{ parish.name }}</span></span>
|
||||
<a role="button" class="navbar-burger" data-target="navMenu" aria-label="menu" aria-expanded="false">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="navbar-menu" id="navMenu">
|
||||
<h5 class="navbar-end has-text-weight-bold">
|
||||
<a class="navbar-item {{ announcements }}" href="{% url 'announcements' parish.slug %}">Ogłoszenia parafialne</a>
|
||||
<a class="navbar-item {{ intentions }}" href="{% url 'intentions' parish.slug %}">Intencje</a>
|
||||
{% if parish.channel %}
|
||||
<a class="navbar-item {{ live }}" href="{% url 'live' parish.slug %}">Transmisja on-line</a>
|
||||
{% endif %}
|
||||
|
||||
{% if parish.submissions %}
|
||||
<a class="navbar-item {{ visit }}" href="{% url 'visit' parish.slug %}">Wizyta Duszpasterska</a>
|
||||
{% endif %}
|
||||
<a class="navbar-item is-clickable is-hidden" id="install">Zainstaluj</a>
|
||||
</h5>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="section">
|
||||
<div id="backgroundDesktop"></div>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
<div id="backgroundMobile"></div>
|
||||
</div>
|
||||
{% endblock %}
|
5
backend/templates/parish/intentions.html
Normal file
5
backend/templates/parish/intentions.html
Normal file
@ -0,0 +1,5 @@
|
||||
{% extends 'parish/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content">{{ res | safe }}</div>
|
||||
{% endblock %}
|
14
backend/templates/parish/live.html
Normal file
14
backend/templates/parish/live.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% extends 'parish/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="is-flex-shrink-0">
|
||||
<figure class="image is-16by9">
|
||||
<iframe
|
||||
class="has-ratio"
|
||||
src="https://www.youtube-nocookie.com/embed/live_stream?channel={{parish.channel}}"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
22
backend/templates/parish/manifest.json
Normal file
22
backend/templates/parish/manifest.json
Normal file
@ -0,0 +1,22 @@
|
||||
{% load pictures %}
|
||||
|
||||
{
|
||||
"name": "{{ parish.name }}",
|
||||
"display": "standalone",
|
||||
"scope": "{% url 'parish' parish.slug %}",
|
||||
"start_url": "{% url 'parish' parish.slug %}",
|
||||
"icons": [
|
||||
{
|
||||
"src": "{% img_url parish.icon file_type="png" width=192 %}",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "{% img_url parish.icon file_type="png" width=512 %}",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
}
|
||||
]
|
||||
}
|
8
backend/templates/parish/visit/auth.html
Normal file
8
backend/templates/parish/visit/auth.html
Normal file
@ -0,0 +1,8 @@
|
||||
{% extends 'parish/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<p class="has-text-centered">
|
||||
<a class="button is-light" href="{% url 'visit_login' parish.slug %}">Zaloguj się</a>
|
||||
<a class="button is-light" href="{% url 'visit_signup' parish.slug %}">Zarejestruj się</a>
|
||||
</p>
|
||||
{% endblock %}
|
18
backend/templates/parish/visit/form.html
Normal file
18
backend/templates/parish/visit/form.html
Normal file
@ -0,0 +1,18 @@
|
||||
{% extends 'parish/base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<p class="is-size-3 has-text-centered">Zapisz się na wizytę duszpasterską</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<p class="has-text-centered">
|
||||
<button type="submit" class="button is-success">Zatwierdź</button>
|
||||
</p>
|
||||
</form>
|
||||
<form action="{% url 'visit_logout' parish.slug %}" method="post" class="is-flex is-justify-content-flex-end is-gap-2">
|
||||
{% csrf_token %}
|
||||
<a href="{% url 'visit_account' parish.slug %}" class="button is-info">Zmień dane konta</a>
|
||||
<button type="submit" class="button is-danger">Wyloguj się</button>
|
||||
</form>
|
||||
{% endblock %}
|
14
backend/templates/parish/visit/login.html
Normal file
14
backend/templates/parish/visit/login.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% extends 'parish/base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<p class="is-size-2 has-text-centered">Zaloguj się</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<p class="has-text-centered">
|
||||
<button type="submit" class="button is-success">Zaloguj się</button>
|
||||
<a href="{% url 'visit' parish.slug %}" class="button is-light">Powrót</a>
|
||||
</p>
|
||||
</form>
|
||||
{% endblock %}
|
14
backend/templates/parish/visit/signup.html
Normal file
14
backend/templates/parish/visit/signup.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% extends 'parish/base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<p class="is-size-2 has-text-centered">Zarejestruj się</p>
|
||||
<form method="post" class="form">
|
||||
{% csrf_token %}
|
||||
{{ form | crispy }}
|
||||
<p class="has-text-centered">
|
||||
<button type="submit" class="button is-success">Zarejestruj się</button>
|
||||
<a href="{% url 'visit' parish.slug %}" class="button is-light">Powrót</a>
|
||||
</p>
|
||||
</form>
|
||||
{% endblock %}
|
28
backend/templates/parish/visit/submitted.html
Normal file
28
backend/templates/parish/visit/submitted.html
Normal file
@ -0,0 +1,28 @@
|
||||
{% extends 'parish/base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<p class="is-size-3 has-text-centered">Twoje zgłoszenie</p>
|
||||
<div class="is-size-5 has-text-centered mb-2">
|
||||
<p>
|
||||
<b>Imię i nazwisko:</b> {{ submission.name }}, <b>Telefon:</b> {{ submission.phone }}
|
||||
</p>
|
||||
<p>
|
||||
<b>Adres:</b> {{ submission.street }} {{ submission.street_number }}{% if submission.apartement_number %}/{% endif %}{{ submission.apartement_number|default_if_none:'' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<p class="has-text-centered">
|
||||
<button type="submit" class="button is-success">Wycofaj zgłoszenie</button>
|
||||
</p>
|
||||
</form>
|
||||
<p>
|
||||
<form action="{% url 'visit_logout' parish.slug %}" method="post" class="is-flex is-justify-content-flex-end">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="button is-danger">Wyloguj się</button>
|
||||
</form>
|
||||
</p>
|
||||
{% endblock %}
|
18
backend/templates/parish/visit/update.html
Normal file
18
backend/templates/parish/visit/update.html
Normal file
@ -0,0 +1,18 @@
|
||||
{% extends 'parish/base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<p class="is-size-3 has-text-centered">Zmień dane konta</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<p class="has-text-centered">
|
||||
<button type="submit" class="button is-success">Zatwierdź</button>
|
||||
<a href="{% url 'visit' parish.slug %}" class="button is-secondary">Powrót</a>
|
||||
</p>
|
||||
</form>
|
||||
{% comment %} <form action="{% url 'visit_logout' parish.slug %}" method="post" class="is-flex is-justify-content-flex-end">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="button is-danger">Wyloguj się</button>
|
||||
</form> {% endcomment %}
|
||||
{% endblock %}
|
3
backend/tests.py
Normal file
3
backend/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
266
backend/views.py
Normal file
266
backend/views.py
Normal file
@ -0,0 +1,266 @@
|
||||
from django.shortcuts import render, redirect
|
||||
from django.urls import path
|
||||
from .models import Parish, Submission
|
||||
import requests # noqa: F401
|
||||
from lxml import html # noqa: F401
|
||||
from django.http import HttpResponse
|
||||
from django.views.generic import CreateView, DeleteView, UpdateView
|
||||
from authtools.forms import UserCreationForm
|
||||
from authtools.models import User
|
||||
from django.contrib.auth.views import LoginView, LogoutView
|
||||
from django.urls import reverse_lazy
|
||||
from django import forms
|
||||
|
||||
|
||||
# Create your views here.
|
||||
|
||||
|
||||
def favicon(request):
|
||||
return HttpResponse(None)
|
||||
|
||||
|
||||
def manifest(request, slug):
|
||||
return render(
|
||||
request, "parish/manifest.json", {"parish": Parish.objects.get(slug=slug)}, content_type="application/json"
|
||||
)
|
||||
|
||||
|
||||
def index(request):
|
||||
parishes = Parish.objects.filter(visible=True)
|
||||
return render(request, "index.html", {"parishes": parishes})
|
||||
|
||||
|
||||
def announcements(request, slug):
|
||||
p = Parish.objects.get(slug=slug)
|
||||
ldict = {}
|
||||
exec(p.announcements, globals(), ldict)
|
||||
|
||||
return render(
|
||||
request, "parish/announcements.html", {"parish": p, "announcements": "is-active", "res": ldict.get("res", "")}
|
||||
)
|
||||
|
||||
|
||||
def intentions(request, slug):
|
||||
p = Parish.objects.get(slug=slug)
|
||||
ldict = {}
|
||||
exec(p.intentions, globals(), ldict)
|
||||
|
||||
return render(
|
||||
request, "parish/intentions.html", {"parish": p, "intentions": "is-active", "res": ldict.get("res", "")}
|
||||
)
|
||||
|
||||
|
||||
def live(request, slug):
|
||||
p = Parish.objects.get(slug=slug)
|
||||
if not p.channel:
|
||||
return redirect("announcements", slug=slug)
|
||||
|
||||
return render(request, "parish/live.html", {"parish": p, "live": "is-active"})
|
||||
|
||||
|
||||
def visit(request, slug):
|
||||
p = Parish.objects.get(slug=slug)
|
||||
c = {"parish": p, "visit": "is-active"}
|
||||
if not p.submissions:
|
||||
return redirect("announcements", slug=slug)
|
||||
|
||||
if not request.user.is_authenticated:
|
||||
return render(request, "parish/visit/auth.html", c)
|
||||
|
||||
if not Submission.objects.filter(user=request.user).exists():
|
||||
return VisitCreateView.as_view()(request, slug=slug)
|
||||
|
||||
c["submission"] = Submission.objects.get(user=request.user)
|
||||
return VisitDeleteView.as_view()(request, slug=slug)
|
||||
|
||||
|
||||
class SubmissionForm(forms.ModelForm):
|
||||
def __init__(self, slug, *args, **kwargs):
|
||||
super(SubmissionForm, self).__init__(*args, **kwargs)
|
||||
streets = Parish.objects.get(slug=slug).streets
|
||||
choices = zip(streets, streets)
|
||||
self.fields["street"] = forms.ChoiceField(label="Ulica", choices=choices)
|
||||
|
||||
class Meta:
|
||||
model = Submission
|
||||
fields = [
|
||||
"name",
|
||||
"phone",
|
||||
"street",
|
||||
"street_number",
|
||||
"apartement_number",
|
||||
]
|
||||
|
||||
|
||||
class VisitCreateView(CreateView):
|
||||
form_class = SubmissionForm
|
||||
template_name = "parish/visit/form.html"
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.parish = Parish.objects.get(slug=self.kwargs["slug"])
|
||||
form.instance.user = self.request.user
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_initial(self):
|
||||
return {"name": self.request.user.name}
|
||||
|
||||
def get_success_url(self):
|
||||
p = Parish.objects.get(slug=self.kwargs["slug"])
|
||||
return reverse_lazy("visit", args=[p.slug])
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
p = Parish.objects.get(slug=self.kwargs["slug"])
|
||||
context["parish"] = p
|
||||
context["visit"] = "is-active"
|
||||
return context
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
form.fields["name"].disabled = True
|
||||
return form
|
||||
|
||||
def get_form_kwargs(self):
|
||||
return {**super().get_form_kwargs(), **self.kwargs}
|
||||
|
||||
|
||||
class VisitDeleteView(DeleteView):
|
||||
model = Submission
|
||||
template_name = "parish/visit/submitted.html"
|
||||
|
||||
def get_object(self):
|
||||
return Submission.objects.get(user=self.request.user)
|
||||
|
||||
def get_success_url(self):
|
||||
p = Parish.objects.get(slug=self.kwargs["slug"])
|
||||
return reverse_lazy("visit", args=[p.slug])
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
p = Parish.objects.get(slug=self.kwargs["slug"])
|
||||
context["parish"] = p
|
||||
context["visit"] = "is-active"
|
||||
return context
|
||||
|
||||
|
||||
class VisitLoginView(LoginView):
|
||||
redirect_authenticated_user = True
|
||||
template_name = "parish/visit/login.html"
|
||||
|
||||
def get_success_url(self):
|
||||
p = Parish.objects.get(slug=self.kwargs["slug"])
|
||||
return reverse_lazy("visit", args=[p.slug])
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
p = Parish.objects.get(slug=kwargs["slug"])
|
||||
if self.request.user.is_authenticated:
|
||||
return redirect("visit", slug=p.slug)
|
||||
|
||||
if not p.submissions:
|
||||
return redirect("announcements", slug=p.slug)
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
p = Parish.objects.get(slug=self.kwargs["slug"])
|
||||
context["parish"] = p
|
||||
context["visit"] = "is-active"
|
||||
return context
|
||||
|
||||
|
||||
class VisitSignUpView(CreateView):
|
||||
form_class = UserCreationForm
|
||||
template_name = "parish/visit/signup.html"
|
||||
|
||||
def get_success_url(self):
|
||||
p = Parish.objects.get(slug=self.kwargs["slug"])
|
||||
return reverse_lazy("visit", args=[p.slug])
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
p = Parish.objects.get(slug=kwargs["slug"])
|
||||
if self.request.user.is_authenticated:
|
||||
return redirect("visit", slug=p.slug)
|
||||
|
||||
if not p.submissions:
|
||||
return redirect("announcements", slug=p.slug)
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
p = Parish.objects.get(slug=self.kwargs["slug"])
|
||||
context["parish"] = p
|
||||
context["visit"] = "is-active"
|
||||
return context
|
||||
|
||||
|
||||
class UserUpdateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ["email", "name"]
|
||||
|
||||
|
||||
class VisitAccountView(UpdateView):
|
||||
form_class = UserUpdateForm
|
||||
template_name = "parish/visit/update.html"
|
||||
|
||||
def get_object(self):
|
||||
return User.objects.get(id=self.request.user.id)
|
||||
|
||||
def get_success_url(self):
|
||||
p = Parish.objects.get(slug=self.kwargs["slug"])
|
||||
return reverse_lazy("visit", args=[p.slug])
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
p = Parish.objects.get(slug=kwargs["slug"])
|
||||
# if self.request.user.is_authenticated:
|
||||
# return redirect("visit", slug=p.slug)
|
||||
|
||||
if not p.submissions:
|
||||
return redirect("announcements", slug=p.slug)
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
p = Parish.objects.get(slug=self.kwargs["slug"])
|
||||
context["parish"] = p
|
||||
context["visit"] = "is-active"
|
||||
return context
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super().get_form(form_class)
|
||||
form.fields["name"].label = "Imię i nazwisko"
|
||||
return form
|
||||
|
||||
|
||||
class VisitLogoutView(LogoutView):
|
||||
template_name = "parish/visit/auth.html"
|
||||
|
||||
def get_next_page(self):
|
||||
p = Parish.objects.get(slug=self.kwargs["slug"])
|
||||
return reverse_lazy("visit", args=[p.slug])
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
p = Parish.objects.get(slug=self.kwargs["slug"])
|
||||
context["parish"] = p
|
||||
context["visit"] = "is-active"
|
||||
return context
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path("", index, name="index"),
|
||||
path("favicon.ico", favicon),
|
||||
path("<str:slug>/", announcements, name="parish"),
|
||||
path("<str:slug>/ogloszenia/", announcements, name="announcements"),
|
||||
path("<str:slug>/intencje/", intentions, name="intentions"),
|
||||
path("<str:slug>/transmisja/", live, name="live"),
|
||||
path("<str:slug>/wizyta/", visit, name="visit"),
|
||||
path("<str:slug>/wizyta/logowanie/", VisitLoginView.as_view(), name="visit_login"),
|
||||
path("<str:slug>/wizyta/rejestracja/", VisitSignUpView.as_view(), name="visit_signup"),
|
||||
path("<str:slug>/wizyta/konto/", VisitAccountView.as_view(), name="visit_account"),
|
||||
path("<str:slug>/wizyta/wylogowanie/", VisitLogoutView.as_view(), name="visit_logout"),
|
||||
path("<str:slug>/manifest.json", manifest, name="manifest"),
|
||||
]
|
Reference in New Issue
Block a user