Remove series truncation endpoint from API
This commit is contained in:
@@ -249,28 +249,6 @@ def series_remove_duplicate_images(request, series_id: int):
|
|||||||
return len(dupes)
|
return len(dupes)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/series_truncate/{series_id}/{start}/{end}/", auth=django_auth)
|
|
||||||
def series_truncate(request, series_id: int, start: int, end: int):
|
|
||||||
series = get_object_or_404(Series, pk=series_id)
|
|
||||||
|
|
||||||
if not series.check_user_can_edit(request.user):
|
|
||||||
return {"status": "permission denied"}
|
|
||||||
|
|
||||||
images_removed = []
|
|
||||||
for n, image in enumerate(series.get_images()):
|
|
||||||
if n >= start and n <= end:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
image.removed = True
|
|
||||||
image.image = None
|
|
||||||
image.save()
|
|
||||||
images_removed.append(image.pk)
|
|
||||||
|
|
||||||
series.get_total_image_size(refresh=True)
|
|
||||||
|
|
||||||
return {"status": "success", "images_removed": images_removed}
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/get_cases_user", auth=django_auth, response=List[CaseSchema])
|
@router.get("/get_cases_user", auth=django_auth, response=List[CaseSchema])
|
||||||
def get_cases_user(request):
|
def get_cases_user(request):
|
||||||
return Case.objects.filter(author=request.user)
|
return Case.objects.filter(author=request.user)
|
||||||
|
|||||||
+67
-54
@@ -131,6 +131,47 @@ class UserSearchSelectMultipleWidget:
|
|||||||
const helpBtn = document.getElementById('user_search_help_btn_{self.name}');
|
const helpBtn = document.getElementById('user_search_help_btn_{self.name}');
|
||||||
const helpPanel = document.getElementById('user_search_help_panel_{self.name}');
|
const helpPanel = document.getElementById('user_search_help_panel_{self.name}');
|
||||||
|
|
||||||
|
// Expose helper functions immediately so clicks won't fail if
|
||||||
|
// earlier listeners throw an exception during setup.
|
||||||
|
window.addUserToField = function(fieldName, id, text, grade) {{
|
||||||
|
const sel = document.getElementById('id_' + fieldName);
|
||||||
|
if (!sel) return;
|
||||||
|
for (let i=0;i<sel.options.length;i++) {{ if (sel.options[i].value == id) return; }}
|
||||||
|
const opt = document.createElement('option'); opt.value = id; opt.selected = true; opt.text = text;
|
||||||
|
sel.appendChild(opt);
|
||||||
|
const listEl = document.getElementById('selected_users_' + fieldName);
|
||||||
|
if (!listEl) return;
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.className = 'list-group-item d-flex justify-content-between align-items-center';
|
||||||
|
li.id = 'selected_user_' + fieldName + '_' + id;
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.innerHTML = text + (grade ? ' <span class="badge bg-secondary ms-2">' + grade + '</span>' : '');
|
||||||
|
const btn = document.createElement('button'); btn.type = 'button'; btn.className = 'btn btn-sm btn-outline-danger ms-2'; btn.innerText = 'Remove';
|
||||||
|
btn.addEventListener('click', function() {{ removeUserFromField(fieldName, id); }});
|
||||||
|
li.appendChild(span); li.appendChild(btn);
|
||||||
|
listEl.appendChild(li);
|
||||||
|
}}
|
||||||
|
|
||||||
|
window.removeUserFromField = function(fieldName, id) {{
|
||||||
|
const sel = document.getElementById('id_' + fieldName);
|
||||||
|
if (!sel) return;
|
||||||
|
for (let i=sel.options.length-1;i>=0;i--) {{ if (sel.options[i].value == id) sel.remove(i); }}
|
||||||
|
const li = document.getElementById('selected_user_' + fieldName + '_' + id);
|
||||||
|
if (li && li.parentNode) li.parentNode.removeChild(li);
|
||||||
|
}}
|
||||||
|
|
||||||
|
window.addAllFromResults = function(fieldName) {{
|
||||||
|
const list = document.getElementById('user_search_results_list_' + fieldName);
|
||||||
|
if (!list) return;
|
||||||
|
const items = list.querySelectorAll('li[data-user-id]');
|
||||||
|
items.forEach(function(it) {{
|
||||||
|
const id = it.getAttribute('data-user-id');
|
||||||
|
const text = it.getAttribute('data-user-text') || it.textContent.trim();
|
||||||
|
const grade = it.getAttribute('data-user-grade') || '';
|
||||||
|
addUserToField(fieldName, id, text, grade);
|
||||||
|
}});
|
||||||
|
}}
|
||||||
|
|
||||||
let timeout = null;
|
let timeout = null;
|
||||||
function fetchResults(q) {{
|
function fetchResults(q) {{
|
||||||
const grade = gradeSelect ? gradeSelect.value : '';
|
const grade = gradeSelect ? gradeSelect.value : '';
|
||||||
@@ -178,23 +219,35 @@ class UserSearchSelectMultipleWidget:
|
|||||||
// fieldName for this widget instance
|
// fieldName for this widget instance
|
||||||
const widgetFieldName = '{self.name}';
|
const widgetFieldName = '{self.name}';
|
||||||
|
|
||||||
// Add button click delegation: results list buttons have data attributes
|
// Expose helper functions early so they're available even if
|
||||||
|
// a later part of this IIFE throws (avoids ReferenceError on click).
|
||||||
|
|
||||||
|
|
||||||
|
// Delegate clicks from results: add single or add-all (works for dynamically inserted content)
|
||||||
results.addEventListener('click', function(e) {{
|
results.addEventListener('click', function(e) {{
|
||||||
const btn = e.target.closest('.user-add-btn');
|
const addBtn = e.target.closest('.user-add-btn');
|
||||||
if (!btn) return;
|
if (addBtn) {{
|
||||||
const id = btn.getAttribute('data-user-id');
|
const id = addBtn.getAttribute('data-user-id');
|
||||||
const text = btn.getAttribute('data-user-text') || btn.textContent.trim();
|
const text = addBtn.getAttribute('data-user-text') || addBtn.textContent.trim();
|
||||||
// Try button attribute, then list-item dataset, then visible badge text
|
// Try button attribute, then list-item dataset, then visible badge text
|
||||||
let grade = btn.getAttribute('data-user-grade') || '';
|
let grade = addBtn.getAttribute('data-user-grade') || '';
|
||||||
if (!grade) {{
|
if (!grade) {{
|
||||||
const li = btn.closest('li[data-user-id]');
|
const li = addBtn.closest('li[data-user-id]');
|
||||||
if (li && li.dataset && li.dataset.userGrade) grade = li.dataset.userGrade;
|
if (li && li.dataset && li.dataset.userGrade) grade = li.dataset.userGrade;
|
||||||
else {{
|
else {{
|
||||||
const badge = btn.closest('li') ? btn.closest('li').querySelector('.badge') : null;
|
const badge = addBtn.closest('li') ? addBtn.closest('li').querySelector('.badge') : null;
|
||||||
if (badge) grade = badge.textContent.trim();
|
if (badge) grade = badge.textContent.trim();
|
||||||
|
}}
|
||||||
}}
|
}}
|
||||||
|
addUserToField(widgetFieldName, id, text, grade);
|
||||||
|
return;
|
||||||
|
}}
|
||||||
|
const addAllBtnDynamic = e.target.closest('.user-add-all-btn');
|
||||||
|
if (addAllBtnDynamic) {{
|
||||||
|
e.preventDefault();
|
||||||
|
addAllFromResults(widgetFieldName);
|
||||||
|
return;
|
||||||
}}
|
}}
|
||||||
addUserToField(widgetFieldName, id, text, grade);
|
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// Wire up the "Add all results" button if present
|
// Wire up the "Add all results" button if present
|
||||||
@@ -206,47 +259,7 @@ class UserSearchSelectMultipleWidget:
|
|||||||
}});
|
}});
|
||||||
}}
|
}}
|
||||||
|
|
||||||
window.addUserToField = function(fieldName, id, text, grade) {{
|
|
||||||
const sel = document.getElementById('id_' + fieldName);
|
|
||||||
if (!sel) return;
|
|
||||||
// prevent duplicate
|
|
||||||
for (let i=0;i<sel.options.length;i++) {{ if (sel.options[i].value == id) return; }}
|
|
||||||
const opt = document.createElement('option'); opt.value = id; opt.selected = true; opt.text = text;
|
|
||||||
sel.appendChild(opt);
|
|
||||||
|
|
||||||
// add to visible list (include grade if provided)
|
|
||||||
const listEl = document.getElementById('selected_users_' + fieldName);
|
|
||||||
if (!listEl) return;
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.className = 'list-group-item d-flex justify-content-between align-items-center';
|
|
||||||
li.id = 'selected_user_' + fieldName + '_' + id;
|
|
||||||
const span = document.createElement('span');
|
|
||||||
span.innerHTML = text + (grade ? ' <span class="badge bg-secondary ms-2">' + grade + '</span>' : '');
|
|
||||||
const btn = document.createElement('button'); btn.type = 'button'; btn.className = 'btn btn-sm btn-outline-danger ms-2'; btn.innerText = 'Remove';
|
|
||||||
btn.addEventListener('click', function() {{ removeUserFromField(fieldName, id); }});
|
|
||||||
li.appendChild(span); li.appendChild(btn);
|
|
||||||
listEl.appendChild(li);
|
|
||||||
}}
|
|
||||||
|
|
||||||
window.removeUserFromField = function(fieldName, id) {{
|
|
||||||
const sel = document.getElementById('id_' + fieldName);
|
|
||||||
if (!sel) return;
|
|
||||||
for (let i=sel.options.length-1;i>=0;i--) {{ if (sel.options[i].value == id) sel.remove(i); }}
|
|
||||||
const li = document.getElementById('selected_user_' + fieldName + '_' + id);
|
|
||||||
if (li && li.parentNode) li.parentNode.removeChild(li);
|
|
||||||
}}
|
|
||||||
|
|
||||||
window.addAllFromResults = function(fieldName) {{
|
|
||||||
const list = document.getElementById('user_search_results_list_' + fieldName);
|
|
||||||
if (!list) return;
|
|
||||||
const items = list.querySelectorAll('li[data-user-id]');
|
|
||||||
items.forEach(function(it) {{
|
|
||||||
const id = it.getAttribute('data-user-id');
|
|
||||||
const text = it.getAttribute('data-user-text') || it.textContent.trim();
|
|
||||||
const grade = it.getAttribute('data-user-grade') || '';
|
|
||||||
addUserToField(fieldName, id, text, grade);
|
|
||||||
}});
|
|
||||||
}}
|
|
||||||
|
|
||||||
// Initialize visible selected-list from any pre-existing <option selected> entries
|
// Initialize visible selected-list from any pre-existing <option selected> entries
|
||||||
(function initSelectedListFromOptions() {{
|
(function initSelectedListFromOptions() {{
|
||||||
|
|||||||
Reference in New Issue
Block a user