mirror of
https://github.com/Akif9748/akf-forum.git
synced 2024-10-31 19:25:04 +03:00
Better pagination and rewiretten threads etc.
This commit is contained in:
parent
83ae8507c0
commit
9ee1d589eb
10 changed files with 633 additions and 47 deletions
|
@ -58,8 +58,11 @@ Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn
|
||||||
- add used open source libraries to README.md
|
- add used open source libraries to README.md
|
||||||
- send public to common/public
|
- send public to common/public
|
||||||
- user.ejs for per theme
|
- user.ejs for per theme
|
||||||
|
- categori search title like thread search
|
||||||
|
|
||||||
### front-end
|
### front-end
|
||||||
|
- working reset button
|
||||||
|
- better pagination
|
||||||
- text alling center body
|
- text alling center body
|
||||||
- add a css file for CodeMirror in threads / send message ok
|
- add a css file for CodeMirror in threads / send message ok
|
||||||
- old contents / titles add to forum interface
|
- old contents / titles add to forum interface
|
||||||
|
|
|
@ -23,8 +23,6 @@ app.ips = [];
|
||||||
app.set("view engine", "ejs");
|
app.set("view engine", "ejs");
|
||||||
app.set("limits", limits);
|
app.set("limits", limits);
|
||||||
|
|
||||||
if (RLS.enabled) app.use(RL(RLS.windowMs, RLS.max));
|
|
||||||
|
|
||||||
for (const theme of fs.readdirSync(join(__dirname, "themes")))
|
for (const theme of fs.readdirSync(join(__dirname, "themes")))
|
||||||
app.use(`/themes/${theme}`, express.static(join(__dirname, "themes", theme, "public")));
|
app.use(`/themes/${theme}`, express.static(join(__dirname, "themes", theme, "public")));
|
||||||
|
|
||||||
|
@ -70,6 +68,8 @@ app.use(express.static(join(__dirname, "public")), express.json(), express.urlen
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (RLS.enabled) app.use(RL(RLS.windowMs, RLS.max));
|
||||||
|
|
||||||
if (discord_auth)
|
if (discord_auth)
|
||||||
app.set("DISCORD_AUTH_URL", `https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_ID}&redirect_uri=${host}%2Fauth%2Fdiscord&response_type=code&scope=identify`);
|
app.set("DISCORD_AUTH_URL", `https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_ID}&redirect_uri=${host}%2Fauth%2Fdiscord&response_type=code&scope=identify`);
|
||||||
|
|
||||||
|
|
|
@ -293,51 +293,6 @@ a {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/***********************************
|
|
||||||
PAGINATION
|
|
||||||
***********************************/
|
|
||||||
.pagination {
|
|
||||||
box-shadow: 0 0 5px 0 var(--box-shadow);
|
|
||||||
margin: 10px auto;
|
|
||||||
padding: 8px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
max-width: 400px;
|
|
||||||
gap: 10px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination .back,
|
|
||||||
.pagination .after {
|
|
||||||
color: var(--second);
|
|
||||||
font-size: 26px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.pagination .numbers {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination .number {
|
|
||||||
color: var(--second);
|
|
||||||
font-size: 22px;
|
|
||||||
border: 0 0 5px var(--second);
|
|
||||||
padding: 8px;
|
|
||||||
border-radius: 2px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
margin: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination .number.active {
|
|
||||||
color: var(--main);
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/************************************
|
/************************************
|
||||||
Threads
|
Threads
|
||||||
|
|
124
src/themes/bootstrap_black/views/admin.ejs
Normal file
124
src/themes/bootstrap_black/views/admin.ejs
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Admin Panel!" }) %>
|
||||||
|
|
||||||
|
|
||||||
|
<body style="text-align: center;">
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
table {
|
||||||
|
font-family: arial, sans-serif;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border: 1px solid #dddddd;
|
||||||
|
text-align: left;
|
||||||
|
padding: 8px;
|
||||||
|
color: var(--anti);
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background-color: #dddddd;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<h2>Welcome to the admin panel of the forum, <%= user.name %>!</h1>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">Banned users</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="document.getElementById('exampleModal').style.display = 'none';">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>IP</th>
|
||||||
|
<th>Reason</th>
|
||||||
|
<th>AuthorID</th>
|
||||||
|
</tr>
|
||||||
|
<% for (const ban of bans) { %>
|
||||||
|
<tr>
|
||||||
|
<td><%=ban.ip%></td>
|
||||||
|
<td><%=ban.reason%></td>
|
||||||
|
<td><%=ban.authorID%></td>
|
||||||
|
</tr>
|
||||||
|
<% } %>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a class="btn-primary" onclick="ban();">IP BAN</a>
|
||||||
|
<a class="btn-outline-primary" onclick="unban();">REMOVE IP BAN</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<button onclick="window.location.href = '/categories/create';" class="btn-primary">Create Category</button>
|
||||||
|
<button onclick="window.location.href = '/admin/config';" class="btn-primary">Edit config</button>
|
||||||
|
|
||||||
|
<button onclick="document.getElementById('exampleModal').style.display = 'block';" class="btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#exampleModal" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
Banned users
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Admin list</th>
|
||||||
|
</tr>
|
||||||
|
<% for (const admin of admins) { %>
|
||||||
|
<tr>
|
||||||
|
<td><a style="color: var(--anti);" href="<%= admin.getLink() %>"><%= admin.name %></a></td>
|
||||||
|
</tr>
|
||||||
|
<% } %>
|
||||||
|
</table>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import request from "../../js/request.js";
|
||||||
|
|
||||||
|
window.unban = async function() {
|
||||||
|
const ip = prompt("Enter ip to unban");
|
||||||
|
if (!ip) return;
|
||||||
|
const response = await request("/api/bans/" + ip, "DELETE");
|
||||||
|
if (response)
|
||||||
|
alert("IP unbanned!");
|
||||||
|
else
|
||||||
|
alert("IP is not unbanned!");
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
window.ban = async function() {
|
||||||
|
const ip = prompt("Enter ip to ban");
|
||||||
|
if (!ip) return;
|
||||||
|
|
||||||
|
const response = await request("/api/bans/" + ip);
|
||||||
|
if (response)
|
||||||
|
alert("IP banned!");
|
||||||
|
else
|
||||||
|
alert("IP is not banned!");
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
74
src/themes/bootstrap_black/views/categories.ejs
Normal file
74
src/themes/bootstrap_black/views/categories.ejs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<%- 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(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||||
|
|
||||||
|
<div class="container my-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<h2 class="h4 text-white bg-info mb-0 p-4 rounded-top"><%= "Categories" %></h2>
|
||||||
|
|
||||||
|
<table class="table table-striped table-bordered table-responsive-lg">
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr>
|
||||||
|
<th scope="col" class="topic-col">Topic</th>
|
||||||
|
<th scope="col">Description</th>
|
||||||
|
<% if (user?.admin){ %> <th scope="col" class="last-post-col">Action</th> <% } %>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% categories.forEach(category=>{ %>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<h3 class="h6">
|
||||||
|
<a href="<%= category.getLink() %>"><%= category.name %></a>
|
||||||
|
</h3>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div><%= category.desp %></div>
|
||||||
|
</td>
|
||||||
|
<% if (user?.admin){ %>
|
||||||
|
<td>
|
||||||
|
<a class="btn-danger" onclick="fetch('/api/categories/<%= category.id %>/',{method:'DELETE'})"><i class="bx bx-trash bx-sm"></i></a>
|
||||||
|
</td>
|
||||||
|
<% } %>
|
||||||
|
</tr>
|
||||||
|
<% }); %>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<% if(typeof page === "number"){ %>
|
||||||
|
<div class="mb-3 clearfix">
|
||||||
|
<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>
|
||||||
|
<% } %>
|
||||||
|
<% for(let i=0; i < pages; i++){ %>
|
||||||
|
<li class="page-item <%= i==page?'active':'' %>"><a href="/categories?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>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% } %>
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
59
src/themes/bootstrap_black/views/create_category.ejs
Normal file
59
src/themes/bootstrap_black/views/create_category.ejs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Create Category!" }) %>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<%- 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>
|
||||||
|
|
||||||
|
<div class="container my-3">
|
||||||
|
<div class="col-12">
|
||||||
|
<h2 class="h4 text-white bg-info mb-3 p-4 rounded">Create new category</h2>
|
||||||
|
<form class="mb-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="topic">Name</label>
|
||||||
|
<input type="text" class="form-control" id="title" placeholder="Give your category a name" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="comment">Description</label>
|
||||||
|
<textarea id="textarea"></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Create</button>
|
||||||
|
<button type="reset" class="btn btn-danger">Reset</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/js/editor.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
const simplemde = editor("category-create");
|
||||||
|
|
||||||
|
import request from "../../js/request.js";
|
||||||
|
|
||||||
|
document.addEventListener("submit", async e => {
|
||||||
|
e.preventDefault();
|
||||||
|
const response = await request("/api/categories/", "POST", {
|
||||||
|
name: document.getElementById("title").value,
|
||||||
|
desp: simplemde.value()
|
||||||
|
});
|
||||||
|
|
||||||
|
simplemde.clearAutosavedValue();
|
||||||
|
|
||||||
|
if (response)
|
||||||
|
window.location.href = "/categories/" + response.id;
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
69
src/themes/bootstrap_black/views/create_thread.ejs
Normal file
69
src/themes/bootstrap_black/views/create_thread.ejs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Create thread!" }) %>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<%- 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>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container my-3">
|
||||||
|
<div class="col-12">
|
||||||
|
<h2 class="h4 text-white bg-info mb-3 p-4 rounded">Create new thread</h2>
|
||||||
|
<form class="mb-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="topic">Title</label>
|
||||||
|
<input type="text" class="form-control" id="title" placeholder="Give your thread a title." required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="comment">Comment:</label>
|
||||||
|
<textarea id="textarea"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-check-label">
|
||||||
|
Category:
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<select id="category" class="input">
|
||||||
|
<% for (const category of categories) { %>
|
||||||
|
<option value="<%= category.id %>"><%= category.name %></option>
|
||||||
|
<% } %>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Create</button>
|
||||||
|
<button type="reset" class="btn btn-danger">Reset</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/js/editor.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
const simplemde = editor("thread-create");
|
||||||
|
|
||||||
|
import request from "../../js/request.js";
|
||||||
|
|
||||||
|
document.addEventListener("submit", async e => {
|
||||||
|
e.preventDefault();
|
||||||
|
const response = await request("/api/threads/", "POST", {
|
||||||
|
title: document.getElementById("title").value,
|
||||||
|
content: simplemde.value(),
|
||||||
|
category: document.getElementById("category").value
|
||||||
|
});
|
||||||
|
if (response) {
|
||||||
|
simplemde.clearAutosavedValue();
|
||||||
|
window.location.href = "/threads/" + response.id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
168
src/themes/bootstrap_black/views/thread.ejs
Normal file
168
src/themes/bootstrap_black/views/thread.ejs
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: thread.title }) %>
|
||||||
|
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<%- 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">
|
||||||
|
<script src="/libs/simplemde/simplemde.min.js"></script>
|
||||||
|
|
||||||
|
<div style="text-align:center;padding:8px">
|
||||||
|
<a href="/categories/<%= thread.categoryID %>" class="title" id="title"><%= thread.title %></a>
|
||||||
|
<div class="date">
|
||||||
|
<%= new Date(thread.time).toLocaleString() %> • Views: <%= thread.views %>
|
||||||
|
</div>
|
||||||
|
<div class="date">
|
||||||
|
<a style="color: var(--anti);" href="/users/<%= thread.author.id %>"><%= thread.author.name %></a> <%= "• "+(thread.edited ? "Edited" : "Not edited")%>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="text-align:center;padding:8px">
|
||||||
|
<% if (user && (user.id === thread.authorID || user.admin ) && !thread.deleted){ %>
|
||||||
|
<a onclick="delete_thread('<%= thread.id %>')" class="btn-outline-primary">DELETE</a>
|
||||||
|
<a onclick="edit_thread('<%= thread.id %>')" class="btn-outline-primary">EDIT</a>
|
||||||
|
<% } else if (thread.deleted) { %>
|
||||||
|
<h4 class="title" style="display:inline; font-size: 20px;">This thread has been deleted</h3>
|
||||||
|
<a onclick="undelete_thread('<%= thread.id %>')" class="btn-primary">UNDELETE</a>
|
||||||
|
<% }; %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="messages">
|
||||||
|
|
||||||
|
<% messages.filter(Boolean).forEach(message=>{ %>
|
||||||
|
|
||||||
|
<div class="message" id="message-<%= message.id %>">
|
||||||
|
<div class="left">
|
||||||
|
<img src="<%= message.author.avatar %>" />
|
||||||
|
<div class="username"><a href="/users/<%=message.author.id %>"><%=message.author.name %></a></div>
|
||||||
|
<div class="date">
|
||||||
|
<%= new Date(message.time).toLocaleDateString() %>
|
||||||
|
</div>
|
||||||
|
<div class="date">
|
||||||
|
<%= new Date(message.time).toLocaleTimeString() %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content"><%= message.content %></div>
|
||||||
|
<% if(user){ %>
|
||||||
|
<% if(user.id === message.authorID || user.admin){ %>
|
||||||
|
|
||||||
|
<div class="dots" modal="#modal-<%=message.id %>">
|
||||||
|
<% if (message.deleted){ %>
|
||||||
|
<i class='bx bx-trash bx-sm' id="deleted" style="color: var(--important);"></i>
|
||||||
|
<% } %>
|
||||||
|
<% if (message.edited){ %>
|
||||||
|
<i class='bx bx-pencil bx-sm' id="edited" style="color: GREEN;"></i>
|
||||||
|
<% } %>
|
||||||
|
<i class='bx bx-dots-horizontal-rounded'></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dots-menu" id="modal-<%=message.id %>">
|
||||||
|
<% if (!message.deleted){ %>
|
||||||
|
<a onclick="delete_message('<%=message.id %>');">Delete</a>
|
||||||
|
<a onclick="edit_message('<%=message.id %>');">Edit</a>
|
||||||
|
<% }else { %>
|
||||||
|
<a onclick="undelete_message('<%=message.id %>');">Undelete</a>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<div class="reactions">
|
||||||
|
<div <% if (message.react.like.includes(user?.id)) { %> style="color: var(--main)" <% } %>>
|
||||||
|
<i onclick='react("<%= message.id %>","like");' class='bx bx-like'></i>
|
||||||
|
<div id="like"><%=message.react.like.length %></div>
|
||||||
|
</div>
|
||||||
|
<div <% if (message.react.dislike.includes(user?.id)) { %> style="color: var(--main)" <% } %>>
|
||||||
|
<i onclick='react("<%= message.id %>","dislike");' class='bx bx-dislike'></i>
|
||||||
|
<div id="dislike"><%=message.react.dislike.length %></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% }; %>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% }); %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const converter = new showdown.Converter();
|
||||||
|
for (const message of document.querySelectorAll(".message")) {
|
||||||
|
const content = message.querySelector(".content");
|
||||||
|
content.innerHTML = converter.makeHtml(content.rawText = content.innerHTML);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="/js/modal.js"></script>
|
||||||
|
<% if (user){ %>
|
||||||
|
<script type="module" src="/js/thread.js"></script>
|
||||||
|
|
||||||
|
<div class="message" id="send-div">
|
||||||
|
|
||||||
|
<form id="send" style="width:100%">
|
||||||
|
<textarea rows="4" id="textarea"></textarea>
|
||||||
|
<input name="page" type="hidden" value="<%= page %>"></input>
|
||||||
|
<button class="btn-primary">Send!</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/js/editor.js"></script>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
const simplemde = editor("thread-<%= thread.id %>");
|
||||||
|
|
||||||
|
import request from "../../js/request.js";
|
||||||
|
|
||||||
|
document.getElementById("send").addEventListener("submit", async e => {
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
const res = await request("/api/messages", "POST", {
|
||||||
|
threadID: "<%= thread.id %>",
|
||||||
|
content: simplemde.value()
|
||||||
|
})
|
||||||
|
simplemde.clearAutosavedValue();
|
||||||
|
let tp = Number("<%= thread.pages %>")
|
||||||
|
let tm = Number("<%= thread.count %>")
|
||||||
|
if (tp * 10 === tm) tp++;
|
||||||
|
if (res) location.href = `/threads/<%= thread.id %>?page=${tp-1}`;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.fa {
|
||||||
|
color: var(--main);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<% }%>
|
||||||
|
|
||||||
|
<div class="mb-3 clearfix">
|
||||||
|
<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="/<%= thread.getLink() %>?page=<%= page-1 %>" class="page-link">Back</a></li>
|
||||||
|
<% } %>
|
||||||
|
<% for(let i=0; i < thread.pages; i++){ %>
|
||||||
|
<li class="page-item <%= i==page?'active':'' %>"><a href="/<%= thread.getLink() %>?page=<%= i %>" class="page-link"><%= i+1 %>
|
||||||
|
<% if (i==page){ %>
|
||||||
|
<span class="sr-only">(current)</span>
|
||||||
|
<% } %>
|
||||||
|
</a></li>
|
||||||
|
<% } %>
|
||||||
|
<% if (thread.pages-1 > page) { %>
|
||||||
|
<li class="page-item"><a href="/<%= thread.getLink() %>?page=<%= page+1 %>" class="page-link">Next</a></li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
83
src/themes/bootstrap_black/views/threads.ejs
Normal file
83
src/themes/bootstrap_black/views/threads.ejs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Thread list!" }) %>
|
||||||
|
|
||||||
|
<body style="text-align: center;">
|
||||||
|
<link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'>
|
||||||
|
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container my-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<h2 class="h4 text-white bg-info mb-0 p-4 rounded-top"><%= title || "Threads" %></h2>
|
||||||
|
<h3 class="h6 text-white bg-info mb-0 p-4 rounded-top"><%= desp %></h2>
|
||||||
|
|
||||||
|
<table class="table table-striped table-bordered table-responsive-lg">
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<% threads.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> </div>
|
||||||
|
<div class="avatar">by <a href="<%= thread.getLink() %>"><%= 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>
|
||||||
|
<div class="mb-3 clearfix">
|
||||||
|
<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="/threads?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="/threads?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="/threads?page=<%= page+1 %>" class="page-link">Next</a></li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<a href="/threads/create" class="btn btn-lg btn-primary">New Thread</a>
|
||||||
|
</div>
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
51
src/themes/bootstrap_black/views/users.ejs
Normal file
51
src/themes/bootstrap_black/views/users.ejs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<%- 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(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;">
|
||||||
|
<div class="user-box">
|
||||||
|
<img src="<%= member.avatar %>" class="user-box-img">
|
||||||
|
<div class="user-box-title"> <a href="<%= member.getLink() %>">
|
||||||
|
<% if (member.deleted) { %> <span style="color: var(--important);">[DELETED]</span><% } %>
|
||||||
|
<%= member.name %></a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% }); %>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<% if(typeof page === "number"){ %>
|
||||||
|
<div class="mb-3 clearfix">
|
||||||
|
<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>
|
||||||
|
<% } %>
|
||||||
|
<% for(let i=0; i < pages; i++){ %>
|
||||||
|
<li class="page-item <%= i==page?'active':'' %>"><a href="/categories?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>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% } %>
|
||||||
|
<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Reference in a new issue