mirror of
https://github.com/daniviga/django-ram.git
synced 2026-02-03 17:40:39 +01:00
Compare commits
8 Commits
8087ab5997
...
v0.19.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
16bd82de39
|
|||
|
2ae7f2685d
|
|||
|
29f9a213b4
|
|||
|
884661d4e1
|
|||
|
c7cace96f7
|
|||
|
d3c099c05b
|
|||
|
903633b5a7
|
|||
|
ee775d737e
|
@@ -59,20 +59,34 @@ class MagazineIssueDocInline(BookDocInline):
|
||||
model = MagazineIssueDocument
|
||||
|
||||
|
||||
class BookTocInline(admin.TabularInline):
|
||||
model = TocEntry
|
||||
min_num = 0
|
||||
extra = 0
|
||||
fields = (
|
||||
"title",
|
||||
"subtitle",
|
||||
"authors",
|
||||
"page",
|
||||
"featured",
|
||||
)
|
||||
|
||||
|
||||
@admin.register(Book)
|
||||
class BookAdmin(SortableAdminBase, admin.ModelAdmin):
|
||||
inlines = (
|
||||
BookTocInline,
|
||||
BookPropertyInline,
|
||||
BookImageInline,
|
||||
BookDocInline,
|
||||
)
|
||||
list_display = (
|
||||
"published",
|
||||
"title",
|
||||
"get_authors",
|
||||
"get_publisher",
|
||||
"publication_year",
|
||||
"number_of_pages",
|
||||
"published",
|
||||
)
|
||||
autocomplete_fields = ("authors", "publisher", "shop")
|
||||
readonly_fields = ("invoices", "creation_time", "updated_time")
|
||||
@@ -364,23 +378,10 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin):
|
||||
actions = [publish, unpublish, download_csv]
|
||||
|
||||
|
||||
class MagazineIssueToc(admin.TabularInline):
|
||||
model = TocEntry
|
||||
min_num = 0
|
||||
extra = 0
|
||||
fields = (
|
||||
"title",
|
||||
"subtitle",
|
||||
"authors",
|
||||
"page",
|
||||
"featured",
|
||||
)
|
||||
|
||||
|
||||
@admin.register(MagazineIssue)
|
||||
class MagazineIssueAdmin(SortableAdminBase, admin.ModelAdmin):
|
||||
inlines = (
|
||||
MagazineIssueToc,
|
||||
BookTocInline,
|
||||
BookPropertyInline,
|
||||
BookImageInline,
|
||||
MagazineIssueDocInline,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 6.0 on 2025-12-31 13:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("bookshelf", "0030_tocentry"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="tocentry",
|
||||
name="authors",
|
||||
field=models.CharField(blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="tocentry",
|
||||
name="subtitle",
|
||||
field=models.CharField(blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="tocentry",
|
||||
name="title",
|
||||
field=models.CharField(),
|
||||
),
|
||||
]
|
||||
@@ -254,9 +254,9 @@ class TocEntry(BaseModel):
|
||||
book = models.ForeignKey(
|
||||
BaseBook, on_delete=models.CASCADE, related_name="toc"
|
||||
)
|
||||
title = models.CharField(max_length=200)
|
||||
subtitle = models.CharField(max_length=200, blank=True)
|
||||
authors = models.CharField(max_length=256, blank=True)
|
||||
title = models.CharField()
|
||||
subtitle = models.CharField(blank=True)
|
||||
authors = models.CharField(blank=True)
|
||||
page = models.SmallIntegerField()
|
||||
featured = models.BooleanField(
|
||||
default=False,
|
||||
@@ -268,9 +268,15 @@ class TocEntry(BaseModel):
|
||||
verbose_name_plural = "Table of Contents Entries"
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.title} (p. {self.page})"
|
||||
if self.subtitle:
|
||||
title = f"{self.title}: {self.subtitle}"
|
||||
else:
|
||||
title = self.title
|
||||
return f"{title} (p. {self.page})"
|
||||
|
||||
def clean(self):
|
||||
if self.page is None:
|
||||
raise ValidationError("Page number is required.")
|
||||
if self.page < 1:
|
||||
raise ValidationError("Page number is invalid.")
|
||||
try:
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="tab-pane" id="nav-toc" role="tabpanel" aria-labelledby="nav-toc-tab">
|
||||
<div class="tab-pane table-responsive" id="nav-toc" role="tabpanel" aria-labelledby="nav-toc-tab">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -216,22 +216,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="tab-pane" id="nav-documents" role="tabpanel" aria-labelledby="nav-documents-tab">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3" scope="row">Documents</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table-group-divider">
|
||||
{% for d in documents.all %}
|
||||
<tr>
|
||||
<td class="w-33">{{ d.description }}</td>
|
||||
<td><a href="{{ d.file.url }}" target="_blank">{{ d.filename }}</a></td>
|
||||
<td class="text-end">{{ d.file.size | filesizeformat }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% include "includes/documents.html" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
<option value="nav-summary" selected>Summary</option>
|
||||
</select>
|
||||
<div class="tab-content" id="nav-tabContent">
|
||||
<div class="tab-pane show active" id="nav-summary" role="tabpanel" aria-labelledby="nav-summary-tab">
|
||||
<div class="tab-pane show active table-responsive" id="nav-summary" role="tabpanel" aria-labelledby="nav-summary-tab">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
26
ram/portal/templates/includes/documents.html
Normal file
26
ram/portal/templates/includes/documents.html
Normal file
@@ -0,0 +1,26 @@
|
||||
{% if documents %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3" scope="row">{{ header|default:"Documents" }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table-group-divider">
|
||||
{% for d in documents.all %}
|
||||
<tr>
|
||||
<td class="w-33">{{ d.description }}</td>
|
||||
<td class="text-nowrap">
|
||||
{% if d.private %}
|
||||
<i class="bi bi-file-earmark-lock2"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-file-earmark-text"></i>
|
||||
{% endif %}
|
||||
<a href="{{ d.file.url }}" target="_blank">{{ d.filename }}</a>
|
||||
</td>
|
||||
<td class="text-end">{{ d.file.size | filesizeformat }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
@@ -402,43 +402,9 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="tab-pane" id="nav-documents" role="tabpanel" aria-labelledby="nav-documents-tab">
|
||||
{% if documents %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3" scope="row">Documents</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table-group-divider">
|
||||
{% for d in documents.all %}
|
||||
<tr>
|
||||
<td class="w-33">{{ d.description }}</td>
|
||||
<td><a href="{{ d.file.url }}" target="_blank">{{ d.filename }}</a></td>
|
||||
<td class="text-end">{{ d.file.size | filesizeformat }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if decoder_documents %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3" scope="row">Decoder documents</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table-group-divider">
|
||||
{% for d in decoder_documents.all %}
|
||||
<tr>
|
||||
<td class="w-33">{{ d.description }}</td>
|
||||
<td><a href="{{ d.file.url }}" target="_blank">{{ d.filename }}</a></td>
|
||||
<td class="text-end">{{ d.file.size | filesizeformat }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<div class="tab-pane table-responsive" id="nav-documents" role="tabpanel" aria-labelledby="nav-documents-tab">
|
||||
{% include "includes/documents.html" %}
|
||||
{% include "includes/documents.html" with documents=decoder_documents header="Decoder documents" %}
|
||||
</div>
|
||||
<div class="tab-pane" id="nav-journal" role="tabpanel" aria-labelledby="nav-journal-tab">
|
||||
<table class="table table-striped">
|
||||
|
||||
@@ -196,6 +196,7 @@ class SearchObjects(View):
|
||||
Q(
|
||||
Q(title__icontains=search)
|
||||
| Q(description__icontains=search)
|
||||
| Q(toc__title__icontains=search)
|
||||
)
|
||||
)
|
||||
.distinct()
|
||||
@@ -217,6 +218,7 @@ class SearchObjects(View):
|
||||
Q(
|
||||
Q(magazine__name__icontains=search)
|
||||
| Q(description__icontains=search)
|
||||
| Q(toc__title__icontains=search)
|
||||
)
|
||||
)
|
||||
.distinct()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from ram.utils import git_suffix
|
||||
|
||||
__version__ = "0.19.2"
|
||||
__version__ = "0.19.5"
|
||||
__version__ += git_suffix(__file__)
|
||||
|
||||
@@ -1,22 +1,60 @@
|
||||
from django.contrib import admin
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.core.cache import cache
|
||||
|
||||
admin.site.site_header = settings.SITE_NAME
|
||||
|
||||
|
||||
def publish(modeladmin, request, queryset):
|
||||
for obj in queryset:
|
||||
obj.published = True
|
||||
obj.save()
|
||||
queryset.update(published=True)
|
||||
cache.clear()
|
||||
|
||||
|
||||
publish.short_description = "Publish selected items"
|
||||
|
||||
|
||||
def unpublish(modeladmin, request, queryset):
|
||||
for obj in queryset:
|
||||
obj.published = False
|
||||
obj.save()
|
||||
queryset.update(published=False)
|
||||
cache.clear()
|
||||
|
||||
|
||||
unpublish.short_description = "Unpublish selected items"
|
||||
|
||||
|
||||
def set_featured(modeladmin, request, queryset):
|
||||
count = queryset.count()
|
||||
if count > settings.FEATURED_ITEMS_MAX:
|
||||
modeladmin.message_user(
|
||||
request,
|
||||
"You can only mark up to {} items as featured.".format(
|
||||
settings.FEATURED_ITEMS_MAX
|
||||
),
|
||||
level="error",
|
||||
)
|
||||
return
|
||||
featured = modeladmin.model.objects.filter(featured=True).count()
|
||||
if featured + count > settings.FEATURED_ITEMS_MAX:
|
||||
modeladmin.message_user(
|
||||
request,
|
||||
"There are already {} featured items. You can only mark {} more items as featured.".format( # noqa: E501
|
||||
featured,
|
||||
settings.FEATURED_ITEMS_MAX - featured,
|
||||
),
|
||||
level="error",
|
||||
)
|
||||
return
|
||||
queryset.update(featured=True)
|
||||
cache.clear()
|
||||
|
||||
|
||||
set_featured.short_description = "Mark selected items as featured"
|
||||
|
||||
|
||||
def unset_featured(modeladmin, request, queryset):
|
||||
queryset.update(featured=False)
|
||||
cache.clear()
|
||||
|
||||
|
||||
unset_featured.short_description = (
|
||||
"Unmark selected items as featured"
|
||||
)
|
||||
|
||||
@@ -6,8 +6,8 @@ from django.utils.html import format_html, format_html_join, strip_tags
|
||||
|
||||
from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin
|
||||
|
||||
from ram.admin import publish, unpublish
|
||||
from ram.utils import generate_csv
|
||||
from ram.admin import publish, unpublish, set_featured, unset_featured
|
||||
from repository.models import RollingStockDocument
|
||||
from portal.utils import get_site_conf
|
||||
from roster.models import (
|
||||
@@ -303,37 +303,4 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin):
|
||||
|
||||
download_csv.short_description = "Download selected items as CSV"
|
||||
|
||||
def set_featured(modeladmin, request, queryset):
|
||||
count = queryset.count()
|
||||
if count > settings.FEATURED_ITEMS_MAX:
|
||||
modeladmin.message_user(
|
||||
request,
|
||||
"You can only mark up to {} items as featured.".format(
|
||||
settings.FEATURED_ITEMS_MAX
|
||||
),
|
||||
level="error",
|
||||
)
|
||||
return
|
||||
featured = RollingStock.objects.filter(featured=True).count()
|
||||
if featured + count > settings.FEATURED_ITEMS_MAX:
|
||||
modeladmin.message_user(
|
||||
request,
|
||||
"There are already {} featured items. You can only mark {} more items as featured.".format( # noqa: E501
|
||||
featured,
|
||||
settings.FEATURED_ITEMS_MAX - featured,
|
||||
),
|
||||
level="error",
|
||||
)
|
||||
return
|
||||
queryset.update(featured=True)
|
||||
|
||||
set_featured.short_description = "Mark selected rolling stock as featured"
|
||||
|
||||
def unset_featured(modeladmin, request, queryset):
|
||||
queryset.update(featured=False)
|
||||
|
||||
unset_featured.short_description = (
|
||||
"Unmark selected rolling stock as featured"
|
||||
)
|
||||
|
||||
actions = [publish, unpublish, set_featured, unset_featured, download_csv]
|
||||
|
||||
Reference in New Issue
Block a user