// controllers/admin/post.js
import config from '../../config.js'
import post from '../../views/admin/post.jsx'
import postdb from '../../models/postdb.ts'
class Post{
async getItem(req, res){
this.config = await config()
this.config.pageTitle = 'ទំព័រការផ្សាយ'
this.config.route = '/admin/post'
this.config.type = 'post'
this.config.count = await postdb.count(req)
this.config.items = await postdb.getItem(req, this.config.adminItemLimit)
const html = await post(this.config)
res.send(html)
}
async postItem(req, res){
const user_role = await req.session.get('user-role')
if(user_role in {'Admin':1,'Editor':1,'Author':1}){
await postdb.insertPost(req)
}
res.redirect('/admin/post')
}
}
export default new Post()
// views/base.jsx
/** @jsx h */
import { h, renderSSR } from "../deps.ts"
function Base(props){
const Page = props.config.page
function generateFooter(){
let footer = ''
if(props.config.items){
for(let item of props.config.items){
footer += `<li>`
footer += `<div class='thumb'>`
footer += `<a href='/${ props.config.type }/${ item.id }'>`
footer += `<img src='${ item.thumb }'/> `
if((item.video)&&(item.video !== '[]')){
footer += `<img class="play-icon" src="/images/play.png"/>`
}
footer += `</a>`
footer += `</div>`
footer += `<div class="title">`
footer += `<a href="/${ props.config.type }/${ item.id }">${ item.title }</a>`
if(props.config.route === '/admin/user'){
footer += `<p>${ item.role }</p>`
}
footer += `<div>${ (new Date(item.postdate)).toLocaleDateString('it-IT') }</div>`
footer += `</div>`
footer += `<div class="edit">`
footer += `<a href="${ props.config.route }/edit/${ item.id }"><img src="/images/edit.png"/></a>`
footer += `<a href="${ props.config.route }/delete/${ item.id }"><img src="/images/delete.png"/></a>`
footer += `</div> `
footer += `</li>`
}
return footer
}
}
return(
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>{props.config.siteTitle} | {props.config.pageTitle}</title>
<link href="/images/siteLogo.png" rel="icon" />
<link href="/styles/base.css" rel="stylesheet" />
<link href="/fonts/setup.css" rel="stylesheet" />
<script src="/scripts/jquery.js"></script>
</head>
<body>
<Page config={props.config} />
<section class="Footer region">
<div class="info">សរុបទាំងអស់មានចំនួនៈ {props.config.count}</div>
<ul class="list" dangerouslySetInnerHTML={{__html:
`${ generateFooter() }` }}/>
<div class="pagination" dangerouslySetInnerHTML={{__html: `
<img onClick="paginate('${ props.config.route }')" src="/images/load-more.png" />
`}}/>
<div class="credit">© 2022 <a href="https://khmerweb.vercel.app/">Khmer Web</a></div>
</section>
</body>
</html>
)
}
export default Base
// models/postdb.js
interface PostSchema {
_id: ObjectId;
id: string;
title: string;
content: string;
categories: string[];
thumb: string;
postdate: string;
video: string;
userid: string;
}
class Postdb{
async count(req, query={}){
const posts = req.mydb.collection<PostSchema>("posts")
return await posts.countDocuments(query)
}
async insertPost(req){
const id = Date.now() + Math.round(Math.random() * 1E9).toString()
if(req.body.categories.includes(',')){
var categories: string[] = req.body.categories.split(',')
}else{
var categories: string[] = [req.body.categories]
}
const user = await req.session.get('user')
let newPost = {
id: id,
title: req.body.title,
content: req.body.content,
categories: categories,
thumb: req.body.thumb,
postdate: req.body.datetime,
video: req.body.video,
userid: user.id,
}
const posts = req.mydb.collection<PostSchema>("posts")
await posts.insertOne(newPost)
}
async getItem(req, amount, query={}){
const posts = req.mydb.collection<PostSchema>("posts")
return await posts.find().sort({date:-1,_id:-1}).limit(amount).toArray()
}
}
export default new Postdb
// views/base.jsx
/** @jsx h */
import { h, renderSSR } from "../deps.ts"
function Base(props){
const Page = props.config.page
function generateFooter(){
let footer = ''
if(props.config.items){
for(let item of props.config.items){
footer += `<li>`
footer += `<div class='thumb'>`
footer += `<a href='/${ props.config.type }/${ item.id }'>`
footer += `<img src='${ item.thumb }'/> `
if((item.video)&&(item.video !== '[]')){
footer += `<img class="play-icon" src="/images/play.png"/>`
}
footer += `</a>`
footer += `</div>`
footer += `<div class="title">`
footer += `<a href="/${ props.config.type }/${ item.id }">${ item.title }</a>`
if(props.config.route === '/admin/user'){
footer += `<p>${ item.role }</p>`
}
footer += `<div>${ (new Date(item.postdate)).toLocaleDateString('it-IT') }</div>`
footer += `</div>`
footer += `<div class="edit">`
footer += `<a href="${ props.config.route }/edit/${ item.id }"><img src="/images/edit.png"/></a>`
footer += `<a href="${ props.config.route }/delete/${ item.id }"><img src="/images/delete.png"/></a>`
footer += `</div> `
footer += `</li>`
}
return footer
}
}
return(
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>{props.config.siteTitle} | {props.config.pageTitle}</title>
<link href="/images/siteLogo.png" rel="icon" />
<link href="/styles/base.css" rel="stylesheet" />
<link href="/fonts/setup.css" rel="stylesheet" />
<script src="/scripts/jquery.js"></script>
</head>
<body>
<Page config={props.config} />
<section class="Footer region">
<div class="info">សរុបទាំងអស់មានចំនួនៈ {props.config.count}</div>
<ul class="list" dangerouslySetInnerHTML={{__html:
`${ generateFooter() }` }}/>
<div class="pagination" dangerouslySetInnerHTML={{__html: `
<img onClick="paginate('${ props.config.route }')" src="/images/load-more.png" />
`}}/>
<div class="credit">© 2022 <a href="https://khmerweb.vercel.app/">Khmer Web</a></div>
</section>
</body>
</html>
)
}
export default Base
/* static/styles/base.css */
:root{
--background-light: lightgrey;
--background: grey;
--background-dark: #272727;
--body-font: 14px/1.5 Vidaloka, OdorMeanChey;
--link: lavender;
--color: black;
}
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
a{
text-decoration: none;
color: var(--link);
}
a:hover{
opacity: .7;
}
.region{
max-width: 1100px;
margin: 0 auto;
}
body{
color: var(--color);
font: var(--body-font);
background: var(--background-light);
}
.Footer .info{
background: var(--background);
text-align: center;
padding: 10px;
color: var(--link);
}
.Footer ul{
display: grid;
grid-template-columns: calc(50% - 5px ) calc(50% - 5px );
grid-gap: 10px;
padding-top: 10px;
}
.Footer ul li{
display: grid;
grid-template-columns: 25% auto 20%;
align-items: center;
background: var(--background);
}
.Footer ul li .thumb{
position: relative;
padding-top: 56.25%;
}
.Footer ul li .thumb a{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.Footer ul li .thumb img{
width: 100%;
height: 100%;
}
.Footer ul li .thumb .play-icon{
position: absolute;
top: 50%;
left: 50%;
width: 25%;
height: auto;
transform: translate(-50%,-50%);
}
.Footer ul li .title{
padding-left: 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--link);
}
.Footer ul li .title a{
color: var(--link);
}
.Footer ul li .edit{
text-align: right;
padding-right: 10px;
visibility: hidden;
}
.Footer ul li:hover .edit{
visibility: visible;
}
.Footer ul li .edit img{
width: 45px;
}
.Footer .pagination{
text-align: center;
background: var(--background);
margin: 10px auto 20px;
padding: 5px 0 0;
}
.Footer .pagination img:hover{
cursor: pointer;
opacity: .7;
}
.Footer .credit{
text-align: center;
padding: 0 0 20px;
}
.Footer .credit a{
color: teal;
}
GitHub: https://github.com/Sokhavuth/khmerweb-deno
Deno Deploy: https://khmerweb-blog.deno.dev/admin/post