// controllers/front/home.js
import config from '../../config.js'
import _Home from '../../views/front/home.jsx'
import postdb from '../../models/postdb.ts'
class Home{
async getItem(req, res){
this.config = await config()
this.config.pageTitle = 'ទំព័រដើម'
this.config.route = '/'
let {items} = await postdb.getItem(req, 10)
this.config.posts = items
const html = await _Home(this.config)
res.send(html)
}
}
export default new Home()
// 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_id = await req.session.get('user-id')
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")
let item = null
if(req.params.id){
item = await posts.findOne({id: req.params.id})
}
const items = await posts.find(query).sort({date:-1,_id:-1}).limit(amount).toArray()
return {item:item, items:items}
}
async editPost(req){
if(req.body.categories.includes(',')){
var categories: string[] = req.body.categories.split(',')
}else{
var categories: string[] = [req.body.categories]
}
let editPost = {$set:{
title: req.body.title,
content: req.body.content,
categories: categories,
thumb: req.body.thumb,
postdate: req.body.datetime,
video: req.body.video,
}}
const posts = req.mydb.collection<PostSchema>("posts")
await posts.updateOne({id: req.params.id}, editPost)
}
async deletePost(req){
const posts = req.mydb.collection<PostSchema>("posts")
if(req.params.id){
var item = await posts.findOne({id: req.params.id})
}
const user_id = await req.session.get('user-id')
const user_role = await req.session.get('user-role')
if(user_role in {'Admin':1,'Editor':1,'Author':1}){
if((user_role === 'Admin') || (user_id === item.userid)){
await posts.deleteOne({id: req.params.id})
}
}
}
}
export default new Postdb
// views/front/home.jsx
/** @jsx h */
import { h, renderSSR } from "../../deps.ts"
function HomeJsx(props){
let content = []
for(let post of props.config.posts){
content.push(
<div class="post">
<a class="thumb" href={`/post/${post.id}`}>
<img src={post.thumb} />
{((post.video) && (post.video !== "[]")) &&
<img class="play-icon" src="/images/play.png" />
}
</a>
<div class="post-content">
<a class="title" href={`/post/${post.id}`}>{post.title}</a>
<span>{(new Date(post.postdate)).toLocaleDateString('it-IT')}</span>
<div class="except" dangerouslySetInnerHTML={{__html: `${post.content}`}}/>
</div>
</div>
)
}
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>
<link rel="stylesheet" href="/styles/front/home.css" />
<link rel="stylesheet" href="/styles/front/menu.css" />
</head>
<body>
<section class="Home">
<header>
<div class="inner region">
<div class="title"><a href="/">{props.config.siteTitle}</a></div>
<form action="/search" method="post">
<select name='category'>
<option>ការផ្សាយ</option>
</select>
<input type="text" name="q" required placeholder="Search" />
<input type="submit" value="ស្វែងរក" />
</form>
<div class="login"><a href="/login">ចូលក្នុង</a> | <a href="#register">ចុះឈ្មោះ</a></div>
</div>
</header>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
<div class="menu">
<div class="innner region">
<div class="topnav" id="myTopnav">
<a href="/" class="active">Home</a>
<a href="#news">News</a>
<a href="#contact">Contact</a>
<a href="#about">About</a>
<span dangerouslySetInnerHTML={{__html: `
<a href="javascript:void(0);" class="icon" onclick='myFunction()'>
<i class="fa fa-bars"></i>
</a>
`}}/>
</div>
</div>
</div>
<script dangerouslySetInnerHTML={{__html: `
function myFunction() {
var x = document.getElementById("myTopnav");
if (x.className === "topnav") {
x.className += " responsive";
} else {
x.className = "topnav";
}
}
`}}/>
<div class="main region">
<div class="content">{(content)}</div>
<div class="sidebar">Sidebar</div>
</div>
</section>
</body>
</html>
)
}
function Home(config){
const str = renderSSR(<HomeJsx config={config} />)
const html = `<!DOCTYPE html>${str}`
return html
}
export default Home
/* static/styles/front/home.css */
.Home header{
background: var(--background-dark);
color: white;
}
.Home header .inner{
display: grid;
grid-template-columns: 25% auto 25%;
align-items: center;
padding: 20px 0;
}
.Home header .inner .title{
font: 35px/1.5 Oswald, Limonf3;
}
.Home header .inner form{
display: grid;
grid-template-columns: 20% auto 20%;
}
.Home header .inner form input,
.Home header .inner form select{
font: var(--body-font);
padding: 2px 5px;
}
.Home header .inner .login{
text-align: right;
}
.Home .menu{
background: var(--background);
margin-bottom: 20px;
}
.Home .main{
display: grid;
grid-template-columns: 70% auto;
grid-gap: 15px;
margin-bottom: 30px;
}
.Home .main .content, .Home .main .sidebar{
background: white;
padding: 20px;
}
.Home .main .content .post{
display: grid;
grid-template-columns: 30% 70%;
margin-bottom: 20px;
background: rgb(243, 243, 243);
color: grey;
}
.Home .main .content .post:last-child{
margin-bottom: 0;
}
.Home .main .content .post .thumb{
position: relative;
padding-top: 56.25%;
}
.Home .main .content .post .thumb img{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.Home .main .content .post .thumb .play-icon{
height: auto;
width: 20%;
top: 2px;
left: 2px;
}
.Home .main .content .post .post-content{
padding: 5px 15px;
color: rgb(32, 32, 32);
}
.Home .main .content .post .post-content .title{
font: 22px/1.5 Oswald, Limonf3;
color: teal;
padding-bottom: 5px;
display: block;
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis
}
.Home .main .content .post .post-content .except p{
padding-top: 5px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
font: 14px/1.5 Vidaloka, OdorMeanChey;
}
@media only screen and (max-width: 600px){
.Home header .inner{
grid-template-columns: 100%;
padding: 5px;
}
.Home header .inner form{
padding: 5px 0;
}
.Home header .inner .title{
text-align: center;
}
.Home .main, .Home .main .content .post{
grid-template-columns: 100%;
}
}
GitHub: https://github.com/Sokhavuth/khmerweb-deno
Deno Deploy: https://khmerweb-blog.deno.dev