mirror of
https://github.com/Akif9748/akf-forum.git
synced 2024-11-22 12:00:41 +03:00
Compare commits
14 commits
922b8e5732
...
6b983f9e55
Author | SHA1 | Date | |
---|---|---|---|
6b983f9e55 | |||
4811c7a0e3 | |||
fed6ea0668 | |||
fe550051a7 | |||
31b9249cf8 | |||
138b585482 | |||
f31003b3f2 | |||
0bdc21ff35 | |||
9598813e66 | |||
ccd04139ba | |||
8923293198 | |||
9c183eb05e | |||
0311f363e2 | |||
8fe083ded0 |
32 changed files with 33789 additions and 102 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -11,5 +11,5 @@ config.json
|
|||
test.js
|
||||
|
||||
# avatars folder
|
||||
public/images/avatars/*
|
||||
!public/images/avatars/default.jpg
|
||||
src/public/images/avatars/*
|
||||
!src/public/images/avatars/default.jpg
|
71
README.md
71
README.md
|
@ -44,49 +44,48 @@ Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn
|
|||
![image](https://github.com/Akif9748/akf-forum/assets/70021050/1ad4ad8e-d000-46a6-834e-7d76cdddda60)
|
||||
|
||||
## TO-DO list
|
||||
- Profile Message or DM
|
||||
- mod role, permissions
|
||||
- upload other photos, model for it
|
||||
- change password.
|
||||
- add approval threads page.
|
||||
- Add a feature list to README.md
|
||||
- delete admin???
|
||||
- change category name
|
||||
- _id
|
||||
- add support for transition around gravatar
|
||||
- BETTER SETUP PAGE
|
||||
- add used open source libraries to README.md
|
||||
- send public to common/public
|
||||
- user.ejs for per theme
|
||||
- categori search title like thread search
|
||||
|
||||
### front-end
|
||||
### Backend:
|
||||
#### Feature:
|
||||
- Profile Message or DM
|
||||
- Upload other photos, model for it
|
||||
- Edit & download template
|
||||
- Roles & Permissions
|
||||
```
|
||||
role: "moderator",
|
||||
permissions: ["see_deleted_message"]
|
||||
```
|
||||
|
||||
#### Fixes:
|
||||
- Admin deleting other admins.
|
||||
- send public to common/public
|
||||
|
||||
#### ETC:
|
||||
- Rewrite apidocs
|
||||
- Add a feature list to README.md
|
||||
- add used open source libraries to README.md
|
||||
|
||||
### Frontend
|
||||
#### Features:
|
||||
- change category name
|
||||
- Add approval threads page.
|
||||
- add support for trans around gravatar and upload photo
|
||||
- old contents / titles add to forum interface
|
||||
- who liked a message
|
||||
- add ban button to user profile
|
||||
|
||||
#### Fixes:
|
||||
- BETTER SETUP PAGE: use setup everytime
|
||||
- better user.ejs: https://github.com/mdbootstrap/bootstrap-profiles
|
||||
- add threads, messages etc. to extra folder
|
||||
- add category to threads
|
||||
- working reset button
|
||||
- better pagination
|
||||
- text alling center body
|
||||
- add a css file for CodeMirror in threads / send message ok
|
||||
- old contents / titles add to forum interface
|
||||
- categories page is need a update, thread count in category (?)
|
||||
- add ban button to user profile
|
||||
- who liked a message
|
||||
- give admin button, not is admin
|
||||
- edit user ++
|
||||
- rewrite main page, list new messages
|
||||
- thread.js unfuction only listener
|
||||
|
||||
|
||||
#### css fix
|
||||
- admin page to css file
|
||||
threads:
|
||||
<style>
|
||||
.fa {
|
||||
color: var(--main);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
## Major Version History
|
||||
- V4: Enchanted Themes
|
||||
- V4: Caching
|
||||
- V3: New Theme
|
||||
- V2: Backend fix, mongoose is fixed. Really big fix.
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"codename": "bootstrap_black",
|
||||
"language": "en"
|
||||
},
|
||||
"forum_name": "akf",
|
||||
"description": "Akf-forum!",
|
||||
"forum_name": "akf-forum",
|
||||
"description": "Akf-forum offical site!",
|
||||
"limits": {
|
||||
"title": 128,
|
||||
"message": 1024,
|
||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "akf-forum",
|
||||
"version": "5.2.6",
|
||||
"version": "5.5.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "akf-forum",
|
||||
"version": "5.2.6",
|
||||
"version": "5.5.1",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.1.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "akf-forum",
|
||||
"version": "5.2.6",
|
||||
"version": "5.5.1",
|
||||
"description": "A Node.js based forum software",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
|
|
|
@ -4,13 +4,26 @@ const app = Router();
|
|||
|
||||
app.get("/", async (req, res) => {
|
||||
|
||||
const categories = await CategoryModel.count(),
|
||||
users = await UserModel.count({ deleted: false }),
|
||||
threads = await ThreadModel.count({ state: "OPEN" }),
|
||||
messages = await MessageModel.count({ deleted: false }),
|
||||
newestMember = await UserModel.findOne({ deleted: false }, "name").sort({ time: -1 });
|
||||
const [
|
||||
categories, users, threads, messages, newestMember, newestMessages, newestThreads, onlineUserCount, onlineUsers
|
||||
] = await Promise.all([
|
||||
CategoryModel.count(),
|
||||
UserModel.count({ deleted: false }),
|
||||
ThreadModel.count({ state: "OPEN" }),
|
||||
MessageModel.count({ deleted: false }),
|
||||
UserModel.findOne({ deleted: false }, "name id").sort({ time: -1 }),
|
||||
MessageModel.find({ deleted: false }).sort({ time: -1 }).limit(10),
|
||||
ThreadModel.find({ state: "OPEN" }).sort({ time: -1 }).limit(10),
|
||||
UserModel.count({ deleted: false, lastSeen: { $gt: Date.now() - 1000 * 60 * 5 } }),
|
||||
UserModel.find({ deleted: false, hideLastSeen: false, lastSeen: { $gt: Date.now() - 1000 * 60 * 5 } }, "name id")
|
||||
])
|
||||
|
||||
res.reply("index", { categories, users, threads, messages, newestMember: newestMember.name });
|
||||
|
||||
res.reply("index", {
|
||||
categories, users, threads, messages,
|
||||
newestMember, newestMessages, newestThreads,
|
||||
onlineUserCount, onlineUsers
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const { UserModel, BanModel } = require("../../../models");
|
||||
const { Router } = require("express");
|
||||
const multer = require("multer");
|
||||
const { themes } = require("../../../lib")
|
||||
const { themes, emailRegEx } = require("../../../lib")
|
||||
const app = Router();
|
||||
const { join } = require("path");
|
||||
app.param("id", async (req, res, next, id) => {
|
||||
|
@ -37,7 +37,7 @@ app.patch("/:id", async (req, res) => {
|
|||
if (req.user.id !== member.id && !user.admin) return res.error(403, "You have not got permission for this.");
|
||||
if (!Object.keys(req.body).some(Boolean)) return res.error(400, "Missing member informations in request body.");
|
||||
|
||||
const { name, about, admin, deleted, hideLastSeen, theme } = req.body;
|
||||
const { name, about, admin, deleted, hideLastSeen, theme, email } = req.body;
|
||||
|
||||
if ((admin?.length || "deleted" in req.body) && !req.user.admin) return res.error(403, "You have not got permission for edit 'admin' and 'deleted' information, or bad request.");
|
||||
const { names, desp } = req.app.get("limits");
|
||||
|
@ -54,6 +54,12 @@ app.patch("/:id", async (req, res) => {
|
|||
if (theme && themes.some(t => t.codename === theme.codename))
|
||||
member.theme = theme;
|
||||
|
||||
if (email) {
|
||||
if (!emailRegEx.test(email)) return res.error(400, "E-mail is not valid");
|
||||
if (await UserModel.exists({ email })) return res.error(400, "E-mail is already in use");
|
||||
member.email = email;
|
||||
}
|
||||
|
||||
if (typeof admin === "boolean" || ["false", "true"].includes(admin)) member.admin = admin;
|
||||
if (deleted === false) member.deleted = false;
|
||||
|
||||
|
@ -95,7 +101,7 @@ app.post("/:id/avatar", upload.single('avatar'), async (req, res) => {
|
|||
if (req.user.id !== member.id && !req.user.admin) return res.error(403, "You have not got permission for this.");
|
||||
|
||||
if (!req.file) return res.error(400, "Missing avatar in request body.");
|
||||
member.avatar = req.file.destination.slice("./public".length) + "/" + req.file.filename;
|
||||
member.avatar = "/images/avatars/" + req.file.filename;
|
||||
res.complate(await member.save());
|
||||
});
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ app.post("/", RL(24 * 60 * 60_000, 5), async (req, res) => {
|
|||
to: email,
|
||||
subject: name + ", please verify your email",
|
||||
html: `
|
||||
<h1>Verify your email in ${forum_name}-forum</h1>
|
||||
<h1>Verify your email in ${forum_name}</h1>
|
||||
<a href="${host}/auth/email?code=${user.email_code}">Click here to verify your email</a>
|
||||
`
|
||||
}, (err) => {
|
||||
|
|
28
src/routes/security.js
Normal file
28
src/routes/security.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
const { UserModel } = require("../models");
|
||||
const { Router } = require("express")
|
||||
const bcrypt = require("bcrypt");
|
||||
const { RL} = require('../lib');
|
||||
const app = Router();
|
||||
|
||||
app.use(async (req, res, next) => {
|
||||
if (!req.user) return res.error(403, "You are not logged in");
|
||||
next();
|
||||
});
|
||||
|
||||
app.get("/", (req, res) => res.reply("security"));
|
||||
|
||||
app.post("/", RL(24 * 60 * 60_000, 5), async (req, res) => {
|
||||
|
||||
let { old_password, password } = req.body;
|
||||
if (!old_password || !password) return res.error(400, "You forgot entering some values");
|
||||
const { names } = req.app.get("limits");
|
||||
if (password.length < 3 || password.length > names) return res.error(400, "Password must be between 3 - 25 characters");
|
||||
const user = await UserModel.get(req.user.id, "+password");
|
||||
if (!await bcrypt.compare(old_password, user.password)) return res.error(401, 'Incorrect password!');
|
||||
user.password = await bcrypt.hash(password, 10);
|
||||
await user.save();
|
||||
res.send("Password changed");
|
||||
|
||||
});
|
||||
|
||||
module.exports = app;
|
|
@ -1,7 +1,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title><%= title || dataset.forum_name +"-forum" %></title>
|
||||
<title><%= title || dataset.forum_name %></title>
|
||||
<meta name="description" content="<%= dataset.description %>">
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link href="/themes/bootstrap_black/bootstrap-night.min.css" rel="stylesheet">
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<a class="navbar-brand" href="/"><%= dataset.forum_name.toUpperCase() %><span>-FORUM</span></a>
|
||||
<a class="navbar-brand" href="/"><%= dataset.forum_name.toUpperCase() %></a>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarText">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: "Welcome to the "+dataset.forum_name+"-forum!" }) %>
|
||||
<%- include("extra/meta", {title: "Welcome to the " + dataset.forum_name }) %>
|
||||
|
||||
|
||||
<body>
|
||||
<%- include("extra/navbar") %>
|
||||
<link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'>
|
||||
|
||||
|
||||
<div class="container my-3">
|
||||
|
@ -14,46 +15,87 @@
|
|||
<% if (user) { %>
|
||||
Welcome, <%= user.name %>
|
||||
<% } else { %>
|
||||
Welcome, Guest! <a href="/register">You can press to register.</a>
|
||||
Welcome, Guest! <a href="/register">You can press here 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>
|
||||
<h2 class="h4 text-white bg-danger mb-0 p-4 rounded-top">New threads</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>
|
||||
<th scope="col" class="topic-col">Topic</th>
|
||||
<th scope="col" class="created-col">Created</th>
|
||||
<th scope="col">Statistics</th>
|
||||
<% if (user?.admin){ %> <th scope="col" class="last-post-col">Action</th> <% } %>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="thread">
|
||||
<% newestThreads.forEach(thread=>{ %>
|
||||
<tr>
|
||||
<td>
|
||||
<h3 class="h6">
|
||||
<% if (thread.deleted) { %> <span class="badge badge-primary">[DELETED]</span><% } %>
|
||||
<a href="<%= thread.getLink() %>"><%= thread.title %></a>
|
||||
</h3>
|
||||
</td>
|
||||
<td>
|
||||
<div class="avatar">by <a href="/users/<%= thread.authorID %>"><%= thread.author.name %></a><img src="<%=thread.author.avatar %>"></div>
|
||||
<div><%= new Date(thread.time).toLocaleString() %></div>
|
||||
</td>
|
||||
<td>
|
||||
<div><%= thread.messages.length %> messages</div>
|
||||
<div><%= thread.views %> views</div>
|
||||
</td>
|
||||
<% if (user?.admin){ %>
|
||||
<td>
|
||||
<% if (!thread.deleted){ %>
|
||||
<a class="btn-danger" onclick="fetch('/api/threads/<%= thread.id %>/',{method:'DELETE'})"><i class="bx bx-trash bx-sm"></i></a>
|
||||
<% } %>
|
||||
</td>
|
||||
<% } %>
|
||||
</tr>
|
||||
<% }); %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="category">
|
||||
|
||||
<h2 class="h4 text-white bg-danger mb-0 p-4 rounded-top">New messages</h2>
|
||||
<table class="table table-striped table-bordered table-responsive">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th scope="col" class="forum-col">Message</th>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col" class="last-post-col">Author</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% newestMessages.forEach(message => { %>
|
||||
<tr>
|
||||
<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>
|
||||
<p class="mb-0">
|
||||
<a href="<%= message.getLink() %>"> <%= message.content.slice(0, 100) %></a>
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
<div>5</div>
|
||||
<div><%= new Date(message.time).toLocaleString() %></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>
|
||||
<div class="avatar">by <a href="<%= message.getLink() %>"><%= message.author.name %></a><img src="<%=message.author.avatar %>"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<% }) %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-12 col-xl-3">
|
||||
<aside>
|
||||
<div class="row">
|
||||
|
@ -62,22 +104,17 @@
|
|||
<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>
|
||||
|
||||
<li>
|
||||
<% onlineUsers.forEach(user => { %>
|
||||
<a href="/users/<%=user.id %>"><%= user.name %></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>
|
||||
<dd class="col-4 mb-0"><%= onlineUserCount %></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -85,11 +122,9 @@
|
|||
<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>
|
||||
<dt class="col-8">Total categories:</dt>
|
||||
<dd class="col-4 mb-0"><%= categories %></dd>
|
||||
</dl>
|
||||
<dl class="row mb-0">
|
||||
|
@ -107,7 +142,7 @@
|
|||
</div>
|
||||
<div class="card-footer">
|
||||
<div>Newest member:</div>
|
||||
<div><a href="#0"><%= newestMember %></a></div>
|
||||
<div><a href="/users/<% newestMember.id %>"><%= newestMember.name %></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -34,8 +34,7 @@
|
|||
</h3>
|
||||
</td>
|
||||
<td>
|
||||
<div> </div>
|
||||
<div class="avatar">by <a href="<%= thread.getLink() %>"><%= thread.author.name %></a><img src="<%=thread.author.avatar %>"></div>
|
||||
<div class="avatar">by <a href="/users/<%= thread.authorID %>"><%= thread.author.name %></a><img src="<%=thread.author.avatar %>"></div>
|
||||
<div><%= new Date(thread.time).toLocaleString() %></div>
|
||||
</td>
|
||||
<td>
|
||||
|
|
|
@ -27,17 +27,17 @@
|
|||
<nav aria-label="Navigate post pages" class="float-lg-right">
|
||||
<ul class="pagination pagination-sm mb-lg-0">
|
||||
<% if (page > 0){ %>
|
||||
<li class="page-item"><a href="/categories?page=<%= page-1 %>" class="page-link">Back</a></li>
|
||||
<li class="page-item"><a href="/users?page=<%= page-1 %>" class="page-link">Back</a></li>
|
||||
<% } %>
|
||||
<% for(let i=0; i < pages; i++){ %>
|
||||
<li class="page-item <%= i==page?'active':'' %>"><a href="/categories?page=<%= i %>" class="page-link"><%= i+1 %>
|
||||
<li class="page-item <%= i==page?'active':'' %>"><a href="/users?page=<%= i %>" class="page-link"><%= i+1 %>
|
||||
<% if (i==page){ %>
|
||||
<span class="sr-only">(current)</span>
|
||||
<% } %>
|
||||
</a></li>
|
||||
<% } %>
|
||||
<% if (pages-1 > page) { %>
|
||||
<li class="page-item"><a href="/categories?page=<%= page+1 %>" class="page-link">Next</a></li>
|
||||
<li class="page-item"><a href="/users?page=<%= page+1 %>" class="page-link">Next</a></li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
</h1>
|
||||
|
||||
<form id="form" style="box-shadow:none">
|
||||
<input type="text" name="name" placeholder="<%=member.name%>" class="input">
|
||||
|
||||
<input type="text" name="name" placeholder="<%= member.name %>" class="input">
|
||||
<input type="email" name="email" placeholder="<%= member.email %>" class="input">
|
||||
<textarea id="textarea" class="input" name="about" rows="4" cols="60" name="content" placeholder="<%=member.about%>"></textarea>
|
||||
<% if (user?.admin){ %>
|
||||
Is Admin? <input id='admin' type='checkbox' value='true' name='admin' <%=member.admin ? "checked": ""%>>
|
||||
|
@ -40,7 +40,8 @@
|
|||
const res = await request("/api/users/<%=member.id%>", "PATCH", {
|
||||
name: formdata.get("name"),
|
||||
about: simplemde.value(),
|
||||
admin: formdata.get("admin")
|
||||
admin: formdata.get("admin"),
|
||||
email: formdata.get("email")
|
||||
});
|
||||
simplemde.clearAutosavedValue();
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<% } %>
|
||||
|
||||
<div class="header">
|
||||
<a class="logo" href="/"><%= dataset.forum_name.toUpperCase() %> <span>FORUM</span></a>
|
||||
<a class="logo" href="/"><%= dataset.forum_name.toUpperCase() %></a>
|
||||
<div class="buttons">
|
||||
|
||||
<% if (user){ %>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/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 }) %>
|
||||
|
||||
|
||||
<body>
|
||||
|
|
23
src/themes/common/views/security.ejs
Normal file
23
src/themes/common/views/security.ejs
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Log in!" }) %>
|
||||
|
||||
<body style="text-align: center;">
|
||||
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
|
||||
<h1 class="title">Change password</h1>
|
||||
|
||||
<form class="login" action="/security" method="post">
|
||||
<input type="password" name="old_password" placeholder="Old Password" class="input" required>
|
||||
<input type="password" name="password" placeholder="Password" class="input" required>
|
||||
<input type="submit" style="width:100%" class="btn-primary" value="Change it">
|
||||
</form>
|
||||
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -11,15 +11,14 @@
|
|||
<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>
|
||||
<form class="search" method="POST">
|
||||
|
||||
Forum name:
|
||||
<input class="input" type="text" name="forum_name" value="akf" required>
|
||||
<input class="input" type="text" name="forum_name" value="akf-forum" required>
|
||||
Forum description:
|
||||
<input class="input" type="text" name="description" value="Akf-forum!" required>
|
||||
Default state for new threads, change with "APPROVAL" for approval system:
|
||||
<input class="input" type="text" name="default_thread_state" value="OPEN" required>
|
||||
Default state for new users, change with "APPROVAL" for approval system:
|
||||
<input class="input" type="text" name="default_thread_state" value="ACTIVE" required>
|
||||
<input class="input" type="text" name="default_user_state" value="ACTIVE" required>
|
||||
Domain of the forum, defaulty setted:
|
||||
<input class="input" type="text" name="host" id="domain" value="Akf-forum!" required>
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title><%= title || dataset.forum_name +"-forum" %></title>
|
||||
<title><%= title || dataset.forum_name %></title>
|
||||
<meta name="description" content="<%= dataset.description %>">
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="stylesheet" href="/themes/default_black/main.css" />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title><%= title || dataset.forum_name +"-forum" %></title>
|
||||
<title><%= title || dataset.forum_name %></title>
|
||||
<meta name="description" content="<%= dataset.description %>">
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="stylesheet" href="/themes/default_white/main.css" />
|
||||
|
|
6
src/themes/xenforo/index.js
Normal file
6
src/themes/xenforo/index.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
name: "Xenforo Black Theme",
|
||||
codename: "xenforo",
|
||||
description: "Xenforo theme by xenforo, and edited by Akif9748",
|
||||
author: "Akif9748"
|
||||
}
|
3120
src/themes/xenforo/public/s1.css
Normal file
3120
src/themes/xenforo/public/s1.css
Normal file
File diff suppressed because it is too large
Load diff
29016
src/themes/xenforo/public/s2.css
Normal file
29016
src/themes/xenforo/public/s2.css
Normal file
File diff suppressed because it is too large
Load diff
47
src/themes/xenforo/views/error.ejs
Normal file
47
src/themes/xenforo/views/error.ejs
Normal file
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html>
|
||||
<html id="XF" lang="tr-TR" dir="LTR" data-app="public" data-template="forum_list" data-container-key data-content-key data-logged-in="true" data-cookie-prefix="xf_" data-csrf="1685091272,f052c88e4c4aa12fbe73a08e269625fa" class="has-no-js v_2_0 template-forum_list">
|
||||
<%- include("extra/meta", {title: "Error" }) %>
|
||||
|
||||
<body data-template="forum_list">
|
||||
<div class="p-pageWrapper" id="top">
|
||||
|
||||
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
<div class="p-body">
|
||||
<div class="p-body-inner">
|
||||
<div class="p-body-header">
|
||||
<div class="p-title ">
|
||||
<h1 class="p-title-value">Hay aksi! Bir sorun yaşıyoruz.</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-body-main ">
|
||||
<div class="p-body-contentCol"></div>
|
||||
<div class="p-body-content">
|
||||
<div class="p-body-pageContent">
|
||||
<div class="blockMessage">
|
||||
İstenen sayfa bulunamadı.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="p-breadcrumbs p-breadcrumbs--bottom" itemscope="" itemtype="https://schema.org/BreadcrumbList">
|
||||
<li itemprop="itemListElement" itemscope="" itemtype="https://schema.org/ListItem">
|
||||
<a href="https://makale.silicone-forum.com" itemprop="item">
|
||||
<span itemprop="name">Ana sayfa</span>
|
||||
</a>
|
||||
<meta itemprop="position" content="1">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="u-scrollButtons js-scrollButtons" data-trigger-type="up">
|
||||
<a href="#top" class="button--scroll button" data-xf-click="scroll-to"><span class="button-text"><i class="fa--xf far fa-arrow-up" aria-hidden="true"></i><span class="u-srOnly">Top</span></span></a>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
64
src/themes/xenforo/views/extra/footer.ejs
Normal file
64
src/themes/xenforo/views/extra/footer.ejs
Normal file
|
@ -0,0 +1,64 @@
|
|||
<footer class="p-footer" id="footer">
|
||||
<div class="p-footer-inner">
|
||||
<div class="p-footer-row">
|
||||
<div class="p-footer-row-main">
|
||||
<ul class="p-footer-linkList">
|
||||
<li>
|
||||
|
||||
<% 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>
|
||||
<% } %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="p-footer-row-opposite">
|
||||
<ul class="p-footer-linkList">
|
||||
<li><a href="/">Ana sayfa</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-footer-copyright">
|
||||
Xenforo theme created <a href="https://xenforo.com" class="u-concealed" dir="ltr" target="_blank" rel="sponsored noopener">by XenForo<sup>®</sup> </a>
|
||||
and <a href="https://github.com/Akif9748"> edited by Akif9748</a>
|
||||
<br>This website is powered by <a class="text-white" href="https://github.com/Akif9748/akf-forum">akf-forum</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<footer class="text-center text-white fixed-bottom">
|
||||
|
||||
<div class="text-center p-3">
|
||||
|
||||
</div>
|
||||
</footer>
|
20
src/themes/xenforo/views/extra/meta.ejs
Normal file
20
src/themes/xenforo/views/extra/meta.ejs
Normal file
|
@ -0,0 +1,20 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title><%= title || dataset.forum_name %></title>
|
||||
<meta name="theme-color" content="#1e1e1e" />
|
||||
<meta name="apple-mobile-web-app-title" content="Silicone">
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS feed for <%= title || dataset.forum_name %>" href="/bolum/-/index.rss" />
|
||||
<meta property="og:site_name" content="<%= title || dataset.forum_name %>" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="<%= title || dataset.forum_name %>" />
|
||||
<meta property="twitter:title" content="<%= title || dataset.forum_name %>" />
|
||||
<meta name="description" content="<%= dataset.description %>">
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<meta property="og:description" content="<%= dataset.description %>" />
|
||||
<meta property="twitter:description" content="<%= dataset.description %>" />
|
||||
|
||||
<link rel="stylesheet" href="/themes/xenforo/s1.css" />
|
||||
<link rel="stylesheet" href="/themes/xenforo/s2.css" />
|
||||
</head>
|
95
src/themes/xenforo/views/extra/navbar.ejs
Normal file
95
src/themes/xenforo/views/extra/navbar.ejs
Normal file
|
@ -0,0 +1,95 @@
|
|||
<header class="p-header" id="header">
|
||||
<div class="p-header-inner">
|
||||
<div class="p-header-content">
|
||||
<a style="font-size: 40px;" href="/"><%= dataset.forum_name.toUpperCase() %></a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="p-navSticky p-navSticky--primary" data-xf-init="sticky-header">
|
||||
<nav class="p-nav">
|
||||
<div class="p-nav-inner">
|
||||
<button type="button" class="button--plain p-nav-menuTrigger button" data-xf-click="off-canvas" data-menu=".js-headerOffCanvasMenu" tabindex="0" aria-label="Menü"><span class="button-text">
|
||||
<i aria-hidden="true"></i>
|
||||
</span></button>
|
||||
|
||||
<div class="p-nav-scroller hScroller" data-xf-init="h-scroller" data-auto-scroll=".p-navEl.is-selected">
|
||||
<div class="hScroller-scroll">
|
||||
<ul class="p-nav-list js-offCanvasNavSource">
|
||||
<li>
|
||||
<div class="p-navEl">
|
||||
<a class="p-navEl-link p-navEl-link--splitMenu " href="/threads/create/">Create Thread</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="p-navEl">
|
||||
<a class="p-navEl-link p-navEl-link--splitMenu " href="/categories">Categories</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="p-navEl">
|
||||
<a class="p-navEl-link p-navEl-link--splitMenu " href="/threads">Threads</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="p-navEl">
|
||||
<a class="p-navEl-link p-navEl-link--splitMenu " href="/users">Users</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="p-navEl">
|
||||
<a class="p-navEl-link p-navEl-link--splitMenu " href="/search">Search</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="p-navEl">
|
||||
<a class="p-navEl-link p-navEl-link--splitMenu " href="/login">Logout</a>
|
||||
</div>
|
||||
</li>
|
||||
<script>
|
||||
const menuItems = document.getElementsByClassName("p-navEl");
|
||||
for (let i = 0; i < menuItems.length; i++)
|
||||
if (window.location.pathname.includes(menuItems[i].children[0].getAttribute("href"))) {
|
||||
menuItems[i].classList.add("is-selected");
|
||||
break;
|
||||
}
|
||||
</script>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-nav-opposite">
|
||||
<div class="p-navgroup p-account p-navgroup--member">
|
||||
<a href="<%=user.getLink()%>" class="p-navgroup-link p-navgroup-link--iconic p-navgroup-link--user" data-xf-click="menu" data-xf-key="m" data-menu-pos-ref="< .p-navgroup" title="Akif" aria-expanded="false" aria-haspopup="true">
|
||||
<span class="avatar avatar--xxs" data-user-id="116">
|
||||
|
||||
<img src="<%=user.avatar %>" alt="Akif" class="avatar-u116-s" width="48" height="48" loading="lazy" />
|
||||
</span>
|
||||
<span class="p-navgroup-linkText"><%=user.name %></span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div class="p-navgroup p-discovery">
|
||||
<a href="/search" class="p-navgroup-link p-navgroup-link--iconic p-navgroup-link--search" data-xf-click="menu" data-xf-key="/" aria-label="Ara" aria-expanded="false" aria-haspopup="true" title="Ara">
|
||||
<i aria-hidden="true"></i>
|
||||
<span class="p-navgroup-linkText">Ara</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
<% if (user?.admin){ %>
|
||||
<div class="p-sectionLinks">
|
||||
<div class="p-sectionLinks-inner hScroller" data-xf-init="h-scroller">
|
||||
<div class="hScroller-scroll">
|
||||
<ul class="p-sectionLinks-list">
|
||||
<li>
|
||||
<div class="p-navEl ">
|
||||
<a href="/admin" class="p-navEl-link ">Admin Page</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
146
src/themes/xenforo/views/index.ejs
Normal file
146
src/themes/xenforo/views/index.ejs
Normal file
|
@ -0,0 +1,146 @@
|
|||
<!DOCTYPE html>
|
||||
<html id="XF">
|
||||
<%- include("extra/meta", {title: "Welcome to the "+dataset.forum_name+"-forum!" }) %>
|
||||
|
||||
<body data-template="forum_list">
|
||||
<div class="p-pageWrapper" id="top">
|
||||
|
||||
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||
|
||||
<div class="p-body">
|
||||
<div class="p-body-inner">
|
||||
<div class="p-body-header">
|
||||
<div class="p-title ">
|
||||
<h1 class="p-title-value"><%=dataset.forum_name%></h1>
|
||||
<div class="p-title-pageAction">
|
||||
<a href="/threads/create" class="button--cta button button--icon button--icon--write" data-xf-click="overlay" rel="nofollow"><span class="button-text">
|
||||
Create Thread
|
||||
</span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-body-main p-body-main--withSidebar ">
|
||||
<div class="p-body-contentCol"></div>
|
||||
<div class="p-body-sidebarCol"></div>
|
||||
<div class="p-body-content">
|
||||
<div class="p-body-pageContent">
|
||||
<div class="block" data-widget-id="24" data-widget-key="new_threads_ust" data-widget-definition="new_threads">
|
||||
<div class="block-container">
|
||||
<h3 class="block-header">
|
||||
<a href="/threads" rel="nofollow">New Threads</a>
|
||||
</h3>
|
||||
<div class="block-body">
|
||||
<div class="structItemContainer">
|
||||
<% newestThreads.forEach(thread => { %>
|
||||
|
||||
<div class="structItem structItem--thread js-inlineModContainer js-threadListItem-14359" data-author="ercncavs">
|
||||
<div class="structItem-cell structItem-cell--icon">
|
||||
<div class="structItem-iconContainer">
|
||||
<a href="" class="avatar avatar--s" data-xf-init="member-tooltip">
|
||||
<img src="<%= thread.author.avatar %>" class="avatar-u1804-s" width="48" height="48" loading="lazy" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="structItem-cell structItem-cell--main" data-xf-init="touch-proxy">
|
||||
|
||||
<div class="structItem-title">
|
||||
<a href="<%= thread.getLink() %>" data-tp-primary="on" data-xf-init="preview-tooltip"><%= thread.title %></a>
|
||||
</div>
|
||||
<div class="structItem-minor">
|
||||
<ul class="structItem-parts">
|
||||
<li>
|
||||
<a href="/users/<%= thread.authorID %>" class="username " dir="auto" data-user-id="1804" data-xf-init="member-tooltip">
|
||||
<%= thread.author.name %>
|
||||
</a>
|
||||
</li>
|
||||
<li class="structItem-startDate">
|
||||
<a>
|
||||
<time class="u-dt"><%= new Date(thread.time).toLocaleString() %></time>
|
||||
</a>
|
||||
</li>
|
||||
<li><a href="/categories/<%= thread.categoryID %>"><%= thread.categoryID %></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="structItem-cell structItem-cell--meta" title="İlk mesajın ifade puanı: 0">
|
||||
<dl class="pairs pairs--justified">
|
||||
<dt>Messages</dt>
|
||||
<dd><%= thread.messages.length %></dd>
|
||||
</dl>
|
||||
<dl class="pairs pairs--justified structItem-minor">
|
||||
<dt>Views</dt>
|
||||
<dd><%= thread.views %></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<% }) %>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-body-sidebar">
|
||||
|
||||
<div class="block" data-widget-section="onlineNow" data-widget-id="6" data-widget-key="forum_overview_members_online" data-widget-definition="members_online">
|
||||
<div class="block-container">
|
||||
<h3 class="block-minorHeader"><a href="/">Online Members</a></h3>
|
||||
<div class="block-body">
|
||||
<div class="block-row block-row--minor">
|
||||
<ul class="listInline listInline--comma">
|
||||
<% onlineUsers.forEach(user => { %>
|
||||
<li><a href="/users/<%=user.id %>" class="username " dir="auto" data-user-id="116" data-xf-init="member-tooltip"><%= user.name %></a></li>
|
||||
<% }); %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="block-footer">
|
||||
<span class="block-footer-counter">Total: <%= onlineUserCount %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="block" data-widget-id="9" data-widget-key="forum_overview_forum_statistics" data-widget-definition="forum_statistics">
|
||||
<div class="block-container">
|
||||
<h3 class="block-minorHeader">Forum statistics</h3>
|
||||
<div class="block-body block-row">
|
||||
<dl class="pairs pairs--justified">
|
||||
<dt>Total categories</dt>
|
||||
<dd><%= categories %></dd>
|
||||
</dl>
|
||||
<dl class="pairs pairs--justified">
|
||||
<dt>Total threads</dt>
|
||||
<dd><%= threads %></dd>
|
||||
</dl>
|
||||
<dl class="pairs pairs--justified">
|
||||
<dt>Total messages</dt>
|
||||
<dd><%= messages %></dd>
|
||||
</dl>
|
||||
<dl class="pairs pairs--justified">
|
||||
<dt>Total members</dt>
|
||||
<dd><%= users %></dd>
|
||||
</dl>
|
||||
<dl class="pairs pairs--justified">
|
||||
<dt>Newest member</dt>
|
||||
<dd><a href="/users/<% newestMember.id %>" class="username " dir="auto" data-xf-init="member-tooltip"><%= newestMember.name %></a></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
1070
src/themes/xenforo/views/thread.ejs
Normal file
1070
src/themes/xenforo/views/thread.ejs
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue