mirror of
https://github.com/Derpy-Leggies/OnlyLegs.git
synced 2025-01-14 10:35:22 +00:00
Clean up JS for Groups and Images page
Clean up group.py and image.py
This commit is contained in:
parent
bdecdaff7c
commit
54a98a8591
117
onlylegs/api.py
117
onlylegs/api.py
|
@ -9,7 +9,6 @@ from uuid import uuid4
|
||||||
|
|
||||||
from flask import (
|
from flask import (
|
||||||
Blueprint,
|
Blueprint,
|
||||||
flash,
|
|
||||||
abort,
|
abort,
|
||||||
send_from_directory,
|
send_from_directory,
|
||||||
jsonify,
|
jsonify,
|
||||||
|
@ -21,7 +20,7 @@ from flask_login import login_required, current_user
|
||||||
from colorthief import ColorThief
|
from colorthief import ColorThief
|
||||||
|
|
||||||
from onlylegs.extensions import db
|
from onlylegs.extensions import db
|
||||||
from onlylegs.models import Users, Pictures, Albums, AlbumJunction
|
from onlylegs.models import Users, Pictures
|
||||||
from onlylegs.utils.metadata import yoink
|
from onlylegs.utils.metadata import yoink
|
||||||
from onlylegs.utils.generate_image import generate_thumbnail
|
from onlylegs.utils.generate_image import generate_thumbnail
|
||||||
|
|
||||||
|
@ -111,8 +110,8 @@ def media(path):
|
||||||
r for resolution, thumb for thumbnail etc
|
r for resolution, thumb for thumbnail etc
|
||||||
e for extension, jpg, png etc
|
e for extension, jpg, png etc
|
||||||
"""
|
"""
|
||||||
res = request.args.get("r", default=None).strip()
|
res = request.args.get("r", "").strip()
|
||||||
ext = request.args.get("e", default=None).strip()
|
ext = request.args.get("e", "").strip()
|
||||||
|
|
||||||
# if no args are passed, return the raw file
|
# if no args are passed, return the raw file
|
||||||
if not res and not ext:
|
if not res and not ext:
|
||||||
|
@ -181,113 +180,3 @@ def upload():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return jsonify({"message": "File uploaded"}), 200
|
return jsonify({"message": "File uploaded"}), 200
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/media/delete/<int:image_id>", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def delete_image(image_id):
|
|
||||||
"""
|
|
||||||
Deletes an image from the server and database
|
|
||||||
"""
|
|
||||||
post = db.get_or_404(Pictures, image_id)
|
|
||||||
|
|
||||||
# Check if image exists and if user is allowed to delete it (author)
|
|
||||||
if post.author_id != current_user.id:
|
|
||||||
logging.info("User %s tried to delete image %s", current_user.id, image_id)
|
|
||||||
return (
|
|
||||||
jsonify({"message": "You are not allowed to delete this image, heck off"}),
|
|
||||||
403,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Delete file
|
|
||||||
try:
|
|
||||||
os.remove(os.path.join(current_app.config["UPLOAD_FOLDER"], post.filename))
|
|
||||||
except FileNotFoundError:
|
|
||||||
logging.warning(
|
|
||||||
"File not found: %s, already deleted or never existed", post.filename
|
|
||||||
)
|
|
||||||
|
|
||||||
# Delete cached files
|
|
||||||
cache_name = post.filename.rsplit(".")[0]
|
|
||||||
for cache_file in pathlib.Path(current_app.config["CACHE_FOLDER"]).glob(
|
|
||||||
cache_name + "*"
|
|
||||||
):
|
|
||||||
os.remove(cache_file)
|
|
||||||
|
|
||||||
AlbumJunction.query.filter_by(picture_id=image_id).delete()
|
|
||||||
db.session.delete(post)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
logging.info("Removed image (%s) %s", image_id, post.filename)
|
|
||||||
flash(["Image was all in Le Head!", "1"])
|
|
||||||
return jsonify({"message": "Image deleted"}), 200
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/group/create", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def create_group():
|
|
||||||
"""
|
|
||||||
Creates a group
|
|
||||||
"""
|
|
||||||
group_name = request.form.get("name", "").strip()
|
|
||||||
group_description = request.form.get("description", "").strip()
|
|
||||||
group_author = current_user.id
|
|
||||||
|
|
||||||
new_group = Albums(
|
|
||||||
name=group_name,
|
|
||||||
description=group_description,
|
|
||||||
author_id=group_author,
|
|
||||||
)
|
|
||||||
|
|
||||||
db.session.add(new_group)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
return jsonify({"message": "Group created", "id": new_group.id})
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/group/modify", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
def modify_group():
|
|
||||||
"""
|
|
||||||
Changes the images in a group
|
|
||||||
"""
|
|
||||||
group_id = request.form.get("group", "").strip()
|
|
||||||
image_id = request.form.get("image", "").strip()
|
|
||||||
action = request.form.get("action", "").strip()
|
|
||||||
|
|
||||||
group = db.get_or_404(Albums, group_id)
|
|
||||||
db.get_or_404(Pictures, image_id) # Check if image exists
|
|
||||||
|
|
||||||
if group.author_id != current_user.id:
|
|
||||||
return jsonify({"message": "You are not the owner of this group"}), 403
|
|
||||||
|
|
||||||
junction_exist = AlbumJunction.query.filter_by(
|
|
||||||
album_id=group_id, picture_id=image_id
|
|
||||||
).first()
|
|
||||||
|
|
||||||
if action == "add" and not junction_exist:
|
|
||||||
db.session.add(AlbumJunction(album_id=group_id, picture_id=image_id))
|
|
||||||
elif request.form["action"] == "remove":
|
|
||||||
AlbumJunction.query.filter_by(album_id=group_id, picture_id=image_id).delete()
|
|
||||||
|
|
||||||
db.session.commit()
|
|
||||||
return jsonify({"message": "Group modified"})
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/group/delete", methods=["POST"])
|
|
||||||
def delete_group():
|
|
||||||
"""
|
|
||||||
Deletes a group
|
|
||||||
"""
|
|
||||||
group_id = request.form.get("group", "").strip()
|
|
||||||
group = db.get_or_404(Albums, group_id)
|
|
||||||
|
|
||||||
if group.author_id != current_user.id:
|
|
||||||
return jsonify({"message": "You are not the owner of this group"}), 403
|
|
||||||
|
|
||||||
AlbumJunction.query.filter_by(album_id=group_id).delete()
|
|
||||||
db.session.delete(group)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
flash(["Group yeeted!", "1"])
|
|
||||||
return jsonify({"message": "Group deleted"})
|
|
||||||
|
|
155
onlylegs/static/js/groupPage.js
Normal file
155
onlylegs/static/js/groupPage.js
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
function groupDeletePopup() {
|
||||||
|
let title = 'Yeet!';
|
||||||
|
let subtitle =
|
||||||
|
'Are you surrrre? This action is irreversible ' +
|
||||||
|
'and very final. This wont delete the images, ' +
|
||||||
|
'but it will remove them from this group.'
|
||||||
|
let body = null;
|
||||||
|
|
||||||
|
let deleteBtn = document.createElement('button');
|
||||||
|
deleteBtn.classList.add('btn-block');
|
||||||
|
deleteBtn.classList.add('critical');
|
||||||
|
deleteBtn.innerHTML = 'Dewww eeeet!';
|
||||||
|
deleteBtn.onclick = groupDeleteConfirm;
|
||||||
|
|
||||||
|
popupShow(title, subtitle, body, [popupCancelButton, deleteBtn]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupDeleteConfirm(event) {
|
||||||
|
// AJAX takes control of subby form :3
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
fetch('/group/' + group_data['id'], {
|
||||||
|
method: 'DELETE',
|
||||||
|
}).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
window.location.href = '/group/';
|
||||||
|
} else {
|
||||||
|
addNotification('Server exploded, returned:' + response.status, 2);
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
addNotification('Error yeeting group!' + error, 2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupEditPopup() {
|
||||||
|
let title = 'Nothing stays the same';
|
||||||
|
let subtitle = 'Add, remove, or change, the power is in your hands...'
|
||||||
|
|
||||||
|
let formSubmitButton = document.createElement('button');
|
||||||
|
formSubmitButton.setAttribute('form', 'groupEditForm');
|
||||||
|
formSubmitButton.setAttribute('type', 'submit');
|
||||||
|
formSubmitButton.classList.add('btn-block');
|
||||||
|
formSubmitButton.classList.add('primary');
|
||||||
|
formSubmitButton.innerHTML = 'Saveeee';
|
||||||
|
|
||||||
|
// Create form
|
||||||
|
let body = document.createElement('form');
|
||||||
|
body.setAttribute('onsubmit', 'return groupEditConfirm(event);');
|
||||||
|
body.id = 'groupEditForm';
|
||||||
|
|
||||||
|
let formImageId = document.createElement('input');
|
||||||
|
formImageId.setAttribute('type', 'text');
|
||||||
|
formImageId.setAttribute('placeholder', 'Image ID');
|
||||||
|
formImageId.setAttribute('required', '');
|
||||||
|
formImageId.classList.add('input-block');
|
||||||
|
formImageId.id = 'groupFormImageId';
|
||||||
|
|
||||||
|
let formAction = document.createElement('input');
|
||||||
|
formAction.setAttribute('type', 'text');
|
||||||
|
formAction.setAttribute('value', 'add');
|
||||||
|
formAction.setAttribute('placeholder', '[add, remove]');
|
||||||
|
formAction.setAttribute('required', '');
|
||||||
|
formAction.classList.add('input-block');
|
||||||
|
formAction.id = 'groupFormAction';
|
||||||
|
|
||||||
|
body.appendChild(formImageId);
|
||||||
|
body.appendChild(formAction);
|
||||||
|
|
||||||
|
popupShow(title, subtitle, body, [popupCancelButton, formSubmitButton]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupEditConfirm(event) {
|
||||||
|
// AJAX takes control of subby form :3
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
let imageId = document.querySelector("#groupFormImageId").value;
|
||||||
|
let action = document.querySelector("#groupFormAction").value;
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("imageId", imageId);
|
||||||
|
formData.append("action", action);
|
||||||
|
|
||||||
|
fetch('/group/' + group_data['id'], {
|
||||||
|
method: 'PUT',
|
||||||
|
body: formData
|
||||||
|
}).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
addNotification('Server exploded, returned:' + response.status, 2);
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
addNotification('Error!!!!! Panic!!!!' + error, 2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupCreatePopup() {
|
||||||
|
let title = 'New stuff!';
|
||||||
|
let subtitle =
|
||||||
|
'Image groups are a simple way to ' +
|
||||||
|
'"group" images together, are you ready?'
|
||||||
|
|
||||||
|
let formSubmitButton = document.createElement('button');
|
||||||
|
formSubmitButton.setAttribute('form', 'groupCreateForm');
|
||||||
|
formSubmitButton.setAttribute('type', 'submit');
|
||||||
|
formSubmitButton.classList.add('btn-block');
|
||||||
|
formSubmitButton.classList.add('primary');
|
||||||
|
formSubmitButton.innerHTML = 'Huzzah!';
|
||||||
|
|
||||||
|
// Create form
|
||||||
|
let body = document.createElement('form');
|
||||||
|
body.setAttribute('onsubmit', 'return groupCreateConfirm(event);');
|
||||||
|
body.id = 'groupCreateForm';
|
||||||
|
|
||||||
|
let formName = document.createElement('input');
|
||||||
|
formName.setAttribute('type', 'text');
|
||||||
|
formName.setAttribute('placeholder', 'Group namey');
|
||||||
|
formName.setAttribute('required', '');
|
||||||
|
formName.classList.add('input-block');
|
||||||
|
formName.id = 'groupFormName';
|
||||||
|
|
||||||
|
let formDescription = document.createElement('input');
|
||||||
|
formDescription.setAttribute('type', 'text');
|
||||||
|
formDescription.setAttribute('placeholder', 'What it about????');
|
||||||
|
formDescription.classList.add('input-block');
|
||||||
|
formDescription.id = 'groupFormDescription';
|
||||||
|
|
||||||
|
body.appendChild(formName);
|
||||||
|
body.appendChild(formDescription);
|
||||||
|
|
||||||
|
popupShow(title, subtitle, body, [popupCancelButton, formSubmitButton]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupCreateConfirm(event) {
|
||||||
|
// AJAX takes control of subby form :3
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
let name = document.querySelector("#groupFormName").value;
|
||||||
|
let description = document.querySelector("#groupFormDescription").value;
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("name", name);
|
||||||
|
formData.append("description", description);
|
||||||
|
|
||||||
|
fetch('/group/', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
}).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
addNotification('Server exploded, returned:' + response.status, 2);
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
addNotification('Error summoning group!' + error, 2);
|
||||||
|
});
|
||||||
|
}
|
101
onlylegs/static/js/imagePage.js
Normal file
101
onlylegs/static/js/imagePage.js
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
function imageFullscreen() {
|
||||||
|
let info = document.querySelector('.info-container');
|
||||||
|
let image = document.querySelector('.image-container');
|
||||||
|
|
||||||
|
if (info.classList.contains('collapsed')) {
|
||||||
|
info.classList.remove('collapsed');
|
||||||
|
image.classList.remove('collapsed');
|
||||||
|
document.cookie = "image-info=0"
|
||||||
|
} else {
|
||||||
|
info.classList.add('collapsed');
|
||||||
|
image.classList.add('collapsed');
|
||||||
|
document.cookie = "image-info=1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function imageDeletePopup() {
|
||||||
|
let title = 'DESTRUCTION!!!!!!';
|
||||||
|
let subtitle =
|
||||||
|
'Do you want to delete this image along with ' +
|
||||||
|
'all of its data??? This action is irreversible!';
|
||||||
|
let body = null;
|
||||||
|
|
||||||
|
let deleteBtn = document.createElement('button');
|
||||||
|
deleteBtn.classList.add('btn-block');
|
||||||
|
deleteBtn.classList.add('critical');
|
||||||
|
deleteBtn.innerHTML = 'Dewww eeeet!';
|
||||||
|
deleteBtn.onclick = imageDeleteConfirm;
|
||||||
|
|
||||||
|
popupShow(title, subtitle, body, [popupCancelButton, deleteBtn]);
|
||||||
|
}
|
||||||
|
function imageDeleteConfirm() {
|
||||||
|
popupDismiss();
|
||||||
|
|
||||||
|
fetch('/image/' + image_data["id"], {
|
||||||
|
method: 'DELETE',
|
||||||
|
}).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
window.location.href = '/';
|
||||||
|
} else {
|
||||||
|
addNotification('Image *clings*', 2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function imageEditPopup() {
|
||||||
|
let title = 'Edit image!';
|
||||||
|
let subtitle = 'Enter funny stuff here!';
|
||||||
|
|
||||||
|
let formSubmitButton = document.createElement('button');
|
||||||
|
formSubmitButton.setAttribute('form', 'imageEditForm');
|
||||||
|
formSubmitButton.setAttribute('type', 'submit');
|
||||||
|
formSubmitButton.classList.add('btn-block');
|
||||||
|
formSubmitButton.classList.add('primary');
|
||||||
|
formSubmitButton.innerHTML = 'Saveeee';
|
||||||
|
|
||||||
|
// Create form
|
||||||
|
let body = document.createElement('form');
|
||||||
|
body.setAttribute('onsubmit', 'return imageEditConfirm(event);');
|
||||||
|
body.id = 'imageEditForm';
|
||||||
|
|
||||||
|
let formAlt = document.createElement('input');
|
||||||
|
formAlt.setAttribute('type', 'text');
|
||||||
|
formAlt.setAttribute('value', image_data["alt"]);
|
||||||
|
formAlt.setAttribute('placeholder', 'Image Alt');
|
||||||
|
formAlt.classList.add('input-block');
|
||||||
|
formAlt.id = 'imageFormAlt';
|
||||||
|
|
||||||
|
let formDescription = document.createElement('input');
|
||||||
|
formDescription.setAttribute('type', 'text');
|
||||||
|
formDescription.setAttribute('value', image_data["description"]);
|
||||||
|
formDescription.setAttribute('placeholder', 'Image Description');
|
||||||
|
formDescription.classList.add('input-block');
|
||||||
|
formDescription.id = 'imageFormDescription';
|
||||||
|
|
||||||
|
body.appendChild(formAlt);
|
||||||
|
body.appendChild(formDescription);
|
||||||
|
|
||||||
|
popupShow(title, subtitle, body, [popupCancelButton, formSubmitButton]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function imageEditConfirm(event) {
|
||||||
|
// Yoink subby form
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
let alt = document.querySelector('#imageFormAlt').value;
|
||||||
|
let description = document.querySelector('#imageFormDescription').value;
|
||||||
|
let form = new FormData();
|
||||||
|
form.append('alt', alt);
|
||||||
|
form.append('description', description);
|
||||||
|
|
||||||
|
fetch('/image/' + image_data["id"], {
|
||||||
|
method: 'PUT',
|
||||||
|
body: form,
|
||||||
|
}).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
popupDismiss();
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
addNotification('Image *clings*', 2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ function showLogin() {
|
||||||
cancelBtn.classList.add('btn-block');
|
cancelBtn.classList.add('btn-block');
|
||||||
cancelBtn.classList.add('transparent');
|
cancelBtn.classList.add('transparent');
|
||||||
cancelBtn.innerHTML = 'nuuuuuuuu';
|
cancelBtn.innerHTML = 'nuuuuuuuu';
|
||||||
cancelBtn.onclick = popupDissmiss;
|
cancelBtn.onclick = popupDismiss;
|
||||||
|
|
||||||
loginBtn = document.createElement('button');
|
loginBtn = document.createElement('button');
|
||||||
loginBtn.classList.add('btn-block');
|
loginBtn.classList.add('btn-block');
|
||||||
|
@ -50,7 +50,7 @@ function showLogin() {
|
||||||
loginForm.appendChild(passwordInput);
|
loginForm.appendChild(passwordInput);
|
||||||
loginForm.appendChild(rememberMeSpan);
|
loginForm.appendChild(rememberMeSpan);
|
||||||
|
|
||||||
popUpShow(
|
popupShow(
|
||||||
'Login!',
|
'Login!',
|
||||||
'Need an account? <span class="link" onclick="showRegister()">Register!</span>',
|
'Need an account? <span class="link" onclick="showRegister()">Register!</span>',
|
||||||
loginForm,
|
loginForm,
|
||||||
|
@ -103,7 +103,7 @@ function showRegister() {
|
||||||
cancelBtn.classList.add('btn-block');
|
cancelBtn.classList.add('btn-block');
|
||||||
cancelBtn.classList.add('transparent');
|
cancelBtn.classList.add('transparent');
|
||||||
cancelBtn.innerHTML = 'nuuuuuuuu';
|
cancelBtn.innerHTML = 'nuuuuuuuu';
|
||||||
cancelBtn.onclick = popupDissmiss;
|
cancelBtn.onclick = popupDismiss;
|
||||||
|
|
||||||
registerBtn = document.createElement('button');
|
registerBtn = document.createElement('button');
|
||||||
registerBtn.classList.add('btn-block');
|
registerBtn.classList.add('btn-block');
|
||||||
|
@ -146,7 +146,7 @@ function showRegister() {
|
||||||
registerForm.appendChild(passwordInput);
|
registerForm.appendChild(passwordInput);
|
||||||
registerForm.appendChild(passwordInputRepeat);
|
registerForm.appendChild(passwordInputRepeat);
|
||||||
|
|
||||||
popUpShow(
|
popupShow(
|
||||||
'Who are you?',
|
'Who are you?',
|
||||||
'Already have an account? <span class="link" onclick="showLogin()">Login!</span>',
|
'Already have an account? <span class="link" onclick="showLogin()">Login!</span>',
|
||||||
registerForm,
|
registerForm,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
function popUpShow(titleText, subtitleText, bodyContent=null, userActions=null) {
|
function popupShow(titleText, subtitleText, bodyContent=null, userActions=null) {
|
||||||
// Get popup elements
|
// Get popup elements
|
||||||
const popupSelector = document.querySelector('.pop-up');
|
const popupSelector = document.querySelector('.pop-up');
|
||||||
const headerSelector = document.querySelector('.pop-up-header');
|
const headerSelector = document.querySelector('.pop-up-header');
|
||||||
|
@ -9,38 +9,47 @@ function popUpShow(titleText, subtitleText, bodyContent=null, userActions=null)
|
||||||
actionsSelector.innerHTML = '';
|
actionsSelector.innerHTML = '';
|
||||||
|
|
||||||
// Set popup header and subtitle
|
// Set popup header and subtitle
|
||||||
const titleElement = document.createElement('h2');
|
let titleElement = document.createElement('h2');
|
||||||
titleElement.innerHTML = titleText;
|
titleElement.innerHTML = titleText;
|
||||||
headerSelector.appendChild(titleElement);
|
headerSelector.appendChild(titleElement);
|
||||||
|
|
||||||
const subtitleElement = document.createElement('p');
|
let subtitleElement = document.createElement('p');
|
||||||
subtitleElement.innerHTML = subtitleText;
|
subtitleElement.innerHTML = subtitleText;
|
||||||
headerSelector.appendChild(subtitleElement);
|
headerSelector.appendChild(subtitleElement);
|
||||||
|
|
||||||
if (bodyContent) {
|
if (bodyContent) { headerSelector.appendChild(bodyContent) }
|
||||||
headerSelector.appendChild(bodyContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set buttons that will be displayed
|
// Set buttons that will be displayed
|
||||||
if (userActions) {
|
if (userActions) {
|
||||||
// for each user action, add the element
|
userActions.forEach((action) => {
|
||||||
for (let i = 0; i < userActions.length; i++) {
|
actionsSelector.appendChild(action);
|
||||||
actionsSelector.appendChild(userActions[i]);
|
});
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
actionsSelector.innerHTML = '<button class="btn-block transparent" onclick="popupDissmiss()">Close</button>';
|
let closeButton = document.createElement('button');
|
||||||
|
closeButton.classList.add('btn-block');
|
||||||
|
closeButton.classList.add('transparent');
|
||||||
|
closeButton.innerHTML = 'Yeet!';
|
||||||
|
closeButton.onclick = popupDismiss;
|
||||||
|
actionsSelector.appendChild(closeButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop scrolling and show popup
|
// Stop scrolling and show popup
|
||||||
document.querySelector("html").style.overflow = "hidden";
|
document.querySelector("html").style.overflow = "hidden";
|
||||||
popupSelector.style.display = 'block';
|
popupSelector.style.display = 'block';
|
||||||
setTimeout(() => { popupSelector.classList.add('active') }, 5); // 2ms delay to allow for css transition >:C
|
|
||||||
|
// 5ms delay to allow for css transition >:C
|
||||||
|
setTimeout(() => { popupSelector.classList.add('active') }, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
function popupDissmiss() {
|
function popupDismiss() {
|
||||||
const popupSelector = document.querySelector('.pop-up');
|
const popupSelector = document.querySelector('.pop-up');
|
||||||
|
|
||||||
document.querySelector("html").style.overflow = "auto";
|
document.querySelector("html").style.overflow = "auto";
|
||||||
popupSelector.classList.remove('active');
|
popupSelector.classList.remove('active');
|
||||||
setTimeout(() => { popupSelector.style.display = 'none'; }, 200);
|
setTimeout(() => { popupSelector.style.display = 'none'; }, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const popupCancelButton = document.createElement('button');
|
||||||
|
popupCancelButton.classList.add('btn-block');
|
||||||
|
popupCancelButton.classList.add('transparent');
|
||||||
|
popupCancelButton.innerHTML = 'nuuuuuuuu';
|
||||||
|
popupCancelButton.onclick = popupDismiss;
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
<button class="top-of-page" aria-label="Jump to top of page"><i class="ph ph-arrow-up"></i></button>
|
<button class="top-of-page" aria-label="Jump to top of page"><i class="ph ph-arrow-up"></i></button>
|
||||||
|
|
||||||
<div class="pop-up">
|
<div class="pop-up">
|
||||||
<span class="pop-up__click-off" onclick="popupDissmiss()"></span>
|
<span class="pop-up__click-off" onclick="popupDismiss()"></span>
|
||||||
<div class="pop-up-wrapper">
|
<div class="pop-up-wrapper">
|
||||||
<div class="pop-up-header"></div>
|
<div class="pop-up-header"></div>
|
||||||
<div class="pop-up-controlls"></div>
|
<div class="pop-up-controlls"></div>
|
||||||
|
@ -160,7 +160,7 @@
|
||||||
infoButton.classList.add('show');
|
infoButton.classList.add('show');
|
||||||
}
|
}
|
||||||
infoButton.onclick = () => {
|
infoButton.onclick = () => {
|
||||||
popUpShow('OnlyLegs',
|
popupShow('OnlyLegs',
|
||||||
'<a href="https://github.com/Fluffy-Bean/onlylegs">v{{ config['APP_VERSION'] }}</a> ' +
|
'<a href="https://github.com/Fluffy-Bean/onlylegs">v{{ config['APP_VERSION'] }}</a> ' +
|
||||||
'using <a href="https://phosphoricons.com/">Phosphoricons</a> and Flask.' +
|
'using <a href="https://phosphoricons.com/">Phosphoricons</a> and Flask.' +
|
||||||
'<br>Made by Fluffy and others with ❤️');
|
'<br>Made by Fluffy and others with ❤️');
|
||||||
|
|
|
@ -11,163 +11,11 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
{% if current_user.id == group.author.id %}
|
group_data = {
|
||||||
function groupDelete() {
|
'id': {{ group.id }},
|
||||||
cancelBtn = document.createElement('button');
|
'name': "{{ group.name }}",
|
||||||
cancelBtn.classList.add('btn-block');
|
'description': "{{ group.description }}",
|
||||||
cancelBtn.classList.add('transparent');
|
}
|
||||||
cancelBtn.innerHTML = 'AAAAAAAAAA';
|
|
||||||
cancelBtn.onclick = popupDissmiss;
|
|
||||||
|
|
||||||
deleteBtn = document.createElement('button');
|
|
||||||
deleteBtn.classList.add('btn-block');
|
|
||||||
deleteBtn.classList.add('critical');
|
|
||||||
deleteBtn.innerHTML = 'No ragrats!';
|
|
||||||
deleteBtn.onclick = deleteConfirm;
|
|
||||||
|
|
||||||
popUpShow('Yeet!',
|
|
||||||
'Are you surrrre? This action is irreversible and very final.' +
|
|
||||||
' This wont delete the images, but it will remove them from this group.',
|
|
||||||
null,
|
|
||||||
[cancelBtn, deleteBtn]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteConfirm(event) {
|
|
||||||
// AJAX takes control of subby form :3
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
let formID = {{ group.id }};
|
|
||||||
|
|
||||||
if (!formID) {
|
|
||||||
addNotification("Dont tamper with the JavaScript pls!", 3);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make form
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("group", formID);
|
|
||||||
|
|
||||||
fetch('{{ url_for('api.delete_group') }}', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
}).then(response => {
|
|
||||||
if (response.status === 200) {
|
|
||||||
// Redirect to groups page
|
|
||||||
window.location.href = '{{ url_for('group.groups') }}';
|
|
||||||
} else {
|
|
||||||
switch (response.status) {
|
|
||||||
case 500:
|
|
||||||
addNotification('Server exploded, F\'s in chat', 2);
|
|
||||||
break;
|
|
||||||
case 403:
|
|
||||||
addNotification('None but devils play past here... Bad information', 2);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
addNotification('Error logging in, blame someone', 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch(error => {
|
|
||||||
addNotification('Error yeeting group!', 2);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function groupEdit() {
|
|
||||||
// Create elements
|
|
||||||
cancelBtn = document.createElement('button');
|
|
||||||
cancelBtn.classList.add('btn-block');
|
|
||||||
cancelBtn.classList.add('transparent');
|
|
||||||
cancelBtn.innerHTML = 'go baaaaack';
|
|
||||||
cancelBtn.onclick = popupDissmiss;
|
|
||||||
|
|
||||||
submitBtn = document.createElement('button');
|
|
||||||
submitBtn.classList.add('btn-block');
|
|
||||||
submitBtn.classList.add('primary');
|
|
||||||
submitBtn.innerHTML = 'Saveeee';
|
|
||||||
submitBtn.type = 'submit';
|
|
||||||
submitBtn.setAttribute('form', 'editForm');
|
|
||||||
|
|
||||||
// Create form
|
|
||||||
editForm = document.createElement('form');
|
|
||||||
editForm.id = 'editForm';
|
|
||||||
editForm.setAttribute('onsubmit', 'return edit(event);');
|
|
||||||
|
|
||||||
groupInput = document.createElement('input');
|
|
||||||
groupInput.classList.add('input-block');
|
|
||||||
groupInput.type = 'text';
|
|
||||||
groupInput.placeholder = 'Group ID';
|
|
||||||
groupInput.value = {{ group.id }};
|
|
||||||
groupInput.id = 'group';
|
|
||||||
|
|
||||||
imageInput = document.createElement('input');
|
|
||||||
imageInput.classList.add('input-block');
|
|
||||||
imageInput.type = 'text';
|
|
||||||
imageInput.placeholder = 'Image ID';
|
|
||||||
imageInput.id = 'image';
|
|
||||||
|
|
||||||
actionInput = document.createElement('input');
|
|
||||||
actionInput.classList.add('input-block');
|
|
||||||
actionInput.type = 'text';
|
|
||||||
actionInput.placeholder = 'add/remove';
|
|
||||||
actionInput.value = 'add';
|
|
||||||
actionInput.id = 'action';
|
|
||||||
|
|
||||||
editForm.appendChild(groupInput);
|
|
||||||
editForm.appendChild(imageInput);
|
|
||||||
editForm.appendChild(actionInput);
|
|
||||||
|
|
||||||
popUpShow(
|
|
||||||
'Nothing stays the same',
|
|
||||||
'Add, remove, or change, the power is in your hands...',
|
|
||||||
editForm,
|
|
||||||
[cancelBtn, submitBtn]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function edit(event) {
|
|
||||||
// AJAX takes control of subby form :3
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
let formGroup = document.querySelector("#group").value;
|
|
||||||
let formImage = document.querySelector("#image").value;
|
|
||||||
let formAction = document.querySelector("#action").value;
|
|
||||||
|
|
||||||
if (!formGroup || !formImage || !formAction) {
|
|
||||||
addNotification("All values must be set!", 3);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make form
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("group", formGroup);
|
|
||||||
formData.append("image", formImage);
|
|
||||||
formData.append("action", formAction);
|
|
||||||
|
|
||||||
fetch('{{ url_for('api.modify_group') }}', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
}).then(response => {
|
|
||||||
if (response.status === 200) {
|
|
||||||
addNotification('Group edited!!!', 1);
|
|
||||||
popupDissmiss();
|
|
||||||
} else {
|
|
||||||
switch (response.status) {
|
|
||||||
case 500:
|
|
||||||
addNotification('Server exploded, F\'s in chat', 2);
|
|
||||||
break;
|
|
||||||
case 403:
|
|
||||||
addNotification('None but devils play past here... Bad information', 2);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
addNotification('Error logging in, blame someone', 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch(error => {
|
|
||||||
addNotification('Error!!!!! Panic!!!!', 2);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
{% endif %}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -234,8 +82,8 @@
|
||||||
</div>
|
</div>
|
||||||
{% if current_user.id == group.author.id %}
|
{% if current_user.id == group.author.id %}
|
||||||
<div>
|
<div>
|
||||||
<button class="pill-item pill__critical" onclick="groupDelete()"><i class="ph ph-trash"></i></button>
|
<button class="pill-item pill__critical" onclick="groupDeletePopup()"><i class="ph ph-trash"></i></button>
|
||||||
<button class="pill-item pill__critical" onclick="groupEdit()"><i class="ph ph-pencil-simple"></i></button>
|
<button class="pill-item pill__critical" onclick="groupEditPopup()"><i class="ph ph-pencil-simple"></i></button>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -252,8 +100,8 @@
|
||||||
</div>
|
</div>
|
||||||
{% if current_user.id == group.author.id %}
|
{% if current_user.id == group.author.id %}
|
||||||
<div>
|
<div>
|
||||||
<button class="pill-item pill__critical" onclick="groupDelete()"><i class="ph ph-trash"></i></button>
|
<button class="pill-item pill__critical" onclick="groupDeletePopup()"><i class="ph ph-trash"></i></button>
|
||||||
<button class="pill-item pill__critical" onclick="groupEdit()"><i class="ph ph-pencil-simple"></i></button>
|
<button class="pill-item pill__critical" onclick="groupEditPopup()"><i class="ph ph-pencil-simple"></i></button>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,65 +9,11 @@
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function fullscreen() {
|
const image_data = {
|
||||||
let info = document.querySelector('.info-container');
|
'id': {{ image.id }},
|
||||||
let image = document.querySelector('.image-container');
|
'description': '{{ image.description }}',
|
||||||
|
'alt': '{{ image.alt }}',
|
||||||
if (info.classList.contains('collapsed')) {
|
};
|
||||||
info.classList.remove('collapsed');
|
|
||||||
image.classList.remove('collapsed');
|
|
||||||
document.cookie = "image-info=0"
|
|
||||||
} else {
|
|
||||||
info.classList.add('collapsed');
|
|
||||||
image.classList.add('collapsed');
|
|
||||||
document.cookie = "image-info=1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{% if current_user.id == image.author.id %}
|
|
||||||
function imageDelete() {
|
|
||||||
cancelBtn = document.createElement('button');
|
|
||||||
cancelBtn.classList.add('btn-block');
|
|
||||||
cancelBtn.classList.add('transparent');
|
|
||||||
cancelBtn.innerHTML = 'nuuuuuuuu';
|
|
||||||
cancelBtn.onclick = popupDissmiss;
|
|
||||||
|
|
||||||
deleteBtn = document.createElement('button');
|
|
||||||
deleteBtn.classList.add('btn-block');
|
|
||||||
deleteBtn.classList.add('critical');
|
|
||||||
deleteBtn.innerHTML = 'Dewww eeeet!';
|
|
||||||
deleteBtn.onclick = deleteConfirm;
|
|
||||||
|
|
||||||
popUpShow('DESTRUCTION!!!!!!',
|
|
||||||
'Do you want to delete this image along with all of its data??? ' +
|
|
||||||
'This action is irreversible!',
|
|
||||||
null,
|
|
||||||
[cancelBtn, deleteBtn]);
|
|
||||||
}
|
|
||||||
function deleteConfirm() {
|
|
||||||
popupDissmiss();
|
|
||||||
|
|
||||||
fetch('{{ url_for('api.delete_image', image_id=image['id']) }}', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
action: 'delete'
|
|
||||||
})
|
|
||||||
}).then(function(response) {
|
|
||||||
if (response.ok) {
|
|
||||||
window.location.href = '/';
|
|
||||||
} else {
|
|
||||||
addNotification(`Image *clings*`, 2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function imageEdit() {
|
|
||||||
addNotification("Not an option, oops!", 3);
|
|
||||||
}
|
|
||||||
{% endif %}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -85,14 +31,14 @@
|
||||||
<div class="pill-row">
|
<div class="pill-row">
|
||||||
{% if next_url %}<div><a class="pill-item" href="{{ next_url }}"><i class="ph ph-arrow-left"></i></a></div>{% endif %}
|
{% if next_url %}<div><a class="pill-item" href="{{ next_url }}"><i class="ph ph-arrow-left"></i></a></div>{% endif %}
|
||||||
<div>
|
<div>
|
||||||
<button class="pill-item" onclick="fullscreen()" id="fullscreenImage"><i class="ph ph-info"></i></button>
|
<button class="pill-item" onclick="imageFullscreen()" id="fullscreenImage"><i class="ph ph-info"></i></button>
|
||||||
<button class="pill-item" onclick="copyToClipboard(window.location.href)"><i class="ph ph-export"></i></button>
|
<button class="pill-item" onclick="copyToClipboard(window.location.href)"><i class="ph ph-export"></i></button>
|
||||||
<a class="pill-item" href="{{ url_for('api.media', path='uploads/' + image.filename) }}" download onclick="addNotification('Download started!', 4)"><i class="ph ph-file-arrow-down"></i></a>
|
<a class="pill-item" href="{{ url_for('api.media', path='uploads/' + image.filename) }}" download onclick="addNotification('Download started!', 4)"><i class="ph ph-file-arrow-down"></i></a>
|
||||||
</div>
|
</div>
|
||||||
{% if current_user.id == image.author.id %}
|
{% if current_user.id == image.author.id %}
|
||||||
<div>
|
<div>
|
||||||
<button class="pill-item pill__critical" onclick="imageDelete()"><i class="ph ph-trash"></i></button>
|
<button class="pill-item pill__critical" onclick="imageDeletePopup()"><i class="ph ph-trash"></i></button>
|
||||||
<button class="pill-item pill__critical" onclick="imageEdit()"><i class="ph ph-pencil-simple"></i></button>
|
<button class="pill-item pill__critical" onclick="imageEditPopup()"><i class="ph ph-pencil-simple"></i></button>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if prev_url %}<div><a class="pill-item" href="{{ prev_url }}"><i class="ph ph-arrow-right"></i></a></div>{% endif %}
|
{% if prev_url %}<div><a class="pill-item" href="{{ prev_url }}"><i class="ph ph-arrow-right"></i></a></div>{% endif %}
|
||||||
|
|
|
@ -3,95 +3,6 @@
|
||||||
|
|
||||||
{% block head %}
|
{% block head %}
|
||||||
{% if images %}<meta name="theme-color" content="rgb{{ images.0.colours.0 }}"/>{% endif %}
|
{% if images %}<meta name="theme-color" content="rgb{{ images.0.colours.0 }}"/>{% endif %}
|
||||||
|
|
||||||
{% if current_user.is_authenticated %}
|
|
||||||
<script type="text/javascript">
|
|
||||||
function showCreate() {
|
|
||||||
// Create elements
|
|
||||||
cancelBtn = document.createElement('button');
|
|
||||||
cancelBtn.classList.add('btn-block');
|
|
||||||
cancelBtn.classList.add('transparent');
|
|
||||||
cancelBtn.innerHTML = 'nuuuuuuuu';
|
|
||||||
cancelBtn.onclick = popupDissmiss;
|
|
||||||
|
|
||||||
submitBtn = document.createElement('button');
|
|
||||||
submitBtn.classList.add('btn-block');
|
|
||||||
submitBtn.classList.add('primary');
|
|
||||||
submitBtn.innerHTML = 'Submit!!';
|
|
||||||
submitBtn.type = 'submit';
|
|
||||||
submitBtn.setAttribute('form', 'createForm');
|
|
||||||
|
|
||||||
// Create form
|
|
||||||
createForm = document.createElement('form');
|
|
||||||
createForm.id = 'createForm';
|
|
||||||
createForm.setAttribute('onsubmit', 'return create(event);');
|
|
||||||
|
|
||||||
titleInput = document.createElement('input');
|
|
||||||
titleInput.classList.add('input-block');
|
|
||||||
titleInput.type = 'text';
|
|
||||||
titleInput.placeholder = 'Group namey';
|
|
||||||
titleInput.id = 'name';
|
|
||||||
|
|
||||||
descriptionInput = document.createElement('input');
|
|
||||||
descriptionInput.classList.add('input-block');
|
|
||||||
descriptionInput.type = 'text';
|
|
||||||
descriptionInput.placeholder = 'What it about????';
|
|
||||||
descriptionInput.id = 'description';
|
|
||||||
|
|
||||||
createForm.appendChild(titleInput);
|
|
||||||
createForm.appendChild(descriptionInput);
|
|
||||||
|
|
||||||
popUpShow(
|
|
||||||
'New stuff!',
|
|
||||||
'Image groups are a simple way to "group" images together, are you ready?',
|
|
||||||
createForm,
|
|
||||||
[cancelBtn, submitBtn]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function create(event) {
|
|
||||||
// AJAX takes control of subby form :3
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
let formName = document.querySelector("#name").value;
|
|
||||||
let formDescription = document.querySelector("#description").value;
|
|
||||||
|
|
||||||
if (!formName) {
|
|
||||||
addNotification("Group name must be set!", 3);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make form
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("name", formName);
|
|
||||||
formData.append("description", formDescription);
|
|
||||||
|
|
||||||
fetch('{{ url_for('api.create_group') }}', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
}).then(response => {
|
|
||||||
if (response.status === 200) {
|
|
||||||
addNotification('Group created!', 1);
|
|
||||||
popupDissmiss();
|
|
||||||
} else {
|
|
||||||
switch (response.status) {
|
|
||||||
case 500:
|
|
||||||
addNotification('Server exploded, F\'s in chat', 2);
|
|
||||||
break;
|
|
||||||
case 403:
|
|
||||||
addNotification('None but devils play past here... Bad information', 2);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
addNotification('Error logging in, blame someone', 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch(error => {
|
|
||||||
addNotification('Error making group! :c', 2);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block header %}
|
{% block header %}
|
||||||
|
@ -108,7 +19,7 @@
|
||||||
{% if current_user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
<div class="pill-row">
|
<div class="pill-row">
|
||||||
<div>
|
<div>
|
||||||
<button class="pill-item" onclick="showCreate()"><i class="ph ph-plus"></i></button>
|
<button class="pill-item" onclick="groupCreatePopup()"><i class="ph ph-plus"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function moreInfo() {
|
function moreInfo() {
|
||||||
popUpShow('{{ user.username }}',
|
popupShow('{{ user.username }}',
|
||||||
'<p>Joined: {{ user.joined_at }}</p><br>' +
|
'<p>Joined: {{ user.joined_at }}</p><br>' +
|
||||||
'<p>Images: {{ images|length }}</p><br>' +
|
'<p>Images: {{ images|length }}</p><br>' +
|
||||||
'<p>Groups: {{ groups|length }}</p>');
|
'<p>Groups: {{ groups|length }}</p>');
|
||||||
|
|
|
@ -7,7 +7,7 @@ from werkzeug.utils import secure_filename
|
||||||
from onlylegs.config import MEDIA_FOLDER, CACHE_FOLDER
|
from onlylegs.config import MEDIA_FOLDER, CACHE_FOLDER
|
||||||
|
|
||||||
|
|
||||||
def generate_thumbnail(file_path, resolution, ext=None):
|
def generate_thumbnail(file_path, resolution, ext=""):
|
||||||
"""
|
"""
|
||||||
Image thumbnail generator
|
Image thumbnail generator
|
||||||
Uses PIL to generate a thumbnail of the image and saves it to the cache directory
|
Uses PIL to generate a thumbnail of the image and saves it to the cache directory
|
||||||
|
@ -25,25 +25,25 @@ def generate_thumbnail(file_path, resolution, ext=None):
|
||||||
if not ext:
|
if not ext:
|
||||||
ext = file_ext.strip(".")
|
ext = file_ext.strip(".")
|
||||||
|
|
||||||
# PIL doesnt like jpg so we convert it to jpeg
|
ext = "jpeg" if ext.lower() == "jpg" else ext.lower()
|
||||||
if ext.lower() == "jpg":
|
|
||||||
ext = "jpeg"
|
|
||||||
|
|
||||||
# Set resolution based on preset resolutions
|
# Set resolution based on preset resolutions
|
||||||
if resolution in ["prev", "preview"]:
|
if resolution in ("prev", "preview"):
|
||||||
res_x, res_y = (1920, 1080)
|
res_x, res_y = (1920, 1080)
|
||||||
elif resolution in ["thumb", "thumbnail"]:
|
elif resolution in ("thumb", "thumbnail"):
|
||||||
res_x, res_y = (300, 300)
|
res_x, res_y = (300, 300)
|
||||||
elif resolution in ["pfp", "profile"]:
|
elif resolution in ("pfp", "profile"):
|
||||||
res_x, res_y = (150, 150)
|
res_x, res_y = (150, 150)
|
||||||
elif resolution in ["icon", "favicon"]:
|
elif resolution in ("icon", "favicon"):
|
||||||
res_x, res_y = (30, 30)
|
res_x, res_y = (30, 30)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
cache_file_name = "{}_{}x{}.{}".format(file_name, res_x, res_y, ext).lower()
|
||||||
|
|
||||||
# If image has been already generated, return it from the cache
|
# If image has been already generated, return it from the cache
|
||||||
if os.path.exists(os.path.join(CACHE_FOLDER, f"{file_name}_{res_x}x{res_y}.{ext}")):
|
if os.path.exists(os.path.join(CACHE_FOLDER, cache_file_name)):
|
||||||
return os.path.join(CACHE_FOLDER, f"{file_name}_{res_x}x{res_y}.{ext}")
|
return os.path.join(CACHE_FOLDER, cache_file_name)
|
||||||
|
|
||||||
# Check if image exists in the uploads directory
|
# Check if image exists in the uploads directory
|
||||||
if not os.path.exists(os.path.join(MEDIA_FOLDER, file_path)):
|
if not os.path.exists(os.path.join(MEDIA_FOLDER, file_path)):
|
||||||
|
@ -61,7 +61,7 @@ def generate_thumbnail(file_path, resolution, ext=None):
|
||||||
# Save image to cache directory
|
# Save image to cache directory
|
||||||
try:
|
try:
|
||||||
image.save(
|
image.save(
|
||||||
os.path.join(CACHE_FOLDER, f"{file_name}_{res_x}x{res_y}.{ext}"),
|
os.path.join(CACHE_FOLDER, cache_file_name),
|
||||||
icc_profile=image_icc,
|
icc_profile=image_icc,
|
||||||
)
|
)
|
||||||
except OSError:
|
except OSError:
|
||||||
|
@ -69,11 +69,11 @@ def generate_thumbnail(file_path, resolution, ext=None):
|
||||||
# so we convert to RGB and try again
|
# so we convert to RGB and try again
|
||||||
image = image.convert("RGB")
|
image = image.convert("RGB")
|
||||||
image.save(
|
image.save(
|
||||||
os.path.join(CACHE_FOLDER, f"{file_name}_{res_x}x{res_y}.{ext}"),
|
os.path.join(CACHE_FOLDER, cache_file_name),
|
||||||
icc_profile=image_icc,
|
icc_profile=image_icc,
|
||||||
)
|
)
|
||||||
|
|
||||||
# No need to keep the image in memory, learned the hard way
|
# No need to keep the image in memory, learned the hard way
|
||||||
image.close()
|
image.close()
|
||||||
|
|
||||||
return os.path.join(CACHE_FOLDER, f"{file_name}_{res_x}x{res_y}.{ext}")
|
return os.path.join(CACHE_FOLDER, cache_file_name)
|
||||||
|
|
|
@ -3,8 +3,8 @@ Onlylegs - Image Groups
|
||||||
Why groups? Because I don't like calling these albums
|
Why groups? Because I don't like calling these albums
|
||||||
sounds more limiting that it actually is in this gallery
|
sounds more limiting that it actually is in this gallery
|
||||||
"""
|
"""
|
||||||
from flask import Blueprint, render_template, url_for, request
|
from flask import Blueprint, render_template, url_for, request, flash, jsonify
|
||||||
|
from flask_login import login_required, current_user
|
||||||
from onlylegs.models import Pictures, Users, AlbumJunction, Albums
|
from onlylegs.models import Pictures, Users, AlbumJunction, Albums
|
||||||
from onlylegs.extensions import db
|
from onlylegs.extensions import db
|
||||||
from onlylegs.utils import colour
|
from onlylegs.utils import colour
|
||||||
|
@ -50,7 +50,29 @@ def groups():
|
||||||
return render_template("list.html", groups=groups)
|
return render_template("list.html", groups=groups)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/<int:group_id>")
|
@blueprint.route("/", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
def groups_post():
|
||||||
|
"""
|
||||||
|
Creates a group
|
||||||
|
"""
|
||||||
|
group_name = request.form.get("name", "").strip()
|
||||||
|
group_description = request.form.get("description", "").strip()
|
||||||
|
|
||||||
|
new_group = Albums(
|
||||||
|
name=group_name,
|
||||||
|
description=group_description,
|
||||||
|
author_id=current_user.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
db.session.add(new_group)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
flash(["Group created!", "1"])
|
||||||
|
return jsonify({"message": "Group created", "id": new_group.id})
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/<int:group_id>", methods=["GET"])
|
||||||
def group(group_id):
|
def group(group_id):
|
||||||
"""
|
"""
|
||||||
Group view, shows all images in a group
|
Group view, shows all images in a group
|
||||||
|
@ -86,6 +108,54 @@ def group(group_id):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/<int:group_id>", methods=["PUT"])
|
||||||
|
@login_required
|
||||||
|
def group_put(group_id):
|
||||||
|
"""
|
||||||
|
Changes the images in a group
|
||||||
|
"""
|
||||||
|
image_id = request.form.get("imageId", "").strip()
|
||||||
|
action = request.form.get("action", "").strip()
|
||||||
|
|
||||||
|
group_record = db.get_or_404(Albums, group_id)
|
||||||
|
db.get_or_404(Pictures, image_id) # Check if image exists
|
||||||
|
|
||||||
|
if group_record.author_id != current_user.id:
|
||||||
|
return jsonify({"message": "You are not the owner of this group"}), 403
|
||||||
|
|
||||||
|
junction_exist = AlbumJunction.query.filter_by(
|
||||||
|
album_id=group_id, picture_id=image_id
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if action == "add" and not junction_exist:
|
||||||
|
db.session.add(AlbumJunction(album_id=group_id, picture_id=image_id))
|
||||||
|
elif request.form["action"] == "remove":
|
||||||
|
AlbumJunction.query.filter_by(album_id=group_id, picture_id=image_id).delete()
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
flash(["Group modified!", "1"])
|
||||||
|
return jsonify({"message": "Group modified"})
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/<int:group_id>", methods=["DELETE"])
|
||||||
|
@login_required
|
||||||
|
def group_delete(group_id):
|
||||||
|
"""
|
||||||
|
Deletes a group
|
||||||
|
"""
|
||||||
|
group_record = db.get_or_404(Albums, group_id)
|
||||||
|
|
||||||
|
if group_record.author_id != current_user.id:
|
||||||
|
return jsonify({"message": "You are not the owner of this group"}), 403
|
||||||
|
|
||||||
|
AlbumJunction.query.filter_by(album_id=group_id).delete()
|
||||||
|
db.session.delete(group_record)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
flash(["Group yeeted!", "1"])
|
||||||
|
return jsonify({"message": "Group deleted"})
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/<int:group_id>/<int:image_id>")
|
@blueprint.route("/<int:group_id>/<int:image_id>")
|
||||||
def group_post(group_id, image_id):
|
def group_post(group_id, image_id):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,8 +1,20 @@
|
||||||
"""
|
"""
|
||||||
Onlylegs - Image View
|
Onlylegs - Image View
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import pathlib
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from flask import Blueprint, render_template, url_for, current_app, request
|
from flask import (
|
||||||
|
Blueprint,
|
||||||
|
render_template,
|
||||||
|
url_for,
|
||||||
|
current_app,
|
||||||
|
request,
|
||||||
|
flash,
|
||||||
|
jsonify,
|
||||||
|
)
|
||||||
|
from flask_login import current_user
|
||||||
from onlylegs.models import Pictures, AlbumJunction, Albums
|
from onlylegs.models import Pictures, AlbumJunction, Albums
|
||||||
from onlylegs.extensions import db
|
from onlylegs.extensions import db
|
||||||
|
|
||||||
|
@ -10,7 +22,7 @@ from onlylegs.extensions import db
|
||||||
blueprint = Blueprint("image", __name__, url_prefix="/image")
|
blueprint = Blueprint("image", __name__, url_prefix="/image")
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/<int:image_id>")
|
@blueprint.route("/<int:image_id>", methods=["GET"])
|
||||||
def image(image_id):
|
def image(image_id):
|
||||||
"""
|
"""
|
||||||
Image view, shows the image and its metadata
|
Image view, shows the image and its metadata
|
||||||
|
@ -50,10 +62,8 @@ def image(image_id):
|
||||||
)
|
)
|
||||||
|
|
||||||
# If there is a next or previous image, get the url
|
# If there is a next or previous image, get the url
|
||||||
if next_url:
|
next_url = url_for("image.image", image_id=next_url[0]) if next_url else None
|
||||||
next_url = url_for("image.image", image_id=next_url[0])
|
prev_url = url_for("image.image", image_id=prev_url[0]) if prev_url else None
|
||||||
if prev_url:
|
|
||||||
prev_url = url_for("image.image", image_id=prev_url[0])
|
|
||||||
|
|
||||||
# Yoink all the images in the database
|
# Yoink all the images in the database
|
||||||
total_images = (
|
total_images = (
|
||||||
|
@ -86,3 +96,60 @@ def image(image_id):
|
||||||
return_page=return_page,
|
return_page=return_page,
|
||||||
close_tab=close_tab,
|
close_tab=close_tab,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/<int:image_id>", methods=["PUT"])
|
||||||
|
def image_put(image_id):
|
||||||
|
"""
|
||||||
|
Update the image metadata
|
||||||
|
"""
|
||||||
|
image_record = db.get_or_404(Pictures, image_id, description="Image not found :<")
|
||||||
|
|
||||||
|
image_record.description = request.form.get("description", image_record.description)
|
||||||
|
image_record.alt = request.form.get("alt", image_record.alt)
|
||||||
|
|
||||||
|
print(request.form.get("description"))
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
flash(["Image updated!", "1"])
|
||||||
|
return "OK", 200
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/<int:image_id>", methods=["DELETE"])
|
||||||
|
def image_delete(image_id):
|
||||||
|
image_record = db.get_or_404(Pictures, image_id)
|
||||||
|
|
||||||
|
# Check if image exists and if user is allowed to delete it (author)
|
||||||
|
if image_record.author_id != current_user.id:
|
||||||
|
logging.info("User %s tried to delete image %s", current_user.id, image_id)
|
||||||
|
return (
|
||||||
|
jsonify({"message": "You are not allowed to delete this image, heck off"}),
|
||||||
|
403,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Delete file
|
||||||
|
try:
|
||||||
|
os.remove(
|
||||||
|
os.path.join(current_app.config["UPLOAD_FOLDER"], image_record.filename)
|
||||||
|
)
|
||||||
|
except FileNotFoundError:
|
||||||
|
logging.warning(
|
||||||
|
"File not found: %s, already deleted or never existed",
|
||||||
|
image_record.filename,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Delete cached files
|
||||||
|
cache_name = image_record.filename.rsplit(".")[0]
|
||||||
|
for cache_file in pathlib.Path(current_app.config["CACHE_FOLDER"]).glob(
|
||||||
|
cache_name + "*"
|
||||||
|
):
|
||||||
|
os.remove(cache_file)
|
||||||
|
|
||||||
|
AlbumJunction.query.filter_by(picture_id=image_id).delete()
|
||||||
|
db.session.delete(image_record)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
logging.info("Removed image (%s) %s", image_id, image_record.filename)
|
||||||
|
flash(["Image was all in Le Head!", "1"])
|
||||||
|
return jsonify({"message": "Image deleted"}), 200
|
||||||
|
|
Loading…
Reference in a new issue