Skip to content

Commit

Permalink
Merge pull request #156 from abe-101/use-htm-modal-forms
Browse files Browse the repository at this point in the history
use htmx modal to update calendar
  • Loading branch information
abe-101 authored Jan 5, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents 6138f1b + ed9492d commit 480c023
Showing 13 changed files with 143 additions and 3,355 deletions.
8 changes: 8 additions & 0 deletions config/settings/base.py
Original file line number Diff line number Diff line change
@@ -92,6 +92,7 @@
"rest_framework.authtoken",
"corsheaders",
"drf_spectacular",
"django_htmx_modal_forms",
"django_htmx_messages",
]

@@ -164,6 +165,7 @@
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"my_hebrew_dates.core.middleware.CustomLoginRequiredMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"allauth.account.middleware.AccountMiddleware",
@@ -366,6 +368,12 @@
# django-cors-headers - https://github.com/adamchainz/django-cors-headers#setup
CORS_URLS_REGEX = r"^/api/.*$"

# Regex patterns for paths that bypass LoginRequiredMiddleware
OPEN_URLS = [
r"^/api/.*",
# ...
]

# By Default swagger ui is available only to admin user(s). You can change permission classes to change that
# See more configuration options at https://drf-spectacular.readthedocs.io/en/latest/settings.html#settings
SPECTACULAR_SETTINGS = {
40 changes: 25 additions & 15 deletions config/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# ruff: noqa
from django.conf import settings
from django.conf.urls.static import static
from django.contrib.auth.decorators import login_not_required
from django.contrib import admin
from django.urls import include, path
from django.views import defaults as default_views
@@ -18,38 +19,47 @@


urlpatterns = [
path("", TemplateView.as_view(template_name="pages/home.html"), name="home"),
path(
"",
login_not_required(TemplateView.as_view(template_name="pages/home.html")),
name="home",
),
path(
"about/",
TemplateView.as_view(template_name="pages/about.html"),
login_not_required(TemplateView.as_view(template_name="pages/about.html")),
name="about",
),
# Django Admin, use {% url 'admin:index' %}
path(settings.ADMIN_URL, admin.site.urls),
# User management
path("users/", include("my_hebrew_dates.users.urls", namespace="users")),
path("accounts/", include("allauth.urls")),
# Your stuff: custom urls includes go here
path("calendars/", include("my_hebrew_dates.hebcal.urls", namespace="hebcal")),
path("automation/", webhook_interest, name="webhook_interest"),
path(
"robots.txt",
TemplateView.as_view(template_name="robots.txt", content_type="text/plain"),
), # add the robots.txt file
login_not_required(
TemplateView.as_view(template_name="robots.txt", content_type="text/plain")
),
),
path(
"favicon.png",
RedirectView.as_view(url="/static/images/favicon.png", permanent=True),
login_not_required(
RedirectView.as_view(url="/static/images/favicon.png", permanent=True)
),
),
path(
"favicon.ico",
RedirectView.as_view(url="static/images/favicons/favicon.ico", permanent=True),
login_not_required(
RedirectView.as_view(
url="static/images/favicons/favicon.ico", permanent=True
)
),
),
path(
"sitemap.xml",
sitemap,
login_not_required(sitemap),
{"sitemaps": sitemaps},
name="django.contrib.sitemaps.views.sitemap",
),
path("automation/", login_not_required(webhook_interest), name="webhook_interest"),
path(settings.ADMIN_URL, admin.site.urls),
path("users/", include("my_hebrew_dates.users.urls", namespace="users")),
path("accounts/", include("allauth.urls")),
path("calendars/", include("my_hebrew_dates.hebcal.urls", namespace="hebcal")),
# Media files
*static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT),
]
21 changes: 21 additions & 0 deletions my_hebrew_dates/core/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""
https://github.com/encode/django-rest-framework/discussions/9503#discussioncomment-10487612
"""

import re

from django.conf import settings
from django.contrib.auth.middleware import LoginRequiredMiddleware


class CustomLoginRequiredMiddleware(LoginRequiredMiddleware):
def __init__(self, get_response=None):
self.get_response = get_response
self.open_urls = [re.compile(url) for url in settings.OPEN_URLS]
super().__init__(get_response)

def process_view(self, request, view_func, view_args, view_kwargs):
for url in self.open_urls:
if url.match(request.path):
return None # Pass through, no login required
return super().process_view(request, view_func, view_args, view_kwargs)
1 change: 1 addition & 0 deletions my_hebrew_dates/hebcal/forms.py
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@ class Meta:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False


class HebrewDateForm(forms.ModelForm):
4 changes: 0 additions & 4 deletions my_hebrew_dates/hebcal/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -153,10 +153,6 @@ def test_unauthorized_access(self):
f"/accounts/login/?next=/calendars/{self.calendar.uuid}/edit/",
)

def test_view_renders_correctly(self):
response = self.client.get(self.url)
self.assertContains(response, "Manage Your Calendar")

def test_filter_by_month(self):
response = self.client.get(self.url, {"month": "1"})
self.assertContains(
27 changes: 23 additions & 4 deletions my_hebrew_dates/hebcal/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from django.contrib.auth.decorators import login_not_required
from django.urls import path

from my_hebrew_dates.hebcal.views import CalendarDeleteView
from my_hebrew_dates.hebcal.views import CalendarUpdateModalView
from my_hebrew_dates.hebcal.views import calendar_detail_view
from my_hebrew_dates.hebcal.views import calendar_edit_view
from my_hebrew_dates.hebcal.views import calendar_file
@@ -21,6 +23,11 @@
edit_hebrew_date_htmx,
name="edit_hebrew_date_htmx",
),
path(
"<int:pk>/update-calendar-modal/",
view=CalendarUpdateModalView.as_view(),
name="update_calendar",
),
path(
"<uuid:uuid>/delete-hebrew-date-htmx/<int:pk>/",
delete_hebrew_date_htmx,
@@ -34,10 +41,22 @@
path("<uuid:uuid>/delete/", CalendarDeleteView.as_view(), name="calendar_delete"),
path("new/", create_calendar_view, name="calendar_new"),
path("", calendar_list_view, name="calendar_list"),
path("<uuid:uuid>/", calendar_detail_view, name="calendar_detail"),
path("<uuid:uuid>.ical", calendar_file, name="legacy_calendar_file"),
path("<uuid:uuid>.ics", calendar_file, name="calendar_file"),
path("serve-image/<uuid:pixel_id>/<int:pk>", serve_pixel, name="serve_pixel"),
path(
"<uuid:uuid>/",
login_not_required(calendar_detail_view),
name="calendar_detail",
),
path(
"<uuid:uuid>.ical",
login_not_required(calendar_file),
name="legacy_calendar_file",
),
path("<uuid:uuid>.ics", login_not_required(calendar_file), name="calendar_file"),
path(
"serve-image/<uuid:pixel_id>/<int:pk>",
login_not_required(serve_pixel),
name="serve_pixel",
),
path(
"update-calendar-links-htmx/<uuid:uuid>/",
update_calendar_links_htmx,
17 changes: 10 additions & 7 deletions my_hebrew_dates/hebcal/views.py
Original file line number Diff line number Diff line change
@@ -7,8 +7,8 @@
from discord import SyncWebhook
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.sites.models import Site
from django.core.mail import send_mail
from django.http import HttpRequest
@@ -20,6 +20,7 @@
from django.views.decorators.cache import cache_page
from django.views.decorators.http import require_POST
from django.views.generic.edit import DeleteView
from django_htmx_modal_forms import HtmxModalUpdateView

from my_hebrew_dates.hebcal.decorators import requires_htmx
from my_hebrew_dates.hebcal.forms import CalendarForm
@@ -36,7 +37,6 @@
logger = logging.getLogger(__name__)


@login_required
def calendar_list_view(request):
user_owned_calendars = Calendar.objects.filter(owner=request.user)

@@ -76,7 +76,6 @@ def calendar_detail_view(request: HttpRequest, uuid: UUID):
return render(request, "hebcal/calendar_detail.html", context)


@login_required
def create_calendar_view(request: HttpRequest):
if request.method == "POST":
form = CalendarForm(request.POST)
@@ -116,7 +115,14 @@ def create_calendar_view(request: HttpRequest):
return render(request, "hebcal/calendar_new.html", context)


@login_required
class CalendarUpdateModalView(SuccessMessageMixin, HtmxModalUpdateView):
model = Calendar
modal_size = "md"
form_class = CalendarForm
detail_template_name = "hebcal/_calendar_name.html"
success_message = "Calendar updated successfully"


def calendar_edit_view(request: HttpRequest, uuid: UUID):
calendar = get_object_or_404(Calendar, owner=request.user, uuid=uuid)
month_values = request.GET.getlist("month")
@@ -187,7 +193,6 @@ def calendar_edit_view(request: HttpRequest, uuid: UUID):
return render(request, "hebcal/calendar_edit.html", context)


@login_required
@requires_htmx
def edit_hebrew_date_htmx(request: HttpRequest, uuid: UUID, pk: int):
calendar = get_object_or_404(Calendar, owner=request.user, uuid=uuid)
@@ -255,7 +260,6 @@ def edit_hebrew_date_htmx(request: HttpRequest, uuid: UUID, pk: int):
return render(request, "hebcal/_hebrew_date_form.html", context)


@login_required
@requires_htmx
def create_hebrew_date_htmx(request: HttpRequest, uuid: UUID):
calendar = get_object_or_404(Calendar, owner=request.user, uuid=uuid)
@@ -297,7 +301,6 @@ def create_hebrew_date_htmx(request: HttpRequest, uuid: UUID):
return render(request, "hebcal/_hebrew_date_form.html", context)


@login_required
@require_POST
@requires_htmx
def delete_hebrew_date_htmx(request: HttpRequest, uuid: UUID, pk: int):
3,306 changes: 1 addition & 3,305 deletions my_hebrew_dates/static/js/htmx.min.js

Large diffs are not rendered by default.

40 changes: 23 additions & 17 deletions my_hebrew_dates/templates/base.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% load static i18n compress %}
{% load static i18n compress django_htmx htmx_modal_forms %}

<!DOCTYPE html>
{% get_current_language as LANGUAGE_CODE %}
@@ -42,12 +42,15 @@
{% block css %}
<!-- Latest compiled and minified Bootstrap CSS -->
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM"
crossorigin="anonymous" />
<!-- Your stuff: Third-party CSS libraries go here -->
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css"
integrity="sha512-b2QcS5SsA8tZodcDtGRELiGv5SaKSk1vDHDaQRda0htPYWZ6046lr3kJ5bAAQdpV2mmA/4v0wQF9MyU6/pDIAg=="
crossorigin="anonymous"
referrerpolicy="no-referrer" />
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" />
href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css"
integrity="sha512-dPXYcDub/aeb08c63jRq/k6GaKccl256JQy/AnOq7CAnEZ9FzSL9wSbcZkMp4R26vBsMLFYH4kQ67/bbV8XaCQ=="
crossorigin="anonymous"
referrerpolicy="no-referrer" />
<!-- Your stuff: Third-party CSS libraries go here -->
<!-- This file stores project-specific CSS -->
{% compress css %}
@@ -61,10 +64,14 @@
================================================== -->
{# Placed at the top of the document so pages load faster with defer #}
{% block javascript %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
crossorigin="anonymous"></script>
<!-- Your stuff: Third-party javascript libraries go here -->
<script defer
src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.min.js"
integrity="sha512-WW8/jxkELe2CAiE4LvQfwm1rajOS8PHasCCx+knHG0gBHt8EXxS6T6tJRTGuDQVnluuAvMxWF4j8SNFDKceLFg=="
crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
<!-- Your stuff: Third-party javascript libraries go here -->
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-MLL490GGCR"></script>
@@ -89,30 +96,29 @@
integrity="sha256-io4G+JpruJtt8SoSUOgLQrhBj/YtQXFgIvkGzBxQAUQ="
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="{% static 'js/htmx.min.js' %}"></script>
<!-- place project specific Javascript in this file -->
{% compress js %}
<script defer src="{% static 'js/project.js' %}"></script>
<script defer src="{% static 'js/theme-toggler.js' %}"></script>
<script defer src="{% static 'js/htmx.min.js' %}"></script>
<script defer src="{% static 'toasts.js' %}"></script>
{% endcompress %}
{% django_htmx_script %}
{% htmx_modal_script %}
{% endblock javascript %}
</head>
<body class="{% block bodyclass %}d-flex flex-column min-vh-100{% endblock bodyclass %}"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
class="{% block bodyclass %}d-flex flex-column min-vh-100{% endblock bodyclass %}">
<div class="container flex-grow-1 mb-5">
<div class="mb-1">
{% include '_navbar.html' %}
<div class="szPlaceholder"></div>
</div>
<div class="mb-1">{% include '_navbar.html' %}</div>
<div class="container">
{% include 'toasts.html' %}
{% block body %}
{% block main %}
{% block content %}
<p>Use this document as a way to quick start any new project.</p>
{% endblock content %}
{% endblock main %}
{% include 'toasts.html' %}
{% endblock body %}
</div>
<!-- /container -->
17 changes: 17 additions & 0 deletions my_hebrew_dates/templates/hebcal/_calendar_name.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div id="calendar-{{ calendar.id }}" class="mb-4">
<div class="d-flex align-items-center gap-3">
<div>
<h1 class="mb-1">{{ calendar.name }}</h1>
<small class="text-muted">Timezone: {{ calendar.timezone }}</small>
</div>
<button hx-get="{% url 'hebcal:update_calendar' pk=calendar.pk %}"
hx-target="body"
hx-swap="beforeend"
data-bs-toggle="tooltip"
class="btn btn-primary align-self-start mt-2"
title="Edit Calendar">
<i class="bi bi-pencil-square me-1"></i>
Edit
</button>
</div>
</div>
2 changes: 1 addition & 1 deletion my_hebrew_dates/templates/hebcal/calendar_edit.html
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
{% endblock title %}
{% block content %}
<div class="container mt-4">
<h1 class="mb-4">Manage Your Calendar: {{ calendar.name }}</h1>
{% include "hebcal/_calendar_name.html" %}
<div class="row mb-4">
<div class="col">
<form id="filter-form"
10 changes: 10 additions & 0 deletions my_hebrew_dates/users/admin.py
Original file line number Diff line number Diff line change
@@ -39,6 +39,16 @@ class UserAdmin(auth_admin.UserAdmin):
),
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = ( # Add this section
(
None,
{
"classes": ("wide",),
"fields": ("username", "email", "password1", "password2"),
},
),
)

list_display = [
"username",
"name",
5 changes: 3 additions & 2 deletions requirements/base.txt
Original file line number Diff line number Diff line change
@@ -13,20 +13,21 @@ discord.py==2.4.0 # https://github.com/Rapptz/discord.py

# Django
# ------------------------------------------------------------------------------
django==5.0.10 # pyup: < 5.1 # https://www.djangoproject.com/
django==5.1.4 # pyup: < 5.2 # https://www.djangoproject.com/
django-environ==0.11.2 # https://github.com/joke2k/django-environ
django-model-utils==5.0.0 # https://github.com/jazzband/django-model-utils
django-allauth[mfa,socialaccount]==65.3.1 # https://github.com/pennersr/django-allauth
django-crispy-forms==2.3 # https://github.com/django-crispy-forms/django-crispy-forms
crispy-bootstrap5==2024.10 # https://github.com/django-crispy-forms/crispy-bootstrap5
django-compressor==4.5.1 # https://github.com/django-compressor/django-compressor
django-redis==5.4.0 # https://github.com/jazzband/django-redis
django-htmx==1.17.2 # https://github.com/adamchainz/django-htmx
django-htmx==1.21.0 # https://github.com/adamchainz/django-htmx
django-extensions==3.2.3 # https://github.com/django-extensions/django-extensions
django-libsass==0.9 # https://github.com/torchbox/django-libsass
# Django REST Framework
djangorestframework==3.15.2 # https://github.com/encode/django-rest-framework
django-cors-headers==4.6.0 # https://github.com/adamchainz/django-cors-headers
# DRF-spectacular for api documentation
drf-spectacular==0.28.0 # https://github.com/tfranzel/drf-spectacular
django-htmx-modal-forms==0.2.1 # https://github.com/abe-101/django-htmx-modal-forms
django-htmx-messages==0.1.1 # https://github.com/abe-101/django-htmx-messages

0 comments on commit 480c023

Please sign in to comment.