-
-
Notifications
You must be signed in to change notification settings - Fork 527
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new fields to attachments, WIP move attachments admin
- Loading branch information
Showing
21 changed files
with
489 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
from django import forms | ||
from django.utils.translation import pgettext_lazy | ||
|
||
from ...attachments.models import AttachmentType | ||
|
||
|
||
def get_searchable_filetypes(): | ||
choices = [(0, pgettext_lazy("admin attachments type filter choice", "All types"))] | ||
choices += [(a.id, a.name) for a in AttachmentType.objects.order_by("name")] | ||
return choices | ||
|
||
|
||
class FilterAttachmentsForm(forms.Form): | ||
uploader = forms.CharField( | ||
label=pgettext_lazy("admin attachments filter form", "Uploader name contains"), | ||
required=False, | ||
) | ||
filename = forms.CharField( | ||
label=pgettext_lazy("admin attachments filter form", "Filename contains"), | ||
required=False, | ||
) | ||
filetype = forms.TypedChoiceField( | ||
label=pgettext_lazy("admin attachments filter form", "File type"), | ||
coerce=int, | ||
choices=get_searchable_filetypes, | ||
empty_value=0, | ||
required=False, | ||
) | ||
is_orphan = forms.ChoiceField( | ||
label=pgettext_lazy("admin attachments filter form", "State"), | ||
required=False, | ||
choices=[ | ||
( | ||
"", | ||
pgettext_lazy( | ||
"admin attachments orphan filter choice", | ||
"All", | ||
), | ||
), | ||
( | ||
"yes", | ||
pgettext_lazy( | ||
"admin attachments orphan filter choice", | ||
"Only orphaned", | ||
), | ||
), | ||
( | ||
"no", | ||
pgettext_lazy( | ||
"admin attachments orphan filter choice", | ||
"Not orphaned", | ||
), | ||
), | ||
], | ||
) | ||
|
||
def filter_queryset(self, criteria, queryset): | ||
if criteria.get("uploader"): | ||
queryset = queryset.filter( | ||
uploader_slug__contains=criteria["uploader"].lower() | ||
) | ||
if criteria.get("filename"): | ||
queryset = queryset.filter(filename__icontains=criteria["filename"]) | ||
if criteria.get("filetype"): | ||
queryset = queryset.filter(filetype_id=criteria["filetype"]) | ||
if criteria.get("is_orphan") == "yes": | ||
queryset = queryset.filter(post__isnull=True) | ||
elif criteria.get("is_orphan") == "no": | ||
queryset = queryset.filter(post__isnull=False) | ||
return queryset |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
from django.contrib import messages | ||
from django.db import transaction | ||
from django.utils.translation import pgettext, pgettext_lazy | ||
|
||
from ...attachments.models import Attachment | ||
from ...threads.models import Post | ||
from ..views import generic | ||
from .forms import FilterAttachmentsForm | ||
|
||
|
||
class AttachmentAdmin(generic.AdminBaseMixin): | ||
root_link = "misago:admin:attachments:index" | ||
model = Attachment | ||
templates_dir = "misago/admin/attachments" | ||
message_404 = pgettext_lazy( | ||
"admin attachments", "Requested attachment does not exist." | ||
) | ||
|
||
def get_queryset(self): | ||
qs = super().get_queryset() | ||
return qs.select_related( | ||
"filetype", "uploader", "post", "post__thread", "post__category" | ||
) | ||
|
||
|
||
class AttachmentsList(AttachmentAdmin, generic.ListView): | ||
items_per_page = 20 | ||
ordering = [ | ||
("-id", pgettext_lazy("admin attachments ordering choice", "From newest")), | ||
("id", pgettext_lazy("admin attachments ordering choice", "From oldest")), | ||
("filename", pgettext_lazy("admin attachments ordering choice", "A to z")), | ||
("-filename", pgettext_lazy("admin attachments ordering choice", "Z to a")), | ||
("size", pgettext_lazy("admin attachments ordering choice", "Smallest files")), | ||
("-size", pgettext_lazy("admin attachments ordering choice", "Largest files")), | ||
] | ||
selection_label = pgettext_lazy("admin attachments", "With attachments: 0") | ||
empty_selection_label = pgettext_lazy("admin attachments", "Select attachments") | ||
mass_actions = [ | ||
{ | ||
"action": "delete", | ||
"name": pgettext_lazy("admin attachments", "Delete attachments"), | ||
"confirmation": pgettext_lazy( | ||
"admin attachments", | ||
"Are you sure you want to delete selected attachments?", | ||
), | ||
"is_atomic": False, | ||
} | ||
] | ||
filter_form = FilterAttachmentsForm | ||
|
||
def action_delete(self, request, attachments): | ||
deleted_attachments = [] | ||
desynced_posts = [] | ||
|
||
for attachment in attachments: | ||
if attachment.post: | ||
deleted_attachments.append(attachment.pk) | ||
desynced_posts.append(attachment.post_id) | ||
|
||
if desynced_posts: | ||
with transaction.atomic(): | ||
for post in Post.objects.filter(id__in=desynced_posts): | ||
self.delete_from_cache(post, deleted_attachments) | ||
|
||
for attachment in attachments: | ||
attachment.delete() | ||
|
||
message = pgettext( | ||
"admin attachments", "Selected attachments have been deleted." | ||
) | ||
messages.success(request, message) | ||
|
||
def delete_from_cache(self, post, attachments): | ||
if not post.attachments_cache: | ||
return # admin action may be taken due to desynced state | ||
|
||
clean_cache = [] | ||
for a in post.attachments_cache: | ||
if a["id"] not in attachments: | ||
clean_cache.append(a) | ||
|
||
post.attachments_cache = clean_cache or None | ||
post.save(update_fields=["attachments_cache"]) | ||
|
||
|
||
class DeleteAttachment(AttachmentAdmin, generic.ButtonView): | ||
def button_action(self, request, target): | ||
if target.post: | ||
self.delete_from_cache(target) | ||
target.delete() | ||
message = pgettext( | ||
"admin attachments", 'Attachment "%(filename)s" has been deleted.' | ||
) | ||
messages.success(request, message % {"filename": target.filename}) | ||
|
||
def delete_from_cache(self, attachment): | ||
if not attachment.post.attachments_cache: | ||
return # admin action may be taken due to desynced state | ||
|
||
clean_cache = [] | ||
for a in attachment.post.attachments_cache: | ||
if a["id"] != attachment.id: | ||
clean_cache.append(a) | ||
|
||
attachment.post.attachments_cache = clean_cache or None | ||
attachment.post.save(update_fields=["attachments_cache"]) |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
from django import forms | ||
from django.utils.translation import pgettext, pgettext_lazy | ||
|
||
from ...attachments.models import AttachmentType | ||
|
||
|
||
class AttachmentTypeForm(forms.ModelForm): | ||
class Meta: | ||
model = AttachmentType | ||
fields = [ | ||
"name", | ||
"extensions", | ||
"mimetypes", | ||
"size_limit", | ||
"status", | ||
"limit_uploads_to", | ||
"limit_downloads_to", | ||
] | ||
labels = { | ||
"name": pgettext_lazy("admin attachment type form", "Type name"), | ||
"extensions": pgettext_lazy( | ||
"admin attachment type form", "File extensions" | ||
), | ||
"mimetypes": pgettext_lazy("admin attachment type form", "Mimetypes"), | ||
"size_limit": pgettext_lazy( | ||
"admin attachment type form", "Maximum allowed uploaded file size" | ||
), | ||
"status": pgettext_lazy("admin attachment type form", "Status"), | ||
"limit_uploads_to": pgettext_lazy( | ||
"admin attachment type form", "Limit uploads to" | ||
), | ||
"limit_downloads_to": pgettext_lazy( | ||
"admin attachment type form", "Limit downloads to" | ||
), | ||
} | ||
help_texts = { | ||
"extensions": pgettext_lazy( | ||
"admin attachment type form", | ||
"List of comma separated file extensions associated with this attachment type.", | ||
), | ||
"mimetypes": pgettext_lazy( | ||
"admin attachment type form", | ||
"Optional list of comma separated mime types associated with this attachment type.", | ||
), | ||
"size_limit": pgettext_lazy( | ||
"admin attachment type form", | ||
"Maximum allowed uploaded file size for this type, in kb. This setting is deprecated and has no effect. It will be deleted in Misago 1.0.", | ||
), | ||
"status": pgettext_lazy( | ||
"admin attachment type form", | ||
"Controls this attachment type availability on your site.", | ||
), | ||
"limit_uploads_to": pgettext_lazy( | ||
"admin attachment type form", | ||
"If you wish to limit option to upload files of this type to users with specific roles, select them on this list. Otherwise don't select any roles to allow all users with permission to upload attachments to be able to upload attachments of this type.", | ||
), | ||
"limit_downloads_to": pgettext_lazy( | ||
"admin attachment type form", | ||
"If you wish to limit option to download files of this type to users with specific roles, select them on this list. Otherwise don't select any roles to allow all users with permission to download attachments to be able to download attachments of this type.", | ||
), | ||
} | ||
widgets = { | ||
"limit_uploads_to": forms.CheckboxSelectMultiple, | ||
"limit_downloads_to": forms.CheckboxSelectMultiple, | ||
} | ||
|
||
def clean_extensions(self): | ||
data = self.clean_list(self.cleaned_data["extensions"]) | ||
if not data: | ||
raise forms.ValidationError( | ||
pgettext("admin attachment type form", "This field is required.") | ||
) | ||
return data | ||
|
||
def clean_mimetypes(self): | ||
data = self.cleaned_data["mimetypes"] | ||
if data: | ||
return self.clean_list(data) | ||
|
||
def clean_list(self, value): | ||
items = [v.lstrip(".") for v in value.lower().replace(" ", "").split(",")] | ||
return ",".join(set(filter(bool, items))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from django.contrib import messages | ||
from django.db.models import Count | ||
from django.utils.translation import pgettext, pgettext_lazy | ||
|
||
from ...attachments.models import AttachmentType | ||
from ..views import generic | ||
from .forms import AttachmentTypeForm | ||
|
||
|
||
class AttachmentTypeAdmin(generic.AdminBaseMixin): | ||
root_link = "misago:admin:settings:attachment-types:index" | ||
model = AttachmentType | ||
form_class = AttachmentTypeForm | ||
templates_dir = "misago/admin/attachmenttypes" | ||
message_404 = pgettext_lazy( | ||
"admin attachments types", "Requested attachment type does not exist." | ||
) | ||
|
||
def update_roles(self, target, roles): | ||
target.roles.clear() | ||
if roles: | ||
target.roles.add(*roles) | ||
|
||
def handle_form(self, form, request, target): | ||
super().handle_form(form, request, target) | ||
form.save() | ||
|
||
|
||
class AttachmentTypesList(AttachmentTypeAdmin, generic.ListView): | ||
ordering = (("name", None),) | ||
|
||
def get_queryset(self): | ||
queryset = super().get_queryset() | ||
return queryset.annotate(num_files=Count("attachment")) | ||
|
||
|
||
class NewAttachmentType(AttachmentTypeAdmin, generic.ModelFormView): | ||
message_submit = pgettext_lazy( | ||
"admin attachments types", 'New type "%(name)s" has been saved.' | ||
) | ||
|
||
|
||
class EditAttachmentType(AttachmentTypeAdmin, generic.ModelFormView): | ||
message_submit = pgettext_lazy( | ||
"admin attachments types", 'Attachment type "%(name)s" has been edited.' | ||
) | ||
|
||
|
||
class DeleteAttachmentType(AttachmentTypeAdmin, generic.ButtonView): | ||
def check_permissions(self, request, target): | ||
if target.attachment_set.exists(): | ||
message = pgettext( | ||
"admin attachments types", | ||
'Attachment type "%(name)s" has associated attachments and can\'t be deleted.', | ||
) | ||
return message % {"name": target.name} | ||
|
||
def button_action(self, request, target): | ||
target.delete() | ||
message = pgettext( | ||
"admin attachments types", 'Attachment type "%(name)s" has been deleted.' | ||
) | ||
messages.success(request, message % {"name": target.name}) |
Oops, something went wrong.