// 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">&copy; 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">&copy; 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