allow staff to delete/merge/unlink

This commit is contained in:
Your Name 2023-01-23 16:31:30 -05:00 committed by Henri Dickson
parent c040af6e6b
commit 236a517d97
10 changed files with 122 additions and 30 deletions

View file

@ -175,7 +175,10 @@ def external_search(request):
)
@login_required
def refetch(request):
if request.method != "POST":
return HttpResponseBadRequest()
url = request.POST.get("url")
if not url:
return HttpResponseBadRequest()

View file

@ -105,9 +105,6 @@
{% if user.is_authenticated %}
<a href="{% url 'catalog:edit' item.url_path item.uuid %}">{% trans '编辑' %}{{ item.demonstrative }}</a>
{% endif %}
{% if user.is_staff %}
/<a href="{% url 'catalog:delete' item.url_path item.uuid %}"> {% trans '删除' %}</a>
{% endif %}
</div>
</div>
{% endblock %}

View file

@ -23,6 +23,7 @@
<section id="content" class="container">
<div class="grid">
<div style="float:right;padding-left:16px">
{% if item %}
<div class="aside-section-wrapper">
{% for res in form.instance.external_resources.all %}
<div class="action-panel">
@ -32,23 +33,63 @@
{% csrf_token %}
<input type="hidden" name="id" value="{{ res.id }}" >
<input type="hidden" name="url" value="{{ res.url }}" >
<input class="button" type="submit" value="{% trans '从源网站重新抓取' %}">
<input class="button" type="submit" value="{% trans '重新抓取' %}">
</form>
</div>
{% if request.user.is_staff %}
<div class="action-panel__button-group">
<form method="post" action="{% url 'catalog:unlink' %}">
{% csrf_token %}
<input type="hidden" name="id" value="{{ res.id }}" >
<input class="button" type="submit" value="{% trans '取消关联' %}">
</form>
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% if request.user.is_staff %}
<div class="aside-section-wrapper">
<div class="action-panel">
<div class="action-panel__label">{% trans '合并到另一条目' %} </div>
<div class="action-panel__button-group">
<form method="post" action="{% url 'catalog:merge' item.url_path item.uuid %}" onsubmit="return confirm('Confirm merging?');">
{% csrf_token %}
<input type="url" name="new_item_url" placeholder="目标条目URL" value="{{ item.merged_to_item.url }}" required><br>
<input class="button" type="submit" value="{% trans '提交' %}">
</form>
</div>
</div>
<div class="action-panel">
<div class="action-panel__label">{% trans '删除' %} </div>
<div class="action-panel__button-group">
<form method="post" action="{% url 'catalog:delete' item.url_path item.uuid %}" onsubmit="return confirm('Confirm deletion?');">
{% csrf_token %}
<input class="button" type="submit" value="{% trans '提交' %}">
</form>
</div>
</div>
{% if item.is_deteled %}
<!-- TODO undelete -->
{% endif %}
</div>
{% endif %}
{% endif %}
</div>
<div class="single-section-wrapper" id="main">
<form class="entity-form" action="{{ submit_url }}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.media }}
{{ form }}
<hr/>
<input class="button" type="submit" value="{% trans '提交' %}">
<a href="javascript:history.go(-1)" style="float:right;">返回</a>
</form>
</div>
<form class="entity-form" action="{{ submit_url }}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.media }}
{{ form }}
<hr/>
<input class="button" type="submit" value="{% trans '提交' %}">
<a href="{{ item.url | default:'javascript:history.go(-1)' }}" style="float:right;">返回</a>
</form>
</div>
</section>
</div>

View file

@ -41,8 +41,8 @@
<div>{% if item.translator %}{% trans '译者:' %}
{% for translator in item.translator %}
<span>{{ translator }}</span>{% if not forloop.last %} / {% endif %}
{% endfor %}
{% endif %}</div>
{% endfor %}
{% endif %}</div>
<div>{% if item.orig_title %}{% trans '原作名:' %}{{ item.orig_title }}{% endif %}</div>
<div>{% if item.language %}{% trans '语言:' %}{{ item.language }}{% endif %}</div>
<div>{%if item.pub_year %}{% trans '出版时间:' %}{{ item.pub_year }}{% trans '年' %}{% if item.pub_month %}{{ item.pub_month }}{% trans '月' %}{% endif %}{% endif %}</div>
@ -53,7 +53,7 @@
<div>{% if item.price %}{% trans '定价:' %}{{ item.price }}{% endif %}</div>
<div>{% if item.pages %}{% trans '页数:' %}{{ item.pages }}{% endif %}</div>
<div>{% if item.imprint %}{% trans '出品方:' %}{{ item.imprint }}{% endif %}</div>
{% if item.other_info %}
{% if item.other_info %}
{% for k, v in item.other_info.items %}
<div>
{{ k }}{{ v | urlize }}
@ -70,9 +70,6 @@
{% if user.is_authenticated %}
<a href="{% url 'catalog:edit' item.url_path item.uuid %}">{% trans '编辑' %}{{ item.demonstrative }}</a>
{% endif %}
{% if user.is_staff %}
/<a href="{% url 'catalog:delete' item.url_path item.uuid %}"> {% trans '删除' %}</a>
{% endif %}
</div>
</div>
{% endblock %}

View file

