Post-tags (#9)
Reviewed-on: #9 Co-authored-by: Armored Dragon <publicmail@armoreddragon.com> Co-committed-by: Armored Dragon <publicmail@armoreddragon.com>pull/2/head
parent
d24f87e23a
commit
78923279be
|
@ -74,7 +74,7 @@ async function registerUser(username, password, options) {
|
|||
return { success: true, message: `Successfully created ${username}` };
|
||||
}
|
||||
// Posts
|
||||
async function getBlog({ id, visibility = "PUBLISHED", owner_id, limit = 10, page = 0, tags = [], search_title = false, search_content = false, search }) {
|
||||
async function getBlog({ id, visibility = "PUBLISHED", owner_id, limit = 10, page = 0, search_title = false, search_content = false, search_tags = false, search }) {
|
||||
// If we have an ID, we want a single post
|
||||
if (id) {
|
||||
// Get the post by the id
|
||||
|
@ -110,14 +110,22 @@ async function getBlog({ id, visibility = "PUBLISHED", owner_id, limit = 10, pag
|
|||
ownerid: owner_id,
|
||||
},
|
||||
],
|
||||
AND: [],
|
||||
|
||||
AND: [
|
||||
{
|
||||
OR: [
|
||||
|
||||
]
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
// Build the "where_object" object
|
||||
if (tags.length > 0) {
|
||||
if (search){
|
||||
if (search_tags) where_object["AND"][0]["OR"].push({ tags: { hasSome: [search?.toLowerCase()] }});
|
||||
if (search_title) where_object["AND"][0]["OR"].push({ title: { contains: search, mode: "insensitive" } });
|
||||
if (search_content) where_object["AND"][0]["OR"].push({ content: { contains: search, mode: "insensitive" } });
|
||||
}
|
||||
if (search_title) where_object["AND"].push({ title: { contains: search, mode: "insensitive" } });
|
||||
if (search_content) where_object["AND"].push({ content: { contains: search, mode: "insensitive" } });
|
||||
|
||||
// Execute search
|
||||
const blog_posts = await prisma.blogPost.findMany({
|
||||
|
@ -171,6 +179,7 @@ async function postBlog(blog_post, owner_id) {
|
|||
content: blog_post.content,
|
||||
visibility: blog_post.visibility,
|
||||
publish_date: blog_post.publish_date,
|
||||
tags: blog_post.tags,
|
||||
};
|
||||
|
||||
// Save to database
|
||||
|
@ -203,13 +212,15 @@ async function postBlog(blog_post, owner_id) {
|
|||
}
|
||||
async function deleteBlog(blog_id, requester_id) {
|
||||
const user = await getUser({ id: requester_id });
|
||||
const post = await getPost({ id: blog_id });
|
||||
const post = await getBlog({ id: blog_id });
|
||||
|
||||
let can_delete = post.owner.id === user.data.id || user.data.role === "ADMIN";
|
||||
if (!post.success) return { success: false, message: post.message || "Post does not exist" };
|
||||
|
||||
let can_delete = post.data.owner.id === user.data.id || user.data.role === "ADMIN";
|
||||
|
||||
if (can_delete) {
|
||||
await prisma.blogPost.delete({ where: { id: post.id } });
|
||||
_deleteS3Directory(post.id, "blog");
|
||||
await prisma.blogPost.delete({ where: { id: post.data.id } });
|
||||
_deleteS3Directory(post.data.id, "blog");
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
|
@ -217,27 +228,35 @@ async function deleteBlog(blog_id, requester_id) {
|
|||
}
|
||||
async function updateBlog(blog_post, requester_id) {
|
||||
const user = await getUser({ id: requester_id });
|
||||
const post = await getPost({ id: blog_post.id, raw: true });
|
||||
const post = await getBlog({ id: blog_post.id, raw: true });
|
||||
let publish_date = null;
|
||||
|
||||
delete blog_post.id;
|
||||
|
||||
let can_update = post.owner.id === user.data.id || user.data.role === "ADMIN";
|
||||
if (!post.success) return { success: false, message: post.message || "Post not found" };
|
||||
|
||||
let can_update = post.data.owner.id === user.data.id || user.data.role === "ADMIN";
|
||||
|
||||
if (!can_update) return { success: false, message: "User not permitted" };
|
||||
|
||||
const [year, month, day] = blog_post.date.split("-");
|
||||
const [hour, minute] = blog_post.time.split(":");
|
||||
let publish_date = new Date(year, month - 1, day, hour, minute);
|
||||
// FIXME: Unsure if this actually works
|
||||
// Check if we already have a formatted publish date
|
||||
if (typeof blog_post.publish_date !== "object") {
|
||||
const [year, month, day] = blog_post.date.split("-");
|
||||
const [hour, minute] = blog_post.time.split(":");
|
||||
publish_date = new Date(year, month - 1, day, hour, minute);
|
||||
}
|
||||
|
||||
let blog_post_formatted = {
|
||||
title: blog_post.title,
|
||||
description: blog_post.description,
|
||||
content: blog_post.content,
|
||||
visibility: blog_post.unlisted ? "UNLISTED" : "PUBLISHED",
|
||||
publish_date: publish_date,
|
||||
publish_date: publish_date || blog_post.publish_date,
|
||||
tags: blog_post.tags,
|
||||
};
|
||||
|
||||
await prisma.blogPost.update({ where: { id: post.id }, data: blog_post_formatted });
|
||||
await prisma.blogPost.update({ where: { id: post.data.id }, data: blog_post_formatted });
|
||||
|
||||
let uploaded_images = [];
|
||||
let uploaded_thumbnail = "DEFAULT";
|
||||
|
@ -253,24 +272,24 @@ async function updateBlog(blog_post, requester_id) {
|
|||
}
|
||||
|
||||
let data_to_update = {
|
||||
images: [...post.raw_images, ...uploaded_images],
|
||||
images: [...post.data.raw_images, ...uploaded_images],
|
||||
};
|
||||
|
||||
if (blog_post.thumbnail) {
|
||||
const image_data = Buffer.from(blog_post.thumbnail.data_blob.split(",")[1], "base64");
|
||||
const name = await _uploadImage(post.id, "blog", true, image_data, blog_post.thumbnail.id);
|
||||
const name = await _uploadImage(post.data.id, "blog", true, image_data, blog_post.thumbnail.id);
|
||||
uploaded_thumbnail = name;
|
||||
|
||||
data_to_update.thumbnail = uploaded_thumbnail;
|
||||
}
|
||||
|
||||
await prisma.blogPost.update({ where: { id: post.id }, data: data_to_update });
|
||||
await prisma.blogPost.update({ where: { id: post.data.id }, data: data_to_update });
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
async function deleteImage(image, requester_id) {
|
||||
const user = await getUser({ id: requester_id });
|
||||
const post = await getPost({ id: image.parent, raw: true });
|
||||
const post = await getBlog({ id: image.parent, raw: true });
|
||||
|
||||
// Check if post exists
|
||||
if (!post) return { success: false, message: "Post does not exist" };
|
||||
|
@ -391,9 +410,7 @@ function _format_blog_content(content, images) {
|
|||
const video = /{video:([^}]+)}/g;
|
||||
|
||||
content = content.replace(video, (match, inner_content) => {
|
||||
return `<div class='video-embed'><iframe src="${_getVideoEmbed(
|
||||
inner_content
|
||||
)}" frameborder="0" allow="accelerometer; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div>`;
|
||||
return `<div class='video-embed'><iframe src="${_getVideoEmbed(inner_content)}" frameborder="0" allow="accelerometer; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div>`;
|
||||
});
|
||||
|
||||
content = content.replace(image_regex, (match, image_name) => {
|
||||
|
|
|
@ -9,8 +9,7 @@ async function postRegister(req, res) {
|
|||
|
||||
// User registration disabled?
|
||||
// We also check if the server was setup. If it was not set up, the server will proceed anyways.
|
||||
if (!core.settings["ACCOUNT_REGISTRATION"] && core.settings["SETUP_COMPLETE"])
|
||||
return res.json({ success: false, message: "Account registrations are disabled" });
|
||||
if (!core.settings["ACCOUNT_REGISTRATION"] && core.settings["SETUP_COMPLETE"]) return res.json({ success: false, message: "Account registrations are disabled" });
|
||||
|
||||
// User data valid?
|
||||
if (!form_validation.success) return res.json({ success: false, message: form_validation.message });
|
||||
|
@ -70,8 +69,15 @@ async function deleteBlog(req, res) {
|
|||
return res.json(await core.deleteBlog(req.body.id, req.session.user.id));
|
||||
}
|
||||
async function patchBlog(req, res) {
|
||||
// FIXME: validate does not return post id
|
||||
// Can user change post?
|
||||
// User is admin, or user is author
|
||||
|
||||
// Validate blog info
|
||||
const valid = await validate.postBlog(req.body);
|
||||
|
||||
// TODO: Permissions for updating blog
|
||||
return res.json(await core.updateBlog(req.body, req.session.user.id));
|
||||
return res.json(await core.updateBlog({ ...valid.data, id: req.body.id }, req.session.user.id));
|
||||
}
|
||||
|
||||
module.exports = { postRegister, postLogin, postSetting, deleteImage, postBlog, deleteBlog, patchBlog };
|
||||
|
|
|
@ -25,6 +25,19 @@ async function postBlog(blog_object) {
|
|||
const [hour, minute] = blog_object.time.split(":");
|
||||
let publish_date = new Date(year, month - 1, day, hour, minute);
|
||||
|
||||
// Go though our tags and ensure they are:
|
||||
let valid_tag_array = [];
|
||||
blog_object.tags.forEach((tag) => {
|
||||
// Trimmed
|
||||
tag = tag.trim();
|
||||
|
||||
// Lowercase
|
||||
tag = tag.toLowerCase();
|
||||
|
||||
// Non-empty
|
||||
if (tag.length !== 0) valid_tag_array.push(tag);
|
||||
});
|
||||
|
||||
// Format our data to save
|
||||
let blog_post_formatted = {
|
||||
title: blog_object.title,
|
||||
|
@ -32,6 +45,7 @@ async function postBlog(blog_object) {
|
|||
content: blog_object.content,
|
||||
visibility: blog_object.visibility,
|
||||
publish_date: publish_date,
|
||||
tags: valid_tag_array,
|
||||
images: blog_object.images,
|
||||
thumbnail: blog_object.thumbnail,
|
||||
};
|
||||
|
|
|
@ -27,7 +27,7 @@ async function author(req, res) {
|
|||
res.render("author.ejs", { ...getDefaults(req), blog_post: profile.data });
|
||||
}
|
||||
async function blogList(req, res) {
|
||||
const blog_list = await core.getBlog({ owner_id: req.session.user?.id, page: req.query.page || 0, search: req.query.search, search_title: true });
|
||||
const blog_list = await core.getBlog({ owner_id: req.session.user?.id, page: req.query.page || 0, search: req.query.search, search_tags: true, search_title: true });
|
||||
res.render("blogList.ejs", {
|
||||
...getDefaults(req),
|
||||
blog_list: blog_list.data,
|
||||
|
|
|
@ -221,6 +221,7 @@ a.bad {
|
|||
.container .setting-row .setting-toggleable input {
|
||||
padding: 0;
|
||||
margin: auto 0 auto auto;
|
||||
height: 25px;
|
||||
width: 100px;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
input {
|
||||
padding: 0;
|
||||
margin: auto 0 auto auto;
|
||||
// width: 100%;
|
||||
height: 25px;
|
||||
width: 100px;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
|
|
|
@ -130,6 +130,20 @@
|
|||
outline: 0;
|
||||
}
|
||||
|
||||
.e-tags {
|
||||
min-height: 40px;
|
||||
width: 100%;
|
||||
background-color: #222;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.e-tags input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.e-settings {
|
||||
min-height: 40px;
|
||||
width: 100%;
|
||||
|
|
|
@ -145,6 +145,21 @@ $background-body: #222;
|
|||
}
|
||||
}
|
||||
|
||||
.e-tags {
|
||||
min-height: 40px;
|
||||
width: 100%;
|
||||
background-color: $background-body;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.e-settings {
|
||||
min-height: 40px;
|
||||
width: 100%;
|
||||
|
|
|
@ -88,10 +88,18 @@ async function publishBlog(unlisted, edit) {
|
|||
description: qs("#description").value,
|
||||
content: qs("#content").value,
|
||||
visibility: unlisted ? "UNLISTED" : "PUBLISHED",
|
||||
tags: [],
|
||||
date: qs("#date").value,
|
||||
time: qs("#time").value,
|
||||
};
|
||||
|
||||
// Get our tags, trim them, then shove them into an array
|
||||
const tags_value = qs("#tags").value || "";
|
||||
if (tags_value.length) {
|
||||
let tags_array = qs("#tags").value.split(",");
|
||||
tags_array.forEach((tag) => form_data.tags.push(tag.trim()));
|
||||
}
|
||||
|
||||
// If we have a thumbnail, read the thumbnail image and store it
|
||||
if (pending_thumbnail.data_blob) {
|
||||
form_data.thumbnail = { ...pending_thumbnail, data_blob: await _readFile(pending_thumbnail.data_blob) };
|
||||
|
@ -161,8 +169,7 @@ function customDragString() {
|
|||
}
|
||||
|
||||
function updateImages() {
|
||||
const image_div = (img_id, img_url) =>
|
||||
`<div class="image"><img data-image_id="${img_id}" src="${img_url}" /><div><a onclick="deleteImage('${img_id}')">X</a></div></div>`;
|
||||
const image_div = (img_id, img_url) => `<div class="image"><img data-image_id="${img_id}" src="${img_url}" /><div><a onclick="deleteImage('${img_id}')">X</a></div></div>`;
|
||||
|
||||
// Clear existing listings
|
||||
qsa(".e-image-area .image").forEach((entry) => entry.remove());
|
||||
|
|
|
@ -67,7 +67,9 @@
|
|||
</div>
|
||||
<textarea id="content" placeholder="Tell us about your subject..."><%= existing_blog.raw_content %></textarea>
|
||||
</div>
|
||||
|
||||
<div class="e-tags">
|
||||
<input id="tags" type="text" placeholder="Enter a comma separated list of tags" value="<%= existing_blog.tags %>" />
|
||||
</div>
|
||||
<div class="e-settings">
|
||||
<div class="publish-date">
|
||||
<div>Publish On</div>
|
||||
|
|
|
@ -36,8 +36,7 @@ model BlogPost {
|
|||
ownerid String?
|
||||
|
||||
// Tags
|
||||
organization_tag String?
|
||||
keyword_tags String[]
|
||||
tags String[]
|
||||
|
||||
// Dates
|
||||
publish_date DateTime?
|
||||
|
|
Loading…
Reference in New Issue