article edit feature
This commit is contained in:
parent
cd878c2b25
commit
60a76979df
@ -11,6 +11,7 @@ const ArticleSchema = new Schema({
|
|||||||
slug: { type: String, index: { unique: true } },
|
slug: { type: String, index: { unique: true } },
|
||||||
image: { type: String, required: true },
|
image: { type: String, required: true },
|
||||||
created_at: { type: Date, default: Date.now },
|
created_at: { type: Date, default: Date.now },
|
||||||
|
updated_at: { type: Date },
|
||||||
html: { type: String, required: true },
|
html: { type: String, required: true },
|
||||||
comments: [{
|
comments: [{
|
||||||
content: { type: String, required: true },
|
content: { type: String, required: true },
|
||||||
|
@ -12,7 +12,9 @@ export async function get(req, res, next) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (article) {
|
if (article) {
|
||||||
article.set({ views: article.views + 1 });
|
if (req.query.no_view !== '1') {
|
||||||
|
article.set({ views: article.views + 1 });
|
||||||
|
}
|
||||||
await article.save();
|
await article.save();
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
|
@ -214,6 +214,9 @@
|
|||||||
<p>Author: <img class="avatar" alt={article.author.realname} src={`/u/${article.author.avatar || 'default.jpg'}`}> <strong>{article.author.realname}</strong></p>
|
<p>Author: <img class="avatar" alt={article.author.realname} src={`/u/${article.author.avatar || 'default.jpg'}`}> <strong>{article.author.realname}</strong></p>
|
||||||
<p>Category: <strong><a href={`/c/${article.category.slug}`}>{article.category.name}</a></strong></p>
|
<p>Category: <strong><a href={`/c/${article.category.slug}`}>{article.category.name}</a></strong></p>
|
||||||
<p>Published: <strong>{new Date(article.created_at).toLocaleString()}</strong></p>
|
<p>Published: <strong>{new Date(article.created_at).toLocaleString()}</strong></p>
|
||||||
|
{#if article.updated_at}
|
||||||
|
<p>Last Updated: <strong>{new Date(article.updated_at).toLocaleString()}</strong></p>
|
||||||
|
{/if}
|
||||||
<p>Views: <strong>{article.views}</strong></p>
|
<p>Views: <strong>{article.views}</strong></p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,9 @@
|
|||||||
}
|
}
|
||||||
const res = await this.fetch('/c.json');
|
const res = await this.fetch('/c.json');
|
||||||
const categories = await res.json();
|
const categories = await res.json();
|
||||||
return { user: session.user, categories };
|
const origRes = page.query.edit && await this.fetch(`/a/${page.query.edit}.json?no_view=1`)
|
||||||
|
const editArticle = origRes && await origRes.json();
|
||||||
|
return { user: session.user, categories, editArticle };
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -20,6 +22,7 @@
|
|||||||
let editor, form, uploadForm;
|
let editor, form, uploadForm;
|
||||||
let loading = false, loadingAttach = false;
|
let loading = false, loadingAttach = false;
|
||||||
let title = '', category = '';
|
let title = '', category = '';
|
||||||
|
export let editArticle = undefined;
|
||||||
export let categories;
|
export let categories;
|
||||||
|
|
||||||
let actions = [
|
let actions = [
|
||||||
@ -55,9 +58,9 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
onMount(function load() {
|
onMount(function load() {
|
||||||
title = window.localStorage['title'] || '';
|
title = editArticle ? editArticle.title : (window.localStorage['title'] || '');
|
||||||
category = window.localStorage['category'] || '';
|
category = editArticle ? editArticle.category.slug : (window.localStorage['category'] || '');
|
||||||
editor.setHtml(window.localStorage['html'] || '', false);
|
editor.setHtml(editArticle ? editArticle.html : (window.localStorage['html'] || ''), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function submit()
|
async function submit()
|
||||||
@ -67,7 +70,7 @@
|
|||||||
fd.append('html', html);
|
fd.append('html', html);
|
||||||
loading = true;
|
loading = true;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/cms/article`, {
|
const res = await fetch(editArticle ? `/cms/article/${editArticle.slug}` : `/cms/article`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
|
42
src/routes/cms/edit.svelte
Normal file
42
src/routes/cms/edit.svelte
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<script context="module">
|
||||||
|
export async function preload(page, session)
|
||||||
|
{
|
||||||
|
if (!session.user || !session.user.author) {
|
||||||
|
return this.redirect(302, '/cms');
|
||||||
|
}
|
||||||
|
const res = await this.fetch(`/c/all.json`);
|
||||||
|
const json = await res.json();
|
||||||
|
if (res.status === 200) {
|
||||||
|
return { articles: json.articles, user: session.user };
|
||||||
|
} else {
|
||||||
|
this.error(res.status, json.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Edit Article | HOWFEED.BIZ</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { goto } from '@sapper/app';
|
||||||
|
export let articles = [];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<a href="/cms">< Back to Dashboard</a>
|
||||||
|
<h1>Edit an Article</h1>
|
||||||
|
<p>Choose an article to edit:</p>
|
||||||
|
<ul>
|
||||||
|
{#each articles as article}
|
||||||
|
<li>
|
||||||
|
<a href={`/cms/create?edit=${encodeURIComponent(article.slug)}`}>
|
||||||
|
<strong>{article.title}</strong> by <strong>{article.author.realname}</strong>
|
||||||
|
({article.views} views)
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{:else}
|
||||||
|
<li>There are no published articles.</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
@ -22,7 +22,7 @@
|
|||||||
{#if user.author}
|
{#if user.author}
|
||||||
<h1>HowFeed Publisher Dashboard</h1>
|
<h1>HowFeed Publisher Dashboard</h1>
|
||||||
<p><a href="/cms/create">Publish a new article</a></p>
|
<p><a href="/cms/create">Publish a new article</a></p>
|
||||||
<p><strike><a href="/cms/update">Edit an existing article</a></strike> Coming soon!</p>
|
<p><a href="/cms/edit">Edit an existing article</a></p>
|
||||||
<p><a href="/cms/delete">Delete an article</a></p>
|
<p><a href="/cms/delete">Delete an article</a></p>
|
||||||
<h1>Account Settings</h1>
|
<h1>Account Settings</h1>
|
||||||
<p><a href="/me/avatar">Change your avatar</a></p>
|
<p><a href="/me/avatar">Change your avatar</a></p>
|
||||||
|
@ -251,13 +251,27 @@ express()
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
.post('/cms/article',
|
.post('/cms/article/:edit?',
|
||||||
isAuthor,
|
isAuthor,
|
||||||
async function(req, res, next) {
|
async function(req, res, next) {
|
||||||
try {
|
try {
|
||||||
|
let editArticle;
|
||||||
|
if (req.params.edit) {
|
||||||
|
editArticle = await Article.findOne({ slug: req.params.edit });
|
||||||
|
if (!editArticle) {
|
||||||
|
res.writeHead(404, {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
});
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
message: `Article to edit not found`
|
||||||
|
}));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { html, title, category } = req.body;
|
const { html, title, category } = req.body;
|
||||||
const { image } = req.files;
|
const image = req.files && req.files.image;
|
||||||
if (!title || !image || !html || !category) {
|
if (!title || (!editArticle && !image) || !html || !category) {
|
||||||
res.writeHead(422, {
|
res.writeHead(422, {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
});
|
});
|
||||||
@ -266,23 +280,30 @@ express()
|
|||||||
}));
|
}));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!/^image\//.test(image.mimetype)) {
|
let ext, filename, url;
|
||||||
res.writeHead(422, {
|
if (image) {
|
||||||
'Content-Type': 'application/json'
|
if (!/^image\//.test(image.mimetype)) {
|
||||||
});
|
res.writeHead(422, {
|
||||||
res.end(JSON.stringify({
|
'Content-Type': 'application/json'
|
||||||
message: `Invalid MIME type for the header image file.`
|
});
|
||||||
}));
|
res.end(JSON.stringify({
|
||||||
return false;
|
message: `Invalid MIME type for the header image file.`
|
||||||
}
|
}));
|
||||||
if (image.truncated) {
|
return false;
|
||||||
res.writeHead(422, {
|
}
|
||||||
'Content-Type': 'application/json'
|
if (image.truncated) {
|
||||||
});
|
res.writeHead(422, {
|
||||||
res.end(JSON.stringify({
|
'Content-Type': 'application/json'
|
||||||
message: `Received truncated image file. Try again with a smaller file.`
|
});
|
||||||
}));
|
res.end(JSON.stringify({
|
||||||
return false;
|
message: `Received truncated image file. Try again with a smaller file.`
|
||||||
|
}));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ext = image.name.match(/(\.[^.]+)$/)[0];
|
||||||
|
filename = crypto.randomBytes(20).toString('hex') + ext;
|
||||||
|
url = `/a/${filename}`;
|
||||||
|
await image.mv('./static' + url);
|
||||||
}
|
}
|
||||||
const cat = await Category.findOne({ slug: category });
|
const cat = await Category.findOne({ slug: category });
|
||||||
if (!cat) {
|
if (!cat) {
|
||||||
@ -294,18 +315,30 @@ express()
|
|||||||
}));
|
}));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const ext = image.name.match(/(\.[^.]+)$/)[0];
|
if (editArticle) {
|
||||||
const filename = crypto.randomBytes(20).toString('hex') + ext;
|
let newObj = {
|
||||||
const url = `/a/${filename}`;
|
html, title, category: cat, updated_at: Date.now()
|
||||||
await image.mv('./static' + url);
|
};
|
||||||
const article = await new Article({ html, title, image: filename, category: cat, author: req.user._id });
|
if (filename) {
|
||||||
await article.save();
|
newObj.image = filename;
|
||||||
res.writeHead(200, {
|
}
|
||||||
'Content-Type': 'application/json'
|
await Article.updateOne({ slug: editArticle.slug }, { $set: newObj });
|
||||||
});
|
res.writeHead(200, {
|
||||||
res.end(JSON.stringify({
|
'Content-Type': 'application/json'
|
||||||
slug: article.slug
|
});
|
||||||
}));
|
res.end(JSON.stringify({
|
||||||
|
slug: editArticle.slug
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
const article = await new Article({ html, title, image: filename, category: cat, author: req.user._id });
|
||||||
|
await article.save();
|
||||||
|
res.writeHead(200, {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
});
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
slug: article.slug
|
||||||
|
}));
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.writeHead(500, {
|
res.writeHead(500, {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user