forked from MarseyWorld/MarseyWorld
make hole sidebar/banner uploading and deleting not require page reload
parent
f2b26e3cc3
commit
4f57533d2c
|
@ -0,0 +1,58 @@
|
|||
function upload_banner(t, hole_name) {
|
||||
postToast(t,
|
||||
`/h/${hole_name}/settings/banners`,
|
||||
{
|
||||
"image": t.files[0]
|
||||
},
|
||||
(xhr) => {
|
||||
const list = document.getElementById('hole-banners')
|
||||
const url = JSON.parse(xhr.response)['url']
|
||||
const html = `
|
||||
<section class="mt-5 d-block hole-settings-subsection">
|
||||
<img class="mr-3" loading="lazy" alt="/h/${hole_name} banner" src="${url}">
|
||||
<button class="btn btn-danger hole-banner-delete-button mt-2" data-nonce="${nonce}" data-onclick="areyousure(this)" data-areyousure="delete_image(this, '/h/${hole_name}/settings/banners/delete')">Delete</button>
|
||||
</section>`
|
||||
|
||||
list.insertAdjacentHTML('afterbegin', html);
|
||||
register_new_elements(list);
|
||||
|
||||
const nobanners = document.getElementById('hole-banner-no-banners')
|
||||
if (nobanners) nobanners.remove()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function upload_sidebar(t, hole_name) {
|
||||
postToast(t,
|
||||
`/h/${hole_name}/settings/sidebars`,
|
||||
{
|
||||
"image": t.files[0]
|
||||
},
|
||||
(xhr) => {
|
||||
const list = document.getElementById('hole-sidebars')
|
||||
const url = JSON.parse(xhr.response)['url']
|
||||
const html = `
|
||||
<section class="mt-5 d-block hole-settings-subsection">
|
||||
<img class="mr-3" loading="lazy" alt="/h/${hole_name} sidebar" src="${url}">
|
||||
<button class="btn btn-danger hole-sidebar-delete-button mt-2" data-nonce="${nonce}" data-onclick="areyousure(this)" data-areyousure="delete_image(this, '/h/${hole_name}/settings/sidebars/delete')">Delete</button>
|
||||
</section>`
|
||||
|
||||
list.insertAdjacentHTML('afterbegin', html);
|
||||
register_new_elements(list);
|
||||
|
||||
const nosidebars = document.getElementById('hole-sidebar-no-sidebars')
|
||||
if (nosidebars) nosidebars.remove()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function delete_image(t, url) {
|
||||
postToast(t, url,
|
||||
{
|
||||
"url": t.previousElementSibling.src
|
||||
},
|
||||
() => {
|
||||
t.parentElement.remove();
|
||||
}
|
||||
);
|
||||
}
|
|
@ -537,7 +537,7 @@ def get_hole_css(hole):
|
|||
resp.headers.add("Content-Type", "text/css")
|
||||
return resp
|
||||
|
||||
@app.post("/h/<hole>/settings/sidebars/")
|
||||
@app.post("/h/<hole>/settings/sidebars")
|
||||
@limiter.limit('1/second', scope=rpath)
|
||||
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
|
||||
@limiter.limit("50/day", deduct_when=lambda response: response.status_code < 400)
|
||||
|
@ -550,7 +550,7 @@ def upload_hole_sidebar(v, hole):
|
|||
if not v.mods_hole(hole.name): abort(403)
|
||||
if v.shadowbanned: abort(500)
|
||||
|
||||
file = request.files["sidebar"]
|
||||
file = request.files["image"]
|
||||
|
||||
name = f'/images/{time.time()}'.replace('.','') + '.webp'
|
||||
file.save(name)
|
||||
|
@ -568,26 +568,28 @@ def upload_hole_sidebar(v, hole):
|
|||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return redirect(f'/h/{hole}/settings')
|
||||
return {"message": "Sidebar image uploaded successfully!", "url": sidebarurl}
|
||||
|
||||
@app.post("/h/<hole>/settings/sidebars/delete/<int:index>")
|
||||
@app.post("/h/<hole>/settings/sidebars/delete")
|
||||
@limiter.limit("1/second", deduct_when=lambda response: response.status_code < 400)
|
||||
@limiter.limit("1/second", deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
|
||||
@auth_required
|
||||
def delete_hole_sidebar(v, hole, index):
|
||||
def delete_hole_sidebar(v, hole):
|
||||
hole = get_hole(hole)
|
||||
if not v.mods_hole(hole.name): abort(403)
|
||||
|
||||
if not hole.sidebarurls:
|
||||
abort(404, f"Sidebar image not found (/h/{hole.name} has no sidebar images)")
|
||||
if index < 0 or index >= len(hole.sidebarurls):
|
||||
abort(404, f'Sidebar image not found (sidebar index {index} is not between 0 and {len(hole.sidebarurls)})')
|
||||
sidebar = hole.sidebarurls[index]
|
||||
try:
|
||||
remove_media_using_link(sidebar)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
del hole.sidebarurls[index]
|
||||
|
||||
sidebar = request.values["url"]
|
||||
|
||||
if sidebar not in hole.sidebarurls:
|
||||
abort(404, "Sidebar image not found!")
|
||||
|
||||
try: remove_media_using_link(sidebar)
|
||||
except FileNotFoundError: pass
|
||||
|
||||
hole.sidebarurls.remove(sidebar)
|
||||
g.db.add(hole)
|
||||
|
||||
ma = HoleAction(
|
||||
|
@ -598,7 +600,7 @@ def delete_hole_sidebar(v, hole, index):
|
|||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return {"message": f"Deleted sidebar {index} from /h/{hole} successfully"}
|
||||
return {"message": "Sidebar image deleted successfully!"}
|
||||
|
||||
@app.post("/h/<hole>/settings/sidebars/delete_all")
|
||||
@limiter.limit("1/10 second;30/day", deduct_when=lambda response: response.status_code < 400)
|
||||
|
@ -626,7 +628,7 @@ def delete_all_hole_sidebars(v, hole):
|
|||
|
||||
return {"message": f"Deleted all sidebar images from /h/{hole} successfully"}
|
||||
|
||||
@app.post("/h/<hole>/settings/banners/")
|
||||
@app.post("/h/<hole>/settings/banners")
|
||||
@limiter.limit('1/second', scope=rpath)
|
||||
@limiter.limit('1/second', scope=rpath, key_func=get_ID)
|
||||
@limiter.limit("50/day", deduct_when=lambda response: response.status_code < 400)
|
||||
|
@ -639,7 +641,7 @@ def upload_hole_banner(v, hole):
|
|||
if not v.mods_hole(hole.name): abort(403)
|
||||
if v.shadowbanned: abort(500)
|
||||
|
||||
file = request.files["banner"]
|
||||
file = request.files["image"]
|
||||
|
||||
name = f'/images/{time.time()}'.replace('.','') + '.webp'
|
||||
file.save(name)
|
||||
|
@ -657,37 +659,39 @@ def upload_hole_banner(v, hole):
|
|||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return redirect(f'/h/{hole}/settings')
|
||||
return {"message": "Banner uploaded successfully!", "url": bannerurl}
|
||||
|
||||
@app.post("/h/<hole>/settings/banners/delete/<int:index>")
|
||||
@app.post("/h/<hole>/settings/banners/delete")
|
||||
@limiter.limit("1/second", deduct_when=lambda response: response.status_code < 400)
|
||||
@limiter.limit("1/second", deduct_when=lambda response: response.status_code < 400, key_func=get_ID)
|
||||
@auth_required
|
||||
def delete_hole_banner(v, hole, index):
|
||||
def delete_hole_banner(v, hole):
|
||||
hole = get_hole(hole)
|
||||
if not v.mods_hole(hole.name): abort(403)
|
||||
|
||||
if not hole.bannerurls:
|
||||
abort(404, f"Banner not found (/h/{hole.name} has no banners)")
|
||||
if index < 0 or index >= len(hole.bannerurls):
|
||||
abort(404, f'Banner not found (banner index {index} is not between 0 and {len(hole.bannerurls)})')
|
||||
banner = hole.bannerurls[index]
|
||||
try:
|
||||
remove_media_using_link(banner)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
del hole.bannerurls[index]
|
||||
abort(404, f"Banner not found (/h/{hole.name} has no banner images)")
|
||||
|
||||
banner = request.values["url"]
|
||||
|
||||
if banner not in hole.bannerurls:
|
||||
abort(404, "Banner not found!")
|
||||
|
||||
try: remove_media_using_link(banner)
|
||||
except FileNotFoundError: pass
|
||||
|
||||
hole.bannerurls.remove(banner)
|
||||
g.db.add(hole)
|
||||
|
||||
ma = HoleAction(
|
||||
hole=hole.name,
|
||||
kind='delete_banner',
|
||||
kind='delete_banner_image',
|
||||
_note=f'<a href="{banner}">{banner}</a>',
|
||||
user_id=v.id
|
||||
)
|
||||
g.db.add(ma)
|
||||
|
||||
return {"message": f"Deleted banner {index} from /h/{hole} successfully"}
|
||||
return {"message": "Banner deleted successfully!"}
|
||||
|
||||
@app.post("/h/<hole>/settings/banners/delete_all")
|
||||
@limiter.limit("1/10 second;30/day", deduct_when=lambda response: response.status_code < 400)
|
||||
|
|
|
@ -99,56 +99,58 @@
|
|||
<h4 class="mb-4 pb-2">Banners</h4>
|
||||
{% if not g.is_tor %}
|
||||
<section id="hole-banner-upload-new" class="mb-5 hole-settings-subsection">
|
||||
<form class="d-inline-block" action="/h/{{hole.name}}/settings/banners/" method="post" enctype="multipart/form-data">
|
||||
<input hidden name="formkey" value="{{v|formkey}}" class="notranslate" translate="no">
|
||||
<label class="btn btn-secondary text-capitalize mr-2 mb-0">
|
||||
Upload New Banner<input autocomplete="off" type="file" accept="image/*" hidden name="banner" data-nonce="{{g.nonce}}" onchange_submit>
|
||||
</label>
|
||||
</form>
|
||||
<input hidden name="formkey" value="{{v|formkey}}" class="notranslate" translate="no">
|
||||
<label class="btn btn-secondary text-capitalize mr-2 mb-0">
|
||||
Upload New Banner<input id="upload_banner" autocomplete="off" type="file" accept="image/*" hidden name="banner" data-nonce="{{g.nonce}}" data-onchange="upload_banner(this, '{{hole.name}}')">
|
||||
</label>
|
||||
<button type="button" class="btn btn-danger hole-banner-delete-button" id="hole-banner-delete-all" data-nonce="{{g.nonce}}" data-onclick="areyousure(this)" data-areyousure="postToastReload(this, '/h/{{hole.name}}/settings/banners/delete_all')">Delete All Banners</button>
|
||||
<div class="text-small text-muted mt-3">
|
||||
All image files are supported. Max file size is {% if v and v.patron %}16{% else %}8{% endif %} MB.
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% for banner in hole.bannerurls %}
|
||||
<section id="hole-banner-update-{{loop.index - 1}}" class="mt-5 d-block hole-settings-subsection">
|
||||
<img class="mr-3" loading="lazy" alt="/h/{{hole.name}} banner" src="{{banner}}">
|
||||
<button class="btn btn-danger hole-banner-delete-button mt-2" id="hole-banner-delete-{{loop.index}}" data-nonce="{{g.nonce}}" data-onclick="areyousure(this)" data-areyousure="postToastReload(this, '/h/{{hole.name}}/settings/banners/delete/{{loop.index - 1}}')">Delete</button>
|
||||
</section>
|
||||
{% else %}
|
||||
<section id="hole-banner-no-banners" class="d-block hole-settings-subsection">
|
||||
{{macros.ghost_box("No banners uploaded", "", 2, "flex:1")}}
|
||||
</section>
|
||||
{% endfor %}
|
||||
<div id="hole-banners">
|
||||
{% for banner in hole.bannerurls %}
|
||||
<section class="mt-5 d-block hole-settings-subsection">
|
||||
<img class="mr-3" loading="lazy" alt="/h/{{hole.name}} banner" src="{{banner}}">
|
||||
<button class="btn btn-danger hole-banner-delete-button mt-2" data-nonce="{{g.nonce}}" data-onclick="areyousure(this)" data-areyousure="delete_image(this, '/h/{{hole.name}}/settings/banners/delete')">Delete</button>
|
||||
</section>
|
||||
{% else %}
|
||||
<section id="hole-banner-no-banners" class="d-block hole-settings-subsection">
|
||||
{{macros.ghost_box("No banners uploaded", "", 2, "flex:1")}}
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hole-settings-section hole-sidebar-update-section">
|
||||
<h4 class="mb-4 pb-2">Sidebar Images</h4>
|
||||
{% if not g.is_tor %}
|
||||
<section id="hole-sidebar-upload-new" class="mb-5 hole-settings-subsection">
|
||||
<form class="d-inline-block" action="/h/{{hole.name}}/settings/sidebars/" method="post" enctype="multipart/form-data">
|
||||
<input hidden name="formkey" value="{{v|formkey}}" class="notranslate" translate="no">
|
||||
<label class="btn btn-secondary text-capitalize mr-2 mb-0">
|
||||
Upload New Sidebar Image<input autocomplete="off" type="file" accept="image/*" hidden name="sidebar" data-nonce="{{g.nonce}}" onchange_submit>
|
||||
</label>
|
||||
</form>
|
||||
<input hidden name="formkey" value="{{v|formkey}}" class="notranslate" translate="no">
|
||||
<label class="btn btn-secondary text-capitalize mr-2 mb-0">
|
||||
Upload New Sidebar Image<input id="upload_sidebar" autocomplete="off" type="file" accept="image/*" hidden name="sidebar" data-nonce="{{g.nonce}}" data-onchange="upload_sidebar(this, '{{hole.name}}')">
|
||||
</label>
|
||||
<button type="button" class="btn btn-danger hole-sidebar-delete-button" id="hole-sidebar-delete-all" data-nonce="{{g.nonce}}" data-onclick="areyousure(this)" data-areyousure="postToastReload(this, '/h/{{hole.name}}/settings/sidebars/delete_all')">Delete All Sidebar Images</button>
|
||||
<div class="text-small text-muted mt-3">
|
||||
All image files are supported. Max file size is {% if v and v.patron %}16{% else %}8{% endif %} MB.
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% for sidebar in hole.sidebarurls %}
|
||||
<section id="hole-sidebar-update-{{loop.index - 1}}" class="mt-5 d-block hole-settings-subsection">
|
||||
<img class="mr-3" loading="lazy" alt="/h/{{hole.name}} sidebar" src="{{sidebar}}">
|
||||
<button class="btn btn-danger hole-sidebar-delete-button mt-2" id="hole-sidebar-delete-{{loop.index}}" data-nonce="{{g.nonce}}" data-onclick="areyousure(this)" data-areyousure="postToastReload(this, '/h/{{hole.name}}/settings/sidebars/delete/{{loop.index - 1}}')">Delete</button>
|
||||
</section>
|
||||
{% else %}
|
||||
<section id="hole-sidebar-no-sidebars" class="d-block hole-settings-subsection">
|
||||
{{macros.ghost_box("No sidebar images uploaded", "", 2, "flex:1")}}
|
||||
</section>
|
||||
{% endfor %}
|
||||
<div id="hole-sidebars">
|
||||
{% for sidebar in hole.sidebarurls %}
|
||||
<section class="mt-5 d-block hole-settings-subsection">
|
||||
<img class="mr-3" loading="lazy" alt="/h/{{hole.name}} sidebar" src="{{sidebar}}">
|
||||
<button class="btn btn-danger hole-sidebar-delete-button mt-2" data-nonce="{{g.nonce}}" data-onclick="areyousure(this)" data-areyousure="delete_image(this, '/h/{{hole.name}}/settings/sidebars/delete')">Delete</button>
|
||||
</section>
|
||||
{% else %}
|
||||
<section id="hole-sidebar-no-sidebars" class="d-block hole-settings-subsection">
|
||||
{{macros.ghost_box("No sidebar images uploaded", "", 2, "flex:1")}}
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script defer src="{{'js/hole_settings.js' | asset}}"></script>
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in New Issue