@ -45,6 +45,12 @@
</a>
<div class="entity-detail__info">
{% if item.is_deleted %}
[DELETED]
{% endif %}
{% if item.merged_to_item %}
[MERGED] <a href="{{ item.merged_to_item.url }}">{{ item.merged_to_item.title }}</a>
{% endif %}
{% block title %}
<h5 class="entity-detail__title">
{{ item.title }}
@ -122,7 +128,7 @@
<a href="{% url 'catalog:mark_list' item.url_path item.uuid %}" class="entity-marks__more-link">{% trans '全部标记' %}</a>
<a href="{% url 'catalog:mark_list' item.url_path item.uuid 'following' %}" class="entity-marks__more-link">关注的人的标记</a>
{% endif %}
<ul class="entity-marks__mark-list">
{% for others_mark in mark_list %}
<li class="entity-marks__mark">

View file

@ -163,9 +163,6 @@
{% if user.is_authenticated %}
<a href="{% url 'catalog:edit' item.url_path item.uuid %}">{% trans '编辑' %}{{ item.demonstrative }}</a>
{% endif %}
{% if user.is_staff %}
/<a href="{% url 'catalog:delete' item.url_path item.uuid %}"> {% trans '删除' %}</a>
{% endif %}
</div>
</div>
{% endblock %}

View file

@ -69,5 +69,6 @@ urlpatterns = [
path("search/external/", external_search, name="external_search"),
path("fetch_refresh/<str:job_id>", fetch_refresh, name="fetch_refresh"),
path("refetch", refetch, name="refetch"),
path("unlink", unlink, name="unlink"),
path("api/", api.urls),
]

View file

@ -17,6 +17,7 @@ from django.db.models import Count
from django.utils import timezone
from django.core.paginator import Paginator
from polymorphic.base import django
from catalog.common.models import ExternalResource
from catalog.common.sites import AbstractSite, SiteManager
from mastodon import mastodon_request_included
from mastodon.models import MastodonApplication
@ -58,6 +59,11 @@ def retrieve(request, item_path, item_uuid):
item_url = f"/{item_path}/{item_uuid}"
if item.url != item_url:
return redirect(item.url)
skipcheck = request.GET.get("skipcheck", False) and request.user.is_staff
if not skipcheck and item.merged_to_item:
return redirect(item.merged_to_item.url)
if not skipcheck and item.is_deleted:
return HttpResponseNotFound("item not found")
mark = None
review = None
mark_list = None
@ -148,7 +154,7 @@ def edit(request, item_path, item_uuid):
"catalog_edit.html",
{
"form": form,
"is_update": True,
"item": item,
},
)
elif request.method == "POST":
@ -176,7 +182,29 @@ def delete(request, item_path, item_uuid):
return HttpResponseBadRequest()
if not request.user.is_staff:
raise PermissionDenied()
return HttpResponseBadRequest()
item = get_object_or_404(Item, uid=base62.decode(item_uuid))
for res in item.external_resources.all():
res.item = None
res.save()
item.delete()
return (
redirect(item.url + "?skipcheck=1") if request.user.is_staff else redirect("/")
)
@login_required
def unlink(request):
if request.method != "POST":
return HttpResponseBadRequest()
if not request.user.is_staff:
raise PermissionDenied()
res_id = request.POST.get("id")
if not res_id:
return HttpResponseBadRequest()
resource = get_object_or_404(ExternalResource, id=res_id)
resource.item = None
resource.save()
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
@login_required
@ -189,6 +217,7 @@ def merge(request, item_path, item_uuid):
new_item = Item.get_by_url(request.POST.get("new_item_url"))
if not new_item or new_item.is_deleted or new_item.merged_to_item_id:
return HttpResponseBadRequest(b"invalid new item")
_logger.warn(f"{request.user} merges {item} to {new_item}")
item.merge_to(new_item)
update_journal_for_merged_item(item_uuid)
return redirect(new_item.url)

View file

@ -0,0 +1,20 @@
# Generated by Django 3.2.16 on 2023-01-23 05:05
from django.conf import settings
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("catalog", "0002_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("journal", "0007_alter_collection_catalog_item"),
]
operations = [
migrations.AlterUniqueTogether(
name="shelfmember",
unique_together={("owner", "item")},
),
]

View file

@ -96,7 +96,7 @@ def query_item_category(item_category):
class Piece(PolymorphicModel, UserOwnedObjectMixin):
url_path = "piece" # subclass must specify this
url_path = "p" # subclass must specify this
uid = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True)
@property
@ -494,7 +494,7 @@ class ShelfMember(ListMember):
)
class Meta:
unique_together = [["parent", "item"]]
unique_together = [["owner", "item"]]
@cached_property
def mark(self):
@ -1054,12 +1054,13 @@ def update_journal_for_merged_item(legacy_item_uuid):
_logger.error("update_journal_for_merged_item: unable to find item")
return
new_item = legacy_item.merged_to_item
for cls in Content.__subclasses__ + ListMember.__subclasses__:
_logger.info(f"update {cls.__name__}: {legacy_item} -> {new_item}")
for cls in list(Content.__subclasses__()) + list(ListMember.__subclasses__()):
for p in cls.objects.filter(item=legacy_item):
try:
p.item = new_item
p.save(update_fields=["item_id"])
except:
_logger.info(f"delete duplicated piece {p}")
_logger.warn(
f"deleted piece {p} when merging {cls.__name__}: {legacy_item} -> {new_item}"
)
p.delete()