Add Geschichte (History) wiki-style feature with reorganized navigation
🆕 NEW FEATURES: - Wiki-style Geschichte (History) section with rich text editor - Image upload support for history pages - Quill.js rich text editor with formatting options - Slug-based URLs for SEO-friendly history pages - Image galleries with descriptions and alt-text support 🔧 MODELS: - GeschichteSeite: Main history pages with rich content - GeschichteBild: Image attachments for history pages - Auto-generated slugs, sorting, publishing controls 📝 TEMPLATES: - geschichte/liste.html: Card-based overview of all history pages - geschichte/detail.html: Full page view with image gallery - geschichte/form.html: Rich text editor for creating/editing pages - geschichte/bild_form.html: Image upload interface 🎨 UI IMPROVEMENTS: - Reorganized navigation menu into logical groups: * Menschen & Finanzen (People & Finance) * Immobilien & Land (Real Estate & Land) * Verwaltung (Administration) * Geschichte (History) - More compact menu design saving horizontal space - Better grouping with dropdown headers 🛠️ TECHNICAL: - Rich text editor with Quill.js integration - Image upload with validation and optimization - Permission-based access controls - Responsive design for all screen sizes - Proper breadcrumb navigation - Auto-slug generation from titles
This commit is contained in:
@@ -1603,3 +1603,92 @@ class BackupTokenRegenerateForm(forms.Form):
|
|||||||
label='Passwort',
|
label='Passwort',
|
||||||
help_text='Geben Sie Ihr Passwort ein, um neue Backup-Codes zu generieren'
|
help_text='Geben Sie Ihr Passwort ein, um neue Backup-Codes zu generieren'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GeschichteSeiteForm(forms.ModelForm):
|
||||||
|
"""Form for creating and editing history pages"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
from .models import GeschichteSeite
|
||||||
|
model = GeschichteSeite
|
||||||
|
fields = ['titel', 'slug', 'inhalt', 'ist_veroeffentlicht', 'sortierung']
|
||||||
|
widgets = {
|
||||||
|
'titel': forms.TextInput(attrs={
|
||||||
|
'class': 'form-control',
|
||||||
|
'placeholder': 'z.B. Gründung der Stiftung'
|
||||||
|
}),
|
||||||
|
'slug': forms.TextInput(attrs={
|
||||||
|
'class': 'form-control',
|
||||||
|
'placeholder': 'z.B. gruendung-der-stiftung'
|
||||||
|
}),
|
||||||
|
'inhalt': forms.Textarea(attrs={
|
||||||
|
'class': 'form-control rich-text-editor',
|
||||||
|
'rows': 20,
|
||||||
|
'placeholder': 'Schreiben Sie hier den Inhalt der Geschichtsseite...'
|
||||||
|
}),
|
||||||
|
'ist_veroeffentlicht': forms.CheckboxInput(attrs={
|
||||||
|
'class': 'form-check-input'
|
||||||
|
}),
|
||||||
|
'sortierung': forms.NumberInput(attrs={
|
||||||
|
'class': 'form-control',
|
||||||
|
'min': 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
help_texts = {
|
||||||
|
'slug': 'URL-freundliche Version des Titels (nur Buchstaben, Zahlen und Bindestriche)',
|
||||||
|
'inhalt': 'Unterstützt Rich-Text-Formatierung, Bilder und Videos',
|
||||||
|
'sortierung': 'Niedrigere Zahlen erscheinen zuerst in der Navigation'
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
# Auto-generate slug from title if not provided
|
||||||
|
if not self.instance.pk:
|
||||||
|
self.fields['slug'].required = False
|
||||||
|
|
||||||
|
def clean_slug(self):
|
||||||
|
slug = self.cleaned_data.get('slug')
|
||||||
|
titel = self.cleaned_data.get('titel')
|
||||||
|
|
||||||
|
if not slug and titel:
|
||||||
|
# Auto-generate slug from title
|
||||||
|
from django.utils.text import slugify
|
||||||
|
slug = slugify(titel)
|
||||||
|
|
||||||
|
return slug
|
||||||
|
|
||||||
|
|
||||||
|
class GeschichteBildForm(forms.ModelForm):
|
||||||
|
"""Form for uploading images to history pages"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
from .models import GeschichteBild
|
||||||
|
model = GeschichteBild
|
||||||
|
fields = ['titel', 'bild', 'beschreibung', 'alt_text', 'sortierung']
|
||||||
|
widgets = {
|
||||||
|
'titel': forms.TextInput(attrs={
|
||||||
|
'class': 'form-control',
|
||||||
|
'placeholder': 'z.B. Gründungsurkunde 1895'
|
||||||
|
}),
|
||||||
|
'bild': forms.ClearableFileInput(attrs={
|
||||||
|
'class': 'form-control'
|
||||||
|
}),
|
||||||
|
'beschreibung': forms.Textarea(attrs={
|
||||||
|
'class': 'form-control',
|
||||||
|
'rows': 3,
|
||||||
|
'placeholder': 'Beschreibung des Bildes...'
|
||||||
|
}),
|
||||||
|
'alt_text': forms.TextInput(attrs={
|
||||||
|
'class': 'form-control',
|
||||||
|
'placeholder': 'Alternativtext für Bildschirmleser'
|
||||||
|
}),
|
||||||
|
'sortierung': forms.NumberInput(attrs={
|
||||||
|
'class': 'form-control',
|
||||||
|
'min': 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
help_texts = {
|
||||||
|
'bild': 'Unterstützte Formate: JPG, PNG, GIF (max. 10MB)',
|
||||||
|
'alt_text': 'Wichtig für Barrierefreiheit',
|
||||||
|
'sortierung': 'Reihenfolge in der Bildergalerie'
|
||||||
|
}
|
||||||
|
|||||||
56
app/stiftung/migrations/0037_add_geschichte_models.py
Normal file
56
app/stiftung/migrations/0037_add_geschichte_models.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Generated by Django 5.0.6 on 2025-10-02 19:48
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('stiftung', '0036_force_semester_deadlines_update'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='GeschichteSeite',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('titel', models.CharField(max_length=200, verbose_name='Titel')),
|
||||||
|
('slug', models.SlugField(max_length=200, unique=True, verbose_name='URL-Slug')),
|
||||||
|
('inhalt', models.TextField(verbose_name='Inhalt')),
|
||||||
|
('erstellt_am', models.DateTimeField(auto_now_add=True, verbose_name='Erstellt am')),
|
||||||
|
('aktualisiert_am', models.DateTimeField(auto_now=True, verbose_name='Aktualisiert am')),
|
||||||
|
('ist_veroeffentlicht', models.BooleanField(default=True, verbose_name='Veröffentlicht')),
|
||||||
|
('sortierung', models.IntegerField(default=0, verbose_name='Sortierung')),
|
||||||
|
('aktualisiert_von', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='geschichte_seiten_aktualisiert', to=settings.AUTH_USER_MODEL, verbose_name='Aktualisiert von')),
|
||||||
|
('erstellt_von', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='geschichte_seiten_erstellt', to=settings.AUTH_USER_MODEL, verbose_name='Erstellt von')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Geschichte Seite',
|
||||||
|
'verbose_name_plural': 'Geschichte Seiten',
|
||||||
|
'ordering': ['sortierung', 'titel'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='GeschichteBild',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('titel', models.CharField(max_length=200, verbose_name='Bildtitel')),
|
||||||
|
('bild', models.ImageField(upload_to='geschichte/bilder/%Y/%m/', verbose_name='Bild')),
|
||||||
|
('beschreibung', models.TextField(blank=True, verbose_name='Beschreibung')),
|
||||||
|
('alt_text', models.CharField(blank=True, max_length=200, verbose_name='Alt-Text')),
|
||||||
|
('hochgeladen_am', models.DateTimeField(auto_now_add=True, verbose_name='Hochgeladen am')),
|
||||||
|
('sortierung', models.IntegerField(default=0, verbose_name='Sortierung')),
|
||||||
|
('hochgeladen_von', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Hochgeladen von')),
|
||||||
|
('seite', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bilder', to='stiftung.geschichteseite', verbose_name='Geschichte Seite')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Geschichte Bild',
|
||||||
|
'verbose_name_plural': 'Geschichte Bilder',
|
||||||
|
'ordering': ['sortierung', 'titel'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -2856,3 +2856,87 @@ class VierteljahresNachweis(models.Model):
|
|||||||
except VierteljahresNachweis.DoesNotExist:
|
except VierteljahresNachweis.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class GeschichteSeite(models.Model):
|
||||||
|
"""Wiki-style pages for foundation history"""
|
||||||
|
|
||||||
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||||
|
titel = models.CharField(max_length=200, verbose_name="Titel")
|
||||||
|
slug = models.SlugField(max_length=200, unique=True, verbose_name="URL-Slug")
|
||||||
|
inhalt = models.TextField(verbose_name="Inhalt")
|
||||||
|
|
||||||
|
# Metadata
|
||||||
|
erstellt_am = models.DateTimeField(auto_now_add=True, verbose_name="Erstellt am")
|
||||||
|
aktualisiert_am = models.DateTimeField(auto_now=True, verbose_name="Aktualisiert am")
|
||||||
|
erstellt_von = models.ForeignKey(
|
||||||
|
'auth.User',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name='geschichte_seiten_erstellt',
|
||||||
|
verbose_name="Erstellt von"
|
||||||
|
)
|
||||||
|
aktualisiert_von = models.ForeignKey(
|
||||||
|
'auth.User',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name='geschichte_seiten_aktualisiert',
|
||||||
|
verbose_name="Aktualisiert von"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Options
|
||||||
|
ist_veroeffentlicht = models.BooleanField(default=True, verbose_name="Veröffentlicht")
|
||||||
|
sortierung = models.IntegerField(default=0, verbose_name="Sortierung")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Geschichte Seite"
|
||||||
|
verbose_name_plural = "Geschichte Seiten"
|
||||||
|
ordering = ['sortierung', 'titel']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.titel
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
from django.urls import reverse
|
||||||
|
return reverse('stiftung:geschichte_detail', kwargs={'slug': self.slug})
|
||||||
|
|
||||||
|
|
||||||
|
class GeschichteBild(models.Model):
|
||||||
|
"""Images for history pages"""
|
||||||
|
|
||||||
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||||
|
seite = models.ForeignKey(
|
||||||
|
GeschichteSeite,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='bilder',
|
||||||
|
verbose_name="Geschichte Seite"
|
||||||
|
)
|
||||||
|
titel = models.CharField(max_length=200, verbose_name="Bildtitel")
|
||||||
|
bild = models.ImageField(
|
||||||
|
upload_to='geschichte/bilder/%Y/%m/',
|
||||||
|
verbose_name="Bild"
|
||||||
|
)
|
||||||
|
beschreibung = models.TextField(blank=True, verbose_name="Beschreibung")
|
||||||
|
alt_text = models.CharField(max_length=200, blank=True, verbose_name="Alt-Text")
|
||||||
|
|
||||||
|
# Metadata
|
||||||
|
hochgeladen_am = models.DateTimeField(auto_now_add=True, verbose_name="Hochgeladen am")
|
||||||
|
hochgeladen_von = models.ForeignKey(
|
||||||
|
'auth.User',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
verbose_name="Hochgeladen von"
|
||||||
|
)
|
||||||
|
|
||||||
|
sortierung = models.IntegerField(default=0, verbose_name="Sortierung")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Geschichte Bild"
|
||||||
|
verbose_name_plural = "Geschichte Bilder"
|
||||||
|
ordering = ['sortierung', 'titel']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.titel} ({self.seite.titel})"
|
||||||
|
|||||||
@@ -385,4 +385,10 @@ urlpatterns = [
|
|||||||
views.quarterly_confirmation_reset,
|
views.quarterly_confirmation_reset,
|
||||||
name="quarterly_confirmation_reset",
|
name="quarterly_confirmation_reset",
|
||||||
),
|
),
|
||||||
|
# Geschichte URLs
|
||||||
|
path("geschichte/", views.geschichte_list, name="geschichte_list"),
|
||||||
|
path("geschichte/neu/", views.geschichte_create, name="geschichte_create"),
|
||||||
|
path("geschichte/<slug:slug>/", views.geschichte_detail, name="geschichte_detail"),
|
||||||
|
path("geschichte/<slug:slug>/bearbeiten/", views.geschichte_edit, name="geschichte_edit"),
|
||||||
|
path("geschichte/<slug:slug>/bild-upload/", views.geschichte_bild_upload, name="geschichte_bild_upload"),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -8030,3 +8030,125 @@ def backup_tokens(request):
|
|||||||
}
|
}
|
||||||
|
|
||||||
return render(request, 'stiftung/auth/backup_tokens_manage.html', context)
|
return render(request, 'stiftung/auth/backup_tokens_manage.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
# Geschichte (History) Views
|
||||||
|
from .models import GeschichteSeite, GeschichteBild
|
||||||
|
from .forms import GeschichteSeiteForm, GeschichteBildForm
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def geschichte_list(request):
|
||||||
|
"""List all published history pages"""
|
||||||
|
seiten = GeschichteSeite.objects.filter(ist_veroeffentlicht=True).order_by('sortierung', 'titel')
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'seiten': seiten,
|
||||||
|
'title': 'Geschichte der Stiftung'
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, 'stiftung/geschichte/liste.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def geschichte_detail(request, slug):
|
||||||
|
"""Display a specific history page"""
|
||||||
|
seite = get_object_or_404(GeschichteSeite, slug=slug, ist_veroeffentlicht=True)
|
||||||
|
bilder = seite.bilder.all().order_by('sortierung', 'titel')
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'seite': seite,
|
||||||
|
'bilder': bilder,
|
||||||
|
'title': seite.titel
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, 'stiftung/geschichte/detail.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def geschichte_create(request):
|
||||||
|
"""Create a new history page"""
|
||||||
|
if not request.user.has_perm('stiftung.add_geschichteseite'):
|
||||||
|
messages.error(request, 'Sie haben keine Berechtigung, neue Geschichtsseiten zu erstellen.')
|
||||||
|
return redirect('stiftung:geschichte_list')
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = GeschichteSeiteForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
seite = form.save(commit=False)
|
||||||
|
seite.erstellt_von = request.user
|
||||||
|
seite.aktualisiert_von = request.user
|
||||||
|
seite.save()
|
||||||
|
|
||||||
|
messages.success(request, f'Geschichtsseite "{seite.titel}" wurde erfolgreich erstellt.')
|
||||||
|
return redirect('stiftung:geschichte_detail', slug=seite.slug)
|
||||||
|
else:
|
||||||
|
form = GeschichteSeiteForm()
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'form': form,
|
||||||
|
'title': 'Neue Geschichtsseite'
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, 'stiftung/geschichte/form.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def geschichte_edit(request, slug):
|
||||||
|
"""Edit an existing history page"""
|
||||||
|
seite = get_object_or_404(GeschichteSeite, slug=slug)
|
||||||
|
|
||||||
|
if not request.user.has_perm('stiftung.change_geschichteseite'):
|
||||||
|
messages.error(request, 'Sie haben keine Berechtigung, diese Geschichtsseite zu bearbeiten.')
|
||||||
|
return redirect('stiftung:geschichte_detail', slug=slug)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = GeschichteSeiteForm(request.POST, instance=seite)
|
||||||
|
if form.is_valid():
|
||||||
|
seite = form.save(commit=False)
|
||||||
|
seite.aktualisiert_von = request.user
|
||||||
|
seite.save()
|
||||||
|
|
||||||
|
messages.success(request, f'Geschichtsseite "{seite.titel}" wurde erfolgreich aktualisiert.')
|
||||||
|
return redirect('stiftung:geschichte_detail', slug=seite.slug)
|
||||||
|
else:
|
||||||
|
form = GeschichteSeiteForm(instance=seite)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'form': form,
|
||||||
|
'seite': seite,
|
||||||
|
'title': f'Bearbeiten: {seite.titel}'
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, 'stiftung/geschichte/form.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def geschichte_bild_upload(request, slug):
|
||||||
|
"""Upload images to a history page"""
|
||||||
|
seite = get_object_or_404(GeschichteSeite, slug=slug)
|
||||||
|
|
||||||
|
if not request.user.has_perm('stiftung.add_geschichtebild'):
|
||||||
|
messages.error(request, 'Sie haben keine Berechtigung, Bilder hochzuladen.')
|
||||||
|
return redirect('stiftung:geschichte_detail', slug=slug)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = GeschichteBildForm(request.POST, request.FILES)
|
||||||
|
if form.is_valid():
|
||||||
|
bild = form.save(commit=False)
|
||||||
|
bild.seite = seite
|
||||||
|
bild.hochgeladen_von = request.user
|
||||||
|
bild.save()
|
||||||
|
|
||||||
|
messages.success(request, f'Bild "{bild.titel}" wurde erfolgreich hochgeladen.')
|
||||||
|
return redirect('stiftung:geschichte_detail', slug=slug)
|
||||||
|
else:
|
||||||
|
form = GeschichteBildForm()
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'form': form,
|
||||||
|
'seite': seite,
|
||||||
|
'title': f'Bild hochladen: {seite.titel}'
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, 'stiftung/geschichte/bild_form.html', context)
|
||||||
|
|||||||
@@ -528,11 +528,14 @@
|
|||||||
<i class="fas fa-tachometer-alt me-1"></i>Dashboard
|
<i class="fas fa-tachometer-alt me-1"></i>Dashboard
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<!-- Menschen & Finanzen -->
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="destinataereDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<a class="nav-link dropdown-toggle" href="#" id="personenDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="fas fa-users me-1"></i>Destinatäre
|
<i class="fas fa-users me-1"></i>Menschen & Finanzen
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu" aria-labelledby="destinataereDropdown">
|
<ul class="dropdown-menu" aria-labelledby="personenDropdown">
|
||||||
|
<li><h6 class="dropdown-header">Destinatäre</h6></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'stiftung:destinataer_list' %}">
|
<li><a class="dropdown-item" href="{% url 'stiftung:destinataer_list' %}">
|
||||||
<i class="fas fa-list me-2"></i>Alle Destinatäre
|
<i class="fas fa-list me-2"></i>Alle Destinatäre
|
||||||
</a></li>
|
</a></li>
|
||||||
@@ -540,6 +543,7 @@
|
|||||||
<i class="fas fa-plus me-2"></i>Neuer Destinatär
|
<i class="fas fa-plus me-2"></i>Neuer Destinatär
|
||||||
</a></li>
|
</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
|
<li><h6 class="dropdown-header">Förderungen</h6></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'stiftung:foerderung_list' %}">
|
<li><a class="dropdown-item" href="{% url 'stiftung:foerderung_list' %}">
|
||||||
<i class="fas fa-gift me-2"></i>Alle Förderungen
|
<i class="fas fa-gift me-2"></i>Alle Förderungen
|
||||||
</a></li>
|
</a></li>
|
||||||
@@ -547,19 +551,28 @@
|
|||||||
<i class="fas fa-plus me-2"></i>Neue Förderung
|
<i class="fas fa-plus me-2"></i>Neue Förderung
|
||||||
</a></li>
|
</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
|
<li><h6 class="dropdown-header">Unterstützungen</h6></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'stiftung:unterstuetzungen_all' %}">
|
<li><a class="dropdown-item" href="{% url 'stiftung:unterstuetzungen_all' %}">
|
||||||
<i class="fas fa-hand-holding-usd me-2"></i>Alle Unterstützungen
|
<i class="fas fa-hand-holding-usd me-2"></i>Alle Unterstützungen
|
||||||
</a></li>
|
</a></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'stiftung:unterstuetzung_create' %}">
|
<li><a class="dropdown-item" href="{% url 'stiftung:unterstuetzung_create' %}">
|
||||||
<i class="fas fa-plus me-2"></i>Neue Unterstützung
|
<i class="fas fa-plus me-2"></i>Neue Unterstützung
|
||||||
</a></li>
|
</a></li>
|
||||||
|
<li><hr class="dropdown-divider"></li>
|
||||||
|
<li><h6 class="dropdown-header">Pächter</h6></li>
|
||||||
|
<li><a class="dropdown-item" href="{% url 'stiftung:paechter_list' %}">
|
||||||
|
<i class="fas fa-user-tie me-2"></i>Alle Pächter
|
||||||
|
</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<!-- Immobilien & Land -->
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="laendereiDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<a class="nav-link dropdown-toggle" href="#" id="immobilienDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="fas fa-tree me-1"></i>Ländereien
|
<i class="fas fa-tree me-1"></i>Immobilien & Land
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu" aria-labelledby="laendereiDropdown">
|
<ul class="dropdown-menu" aria-labelledby="immobilienDropdown">
|
||||||
|
<li><h6 class="dropdown-header">Ländereien</h6></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'stiftung:land_list' %}">
|
<li><a class="dropdown-item" href="{% url 'stiftung:land_list' %}">
|
||||||
<i class="fas fa-list me-2"></i>Alle Ländereien
|
<i class="fas fa-list me-2"></i>Alle Ländereien
|
||||||
</a></li>
|
</a></li>
|
||||||
@@ -567,6 +580,7 @@
|
|||||||
<i class="fas fa-plus me-2"></i>Neue Länderei
|
<i class="fas fa-plus me-2"></i>Neue Länderei
|
||||||
</a></li>
|
</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
|
<li><h6 class="dropdown-header">Verpachtungen</h6></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'stiftung:verpachtung_list' %}">
|
<li><a class="dropdown-item" href="{% url 'stiftung:verpachtung_list' %}">
|
||||||
<i class="fas fa-handshake me-2"></i>Alle Verpachtungen
|
<i class="fas fa-handshake me-2"></i>Alle Verpachtungen
|
||||||
</a></li>
|
</a></li>
|
||||||
@@ -574,28 +588,35 @@
|
|||||||
<i class="fas fa-plus me-2"></i>Neue Verpachtung
|
<i class="fas fa-plus me-2"></i>Neue Verpachtung
|
||||||
</a></li>
|
</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
|
<li><h6 class="dropdown-header">Abrechnungen</h6></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'stiftung:land_abrechnung_list' %}">
|
<li><a class="dropdown-item" href="{% url 'stiftung:land_abrechnung_list' %}">
|
||||||
<i class="fas fa-calculator me-2"></i>Abrechnungen
|
<i class="fas fa-calculator me-2"></i>Alle Abrechnungen
|
||||||
</a></li>
|
</a></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'stiftung:land_abrechnung_create' %}">
|
<li><a class="dropdown-item" href="{% url 'stiftung:land_abrechnung_create' %}">
|
||||||
<i class="fas fa-plus me-2"></i>Neue Abrechnung
|
<i class="fas fa-plus me-2"></i>Neue Abrechnung
|
||||||
</a></li>
|
</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="{% url 'stiftung:paechter_list' %}">
|
<!-- Verwaltung & Dokumente -->
|
||||||
<i class="fas fa-user-tie me-1"></i>Pächter
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" href="#" id="verwaltungDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i class="fas fa-briefcase me-1"></i>Verwaltung
|
||||||
</a>
|
</a>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="verwaltungDropdown">
|
||||||
|
<li><a class="dropdown-item" href="{% url 'stiftung:dokument_management' %}">
|
||||||
|
<i class="fas fa-folder-open me-2"></i>Dokumente
|
||||||
|
</a></li>
|
||||||
|
<li><a class="dropdown-item" href="{% url 'stiftung:geschaeftsfuehrung' %}">
|
||||||
|
<i class="fas fa-briefcase me-2"></i>Geschäftsführung
|
||||||
|
</a></li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<!-- Legacy Verpachtungen aus Navigation entfernt -->
|
|
||||||
|
<!-- Geschichte -->
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'stiftung:dokument_management' %}">
|
<a class="nav-link" href="{% url 'stiftung:geschichte_list' %}">
|
||||||
<i class="fas fa-folder-open me-1"></i>Dokumente
|
<i class="fas fa-book-open me-1"></i>Geschichte
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="{% url 'stiftung:geschaeftsfuehrung' %}">
|
|
||||||
<i class="fas fa-briefcase me-1"></i>Geschäftsführung
|
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
|||||||
120
app/templates/stiftung/geschichte/bild_form.html
Normal file
120
app/templates/stiftung/geschichte/bild_form.html
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load help_tags %}
|
||||||
|
|
||||||
|
{% block title %}{{ title }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'stiftung:dashboard' %}">Dashboard</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'stiftung:geschichte_list' %}">Geschichte</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="{{ seite.get_absolute_url }}">{{ seite.titel }}</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">Bild hinzufügen</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card shadow">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h5 class="mb-0">
|
||||||
|
<i class="fas fa-image me-2"></i>{{ title }}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="{{ form.titel.id_for_label }}">{{ form.titel.label }}</label>
|
||||||
|
{{ form.titel }}
|
||||||
|
{% if form.titel.errors %}
|
||||||
|
<div class="text-danger">{{ form.titel.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="{{ form.bild.id_for_label }}">{{ form.bild.label }}</label>
|
||||||
|
{{ form.bild }}
|
||||||
|
{% if form.bild.errors %}
|
||||||
|
<div class="text-danger">{{ form.bild.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-text">{{ form.bild.help_text }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="{{ form.alt_text.id_for_label }}">{{ form.alt_text.label }}</label>
|
||||||
|
{{ form.alt_text }}
|
||||||
|
{% if form.alt_text.errors %}
|
||||||
|
<div class="text-danger">{{ form.alt_text.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-text">{{ form.alt_text.help_text }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="{{ form.beschreibung.id_for_label }}">{{ form.beschreibung.label }}</label>
|
||||||
|
{{ form.beschreibung }}
|
||||||
|
{% if form.beschreibung.errors %}
|
||||||
|
<div class="text-danger">{{ form.beschreibung.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="{{ form.sortierung.id_for_label }}">{{ form.sortierung.label }}</label>
|
||||||
|
{{ form.sortierung }}
|
||||||
|
{% if form.sortierung.errors %}
|
||||||
|
<div class="text-danger">{{ form.sortierung.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-text">{{ form.sortierung.help_text }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<a href="{{ seite.get_absolute_url }}" class="btn btn-secondary">
|
||||||
|
<i class="fas fa-arrow-left me-1"></i>Abbrechen
|
||||||
|
</a>
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="fas fa-upload me-1"></i>Hochladen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-info text-white">
|
||||||
|
<h6 class="mb-0"><i class="fas fa-info-circle me-2"></i>Bildrichtlinien</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<ul class="small">
|
||||||
|
<li><strong>Formate:</strong> JPG, PNG, GIF</li>
|
||||||
|
<li><strong>Maximale Größe:</strong> 10 MB</li>
|
||||||
|
<li><strong>Empfohlene Auflösung:</strong> 1200x800px oder höher</li>
|
||||||
|
<li><strong>Alt-Text:</strong> Wichtig für Barrierefreiheit</li>
|
||||||
|
</ul>
|
||||||
|
<hr>
|
||||||
|
<p class="small text-muted">
|
||||||
|
Bilder werden automatisch für die Web-Anzeige optimiert.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if seite.bilder.exists %}
|
||||||
|
<div class="card mt-3">
|
||||||
|
<div class="card-header bg-secondary text-white">
|
||||||
|
<h6 class="mb-0"><i class="fas fa-images me-2"></i>Vorhandene Bilder</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% for bild in seite.bilder.all %}
|
||||||
|
<div class="mb-2">
|
||||||
|
<img src="{{ bild.bild.url }}" alt="{{ bild.alt_text }}" class="img-thumbnail" style="width: 60px; height: 40px; object-fit: cover;">
|
||||||
|
<small class="ms-2">{{ bild.titel }}</small>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
131
app/templates/stiftung/geschichte/detail.html
Normal file
131
app/templates/stiftung/geschichte/detail.html
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load help_tags %}
|
||||||
|
|
||||||
|
{% block title %}{{ title }}{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_css %}
|
||||||
|
<style>
|
||||||
|
.geschichte-content {
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
.geschichte-content h1, .geschichte-content h2, .geschichte-content h3 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #2c5234;
|
||||||
|
}
|
||||||
|
.geschichte-content p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.geschichte-bilder {
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
.geschichte-bild {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.geschichte-bild img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.bild-beschreibung {
|
||||||
|
font-style: italic;
|
||||||
|
color: #666;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'stiftung:dashboard' %}">Dashboard</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'stiftung:geschichte_list' %}">Geschichte</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">{{ seite.titel }}</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card shadow">
|
||||||
|
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0">
|
||||||
|
<i class="fas fa-book-open me-2"></i>{{ seite.titel }}
|
||||||
|
</h5>
|
||||||
|
<div class="btn-group">
|
||||||
|
{% if perms.stiftung.change_geschichteseite %}
|
||||||
|
<a href="{% url 'stiftung:geschichte_edit' slug=seite.slug %}" class="btn btn-light btn-sm">
|
||||||
|
<i class="fas fa-edit me-1"></i>Bearbeiten
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.stiftung.add_geschichtebild %}
|
||||||
|
<a href="{% url 'stiftung:geschichte_bild_upload' slug=seite.slug %}" class="btn btn-light btn-sm">
|
||||||
|
<i class="fas fa-image me-1"></i>Bild hinzufügen
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="geschichte-content">
|
||||||
|
{{ seite.inhalt|linebreaks }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if bilder %}
|
||||||
|
<div class="geschichte-bilder">
|
||||||
|
<h4><i class="fas fa-images me-2"></i>Bildergalerie</h4>
|
||||||
|
{% for bild in bilder %}
|
||||||
|
<div class="geschichte-bild">
|
||||||
|
<img src="{{ bild.bild.url }}" alt="{{ bild.alt_text|default:bild.titel }}" class="img-fluid">
|
||||||
|
<div class="bild-beschreibung">
|
||||||
|
<strong>{{ bild.titel }}</strong>
|
||||||
|
{% if bild.beschreibung %}
|
||||||
|
<br>{{ bild.beschreibung }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-muted">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<small>
|
||||||
|
<i class="fas fa-user me-1"></i>
|
||||||
|
Erstellt von: {{ seite.erstellt_von.get_full_name|default:seite.erstellt_von.username }}
|
||||||
|
am {{ seite.erstellt_am|date:"d.m.Y H:i" }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 text-md-end">
|
||||||
|
<small>
|
||||||
|
<i class="fas fa-clock me-1"></i>
|
||||||
|
Zuletzt aktualisiert: {{ seite.aktualisiert_am|date:"d.m.Y H:i" }}
|
||||||
|
{% if seite.aktualisiert_von %}
|
||||||
|
von {{ seite.aktualisiert_von.get_full_name|default:seite.aktualisiert_von.username }}
|
||||||
|
{% endif %}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-secondary text-white">
|
||||||
|
<h6 class="mb-0"><i class="fas fa-list me-2"></i>Weitere Seiten</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<!-- This could be populated with other history pages -->
|
||||||
|
<p class="text-muted small">Navigation zu anderen Geschichtsseiten wird hier angezeigt.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-3">
|
||||||
|
<a href="{% url 'stiftung:geschichte_list' %}" class="btn btn-secondary">
|
||||||
|
<i class="fas fa-arrow-left me-1"></i>Zurück zur Übersicht
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
177
app/templates/stiftung/geschichte/form.html
Normal file
177
app/templates/stiftung/geschichte/form.html
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load help_tags %}
|
||||||
|
|
||||||
|
{% block title %}{{ title }}{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_css %}
|
||||||
|
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
.ql-editor {
|
||||||
|
min-height: 300px;
|
||||||
|
}
|
||||||
|
.ql-toolbar {
|
||||||
|
border-top-left-radius: 0.375rem;
|
||||||
|
border-top-right-radius: 0.375rem;
|
||||||
|
}
|
||||||
|
.ql-container {
|
||||||
|
border-bottom-left-radius: 0.375rem;
|
||||||
|
border-bottom-right-radius: 0.375rem;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'stiftung:dashboard' %}">Dashboard</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'stiftung:geschichte_list' %}">Geschichte</a></li>
|
||||||
|
{% if seite %}
|
||||||
|
<li class="breadcrumb-item"><a href="{{ seite.get_absolute_url }}">{{ seite.titel }}</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">Bearbeiten</li>
|
||||||
|
{% else %}
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">Neue Seite</li>
|
||||||
|
{% endif %}
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card shadow">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h5 class="mb-0">
|
||||||
|
<i class="fas fa-{% if seite %}edit{% else %}plus{% endif %} me-2"></i>{{ title }}
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post" id="geschichteForm">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<label class="form-label" for="{{ form.titel.id_for_label }}">{{ form.titel.label }}</label>
|
||||||
|
{{ form.titel }}
|
||||||
|
{% if form.titel.errors %}
|
||||||
|
<div class="text-danger">{{ form.titel.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label" for="{{ form.sortierung.id_for_label }}">{{ form.sortierung.label }}</label>
|
||||||
|
{{ form.sortierung }}
|
||||||
|
{% if form.sortierung.errors %}
|
||||||
|
<div class="text-danger">{{ form.sortierung.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-text">{{ form.sortierung.help_text }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="{{ form.slug.id_for_label }}">{{ form.slug.label }}</label>
|
||||||
|
{{ form.slug }}
|
||||||
|
{% if form.slug.errors %}
|
||||||
|
<div class="text-danger">{{ form.slug.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-text">{{ form.slug.help_text }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">{{ form.inhalt.label }}</label>
|
||||||
|
<div id="quill-editor" style="height: 400px;"></div>
|
||||||
|
<textarea name="inhalt" id="id_inhalt" style="display: none;">{{ form.inhalt.value|default:"" }}</textarea>
|
||||||
|
{% if form.inhalt.errors %}
|
||||||
|
<div class="text-danger">{{ form.inhalt.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="form-text">{{ form.inhalt.help_text }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3 form-check">
|
||||||
|
{{ form.ist_veroeffentlicht }}
|
||||||
|
<label class="form-check-label" for="{{ form.ist_veroeffentlicht.id_for_label }}">
|
||||||
|
{{ form.ist_veroeffentlicht.label }}
|
||||||
|
</label>
|
||||||
|
{% if form.ist_veroeffentlicht.errors %}
|
||||||
|
<div class="text-danger">{{ form.ist_veroeffentlicht.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<a href="{% if seite %}{{ seite.get_absolute_url }}{% else %}{% url 'stiftung:geschichte_list' %}{% endif %}" class="btn btn-secondary">
|
||||||
|
<i class="fas fa-arrow-left me-1"></i>Abbrechen
|
||||||
|
</a>
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="fas fa-save me-1"></i>Speichern
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-info text-white">
|
||||||
|
<h6 class="mb-0"><i class="fas fa-info-circle me-2"></i>Rich Text Editor</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="small">Der Editor unterstützt:</p>
|
||||||
|
<ul class="small">
|
||||||
|
<li>Formatierung (Fett, Kursiv, Unterstrichen)</li>
|
||||||
|
<li>Überschriften (H1, H2, H3)</li>
|
||||||
|
<li>Listen (nummeriert und Aufzählungen)</li>
|
||||||
|
<li>Links</li>
|
||||||
|
<li>Bilder (über separaten Upload)</li>
|
||||||
|
</ul>
|
||||||
|
<hr>
|
||||||
|
<p class="small text-muted">
|
||||||
|
Tipp: Verwenden Sie die Bild-Upload-Funktion, um Bilder zur Seite hinzuzufügen.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_js %}
|
||||||
|
<script src="https://cdn.quilljs.com/1.3.6/quill.min.js"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Initialize Quill editor
|
||||||
|
var quill = new Quill('#quill-editor', {
|
||||||
|
theme: 'snow',
|
||||||
|
modules: {
|
||||||
|
toolbar: [
|
||||||
|
[{ 'header': [1, 2, 3, false] }],
|
||||||
|
['bold', 'italic', 'underline', 'strike'],
|
||||||
|
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
|
||||||
|
[{ 'color': [] }, { 'background': [] }],
|
||||||
|
['link'],
|
||||||
|
['clean']
|
||||||
|
]
|
||||||
|
},
|
||||||
|
placeholder: 'Schreiben Sie hier den Inhalt der Geschichtsseite...'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load existing content
|
||||||
|
var existingContent = document.getElementById('id_inhalt').value;
|
||||||
|
if (existingContent) {
|
||||||
|
quill.root.innerHTML = existingContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-generate slug from title
|
||||||
|
document.getElementById('{{ form.titel.id_for_label }}').addEventListener('input', function() {
|
||||||
|
var title = this.value;
|
||||||
|
var slug = title.toLowerCase()
|
||||||
|
.replace(/ä/g, 'ae').replace(/ö/g, 'oe').replace(/ü/g, 'ue').replace(/ß/g, 'ss')
|
||||||
|
.replace(/[^\w\s-]/g, '')
|
||||||
|
.replace(/[-\s]+/g, '-')
|
||||||
|
.trim();
|
||||||
|
document.getElementById('{{ form.slug.id_for_label }}').value = slug;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update textarea on form submit
|
||||||
|
document.getElementById('geschichteForm').addEventListener('submit', function() {
|
||||||
|
document.getElementById('id_inhalt').value = quill.root.innerHTML;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
76
app/templates/stiftung/geschichte/liste.html
Normal file
76
app/templates/stiftung/geschichte/liste.html
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load help_tags %}
|
||||||
|
|
||||||
|
{% block title %}{{ title }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'stiftung:dashboard' %}">Dashboard</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page">Geschichte</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card shadow">
|
||||||
|
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0">
|
||||||
|
<i class="fas fa-book-open me-2"></i>{{ title }}
|
||||||
|
</h5>
|
||||||
|
{% if perms.stiftung.add_geschichteseite %}
|
||||||
|
<a href="{% url 'stiftung:geschichte_create' %}" class="btn btn-light btn-sm">
|
||||||
|
<i class="fas fa-plus me-1"></i>Neue Seite
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% if seiten %}
|
||||||
|
<div class="row">
|
||||||
|
{% for seite in seiten %}
|
||||||
|
<div class="col-lg-4 col-md-6 mb-4">
|
||||||
|
<div class="card h-100">
|
||||||
|
{% if seite.bilder.first %}
|
||||||
|
<img src="{{ seite.bilder.first.bild.url }}" class="card-img-top" alt="{{ seite.bilder.first.alt_text }}" style="height: 200px; object-fit: cover;">
|
||||||
|
{% endif %}
|
||||||
|
<div class="card-body d-flex flex-column">
|
||||||
|
<h6 class="card-title">{{ seite.titel }}</h6>
|
||||||
|
<p class="card-text text-muted small flex-grow-1">
|
||||||
|
{{ seite.inhalt|truncatewords:20|striptags }}
|
||||||
|
</p>
|
||||||
|
<div class="mt-auto">
|
||||||
|
<a href="{{ seite.get_absolute_url }}" class="btn btn-primary btn-sm">
|
||||||
|
<i class="fas fa-eye me-1"></i>Lesen
|
||||||
|
</a>
|
||||||
|
{% if perms.stiftung.change_geschichteseite %}
|
||||||
|
<a href="{% url 'stiftung:geschichte_edit' slug=seite.slug %}" class="btn btn-outline-secondary btn-sm">
|
||||||
|
<i class="fas fa-edit me-1"></i>Bearbeiten
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-muted small">
|
||||||
|
<i class="fas fa-clock me-1"></i>
|
||||||
|
Aktualisiert: {{ seite.aktualisiert_am|date:"d.m.Y H:i" }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="text-center py-5">
|
||||||
|
<i class="fas fa-book-open fa-3x text-muted mb-3"></i>
|
||||||
|
<h5 class="text-muted">Keine Geschichtsseiten vorhanden</h5>
|
||||||
|
<p class="text-muted">Es wurden noch keine Seiten zur Stiftungsgeschichte erstellt.</p>
|
||||||
|
{% if perms.stiftung.add_geschichteseite %}
|
||||||
|
<a href="{% url 'stiftung:geschichte_create' %}" class="btn btn-primary">
|
||||||
|
<i class="fas fa-plus me-1"></i>Erste Seite erstellen
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user