mirror of
https://github.com/Akif9748/akf-forum.git
synced 2024-12-22 15:49:08 +03:00
full support for multiple themes, secure templates
This commit is contained in:
parent
080e1e6ced
commit
edf9fc83dc
60 changed files with 403 additions and 257 deletions
|
@ -13,7 +13,8 @@ Run `node util/reset` to **reset the database** for duplicate key errors, and ru
|
|||
Edit `config.json` for default theme color (`black` or `white`) of users, and forum name, meta description, character limits, discord auth enabler, global ratelimit.
|
||||
|
||||
### How to install theme:
|
||||
- Copy your theme to `public/themes` folder.
|
||||
- Copy your theme to `themes` folder.
|
||||
Additional note for themes: If a theme has not got any .ejs file, it will use default theme's .ejs files. default theme is in themes folder, named as `common`.
|
||||
|
||||
### DISCORD AUTH:
|
||||
`"discord_auth": "your_app_id"` in config.json.
|
||||
|
@ -61,6 +62,7 @@ Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn
|
|||
- add support for transition around gravatar
|
||||
- BETTER SETUP PAGE
|
||||
- add used open source libraries to README.md
|
||||
- better folder system
|
||||
### front-end
|
||||
- text alling center body
|
||||
- add a css file for CodeMirror in threads / send message ok
|
||||
|
|
30
index.js
30
index.js
|
@ -23,6 +23,12 @@ app.ips = [];
|
|||
app.set("view engine", "ejs");
|
||||
app.set("limits", limits);
|
||||
|
||||
if (RLS.enabled) app.use(RL(RLS.windowMs, RLS.max));
|
||||
|
||||
for (const theme of fs.readdirSync("./themes"))
|
||||
app.use(`/themes/${theme}`, express.static(`./themes/${theme}/public/`));
|
||||
|
||||
|
||||
app.use(express.static("public"), express.json(), express.urlencoded({ extended: true }), IP(),
|
||||
SES({ secret: process.env.SECRET, store: MS.create({ clientPromise: DB, stringify: false }), resave: false, saveUninitialized: false }),
|
||||
async (req, res, next) => {
|
||||
|
@ -37,15 +43,19 @@ app.use(express.static("public"), express.json(), express.urlencoded({ extended:
|
|||
if (!themes.some(t => t.codename === theme.codename))
|
||||
theme = def_theme;
|
||||
|
||||
res.reply = (page, options = {}, status = 200) => res.status(status).render(page, {
|
||||
dataset: {
|
||||
themes, theme, forum_name, description,
|
||||
getFile: file => join(__dirname, "public", "themes", file),
|
||||
},
|
||||
user: req.user,
|
||||
...options
|
||||
});
|
||||
|
||||
res.reply = (page, options = {}, status = 200) => {
|
||||
const road = join(__dirname, "themes", theme.codename, "views", `${page}.ejs`);
|
||||
const renderpage = fs.existsSync(road) ? road : join(__dirname, "themes", "common", "views", `${page}.ejs`);
|
||||
return res.status(status).render(renderpage, {
|
||||
dataset: {
|
||||
themes, theme, forum_name, description,
|
||||
getFile: file => join(__dirname, "themes", file),
|
||||
},
|
||||
user: req.user,
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
res.error = (type, error) => res.reply("error", { type, error }, type);
|
||||
|
||||
|
@ -63,11 +73,9 @@ app.use(express.static("public"), express.json(), express.urlencoded({ extended:
|
|||
if (discord_auth)
|
||||
app.set("discord_auth", `https://discord.com/api/oauth2/authorize?client_id=${discord_auth}&redirect_uri=${host}%2Fauth%2Fdiscord&response_type=code&scope=identify`);
|
||||
|
||||
if (RLS.enabled) app.use(RL(RLS.windowMs, RLS.max));
|
||||
|
||||
for (const file of fs.readdirSync("./routes"))
|
||||
app.use("/" + file.replace(".js", ""), require(`./routes/${file}`));
|
||||
|
||||
app.all("*", (req, res) => res.error(404, "This page does not exist on this forum."));
|
||||
|
||||
app.listen(port, () => console.log(`${forum_name}-forum on port:`, port));
|
||||
app.listen(port, () => console.log(`${forum_name} on port:`, port));
|
2
lib.js
2
lib.js
|
@ -6,7 +6,7 @@ const { readdirSync } = require('fs');
|
|||
|
||||
require("dotenv").config();
|
||||
module.exports = {
|
||||
themes: readdirSync("./public/themes").filter(f => f !== "common").map(f => require(`./public/themes/${f}`)),
|
||||
themes: readdirSync("./themes").filter(f => f !== "common").map(f => require(`./themes/${f}`)),
|
||||
threadEnum: ["OPEN", "APPROVAL", "DELETED"],
|
||||
userEnum: ["ACTIVE", "APPROVAL", "DELETED", "BANNED"],
|
||||
RL(windowMs = 60_000, max = 1) {
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
<div class="footer">
|
||||
<% if (user){ %>
|
||||
<select id="theme_select">
|
||||
<% for(const theme of dataset.themes){%>
|
||||
<option value="<%= theme.codename %>"><%= theme.name %></option>
|
||||
<% } %>
|
||||
</select>
|
||||
<script>
|
||||
const theme_select = document.getElementById("theme_select");
|
||||
theme_select.querySelector(`option[value=<%= user.theme.codename %>]`).selected = true;
|
||||
theme_select.addEventListener("change", async e => {
|
||||
const codename = e.target.value;
|
||||
await fetch('/api/users/<%= user.id %>', {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({
|
||||
theme: {
|
||||
codename
|
||||
}
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
const theme = await fetch("/api/themes/" + codename).then(res => res.json());
|
||||
const txt = "Theme changed to:\n" +
|
||||
"Name: " + theme.name + "\n" +
|
||||
"Description: " + theme.description + "\n" +
|
||||
"Author: " + theme.author + "\n";
|
||||
alert(txt);
|
||||
location.reload();
|
||||
});
|
||||
</script>
|
||||
<% } %>
|
||||
|
||||
<a href="https://github.com/Akif9748/akf-forum" style="color: white;"> This website is powered by
|
||||
<span style="color: #ffbf00;">akf-forum</span>
|
||||
</a>
|
||||
<div>
|
||||
<span style="color:white">Coders</span> <br>
|
||||
<div style="text-align:center;">
|
||||
<a href="https://github.com/Akif9748/" style="color: #ffbf00;">Akif</a><br><a href="#" style="color:#ffbf00;">Tokmak</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,45 +0,0 @@
|
|||
|
||||
<% if (user?.admin){ %>
|
||||
<div class="admin-bar">
|
||||
<a href="/admin" class="admin-bar">Click here to reach admin panel</a>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="header">
|
||||
<a class="logo" href="/"><%= dataset.forum_name.toUpperCase() %> <span>FORUM</span></a>
|
||||
<div class="buttons">
|
||||
|
||||
<% if (user){ %>
|
||||
<a href="<%=user.getLink() %>" class="btn-outline-primary">
|
||||
<div class="box-username"><%= user.name %>
|
||||
<div class="avatar"><img src="<%=user.avatar %>"></div>
|
||||
</div>
|
||||
</a>
|
||||
<a id="logout" href="/login" class="btn-primary">Logout</a>
|
||||
<% } else { %>
|
||||
|
||||
<a id="login" href="/login" class="btn-primary">Login</a>
|
||||
<a href="/register" class="btn-outline-primary">Register</a>
|
||||
<script>
|
||||
document.getElementById("login").href += "?redirect=" + location.pathname;
|
||||
</script>
|
||||
<% } %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<a href="/threads/create/" class="menu-item">Create Thread</a>
|
||||
<a href="/categories" class="menu-item">Categories</a>
|
||||
<a href="/threads" class="menu-item">Threads</a>
|
||||
<a href="/users" class="menu-item">Users</a>
|
||||
<a href="/search" class="menu-item">Search</a>
|
||||
<script>
|
||||
const menuItems = document.getElementsByClassName("menu-item");
|
||||
|
||||
for (let i = 0; i < menuItems.length; i++)
|
||||
if (window.location.pathname.includes(menuItems[i].getAttribute("href"))) {
|
||||
menuItems[i].classList.add("active-menu");
|
||||
break;
|
||||
}
|
||||
</script>
|
||||
</div>
|
|
@ -1,72 +0,0 @@
|
|||
<% if(user?.admin || user?.id === member.id){ %>
|
||||
<details>
|
||||
<summary class="btn-outline-primary">User Menu:</summary>
|
||||
|
||||
<% if (!member.discordID && discord && user?.id === member.id) { %>
|
||||
<a href="<%=discord%>" class="btn-outline-primary">DC auth</a>
|
||||
<% } else if(member.discordID && user?.id === member.id) { %>
|
||||
<a class="btn-outline-primary" id="un_discord">Unauth DC!</a>
|
||||
<% } %>
|
||||
<a href="/users/<%=member.id%>/avatar" class="btn-outline-primary">Upload avatar</a>
|
||||
<a class="btn-outline-primary" id="toogle">Edit user!</a>
|
||||
<script type="module">
|
||||
import request from "/js/request.js";
|
||||
|
||||
const form = document.getElementById("form");
|
||||
|
||||
document.addEventListener("click", async e => {
|
||||
if (e.target.id == "delete") {
|
||||
const response = await request("/api/users/<%= member.id %>", "DELETE");
|
||||
if (response.state !== "DELETED") return
|
||||
alert("User is deleted!");
|
||||
location.reload()
|
||||
} else if (e.target.id == "undelete") {
|
||||
const response = await request("/api/users/<%= member.id %>/", "PATCH", {
|
||||
deleted: false
|
||||
});
|
||||
if (response.state == "DELETED") return;
|
||||
alert("User is undeleted successfully!");
|
||||
location.reload()
|
||||
} else if (e.target.id == "un_discord") {
|
||||
const response = await fetch("/auth/discord/", {
|
||||
method: "DELETE"
|
||||
});
|
||||
alert(await response.text());
|
||||
location.reload()
|
||||
} else if (e.target.id.startsWith("last_")) {
|
||||
let hideLastSeen = e.target.id.replace("last_", "") == "hide" ? true : false;
|
||||
const response = await request("/api/users/<%= member.id %>/", "PATCH", {
|
||||
hideLastSeen
|
||||
});
|
||||
alert(`Last seen is ${!hideLastSeen?"un":""}hided!`);
|
||||
location.reload()
|
||||
|
||||
} else if (e.target.id == "toogle")
|
||||
document.getElementById('user-edit').classList.toggle('no-active')
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<% if (member.hideLastSeen) {%>
|
||||
<a id="last_unhide" class="btn-primary">Unhide last seen! </a>
|
||||
<% } else { %>
|
||||
<a id="last_hide" class="btn-outline-primary">Hide last seen! </a>
|
||||
<% } %>
|
||||
|
||||
<% if (member.deleted) {%>
|
||||
<h1>This user has been deleted!</h1>
|
||||
<a id="undelete" class="btn-primary">Undelete user! </a>
|
||||
<% } else if (user?.admin){ %>
|
||||
<a id="delete" class="btn-outline-primary">Delete user! </a>
|
||||
<% } %>
|
||||
<% if (user?.admin) {%>
|
||||
<h2>IP adresses of the user:</h2>
|
||||
<select>
|
||||
<% for(const ip of member.ips) { %>
|
||||
<option><%= ip %></option>
|
||||
<% } %>
|
||||
</select>
|
||||
|
||||
<% } %>
|
||||
</details>
|
||||
<% } %>
|
|
@ -1 +0,0 @@
|
|||
<%- include(dataset.getFile("common/footer")) %>
|
|
@ -1 +0,0 @@
|
|||
<%- include(dataset.getFile("common/navbar")) %>
|
|
@ -1 +0,0 @@
|
|||
<%- include(dataset.getFile("common/usermenu")) %>
|
|
@ -1 +0,0 @@
|
|||
<%- include(dataset.getFile("common/footer")) %>
|
|
@ -1 +0,0 @@
|
|||
<%- include(dataset.getFile("common/navbar")) %>
|
|
@ -1 +0,0 @@
|
|||
<%- include(dataset.getFile("common/usermenu")) %>
|
10
routes/.js
10
routes/.js
|
@ -1,16 +1,16 @@
|
|||
const { UserModel, ThreadModel, MessageModel } = require("../models")
|
||||
const { UserModel, ThreadModel, MessageModel, CategoryModel } = require("../models")
|
||||
const { Router } = require("express");
|
||||
const app = Router();
|
||||
|
||||
app.get("/", async (req, res) => {
|
||||
|
||||
const
|
||||
mem = process.memoryUsage().heapUsed / Math.pow(2, 20),
|
||||
const categories = await CategoryModel.count(),
|
||||
users = await UserModel.count({ deleted: false }),
|
||||
threads = await ThreadModel.count({ state: "OPEN" }),
|
||||
messages = await MessageModel.count({ deleted: false });
|
||||
messages = await MessageModel.count({ deleted: false }),
|
||||
newestMember = await UserModel.findOne({ deleted: false }, "name").sort({ time: -1 });
|
||||
|
||||
res.reply("index", { mem, users, threads, messages });
|
||||
res.reply("index", { categories, users, threads, messages, newestMember: newestMember.name });
|
||||
|
||||
});
|
||||
|
||||
|
|
124
themes/bootstrap_black/views/index.ejs
Normal file
124
themes/bootstrap_black/views/index.ejs
Normal file
|
@ -0,0 +1,124 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: "Welcome to the "+dataset.forum_name+"-forum!" }) %>
|
||||
|
||||
|
||||
<body>
|
||||
<%- include("extra/navbar") %>
|
||||
|
||||
|
||||
<div class="container my-3">
|
||||
<nav class="breadcrumb">
|
||||
<span class="breadcrumb-item active">
|
||||
<% if (user) { %>
|
||||
Welcome, <%= user.name %>
|
||||
<% } else { %>
|
||||
Welcome, Guest! <a href="/register">You can press to register.</a>
|
||||
<% } %>
|
||||
</span>
|
||||
</nav>
|
||||
<div class="row">
|
||||
<div class="col-12 col-xl-9">
|
||||
<div class="category">
|
||||
|
||||
<h2 class="h4 text-white bg-danger mb-0 p-4 rounded-top">Forum category</h2>
|
||||
<table class="table table-striped table-bordered table-responsive">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th scope="col" class="forum-col">Forum</th>
|
||||
<th scope="col">Topics</th>
|
||||
<th scope="col">Posts</th>
|
||||
<th scope="col" class="last-post-col">Last post</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="thread">
|
||||
<td>
|
||||
<h3 class="h5 mb-0"><a href="#0" class="text-uppercase">Forum name</a></h3>
|
||||
<p class="mb-0">Lorem ipsum dolor sit amet, consectetur adipiscing elit. In laoreet pellentesque lorem sed elementum.</p>
|
||||
</td>
|
||||
<td>
|
||||
<div>5</div>
|
||||
</td>
|
||||
<td>
|
||||
<div>18</div>
|
||||
</td>
|
||||
<td>
|
||||
<h4 class="h6 mb-0 font-weight-bold"><a href="#0">Post name</a></h4>
|
||||
<div>by <a href="#0">Author name</a></div>
|
||||
<div>05 Apr 2017, 20:07</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-xl-3">
|
||||
<aside>
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6 col-xl-12">
|
||||
<div class="card mb-3 mb-sm-0 mb-xl-3">
|
||||
<div class="card-body">
|
||||
<h2 class="h4 card-title">Members online</h2>
|
||||
<ul class="list-unstyled mb-0">
|
||||
<li><a href="/users">You</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-8">Total:</dt>
|
||||
<dd class="col-4 mb-0">-</dd>
|
||||
</dl>
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-8">Members:</dt>
|
||||
<dd class="col-4 mb-0">-</dd>
|
||||
</dl>
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-8">Guests:</dt>
|
||||
<dd class="col-4 mb-0">-</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-xl-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
|
||||
<h2 class="h4 card-title">Forum statistics</h2>
|
||||
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-8">Total forums:</dt>
|
||||
<dd class="col-4 mb-0"><%= categories %></dd>
|
||||
</dl>
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-8">Total threads:</dt>
|
||||
<dd class="col-4 mb-0"><%= threads %></dd>
|
||||
</dl>
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-8">Total messages:</dt>
|
||||
<dd class="col-4 mb-0"><%= messages %></dd>
|
||||
</dl>
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-8">Total members:</dt>
|
||||
<dd class="col-4 mb-0"><%= users %></dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div>Newest member:</div>
|
||||
<div><a href="#0"><%= newestMember %></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%- include("extra/footer") %>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,11 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: "Admin Panel!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Admin Panel!" }) %>
|
||||
|
||||
|
||||
<body style="text-align: center;">
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
<style>
|
||||
table {
|
||||
font-family: arial, sans-serif;
|
||||
|
@ -83,7 +84,7 @@
|
|||
}
|
||||
</script>
|
||||
</div>
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: "Avatar Upload Panel!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Avatar Upload Panel!" }) %>
|
||||
|
||||
<body style="text-align: center;">
|
||||
<link rel="stylesheet" href="/libs/cropper/cropper.css">
|
||||
|
@ -27,7 +27,8 @@
|
|||
</style>
|
||||
<script src="/libs/cropper/cropper.js"></script>
|
||||
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
|
||||
|
||||
<div class="container">
|
||||
|
@ -107,7 +108,7 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<%- include("extra/meta", {title: "Thread list!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Category list!" }) %>
|
||||
|
||||
<body>
|
||||
<link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'>
|
||||
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
<div class="threads">
|
||||
<% categories.forEach(category=>{ %>
|
||||
<a href="<%= category.getLink() %>">
|
||||
|
@ -48,7 +49,7 @@
|
|||
|
||||
</div>
|
||||
<% } %>
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: "Edit Forum Config!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Edit Forum Config!" }) %>
|
||||
|
||||
<body style="text-align: center;">
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
<h1>Edit forum config</h1>
|
||||
<textarea rows="30" cols="75"><%= config %></textarea>
|
||||
<a onclick="send();" class="btn-primary">Edit config</a>
|
||||
|
@ -24,7 +25,7 @@
|
|||
|
||||
}
|
||||
</script>
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<%- include("extra/meta", {title: "Create Category!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Create Category!" }) %>
|
||||
|
||||
<body>
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
<link rel="stylesheet" href="/libs/simplemde/simplemde.min.css">
|
||||
<script src="/libs/simplemde/simplemde.min.js"></script>
|
||||
|
||||
|
@ -41,7 +42,7 @@
|
|||
|
||||
});
|
||||
</script>
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: "Create thread!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Create thread!" }) %>
|
||||
|
||||
<body >
|
||||
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
<link rel="stylesheet" href="/libs/simplemde/simplemde.min.css">
|
||||
<script src="/libs/simplemde/simplemde.min.js"></script>
|
||||
|
||||
|
@ -53,7 +54,7 @@
|
|||
|
||||
});
|
||||
</script>
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: member.name }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: member.name }) %>
|
||||
|
||||
|
||||
<body>
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
<link rel="stylesheet" href="/libs/simplemde/simplemde.min.css">
|
||||
<script src="/libs/simplemde/simplemde.min.js"></script>
|
||||
|
||||
|
@ -47,7 +48,7 @@
|
|||
location.reload();
|
||||
});
|
||||
</script>
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
17
themes/common/views/error.ejs
Normal file
17
themes/common/views/error.ejs
Normal file
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: type+" error!" }) %>
|
||||
|
||||
|
||||
<body style="text-align: center;">
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
|
||||
<h1 style="color: var(--main);"><%= type %></h1>
|
||||
<h2 style="color: var(--second);"><%= error %></h2>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
44
themes/common/views/extra/footer.ejs
Normal file
44
themes/common/views/extra/footer.ejs
Normal file
|
@ -0,0 +1,44 @@
|
|||
<div class="footer">
|
||||
<% if (user){ %>
|
||||
<select id="theme_select">
|
||||
<% for(const theme of dataset.themes){%>
|
||||
<option value="<%= theme.codename %>"><%= theme.name %></option>
|
||||
<% } %>
|
||||
</select>
|
||||
<script>
|
||||
const theme_select = document.getElementById("theme_select");
|
||||
theme_select.querySelector(`option[value=<%= user.theme.codename %>]`).selected = true;
|
||||
theme_select.addEventListener("change", async e => {
|
||||
const codename = e.target.value;
|
||||
await fetch('/api/users/<%= user.id %>', {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({
|
||||
theme: {
|
||||
codename
|
||||
}
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
const theme = await fetch("/api/themes/" + codename).then(res => res.json());
|
||||
const txt = "Theme changed to:\n" +
|
||||
"Name: " + theme.name + "\n" +
|
||||
"Description: " + theme.description + "\n" +
|
||||
"Author: " + theme.author + "\n";
|
||||
alert(txt);
|
||||
location.reload();
|
||||
});
|
||||
</script>
|
||||
<% } %>
|
||||
|
||||
<a href="https://github.com/Akif9748/akf-forum" style="color: white;"> This website is powered by
|
||||
<span style="color: #ffbf00;">akf-forum</span>
|
||||
</a>
|
||||
<div>
|
||||
<span style="color:white">Coders</span> <br>
|
||||
<div style="text-align:center;">
|
||||
<a href="https://github.com/Akif9748/" style="color: #ffbf00;">Akif</a><br><a href="#" style="color:#ffbf00;">Tokmak</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
44
themes/common/views/extra/navbar.ejs
Normal file
44
themes/common/views/extra/navbar.ejs
Normal file
|
@ -0,0 +1,44 @@
|
|||
<% if (user?.admin){ %>
|
||||
<div class="admin-bar">
|
||||
<a href="/admin" class="admin-bar">Click here to reach admin panel</a>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="header">
|
||||
<a class="logo" href="/"><%= dataset.forum_name.toUpperCase() %> <span>FORUM</span></a>
|
||||
<div class="buttons">
|
||||
|
||||
<% if (user){ %>
|
||||
<a href="<%=user.getLink() %>" class="btn-outline-primary">
|
||||
<div class="box-username"><%= user.name %>
|
||||
<div class="avatar"><img src="<%=user.avatar %>"></div>
|
||||
</div>
|
||||
</a>
|
||||
<a id="logout" href="/login" class="btn-primary">Logout</a>
|
||||
<% } else { %>
|
||||
|
||||
<a id="login" href="/login" class="btn-primary">Login</a>
|
||||
<a href="/register" class="btn-outline-primary">Register</a>
|
||||
<script>
|
||||
document.getElementById("login").href += "?redirect=" + location.pathname;
|
||||
</script>
|
||||
<% } %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<a href="/threads/create/" class="menu-item">Create Thread</a>
|
||||
<a href="/categories" class="menu-item">Categories</a>
|
||||
<a href="/threads" class="menu-item">Threads</a>
|
||||
<a href="/users" class="menu-item">Users</a>
|
||||
<a href="/search" class="menu-item">Search</a>
|
||||
<script>
|
||||
const menuItems = document.getElementsByClassName("menu-item");
|
||||
|
||||
for (let i = 0; i < menuItems.length; i++)
|
||||
if (window.location.pathname.includes(menuItems[i].getAttribute("href"))) {
|
||||
menuItems[i].classList.add("active-menu");
|
||||
break;
|
||||
}
|
||||
</script>
|
||||
</div>
|
72
themes/common/views/extra/usermenu.ejs
Normal file
72
themes/common/views/extra/usermenu.ejs
Normal file
|
@ -0,0 +1,72 @@
|
|||
<% if(user?.admin || user?.id === member.id){ %>
|
||||
<details>
|
||||
<summary class="btn-outline-primary">User Menu:</summary>
|
||||
|
||||
<% if (!member.discordID && discord && user?.id === member.id) { %>
|
||||
<a href="<%=discord%>" class="btn-outline-primary">DC auth</a>
|
||||
<% } else if(member.discordID && user?.id === member.id) { %>
|
||||
<a class="btn-outline-primary" id="un_discord">Unauth DC!</a>
|
||||
<% } %>
|
||||
<a href="/users/<%=member.id%>/avatar" class="btn-outline-primary">Upload avatar</a>
|
||||
<a class="btn-outline-primary" id="toogle">Edit user!</a>
|
||||
<script type="module">
|
||||
import request from "/js/request.js";
|
||||
|
||||
const form = document.getElementById("form");
|
||||
|
||||
document.addEventListener("click", async e => {
|
||||
if (e.target.id == "delete") {
|
||||
const response = await request("/api/users/<%= member.id %>", "DELETE");
|
||||
if (response.state !== "DELETED") return
|
||||
alert("User is deleted!");
|
||||
location.reload()
|
||||
} else if (e.target.id == "undelete") {
|
||||
const response = await request("/api/users/<%= member.id %>/", "PATCH", {
|
||||
deleted: false
|
||||
});
|
||||
if (response.state == "DELETED") return;
|
||||
alert("User is undeleted successfully!");
|
||||
location.reload()
|
||||
} else if (e.target.id == "un_discord") {
|
||||
const response = await fetch("/auth/discord/", {
|
||||
method: "DELETE"
|
||||
});
|
||||
alert(await response.text());
|
||||
location.reload()
|
||||
} else if (e.target.id.startsWith("last_")) {
|
||||
let hideLastSeen = e.target.id.replace("last_", "") == "hide" ? true : false;
|
||||
const response = await request("/api/users/<%= member.id %>/", "PATCH", {
|
||||
hideLastSeen
|
||||
});
|
||||
alert(`Last seen is ${!hideLastSeen?"un":""}hided!`);
|
||||
location.reload()
|
||||
|
||||
} else if (e.target.id == "toogle")
|
||||
document.getElementById('user-edit').classList.toggle('no-active')
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<% if (member.hideLastSeen) {%>
|
||||
<a id="last_unhide" class="btn-primary">Unhide last seen! </a>
|
||||
<% } else { %>
|
||||
<a id="last_hide" class="btn-outline-primary">Hide last seen! </a>
|
||||
<% } %>
|
||||
|
||||
<% if (member.deleted) {%>
|
||||
<h1>This user has been deleted!</h1>
|
||||
<a id="undelete" class="btn-primary">Undelete user! </a>
|
||||
<% } else if (user?.admin){ %>
|
||||
<a id="delete" class="btn-outline-primary">Delete user! </a>
|
||||
<% } %>
|
||||
<% if (user?.admin) {%>
|
||||
<h2>IP adresses of the user:</h2>
|
||||
<select>
|
||||
<% for(const ip of member.ips) { %>
|
||||
<option><%= ip %></option>
|
||||
<% } %>
|
||||
</select>
|
||||
|
||||
<% } %>
|
||||
</details>
|
||||
<% } %>
|
|
@ -1,13 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: "Welcome to the "+dataset.forum_name+"-forum!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Welcome to the "+dataset.forum_name+"-forum!" }) %>
|
||||
|
||||
|
||||
<body>
|
||||
<link rel="stylesheet" href="/css/user.css" />
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
<%- include("extra/navbar") %>
|
||||
|
||||
<div class="usercontent">
|
||||
<% if (user) { %>
|
||||
|
@ -44,12 +43,12 @@
|
|||
</div>
|
||||
|
||||
<div class="userbox">
|
||||
<h2 class="userbox-title">Memory usage:</h2>
|
||||
<h2 class="userbox-value"><%= mem.toFixed(2); %> MB</h2>
|
||||
<h2 class="userbox-title">Category count:</h2>
|
||||
<h2 class="userbox-value"><%= categories %></h2>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: "Log in!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Log in!" }) %>
|
||||
|
||||
<body style="text-align: center;">
|
||||
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
|
||||
<h1 class="title">Login</h1>
|
||||
|
||||
|
@ -20,7 +21,7 @@
|
|||
<% } %>
|
||||
<a href="/register" class="btn-outline-primary">Register</a>
|
||||
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title:"Message search!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title:"Message search!" }) %>
|
||||
|
||||
|
||||
<body style="text-align: center;">
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
|
||||
<div id="messages">
|
||||
|
||||
|
@ -27,7 +28,7 @@
|
|||
|
||||
<% }); %>
|
||||
</div>
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: "Register!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Register!" }) %>
|
||||
|
||||
|
||||
<body style="text-align: center;">
|
||||
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
|
||||
<h1 class="title">Register</h1>
|
||||
|
||||
|
@ -24,7 +25,7 @@
|
|||
<a href="/login" class="btn-outline-primary">Login</a>
|
||||
|
||||
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: "Search page!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Search page!" }) %>
|
||||
|
||||
|
||||
<body style="text-align: center;">
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
|
||||
<h1 class="title">Search</h1>
|
||||
|
||||
|
@ -27,7 +28,7 @@
|
|||
<input type="submit" value="Search" class="btn-primary" />
|
||||
</form>
|
||||
</div>
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: "Setup the Akf-forum!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Setup the Akf-forum!" }) %>
|
||||
|
||||
|
||||
<body style="text-align: center;">
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
|
||||
<h1 style="color: var(--main);">Setup</h1>
|
||||
<h2 style="color: var(--second);">There is default settings for akf-forum, you not need to edit them, but you can! And, the first registered user will be admin.</h2>
|
|
@ -1,11 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: thread.title }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: thread.title }) %>
|
||||
|
||||
|
||||
<body>
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js"></script>
|
||||
<link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'>
|
||||
<link rel="stylesheet" href="/libs/simplemde/simplemde.min.css">
|
||||
|
@ -159,7 +160,7 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<%- include("extra/meta", {title: "Thread list!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Thread list!" }) %>
|
||||
|
||||
<body style="color: var(--anti); text-align: center;">
|
||||
<link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'>
|
||||
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
<h1><%= title || "Threads" %></h1>
|
||||
<h2><%= desp %></h2>
|
||||
<div class="threads">
|
||||
|
@ -54,7 +55,7 @@
|
|||
|
||||
</div>
|
||||
<% } %>
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: member.name }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: member.name }) %>
|
||||
|
||||
|
||||
<body>
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
|
@ -72,7 +73,7 @@ color: var(--anti);
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
|
@ -1,13 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: "User list!" }) %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "User list!" }) %>
|
||||
|
||||
|
||||
<body>
|
||||
<link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'>
|
||||
|
||||
<%- include("extra/navbar") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
<div class="users">
|
||||
<% users.filter(member=> !member.deleted || user.admin ).forEach(member => { %>
|
||||
<div style="display:flex;justify-content:center;">
|
||||
|
@ -43,7 +44,7 @@
|
|||
|
||||
</div>
|
||||
<% } %>
|
||||
<%- include("extra/footer") %>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
1
themes/default_black/views/extra/footer.ejs
Normal file
1
themes/default_black/views/extra/footer.ejs
Normal file
|
@ -0,0 +1 @@
|
|||
<%- include(dataset.getFile("common/views/extra/footer")) %>
|
1
themes/default_black/views/extra/navbar.ejs
Normal file
1
themes/default_black/views/extra/navbar.ejs
Normal file
|
@ -0,0 +1 @@
|
|||
<%- include(dataset.getFile("common/views/extra/navbar")) %>
|
1
themes/default_black/views/extra/usermenu.ejs
Normal file
1
themes/default_black/views/extra/usermenu.ejs
Normal file
|
@ -0,0 +1 @@
|
|||
<%- include(dataset.getFile("common/views/extra/usermenu")) %>
|
1
themes/default_white/views/extra/footer.ejs
Normal file
1
themes/default_white/views/extra/footer.ejs
Normal file
|
@ -0,0 +1 @@
|
|||
<%- include(dataset.getFile("common/views/extra/footer")) %>
|
1
themes/default_white/views/extra/navbar.ejs
Normal file
1
themes/default_white/views/extra/navbar.ejs
Normal file
|
@ -0,0 +1 @@
|
|||
<%- include(dataset.getFile("common/views/extra/navbar")) %>
|
1
themes/default_white/views/extra/usermenu.ejs
Normal file
1
themes/default_white/views/extra/usermenu.ejs
Normal file
|
@ -0,0 +1 @@
|
|||
<%- include(dataset.getFile("common/views/extra/usermenu")) %>
|
|
@ -1,16 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: type+" error!" }) %>
|
||||
|
||||
|
||||
<body style="text-align: center;">
|
||||
<%- include("extra/navbar") %>
|
||||
|
||||
<h1 style="color: var(--main);"><%= type %></h1>
|
||||
<h2 style="color: var(--second);"><%= error %></h2>
|
||||
<%- include("extra/footer") %>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1 +0,0 @@
|
|||
<%- include(dataset.getFile(dataset.theme.codename +"/extra/footer")) %>
|
|
@ -1 +0,0 @@
|
|||
<%- include(dataset.getFile(dataset.theme.codename +"/extra/meta")) %>
|
|
@ -1 +0,0 @@
|
|||
<%- include(dataset.getFile(dataset.theme.codename +"/extra/navbar")) %>
|
Loading…
Reference in a new issue