mirror of
https://github.com/jointakahe/takahe.git
synced 2024-11-25 16:51:00 +00:00
Copy Emoji to local and delete file with record (#407)
This commit is contained in:
parent
feb7a673eb
commit
21d565d282
6 changed files with 81 additions and 1 deletions
|
@ -73,7 +73,15 @@ class EmojiAdmin(admin.ModelAdmin):
|
|||
|
||||
readonly_fields = ["preview", "created", "updated", "to_ap_tag"]
|
||||
|
||||
actions = ["force_execution", "approve_emoji", "reject_emoji"]
|
||||
actions = ["force_execution", "approve_emoji", "reject_emoji", "copy_to_local"]
|
||||
|
||||
def delete_queryset(self, request, queryset):
|
||||
for instance in queryset:
|
||||
# individual deletes to ensure file is deleted
|
||||
instance.delete()
|
||||
|
||||
def delete_model(self, request, obj):
|
||||
super().delete_model(request, obj)
|
||||
|
||||
@admin.action(description="Force Execution")
|
||||
def force_execution(self, request, queryset):
|
||||
|
@ -96,6 +104,17 @@ class EmojiAdmin(admin.ModelAdmin):
|
|||
f'<img src="{instance.full_url().relative}" style="height: 22px">'
|
||||
)
|
||||
|
||||
@admin.action(description="Copy Emoji to Local")
|
||||
def copy_to_local(self, request, queryset):
|
||||
emojis = {}
|
||||
for instance in queryset:
|
||||
emoji = instance.copy_to_local(save=False)
|
||||
if emoji:
|
||||
emojis[emoji.shortcode] = emoji
|
||||
|
||||
Emoji.objects.bulk_create(emojis.values(), batch_size=50, ignore_conflicts=True)
|
||||
Emoji.locals = Emoji.load_locals()
|
||||
|
||||
|
||||
@admin.register(PostAttachment)
|
||||
class PostAttachmentAdmin(admin.ModelAdmin):
|
||||
|
|
|
@ -9,6 +9,7 @@ from asgiref.sync import sync_to_async
|
|||
from cachetools import TTLCache, cached
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db import models
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
|
@ -131,9 +132,15 @@ class Emoji(StatorModel):
|
|||
admin_delete = "{admin}{self.pk}/delete/"
|
||||
admin_enable = "{admin}{self.pk}/enable/"
|
||||
admin_disable = "{admin}{self.pk}/disable/"
|
||||
admin_copy = "{admin}{self.pk}/copy/"
|
||||
|
||||
emoji_regex = re.compile(r"\B:([a-zA-Z0-9(_)-]+):\B")
|
||||
|
||||
def delete(self, using=None, keep_parents=False):
|
||||
if self.file:
|
||||
self.file.delete()
|
||||
return super().delete(using=using, keep_parents=keep_parents)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
if self.local ^ (self.domain is None):
|
||||
|
@ -195,6 +202,40 @@ class Emoji(StatorModel):
|
|||
)
|
||||
return self.fullcode
|
||||
|
||||
@property
|
||||
def can_copy_local(self):
|
||||
if not hasattr(Emoji, "locals"):
|
||||
Emoji.locals = Emoji.load_locals()
|
||||
return not self.local and self.is_usable and self.shortcode not in Emoji.locals
|
||||
|
||||
def copy_to_local(self, *, save: bool = True):
|
||||
"""
|
||||
Copy this (non-local) Emoji to local for use by Users of this instance. Returns
|
||||
the Emoji instance, or None if the copy failed to happen. Specify save=False to
|
||||
return the object without saving to database (for bulk saving).
|
||||
"""
|
||||
if not self.can_copy_local:
|
||||
return None
|
||||
|
||||
emoji = None
|
||||
if self.file:
|
||||
# new emoji gets its own copy of the file
|
||||
file = ContentFile(self.file.read())
|
||||
file.name = self.file.name
|
||||
emoji = Emoji(
|
||||
shortcode=self.shortcode,
|
||||
domain=None,
|
||||
local=True,
|
||||
mimetype=self.mimetype,
|
||||
file=file,
|
||||
category=self.category,
|
||||
)
|
||||
if save:
|
||||
emoji.save()
|
||||
# add this new one to the locals cache
|
||||
Emoji.locals[self.shortcode] = emoji
|
||||
return emoji
|
||||
|
||||
@classmethod
|
||||
def emojis_from_content(cls, content: str, domain: Domain | None) -> list["Emoji"]:
|
||||
"""
|
||||
|
|
|
@ -173,6 +173,7 @@ urlpatterns = [
|
|||
path("admin/emoji/<pk>/enable/", admin.EmojiEnable.as_view()),
|
||||
path("admin/emoji/<pk>/disable/", admin.EmojiEnable.as_view(enable=False)),
|
||||
path("admin/emoji/<pk>/delete/", admin.EmojiDelete.as_view()),
|
||||
path("admin/emoji/<pk>/copy/", admin.EmojiCopyLocal.as_view()),
|
||||
path(
|
||||
"admin/announcements/",
|
||||
admin.AnnouncementsRoot.as_view(),
|
||||
|
|
|
@ -32,6 +32,9 @@
|
|||
<td>
|
||||
</td>
|
||||
<td class="actions">
|
||||
{% if emoji.can_copy_local %}
|
||||
<a hx-post="{{ emoji.urls.admin_copy }}" title="Copy to Local"><i class="fa-solid fa-copy"></i></a>
|
||||
{% endif %}
|
||||
{% if not emoji.is_usable %}
|
||||
<span class="bad">Disabled</span>
|
||||
<a hx-post="{{ emoji.urls.admin_enable }}" title="Enable"><i class="fa-solid fa-circle-check"></i></a>
|
||||
|
|
|
@ -17,6 +17,7 @@ from users.views.admin.domains import ( # noqa
|
|||
Domains,
|
||||
)
|
||||
from users.views.admin.emoji import ( # noqa
|
||||
EmojiCopyLocal,
|
||||
EmojiCreate,
|
||||
EmojiDelete,
|
||||
EmojiEnable,
|
||||
|
|
|
@ -99,3 +99,18 @@ class EmojiEnable(HTMXActionView):
|
|||
def action(self, emoji: Emoji):
|
||||
emoji.public = self.enable
|
||||
emoji.save()
|
||||
|
||||
|
||||
@method_decorator(moderator_required, name="dispatch")
|
||||
class EmojiCopyLocal(HTMXActionView):
|
||||
"""
|
||||
Duplicates a domain emoji to be local, as long as it is usable and the
|
||||
shortcode is available.
|
||||
"""
|
||||
|
||||
model = Emoji
|
||||
|
||||
def action(self, emoji: Emoji):
|
||||
# Force reload locals cache to avoid potential for shortcode dupes
|
||||
Emoji.locals = Emoji.load_locals()
|
||||
emoji.copy_to_local()
|
||||
|
|
Loading…
Reference in a new issue