feat: init 🎉

Harmony starts here
Blank slate for now; bye bye styles
Let us now begin.
This commit is contained in:
Julian Lam 2022-11-25 11:59:07 -05:00
commit 5f176cfeaa
137 changed files with 6330 additions and 0 deletions

3
.eslintrc Normal file
View file

@ -0,0 +1,3 @@
{
"extends": "nodebb"
}

9
.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
*.css
!less/bootstrap-flipped.css
npm-debug.log
sftp-config.json
*.sublime-project
*.sublime-workspace
.idea
.vscode
node_modules/

6
.npmignore Normal file
View file

@ -0,0 +1,6 @@
*.css
!less/bootstrap-flipped.css
npm-debug.log
sftp-config.json
*.sublime-project
*.sublime-workspace

8
README.md Normal file
View file

@ -0,0 +1,8 @@
Harmony theme for NodeBB
====================

The Harmony theme is the default theme for NodeBB for versions spanning v3.0.0 onwards.

## Issues

Issues are tracked in [the main project issue tracker](https://github.com/NodeBB/NodeBB/issues?q=is%3Aopen+is%3Aissue+label%3Athemes).

7
lib/controllers.js Normal file
View file

@ -0,0 +1,7 @@
'use strict';

const Controllers = module.exports;

Controllers.renderAdminPage = (req, res) => {
res.render('admin/plugins/harmony', {});
};

51
library.js Normal file
View file

@ -0,0 +1,51 @@
'use strict';

const controllers = require('./lib/controllers');

const library = module.exports;

library.init = async function (params) {
const { router } = params;
const routeHelpers = require.main.require('./src/routes/helpers');

routeHelpers.setupAdminPageRoute(router, '/admin/plugins/harmony', [], controllers.renderAdminPage);
};

library.addAdminNavigation = async function (header) {
header.plugins.push({
route: '/plugins/harmony',
icon: 'fa-paint-brush',
name: 'Harmony Theme',
});
return header;
};

library.defineWidgetAreas = async function (areas) {
// const locations = ['header', 'sidebar', 'footer'];
// const templates = [
// 'categories.tpl', 'category.tpl', 'topic.tpl', 'users.tpl',
// 'unread.tpl', 'recent.tpl', 'popular.tpl', 'top.tpl', 'tags.tpl', 'tag.tpl',
// 'login.tpl', 'register.tpl',
// ];
// function capitalizeFirst(str) {
// return str.charAt(0).toUpperCase() + str.slice(1);
// }
// templates.forEach((template) => {
// locations.forEach((location) => {
// areas.push({
// name: `${capitalizeFirst(template.split('.')[0])} ${capitalizeFirst(location)}`,
// template: template,
// location: location,
// });
// });
// });

// areas = areas.concat([
// {
// name: 'Account Header',
// template: 'account/profile.tpl',
// location: 'header',
// },
// ]);
return areas;
};

45
package.json Normal file
View file

@ -0,0 +1,45 @@
{
"name": "nodebb-theme-harmony",
"version": "0.0.0",
"nbbpm": {
"compatibility": "^3.0.0"
},
"description": "Harmony theme for NodeBB",
"main": "library.js",
"repository": {
"type": "git",
"url": "https://github.com/NodeBB/nodebb-theme-harmony"
},
"scripts": {
"lint": "eslint ."
},
"keywords": [
"nodebb",
"theme",
"forum",
"bootstrap",
"responsive"
],
"contributors": [
{
"name": "Julian Lam",
"email": "julian@nodebb.org",
"url": "https://github.com/julianlam"
},
{
"name": "Barış Soner Uşaklı",
"email": "baris@nodebb.org",
"url": "https://github.com/barisusakli"
}
],
"license": "MIT",
"bugs": {
"url": "https://github.com/NodeBB/nodebb-theme-harmony/issues"
},
"dependencies": {},
"devDependencies": {
"eslint": "^7.32.0",
"eslint-config-nodebb": "^0.0.2",
"eslint-plugin-import": "^2.24.2"
}
}

11
plugin.json Normal file
View file

@ -0,0 +1,11 @@
{
"id": "nodebb-theme-harmony",
"hooks": [
{ "hook": "static:app.load", "method": "init" },
{ "hook": "filter:admin.header.build", "method": "addAdminNavigation" },
{ "hook": "filter:widgets.getAreas", "method": "defineWidgetAreas" }
],
"modules": {
"../admin/plugins/harmony.js": "public/admin.js"
}
}

3
public/.eslintrc Normal file
View file

@ -0,0 +1,3 @@
{
"extends": "nodebb/public"
}

15
public/admin.js Normal file
View file

@ -0,0 +1,15 @@
'use strict';

define('admin/plugins/harmony', ['settings'], function (Settings) {
var ACP = {};

ACP.init = function () {
Settings.load('harmony', $('.harmony-settings'));

$('#save').on('click', function () {
Settings.save('harmony', $('.harmony-settings'));
});
};

return ACP;
});

3
scss/harmony.scss Normal file
View file

@ -0,0 +1,3 @@
// @import "variables";
// @import "style";
// @import "mixins";

1
scss/overrides.scss Normal file
View file

@ -0,0 +1 @@
// only overrides to bs5 variables here

View file

@ -0,0 +1 @@
<!-- IMPORT account/posts.tpl -->

View file

@ -0,0 +1,33 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->
<h1>[[pages:account/blocks, {username}]]</h1>
<div class="row justify-content-end mb-2">
<div class="col-3">
<div class="dropdown">
<input class="form-control" type="text" id="user-search" placeholder="[[users:enter_username]]" data-bs-toggle="dropdown" autocomplete="off"/>

<ul class="dropdown-menu block-edit list-unstyled">
<li><a href="#" class="dropdown-item">[[admin/menu:search.start-typing]]</a></li>
{{{ each edit }}}
<li class="">
<div class="dropdown-item d-flex flex-nowrap gap-2 justify-content-between">
<div class="text-truncate">
<a href="{config.relative_path}/uid/{../uid}">{buildAvatar(edit, "24px", true)} {../username}</a>
</div>
<button class="btn btn-sm btn-primary text-nowrap" data-uid="{../uid}" data-action="toggle">[[user:block_toggle]]</button>
</div>
</li>
{{{ end }}}
</ul>
</div>
</div>
</div>

<div class="users row">
<div class="col-12">
<!-- IMPORT partials/users_list.tpl -->
<div class="alert alert-warning text-center"<!-- IF users.length --> style="display: none;"<!-- END -->>[[user:has_no_blocks]]</div>
<!-- IMPORT partials/paginator.tpl -->
</div>
</div>
</div>

View file

@ -0,0 +1 @@
<!-- IMPORT account/posts.tpl -->

View file

@ -0,0 +1,28 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->

<div class="row">
<h1>{title}</h1>
<div class="col-lg-12 mb-2">
<div class="btn-group bottom-sheet" component="category/watch/all">
<button class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" type="button">
<span>[[user:change_all]]</span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" component="category/watching" data-state="watching"><i class="fa fa-fw fa-inbox"></i> [[category:watching]]<p class="help-text"><small>[[category:watching.description]]</small></p></a></li>
<li><a class="dropdown-item" href="#" component="category/notwatching" data-state="notwatching"><i class="fa fa-fw fa-clock-o"></i> [[category:not-watching]]<p class="help-text"><small>[[category:not-watching.description]]</small></p></a></li>
<li><a class="dropdown-item" href="#" component="category/ignoring" data-state="ignoring"><i class="fa fa-fw fa-eye-slash"></i> [[category:ignoring]]<p class="help-text"><small>[[category:ignoring.description]]</small></p></a></li>
</ul>
</div>
</div>
<div class="col-lg-12">
<ul class="categories list-unstyled" itemscope itemtype="http://www.schema.org/ItemList">
{{{each categories}}}
<!-- IMPORT partials/account/category-item.tpl -->
{{{end}}}
</ul>
<!-- IMPORT partials/paginator.tpl -->
</div>
</div>
</div>

View file

@ -0,0 +1,69 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->
<h2>[[user:consent.title]]</h2>
<p class="lead">[[user:consent.lead]]</p>
<p>[[user:consent.intro]]</p>

<hr />

<div class="row">
<div class="col-sm-6">
<!-- IF gdpr_consent -->
<div class="alert alert-success">
<i class="fa fa-check float-end fa-3x"></i>
[[user:consent.received]]
</div>
<!-- ELSE -->
<div class="alert alert-warning">
[[user:consent.not_received]]
<br /><br />
<div class="text-center">
<button class="btn btn-warning" data-action="consent">[[user:consent.give]]</button>
</div>
</div>
<!-- END -->
<div class="card">
<div class="card-body">
<p>[[user:consent.email_intro]]</p>
<!-- IF digest.enabled -->
<p>[[user:consent.digest_frequency, {digest.frequency}]]</p>
<!-- ELSE -->
[[user:consent.digest_off]]
<!-- END -->

<div class="text-center">
<a class="btn btn-outline-secondary" href="./settings">
<i class="fa fa-cog"></i>
[[pages:account/settings]]
</a>
</div>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="card">
<div class="card-body">
<p><strong>[[user:consent.right_of_access]]</strong></p>
<p>[[user:consent.right_of_access_description]]</p>
<p><strong>[[user:consent.right_to_rectification]]</strong></p>
<p>[[user:consent.right_to_rectification_description]]</p>
<p><strong>[[user:consent.right_to_erasure]]</strong></p>
<p>[[user:consent.right_to_erasure_description]]</p>
<p><strong>[[user:consent.right_to_data_portability]]</strong></p>
<p>[[user:consent.right_to_data_portability_description]]</p>

<div class="btn-group-vertical d-grid">
<a data-action="export-profile" class="btn btn-outline-secondary">
<i class="fa fa-download"></i> [[user:consent.export_profile]]
</a>
<a data-action="export-posts" class="btn btn-outline-secondary">
<i class="fa fa-download"></i> [[user:consent.export_posts]]
</a>
<a data-action="export-uploads" class="btn btn-outline-secondary">
<i class="fa fa-download"></i> [[user:consent.export_uploads]]
</a>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1 @@
<!-- IMPORT account/posts.tpl -->

View file

@ -0,0 +1 @@
<!-- IMPORT account/posts.tpl -->

135
templates/account/edit.tpl Normal file
View file

@ -0,0 +1,135 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->
<!-- IF sso.length --><div><!-- ENDIF sso.length -->
<div class="row">
<div class="col-md-3 col-sm-4">
<div class="account-picture-block text-center">
<div class="row mb-3">
<div class="col-12 hidden-xs">
<!-- IF picture -->
<img id="user-current-picture" class="avatar avatar-rounded" style="--avatar-size: 128px;" src="{picture}" />
<!-- ELSE -->
<div class="avatar avatar-rounded" style="background-color: {icon:bgColor}; --avatar-size: 128px;">{icon:text}</div>
<!-- ENDIF picture -->
</div>
</div>
<ul class="list-group mb-3">
<!-- IF allowProfilePicture -->
<a id="changePictureBtn" href="#" class="list-group-item">[[user:change_picture]]</a>
<!-- ENDIF allowProfilePicture -->
<!-- IF !username:disableEdit -->
<a href="{config.relative_path}/user/{userslug}/edit/username" class="list-group-item">[[user:change_username]]</a>
<!-- ENDIF !username:disableEdit -->
<!-- IF !email:disableEdit -->
<a href="{config.relative_path}/user/{userslug}/edit/email" class="list-group-item">[[user:change_email]]</a>
<!-- ENDIF !email:disableEdit -->
<!-- IF canChangePassword -->
<a href="{config.relative_path}/user/{userslug}/edit/password" class="list-group-item">[[user:change_password]]</a>
<!-- ENDIF canChangePassword -->
{{{each editButtons}}}
<a href="{config.relative_path}{editButtons.link}" class="list-group-item">{editButtons.text}</a>
{{{end}}}
</ul>

<!-- IF config.requireEmailConfirmation -->
<!-- IF email -->
<!-- IF isSelf -->
<a id="confirm-email" href="#" class="btn btn-warning <!-- IF email:confirmed -->hide<!-- ENDIF email:confirmed -->">[[user:confirm_email]]</a><br/><br/>
<!-- ENDIF isSelf -->
<!-- ENDIF email -->
<!-- ENDIF config.requireEmailConfirmation -->

<!-- IF allowAccountDelete -->
<!-- IF isSelf -->
<a id="deleteAccountBtn" href="#" class="btn btn-danger">[[user:delete_account]]</a><br/><br/>
<!-- ENDIF isSelf -->
<!-- ENDIF allowAccountDelete -->

</div>
</div>

<div class="<!-- IF !sso.length -->col-md-9 col-sm-8<!-- ELSE -->col-md-5 col-sm-4<!-- ENDIF !sso.length -->">
<form role="form" component="profile/edit/form">
<div class="mb-2">
<label class="form-label fw-bold" for="fullname">[[user:fullname]]</label>
<input class="form-control" type="text" id="fullname" name="fullname" placeholder="[[user:fullname]]" value="{fullname}">
</div>
<!-- IF allowWebsite -->
<div class="mb-2">
<label class="form-label fw-bold" for="website">[[user:website]]</label>
<input class="form-control" type="text" id="website" name="website" placeholder="http://..." value="{website}">
</div>
<!-- ENDIF allowWebsite -->

<div class="mb-2">
<label class="form-label fw-bold" for="location">[[user:location]]</label>
<input class="form-control" type="text" id="location" name="location" placeholder="[[user:location]]" value="{location}">
</div>

<div class="mb-2">
<label class="form-label fw-bold" for="birthday">[[user:birthday]]</label>
<input class="form-control" type="date" id="birthday" name="birthday" value="{birthday}" placeholder="mm/dd/yyyy">
</div>

<div class="mb-2">
<label class="form-label fw-bold" for="groupTitle">[[user:grouptitle]]</label>

<select class="form-select" id="groupTitle" name="groupTitle" <!-- IF allowMultipleBadges --> size="{groupSelectSize}" multiple<!-- ENDIF allowMultipleBadges -->>
<option value="">[[user:no-group-title]]</option>
{{{each groups}}}
<!-- IF groups.userTitleEnabled -->
<option value="{groups.displayName}" <!-- IF groups.selected -->selected<!-- ENDIF groups.selected -->>{groups.userTitle}</option>
<!-- ENDIF groups.userTitleEnabled -->
{{{end}}}
</select>
<!-- IF allowMultipleBadges -->
<span>[[user:group-order-help]]</span>
<i role="button" component="group/order/up" class="fa fa-chevron-up"></i> <i role="button" component="group/order/down" class="fa fa-chevron-down"></i>
<!-- ENDIF -->
</div>

<!-- IF allowAboutMe -->
<div class="mb-2">
<label class="form-label fw-bold" for="aboutme">[[user:aboutme]]</label> <small><label id="aboutMeCharCountLeft"></label></small>
<textarea class="form-control" id="aboutme" name="aboutme" rows="5">{aboutme}</textarea>
</div>
<!-- ENDIF allowAboutMe -->

<!-- IF allowSignature -->
<!-- IF !disableSignatures -->
<div class="mb-2">
<label class="form-label fw-bold" for="signature">[[user:signature]]</label> <small><label id="signatureCharCountLeft"></label></small>
<textarea class="form-control" id="signature" name="signature" rows="5">{signature}</textarea>
</div>
<!-- ENDIF !disableSignatures -->
<!-- ENDIF allowSignature -->

<a id="submitBtn" href="#" class="btn btn-primary">[[global:save_changes]]</a>
</form>

<hr class="visible-xs visible-sm"/>
</div>

<!-- IF sso.length -->
<div class="col-md-4 col-sm-4">
<label>[[user:sso.title]]</label>
<div class="list-group">
{{{each sso}}}
<div class="list-group-item">
<!-- IF ../deauthUrl -->
<a data-component="{../component}" class="btn btn-outline-secondary btn-sm float-end" href="{../deauthUrl}">[[user:sso.dissociate]]</a>
<!-- END -->
<a data-component="{../component}" href="{../url}" target="<!-- IF ../associated -->_blank<!-- ELSE -->_top<!-- ENDIF ../associated -->">
<!-- IF ../icon --><i class="fa {../icon}"></i><!-- ENDIF ../icon -->
<!-- IF ../associated -->[[user:sso.associated]]<!-- ELSE -->[[user:sso.not-associated]]<!-- ENDIF ../associated -->
{../name}
</a>
</div>
{{{end}}}
</div>
</div>
<!-- ENDIF sso.length -->
</div>
<!-- IF sso.length --></div><!-- ENDIF sso.length -->
</div>

View file

@ -0,0 +1,30 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->

<form class="edit-form">
<!-- disables autocomplete on FF --><input type="password" style="display:none">

<!-- IF isSelf -->
<div class="mb-2">
<label class="form-label fw-bold" for="inputCurrentPassword">[[user:current_password]]</label>
<input autocomplete="off" class="form-control" type="password" id="inputCurrentPassword" placeholder="[[user:current_password]]" value=""<!-- IF !hasPassword --> disabled<!-- ENDIF !hasPassword -->>
</div>
<!-- ENDIF isSelf -->

<div class="mb-2">
<label class="form-label fw-bold" for="inputNewPassword">[[user:new_password]]</label>
<input class="form-control" type="password" id="inputNewPassword" placeholder="[[user:password]]" value="">
<span class="form-feedback" id="password-notify"></span>
</div>

<div class="mb-2">
<label class="form-label fw-bold" for="inputNewPasswordAgain">[[user:confirm_password]]</label>
<input class="form-control" type="password" id="inputNewPasswordAgain" placeholder="[[user:confirm_password]]" value="">
<span class="form-feedback" id="password-confirm-notify"></span>
</div>

<div class="form-actions">
<button id="changePasswordBtn" class="btn btn-primary btn-block"><i class="hide fa fa-spinner fa-spin"></i> [[user:change_password]]</button>
</div>
</form>
</div>

View file

@ -0,0 +1,26 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->

<form class="form-horizontal edit-form">
<div class="mb-2">
<label class="form-label fw-bold" for="inputNewUsername">[[user:username]]</label>
<input class="form-control" type="text" id="inputNewUsername" placeholder="[[user:username]]" value="{username}">
</div>

<!-- disables autocomplete on FF --><input type="password" style="display:none">

<!-- IF isSelf -->
<div class="mb-2">
<label class="form-label fw-bold" for="inputCurrentPassword">[[user:current_password]]</label>
<input autocomplete="off" class="form-control" type="password" id="inputCurrentPassword" placeholder="[[user:current_password]]" value=""<!-- IF !hasPassword --> disabled<!-- ENDIF !hasPassword -->>
</div>
<!-- ENDIF isSelf -->

<input type="hidden" name="uid" id="inputUID" value="{uid}" />

<br/>
<div class="form-actions">
<button id="submitBtn" class="btn btn-primary btn-block"><i class="hide fa fa-spinner fa-spin"></i> [[user:change_username]]</button>
</div>
</form>
</div>

View file

@ -0,0 +1,17 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->

<div class="users row">
<div class="col-12">
<h1>[[pages:{template.name}, {username}]]</h1>

<!-- IMPORT partials/users_list.tpl -->

<!-- IF !users.length -->
<div class="alert alert-warning text-center">[[user:has_no_follower]]</div>
<!-- ENDIF !users.length -->

<!-- IMPORT partials/paginator.tpl -->
</div>
</div>
</div>

View file

@ -0,0 +1,17 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->

<div class="users row">
<div class="col-12">
<h1>[[pages:{template.name}, {username}]]</h1>

<!-- IMPORT partials/users_list.tpl -->

<!-- IF !users.length -->
<div class="alert alert-warning text-center">[[user:follows_no_one]]</div>
<!-- ENDIF !users.length -->

<!-- IMPORT partials/paginator.tpl -->
</div>
</div>
</div>

View file

@ -0,0 +1,17 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->

<div class="row">
<h1>[[pages:{template.name}, {username}]]</h1>

<div class="col-12 groups list">
<div component="groups/container" id="groups-list" class="row">
<!-- IF !groups.length -->
<div class="alert alert-warning text-center">[[groups:no_groups_found]]</div>
<!-- ELSE -->
<!-- IMPORT partials/groups/list.tpl -->
<!-- ENDIF !groups.length -->
</div>
</div>
</div>
</div>

View file

@ -0,0 +1 @@
<!-- IMPORT account/topics.tpl -->

226
templates/account/info.tpl Normal file
View file

@ -0,0 +1,226 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->

<!-- IF sessions.length -->
<div class="row mb-3">
<div class="col-12 col-md-12">
<h4>[[global:sessions]]</h4>
<ul class="list-group" component="user/sessions">
{{{each sessions}}}
<li class="list-group-item" data-uuid="{../uuid}">
<div class="float-end">
<!-- IF isSelfOrAdminOrGlobalModerator -->
<!-- IF !../current -->
<button class="btn btn-sm btn-outline-secondary" type="button" data-action="revokeSession">Revoke Session</button>
<!-- ENDIF !../current -->
<!-- ENDIF isSelfOrAdminOrGlobalModerator -->
{function.userAgentIcons}
<i class="fa fa-circle text-<!-- IF ../current -->success<!-- ELSE -->muted<!-- ENDIF ../current -->"></i>
</div>
{../browser} {../version} on {../platform}<br />
<small class="timeago text-muted" title="{../datetimeISO}"></small>
<ul>
<li><strong>[[global:ip_address]]</strong>: {../ip}</li>
</ul>
</li>
{{{end}}}
</ul>
</div>
</div>
<!-- ENDIF sessions.length -->

<div class="row">
<div class="col-sm-6">
<div class="card mb-3">
<h5 class="card-header">
[[global:recentips]]
</h5>
<div class="card-body">
<ul>
{{{each ips}}}
<li>{@value}</li>
{{{end}}}
</ul>
</div>
</div>

<div class="card mb-3">
<h5 class="card-header">
[[user:info.username-history]]
</h5>
<div class="card-body">
<ul class="list-group">
{{{each usernames}}}
<li class="list-group-item">
{../value}
<small class="float-end"><span class="timeago" title="{../timestampISO}"></span></small>
</li>
{{{end}}}
</ul>
</div>
</div>

<div class="card">
<h5 class="card-header">
[[user:info.email-history]]
</h5>
<div class="card-body">
<ul class="list-group">
{{{each emails}}}
<li class="list-group-item">
{../value}
<small class="float-end"><span class="timeago" title="{../timestampISO}"></span></small>
</li>
{{{end}}}
</ul>
</div>
</div>
</div>
<div class="col-sm-6">
<div class="card mb-3">
<h5 class="card-header">
[[user:info.latest-flags]]
</h5>
<div class="card-body">
<!-- IF history.flags.length -->
<ul class="recent-flags list-unstyled">
{{{each history.flags}}}
<li>
<p>
{{{ if history.flags.targetPurged }}}
<div>[[flags:target-purged]]</div>
{{{ else }}}
<a class="title" href="{config.relative_path}/post/{../pid}">{../title}</a><br />
{{{ end }}}
<span class="timestamp">[[flags:flagged-timeago-readable, {../timestampISO}, {../timestampReadable}]]</span>
</p>
</li>
{{{end}}}
</ul>
<!-- ELSE -->
<div class="alert alert-success">[[user:info.no-flags]]</div>
<!-- ENDIF history.flags.length -->
</div>
</div>

<div class="card mb-3">
<h5 class="card-header">
[[user:info.ban-history]]

<!-- IF !banned -->
<!-- IF !isSelf -->
<button class="btn btn-sm float-end btn-danger" component="account/ban">[[user:ban_account]]</button>
<!-- ENDIF !isSelf -->
<!-- ELSE -->
<!-- IF !isSelf -->
<button class="btn btn-sm float-end btn-success" component="account/unban">[[user:unban_account]]</button>
<!-- ENDIF !isSelf -->
<!-- ENDIF !banned -->
</h5>
<div class="card-body">
<!-- IF history.bans.length -->
<ul class="ban-history list-unstyled">
{{{each history.bans}}}
<li>
<p>
<a href="{config.relative_path}/user/{history.bans.user.userslug}">{buildAvatar(history.bans.user, "24px", true)}</a>
<strong>
<a href="<!-- IF history.bans.user.userslug -->{config.relative_path}/user/{history.bans.user.userslug}<!-- ELSE -->#<!-- ENDIF history.bans.user.userslug -->" itemprop="author" data-username="{history.bans.user.username}" data-uid="{history.bans.user.uid}">{history.bans.user.username}</a>
</strong>
<span class="timestamp timeago" title="{../timestampISO}"></span> &mdash; {../timestampReadable}<br />
<!-- IF ../until -->
<span class="expiry">[[user:info.banned-until, {../untilReadable}]]</span><br />
<!-- ELSE -->
<span class="expiry">[[user:info.banned-permanently]]</span><br />
<!-- ENDIF ../until -->
<span class="reason"><strong>[[user:info.banned-reason-label]]</strong>: {../reason}</span>
</p>
</li>
{{{end}}}
</ul>
<!-- ELSE -->
<div class="alert alert-success">[[user:info.no-ban-history]]</div>
<!-- ENDIF history.bans.length -->
</div>
</div>

<div class="card mb-3">
<h5 class="card-header">
[[user:info.mute-history]]

{{{ if !muted }}}
{{{ if !isSelf }}}
<button class="btn btn-sm float-end btn-danger" component="account/mute">[[user:mute_account]]</button>
{{{ end }}}
{{{ else }}}
{{{ if !isSelf }}}
<button class="btn btn-sm float-end btn-success" component="account/unmute">[[user:unmute_account]]</button>
{{{ end }}}
{{{ end }}}
</h5>
<div class="card-body">
{{{ if history.mutes.length }}}
<ul class="ban-history list-unstyled">
{{{ each history.mutes }}}
<li>
<p>
<a href="{config.relative_path}/user/{history.mutes.user.userslug}">{buildAvatar(history.mutes.user, "24px", true)}</a>
<strong>
<a href="<!-- IF history.mutes.user.userslug -->{config.relative_path}/user/{history.mutes.user.userslug}<!-- ELSE -->#<!-- ENDIF history.mutes.user.userslug -->" itemprop="author" data-username="{history.mutes.user.username}" data-uid="{history.mutes.user.uid}">{history.mutes.user.username}</a>
</strong>
<span class="timestamp timeago" title="{../timestampISO}"></span> &mdash; {../timestampReadable}<br />
{{{ if ../until }}}
<span class="expiry">[[user:info.muted-until, {../untilReadable}]]</span><br />
{{{ end }}}

<span class="reason"><strong>[[user:info.banned-reason-label]]</strong>: {../reason}</span>
</p>
</li>
{{{end}}}
</ul>
{{{ else }}}
<div class="alert alert-success">[[user:info.no-mute-history]]</div>
{{{ end }}}
</div>
</div>

<!-- IF isAdminOrGlobalModerator -->
<div class="card">
<h5 class="card-header">
[[user:info.moderation-note]]
</h5>
<div class="card-body">
<textarea component="account/moderation-note" class="form-control"></textarea>
<br/>
<button class="btn btn-sm float-end btn-success" component="account/save-moderation-note">[[user:info.moderation-note.add]]</button>
<br/>
<div component="account/moderation-note/list">
{{{each moderationNotes}}}
<hr/>

<div class="clearfix">
<div class="float-start">
<a href="<!-- IF moderationNotes.user.userslug -->{config.relative_path}/user/{moderationNotes.user.userslug}<!-- ELSE -->#<!-- ENDIF moderationNotes.user.userslug -->">{buildAvatar(moderationNotes.user, "24px", true)}</a>
<strong>
<a href="<!-- IF moderationNotes.user.userslug -->{config.relative_path}/user/{moderationNotes.user.userslug}<!-- ELSE -->#<!-- ENDIF moderationNotes.user.userslug -->" itemprop="author" data-username="{moderationNotes.user.username}" data-uid="{moderationNotes.user.uid}">{moderationNotes.user.username}</a>
</strong>

<div class="visible-xs-inline-block visible-sm-inline-block visible-md-inline-block visible-lg-inline-block">
<span class="timeago" title="{moderationNotes.timestampISO}"></span>
</div>
<br />

<div class="content">
{moderationNotes.note}
</div>
</div>
</div>
{{{end}}}
</div>
<!-- IMPORT partials/paginator.tpl -->
</div>
</div>
<!-- ENDIF isAdminOrGlobalModerator -->
</div>
</div>
</div>

View file

@ -0,0 +1,19 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->

<div class="row">
<h1>{title}</h1>

<!-- IF !posts.length -->
<div class="alert alert-warning text-center">{noItemsFoundKey}</div>
<!-- ENDIF !posts.length -->

<div class="col-12">
<!-- IMPORT partials/posts_list.tpl -->

<!-- IF config.usePagination -->
<!-- IMPORT partials/paginator.tpl -->
<!-- ENDIF config.usePagination -->
</div>
</div>
</div>

View file

@ -0,0 +1,166 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->

<div class="profile row">
<h1 class="fullname"><!-- IF fullname -->{fullname}<!-- ELSE -->{username}<!-- ENDIF fullname --></h1>
<h2 class="username"><!-- IF !banned -->@{username}<!-- ELSE -->[[user:banned]]<!-- ENDIF !banned --></h2>
<!-- IF isAdminOrGlobalModeratorOrModerator -->
<!-- IF banned -->
<div class="text-center">
<!-- IF banned_until -->
[[user:info.banned-until, {banned_until_readable}]]
<!-- ELSE -->
[[user:info.banned-permanently]]
<!-- ENDIF banned_until -->
</div>
<!-- ENDIF banned -->
<!-- ENDIF isAdminOrGlobalModeratorOrModerator -->

<!-- IF selectedGroup.length -->
<div class="text-center">
{{{each selectedGroup}}}
<!-- IF selectedGroup.slug -->
<!-- IMPORT partials/groups/badge.tpl -->
<!-- ENDIF selectedGroup.slug -->
{{{end}}}
</div>
<br/>
<!-- ENDIF selectedGroup.length -->

<!-- IF aboutme -->
<span component="aboutme" class="text-center aboutme">{aboutmeParsed}</span>
<!-- ENDIF aboutme -->

<div class="account-stats">
<!-- IF !reputation:disabled -->
<div class="stat">
<div class="human-readable-number" title="{reputation}">{reputation}</div>
<span class="stat-label">[[global:reputation]]</span>
</div>
<!-- ENDIF !reputation:disabled -->

<div class="stat">
<div class="human-readable-number" title="{profileviews}">{profileviews}</div>
<span class="stat-label">[[user:profile_views]]</span>
</div>

<div class="stat">
<div><a class="human-readable-number" title="{counts.posts}" href="{config.relative_path}/user/{userslug}/posts">{counts.posts}</a></div>
<span class="stat-label">[[global:posts]]</span>
</div>

<div class="stat">
<div><a class="human-readable-number" title="{counts.followers}" href="{config.relative_path}/user/{userslug}/followers">{counts.followers}</a></div>
<span class="stat-label">[[user:followers]]</span>
</div>

<div class="stat">
<div><a class="human-readable-number" title="{counts.following}" href="{config.relative_path}/user/{userslug}/following">{counts.following}</a></div>
<span class="stat-label">[[user:following]]</span>
</div>
</div>

<div class="text-center profile-meta">
<span>[[user:joined]]</span>
<strong class="timeago" title="{joindateISO}"></strong>

<span>[[user:lastonline]]</span>
<strong class="timeago" title="{lastonlineISO}"></strong><br />

<!-- IF email -->
<span>[[user:email]]</span>
<strong><i class="fa fa-eye-slash {emailClass}" title="[[user:email_hidden]]"></i> {email}</strong>
<!-- ENDIF email -->

<!-- IF websiteName -->
<span>[[user:website]]</span>
<strong><a href="{websiteLink}" rel="nofollow noopener noreferrer">{websiteName}</a></strong>
<!-- ENDIF websiteName -->

<!-- IF location -->
<span>[[user:location]]</span>
<strong>{location}</strong>
<!-- ENDIF location -->

<!-- IF age -->
<span>[[user:age]]</span>
<strong>{age}</strong>
<!-- ENDIF age -->
</div>
</div>


<hr />

<div class="row">
<div class="col-12 account-block hidden">
<div class="account-picture-block text-center">
<span>
<span class="account-username"> {username}</span>
</span>

<!-- IF !isSelf -->
<a component="account/unfollow" href="#" class="btn btn-outline-secondary{{{ if !isFollowing }}} hide{{{ end }}}">[[user:unfollow]]</a>
<a component="account/follow" href="#" class="btn btn-primary{{{ if isFollowing }}} hide{{{ end }}}">[[user:follow]]</a>
<!-- ENDIF !isSelf -->
</div>
</div>
</div>

<!-- IF groups.length -->
<div class="row">
<div class="col-12 hidden">
{{{each groups}}}
<a href="{config.relative_path}/groups/{groups.slug}"><span class="label group-label inline-block" style="background-color: {groups.labelColor};"><!-- IF groups.icon --><i class="fa {groups.icon}"></i> <!-- ENDIF groups.icon -->{groups.userTitle}</span></a>
{{{end}}}
</div>
</div>
<!-- ENDIF groups.length -->

<!-- IF ips.length -->
<div class="row">
<div class="col-12 hidden">
<div class="card">
<h5 class="card-header">
[[global:recentips]]
</h5>
<div class="card-body">
{{{each ips}}}
<div>{ips}</div>
{{{end}}}
</div>
</div>
</div>
</div>
<!-- ENDIF ips.length -->

<div class="row">
{{{ if bestPosts.length }}}
<div class="col-lg-12 col-12">
<h1>[[pages:account/best, {username}]]</h1>

<div class="col-12">
<ul component="posts" class="posts-list list-unstyled">
{{{each bestPosts}}}
<!-- IMPORT partials/posts_list_item.tpl -->
{{{end}}}
</ul>
</div>
</div>
{{{ end }}}
{{{ if latestPosts.length}}}
<div class="col-lg-12 col-12">
<h1>[[pages:account/latest-posts, {username}]]</h1>
<div class="col-12">
<ul component="posts" class="posts-list list-unstyled">
{{{each latestPosts}}}
<!-- IMPORT partials/posts_list_item.tpl -->
{{{end}}}
</ul>
</div>
</div>
{{{ end }}}
</div>

<div id="user-action-alert" class="alert alert-success hide"></div>
</div>

View file

@ -0,0 +1,32 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->

<!-- IF sessions.length -->
<div class="row">
<div class="col-12 col-md-12">
<p class="lead">[[user:sessions.description]]</p>
<hr />
<ul class="list-group" component="user/sessions">
{{{each sessions}}}
<li class="list-group-item" data-uuid="{../uuid}">
<div class="float-end">
<!-- IF isSelfOrAdminOrGlobalModerator -->
<!-- IF !../current -->
<button class="btn btn-sm btn-outline-secondary" type="button" data-action="revokeSession">Revoke Session</button>
<!-- ENDIF !../current -->
<!-- ENDIF isSelfOrAdminOrGlobalModerator -->
{function.userAgentIcons}
<i class="fa fa-circle text-<!-- IF ../current -->success<!-- ELSE -->muted<!-- ENDIF ../current -->"></i>
</div>
{../browser} {../version} on {../platform}<br />
<small class="timeago text-muted" title="{../datetimeISO}"></small>
<ul>
<li><strong>[[global:ip_address]]</strong>: {../ip}</li>
</ul>
</li>
{{{end}}}
</ul>
</div>
</div>
<!-- ENDIF sessions.length -->
</div>

View file

@ -0,0 +1,229 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->

<div class="row">
<div class="col-12 col-md-6">
<!-- IF !disableCustomUserSkins -->
<h4>[[user:select-skin]]</h4>
<div class="card card-body mb-3">
<select class="form-select" id="bootswatchSkin" data-property="bootswatchSkin">
{{{each bootswatchSkinOptions}}}
<option value="{bootswatchSkinOptions.value}" <!-- IF bootswatchSkinOptions.selected -->selected<!-- ENDIF bootswatchSkinOptions.selected -->>{bootswatchSkinOptions.name}</option>
{{{end}}}
</select>
</div>
<!-- ENDIF !disableCustomUserSkins -->

<!-- IF allowUserHomePage -->
<h4>[[user:select-homepage]]</h4>
<div class="card card-body mb-3">
<div class="mb-2">
<label for="homePageRoute">[[user:homepage]]</label>
<select class="form-select" id="homePageRoute" data-property="homePageRoute">
<option value="none">None</option>
{{{each homePageRoutes}}}
<option value="{homePageRoutes.route}" <!-- IF homePageRoutes.selected -->selected="1"<!-- ENDIF homePageRoutes.selected -->>{homePageRoutes.name}</option>
{{{end}}}
</select>
<p class="form-text">[[user:homepage_description]]</p>
</div>
<div id="homePageCustom" class="mb-2" style="display: none;">
<label for="homePageCustom">[[user:custom_route]]</label>
<input type="text" class="form-control" data-property="homePageCustom" id="homePageCustom" value="{settings.homePageRoute}"/>
<p class="form-text">[[user:custom_route_help]]</p>
</div>
</div>
<!-- ENDIF allowUserHomePage -->

<h4>[[global:privacy]]</h4>
<div class="card card-body mb-3">
<!-- IF !hideEmail -->
<div class="form-check">
<input class="form-check-input" type="checkbox" data-property="showemail" <!-- IF settings.showemail -->checked <!-- ENDIF settings.showemail -->/>
<strong>
<label class="form-check-label">[[user:show_email]]</label>
</strong>
</div>
<!-- ENDIF !hideEmail -->

<!-- IF !hideFullname -->
<div class="form-check">
<input class="form-check-input" type="checkbox" data-property="showfullname" <!-- IF settings.showfullname -->checked<!-- ENDIF settings.showfullname -->/>
<strong>
<label class="form-check-label">[[user:show_fullname]]</label>
</strong>
</div>
<!-- ENDIF !hideFullname -->
<!-- IF !config.disableChat -->
<div class="form-check">
<input class="form-check-input" type="checkbox" data-property="restrictChat" <!-- IF settings.restrictChat -->checked<!-- ENDIF settings.restrictChat -->/>
<strong>
<label class="form-check-label">[[user:restrict_chats]]</label>
</strong>
</div>
<!-- ENDIF !config.disableChat -->
</div>

<h4>[[user:browsing]]</h4>
<div class="card card-body mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" data-property="openOutgoingLinksInNewTab" <!-- IF settings.openOutgoingLinksInNewTab -->checked<!-- ENDIF settings.openOutgoingLinksInNewTab -->/>
<strong>
<label class="form-check-label">[[user:open_links_in_new_tab]]</label>
</strong>
</div>
<!-- IF inTopicSearchAvailable -->
<div class="form-check">
<input class="form-check-input" type="checkbox" data-property="topicSearchEnabled" <!-- IF settings.topicSearchEnabled -->checked<!-- ENDIF settings.topicSearchEnabled -->/>
<strong>
<label class="form-check-label">[[user:enable_topic_searching]]</label>
</strong>
</div>
<p class="form-text">[[user:topic_search_help]]</p>
<!-- ENDIF inTopicSearchAvailable -->
<div class="form-check">
<input class="form-check-input" type="checkbox" data-property="updateUrlWithPostIndex" {{{ if settings.updateUrlWithPostIndex }}}checked{{{ end }}}/>
<strong>
<label class="form-check-label">[[user:update_url_with_post_index]]</label>
</strong>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" data-property="scrollToMyPost" <!-- IF settings.scrollToMyPost -->checked<!-- ENDIF settings.scrollToMyPost -->/>
<strong>
<label class="form-check-label">[[user:scroll_to_my_post]]</label>
</strong>
</div>
</div>

<h4>[[global:pagination]]</h4>
<div class="card card-body mb-3">
<div class="mb-2 form-check">
<input type="checkbox" class="form-check-input" data-property="usePagination" <!-- IF settings.usePagination -->checked<!-- ENDIF settings.usePagination -->> <strong><label class="form-check-label">[[user:paginate_description]]</label></strong>
</div>
<div class="mb-3">
<strong><label class="form-label">[[user:topics_per_page]] ([[user:max_items_per_page, {maxTopicsPerPage}]])</label></strong>
<input type="text" class="form-control" data-property="topicsPerPage" value="{settings.topicsPerPage}">
</div>
<div class="">
<strong><label class="form-label">[[user:posts_per_page]] ([[user:max_items_per_page, {maxPostsPerPage}]])</label></strong>
<input type="text" class="form-control" data-property="postsPerPage" value="{settings.postsPerPage}">
</div>
</div>

<!-- IF !disableEmailSubscriptions -->
<h4>[[global:email]]</h4>
<div class="card card-body mb-3">
<div class="mb-2">
<label for="dailyDigestFreq">[[user:digest_label]]</label>
<select class="form-select" id="dailyDigestFreq" data-property="dailyDigestFreq" autocomplete="off">
{{{each dailyDigestFreqOptions}}}
<option value="{dailyDigestFreqOptions.value}" <!-- IF dailyDigestFreqOptions.selected -->selected="1"<!-- ENDIF dailyDigestFreqOptions.selected -->>{dailyDigestFreqOptions.name}</option>
{{{end}}}
</select>
<p class="form-text">[[user:digest_description]]</p>
</div>
</div>
<!-- ENDIF !disableEmailSubscriptions -->

{{{each customSettings}}}
<h4>{customSettings.title}</h4>
<div class="card card-body mb-3">
{customSettings.content}
</div>
{{{end}}}

</div>

<div class="col-12 col-md-6">
<h4>[[global:language]]</h4>
<div class="card card-body mb-3">
<div class="row">
<div class="mb-2 col-lg-12">
<select data-property="userLang" class="form-select">
{{{each languages}}}
<option value="{languages.code}" <!-- IF languages.selected -->selected<!-- ENDIF languages.selected -->>{languages.name} ({languages.code})</option>
{{{end}}}
</select>
</div>
</div>
<!-- IF isAdmin -->
<!-- IF isSelf -->
<label>[[user:acp_language]]</label>
<div class="row">
<div class="mb-2 col-lg-12">
<select data-property="acpLang" class="form-select">
{{{each acpLanguages}}}
<option value="{acpLanguages.code}" <!-- IF acpLanguages.selected -->selected<!-- ENDIF acpLanguages.selected -->>{acpLanguages.name} ({acpLanguages.code})</option>
{{{end}}}
</select>
</div>
</div>
<!-- ENDIF isSelf -->
<!-- ENDIF isAdmin -->
</div>

<h4>[[topic:watch]]</h4>
<div class="card card-body mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" data-property="followTopicsOnCreate" <!-- IF settings.followTopicsOnCreate -->checked <!-- ENDIF settings.followTopicsOnCreate -->/>
<strong>
<label class="form-check-label">[[user:follow_topics_you_create]]</label>
</strong>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" data-property="followTopicsOnReply" <!-- IF settings.followTopicsOnReply -->checked<!-- ENDIF settings.followTopicsOnReply -->/>
<strong>
<label class="form-check-label">[[user:follow_topics_you_reply_to]]</label>
</strong>
</div>
<div class="mb-2">
<label>[[user:default-category-watch-state]]</label>
<select class="form-select" data-property="categoryWatchState">
<option value="watching" <!-- IF categoryWatchState.watching -->selected<!-- ENDIF categoryWatchState.watching -->>[[category:watching]]</option>
<option value="notwatching" <!-- IF categoryWatchState.notwatching -->selected<!-- ENDIF categoryWatchState.notwatching -->>[[category:not-watching]]</option>
<option value="ignoring" <!-- IF categoryWatchState.ignoring -->selected<!-- ENDIF categoryWatchState.ignoring -->>[[category:ignoring]]</option>
</select>
</div>
</div>


<h4>[[user:notifications]]</h4>
<div class="card card-body mb-3">
{{{each notificationSettings}}}
<div class="row mb-3">
<div class="col-7">
<label>{notificationSettings.label}</label>
</div>
<div class="mb-2 col-5">
<select class="form-select" data-property="{notificationSettings.name}">
<option value="none" <!-- IF notificationSettings.none -->selected<!-- ENDIF notificationSettings.none -->>[[notifications:none]]</option>
<option value="notification" <!-- IF notificationSettings.notification -->selected<!-- ENDIF notificationSettings.notification -->>[[notifications:notification_only]]</option>
<option value="email" <!-- IF notificationSettings.email -->selected<!-- ENDIF notificationSettings.email -->>[[notifications:email_only]]</option>
<option value="notificationemail" <!-- IF notificationSettings.notificationemail -->selected<!-- ENDIF notificationSettings.notificationemail -->>[[notifications:notification_and_email]]</option>
</select>
</div>
</div>
{{{end}}}

<div class="row">
<div class="col-7">
<label for="upvote-notif-freq">[[user:upvote-notif-freq]]</label>
</div>
<div class="mb-2 col-5">
<select class="form-select" id="upvote-notif-freq" name="upvote-notif-freq" data-property="upvoteNotifFreq">
{{{each upvoteNotifFreq}}}
<option value="{upvoteNotifFreq.name}" <!-- IF upvoteNotifFreq.selected -->selected<!-- ENDIF upvoteNotifFreq.selected -->>
[[user:upvote-notif-freq.{upvoteNotifFreq.name}]]
</option>
{{{end}}}
</select>
</div>
</div>
</div>
</div>
</div>
<div class="form-actions">
<a id="submitBtn" href="#" class="btn btn-primary">[[global:save_changes]]</a>
</div>
</div>

View file

@ -0,0 +1,27 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->

<p>[[persona:settings.intro]]</p>

<hr />

<form id="theme-settings" role="form">
<div class="checkbox">
<label>
<input type="checkbox" id="persona:menus:legacy-layout" name="persona:menus:legacy-layout"> <strong>[[persona:settings.mobile-menu-side]]</strong>
</label>
</div><br />

<div class="form-group">
<label for="persona:navbar:autohide">[[persona:settings.autoHidingNavbar]]</label>
<select multiple class="form-control" name="persona:navbar:autohide" id="persona:navbar:autohide">
<option value="xs">[[persona:settings.autoHidingNavbar-xs]]</option>
<option value="sm">[[persona:settings.autoHidingNavbar-sm]]</option>
<option value="md">[[persona:settings.autoHidingNavbar-md]]</option>
<option value="lg">[[persona:settings.autoHidingNavbar-lg]]</option>
</select>
</div>

<button id="save" type="button" class="btn btn-primary">[[global:save_changes]]</button>
</form>
</div>

View file

@ -0,0 +1,30 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->

<div class="row">
<h1>{title}</h1>
<!-- IF showSort -->
<div class="btn-toolbar justify-content-end mb-2">
<div class="btn-group bottom-sheet" component="thread/sort">
<button class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" type="button"><span>[[topic:sort_by]]</span> <span class="caret"></span></button>
<ul class="dropdown-menu dropdown-menu-end">
{{{each sortOptions }}}
<li><a class="dropdown-item" href="{config.relative_path}{sortOptions.url}"><i class="fa fa-fw {{{if sortOptions.selected}}}fa-check{{{end}}}"></i>{sortOptions.name}</a></li>
{{{end}}}
</ul>
</div>
</div>
<!-- ENDIF showSort -->

<!-- IF !topics.length -->
<div class="alert alert-warning text-center">{noItemsFoundKey}</div>
<!-- ENDIF !topics.length -->

<div class="category">
<!-- IMPORT partials/topics_list.tpl -->
<!-- IF config.usePagination -->
<!-- IMPORT partials/paginator.tpl -->
<!-- ENDIF config.usePagination -->
</div>
</div>
</div>

View file

@ -0,0 +1,43 @@
<div class="account">
<!-- IMPORT partials/account/header.tpl -->
<div class="row">
<h1>{title}</h1>

<!-- IF privateUploads -->
<div class="alert alert-info text-center">[[uploads:private-uploads-info]]</div>
<!-- ELSE -->
<div class="alert alert-info text-center">[[uploads:public-uploads-info]]</div>
<!-- ENDIF privateUploads -->

<!-- IF !uploads.length -->
<div class="alert alert-warning text-center">[[uploads:no-uploads-found]]</div>
<!-- ENDIF !uploads.length -->

<div class="col-12">
<table class="table table-striped">
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{{{each uploads}}}
<tr data-name="{uploads.name}">
<td>
<a href="{config.relative_path}{uploads.url}">{uploads.url}</a>
</td>
<td>
<div class="btn-group ">
<button class="btn btn-danger btn-sm" data-action="delete"><i class="fa fa-trash"></i></button>
</div>
</td>
</tr>
{{{end}}}
</tbody>
</table>

<!-- IMPORT partials/paginator.tpl -->
</div>
</div>
</div>

View file

@ -0,0 +1 @@
<!-- IMPORT account/posts.tpl -->

View file

@ -0,0 +1 @@
<!-- IMPORT account/topics.tpl -->

View file

@ -0,0 +1,27 @@
<div class="row">
<div class="col-sm-2 col-12 settings-header">Theme Settings</div>
<div class="col-sm-10 col-12">
<form role="form" class="persona-settings">
<div class="checkbox">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input class="mdl-switch__input" type="checkbox" id="hideSubCategories" name="hideSubCategories">
<span class="mdl-switch__label"><strong>Hide subcategories on categories view</strong></span>
</label>
</div>
<div class="checkbox">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input class="mdl-switch__input" type="checkbox" id="hideCategoryLastPost" name="hideCategoryLastPost">
<span class="mdl-switch__label"><strong>Hide last post on categories view</strong></span>
</label>
</div>
<div class="checkbox">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input class="mdl-switch__input" type="checkbox" id="enableQuickReply" name="enableQuickReply">
<span class="mdl-switch__label"><strong>Enable quick reply</strong></span>
</label>
</div>
</form>
</div>
</div>

<!-- IMPORT admin/partials/save_button.tpl -->

31
templates/categories.tpl Normal file
View file

@ -0,0 +1,31 @@
<!-- IMPORT partials/breadcrumbs.tpl -->
<div data-widget-area="header">
{{{ each widgets.header }}}
{{widgets.header.html}}
{{{ end }}}
</div>
<div class="row">
<div class="{{{ if widgets.sidebar.length }}}col-lg-9 col-sm-12{{{ else }}}col-lg-12{{{ end }}}">
{{{ if pagination.pages.length }}}
<div><!-- IMPORT partials/category-selector.tpl --></div>
{{{ else }}}
<h1 class="categories-title">[[pages:categories]]</h1>
{{{ end }}}
<ul class="categories" itemscope itemtype="http://www.schema.org/ItemList">
{{{ each categories }}}
<!-- IMPORT partials/categories/item.tpl -->
{{{ end }}}
</ul>
<!-- IMPORT partials/paginator.tpl -->
</div>
<div data-widget-area="sidebar" class="col-lg-3 col-sm-12 {{{ if !widgets.sidebar.length }}}hidden{{{ end }}}">
{{{ each widgets.sidebar }}}
{{widgets.sidebar.html}}
{{{ end }}}
</div>
</div>
<div data-widget-area="footer">
{{{ each widgets.footer }}}
{{widgets.footer.html}}
{{{ end }}}
</div>

63
templates/category.tpl Normal file
View file

@ -0,0 +1,63 @@
<!-- IMPORT partials/breadcrumbs.tpl -->
<div data-widget-area="header">
{{{ each widgets.header }}}
{{widgets.header.html}}
{{{ end }}}
</div>
<div class="row">
<div class="category {{{if widgets.sidebar.length }}}col-lg-9 col-sm-12{{{ else }}}col-lg-12{{{ end }}}">
<!-- IMPORT partials/category/subcategory.tpl -->

<div class="topic-list-header sticky-top btn-toolbar justify-content-between py-2 mb-2 flex-nowrap">
<div class="d-flex gap-1 align-items-stretch">
{{{ if privileges.topics:create }}}
<a href="{config.relative_path}/compose?cid={cid}" component="category/post" id="new_topic" class="btn btn-primary text-nowrap" data-ajaxify="false" role="button">[[category:new_topic_button]]</a>
{{{ else }}}
{{{ if !loggedIn }}}
<a component="category/post/guest" href="{config.relative_path}/login" class="btn btn-primary">[[category:guest-login-post]]</a>
{{{ end }}}
{{{ end }}}

<a href="{config.relative_path}/{selectedFilter.url}{querystring}" class="d-inline-block">
<div class="alert alert-warning h-100 m-0 px-2 py-1 d-flex align-items-center hide" id="new-topics-alert"></div>
</a>
</div>
<div class="d-flex gap-1 align-items-stretch">
<!-- IMPORT partials/category/watch.tpl -->
<!-- IMPORT partials/category/sort.tpl -->
<!-- IMPORT partials/category/tools.tpl -->
</div>
</div>

{{{ if !topics.length }}}
{{{ if privileges.topics:create }}}
<hr class="visible-xs" />
<div class="alert alert-warning" id="category-no-topics">
[[category:no_topics]]
</div>
{{{ end }}}
{{{ end }}}

<!-- IMPORT partials/topics_list.tpl -->

{{{ if config.usePagination }}}
<!-- IMPORT partials/paginator.tpl -->
{{{ end }}}
</div>
<div data-widget-area="sidebar" class="col-lg-3 col-sm-12 {{{ if !widgets.sidebar.length }}}hidden{{{ end }}}">
{{{ each widgets.sidebar }}}
{{widgets.sidebar.html}}
{{{ end }}}
</div>
</div>
<div data-widget-area="footer">
{{{each widgets.footer}}}
{{widgets.footer.html}}
{{{end}}}
</div>

<!-- IF !config.usePagination -->
<noscript>
<!-- IMPORT partials/paginator.tpl -->
</noscript>
<!-- ENDIF !config.usePagination -->

39
templates/chat.tpl Normal file
View file

@ -0,0 +1,39 @@
<div id="chat-modal" class="chat-modal modal hide" tabindex="-1" role="dialog" aria-labelledby="Chat" aria-hidden="true" data-backdrop="none">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header d-flex">
<div class="fs-5 flex-grow-1" component="chat/room/name">{{{ if roomName }}}{roomName}{{{ else }}}{usernames}{{{ end }}}</div>
<button type="button" class="btn btn-link d-none d-md-block p-2 text-muted align-text-top" data-action="maximize">
<span aria-hidden="true"><i class="fa fa-fw fa-expand"></i></span>
<span class="sr-only">[[modules:chat.maximize]]</span>
</button>
<button type="button" class="btn btn-link d-none d-md-block p-2 text-muted align-text-top" data-action="minimize">
<span aria-hidden="true"><i class="fa fa-fw fa-minus"></i></span>
<span class="sr-only">[[modules:chat.minimize]]</span>
</button>
<!-- IMPORT partials/chats/options.tpl -->

<button id="chat-close-btn" type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>

<div class="modal-body">
<div class="position-relative">
<div component="chat/messages/scroll-up-alert" class="position-absolute scroll-up-alert alert alert-info hidden w-100" role="button" style="z-index: 1;">[[modules:chat.scroll-up-alert]]</div>
</div>
<ul class="chat-content" component="chat/messages">
<!-- IMPORT partials/chats/messages.tpl -->
</ul>

<div component="chat/composer">
<textarea component="chat/input" placeholder="[[modules:chat.placeholder]]" class="form-control chat-input mousetrap" rows="1"></textarea>
<button class="btn btn-primary" type="button" data-action="send"><i class="fa fa-fw fa-2x fa-paper-plane"></i></button>
<span component="chat/message/remaining">{maximumChatMessageLength}</span>
<form component="chat/upload" method="post" enctype="multipart/form-data">
<input type="file" name="files[]" multiple class="hidden"/>
</form>
</div>
</div>
<div class="imagedrop"><div>[[topic:composer.drag_and_drop_images]]</div></div>
</div>
</div>
</div>

19
templates/chats.tpl Normal file
View file

@ -0,0 +1,19 @@
<div class="chats-full">
<div component="chat/nav-wrapper" data-loaded="{{{ if roomId }}}1{{{ else }}}0{{{ end }}}">
<div class="chat-search dropdown">
<input class="form-control" type="text" component="chat/search" placeholder="[[users:search-user-for-chat]]" data-bs-toggle="dropdown" />
<ul component="chat/search/list" class="dropdown-menu">
<li><a href="#" class="dropdown-item">[[admin/menu:search.start-typing]]</a></li>
</ul>
</div>
<ul component="chat/recent" class="chats-list list-unstyled" data-nextstart="{nextStart}">
{{{each rooms}}}
<!-- IMPORT partials/chats/recent_room.tpl -->
{{{end}}}
</ul>
</div>
<div component="chat/main-wrapper">
<!-- IMPORT partials/chats/message-window.tpl -->
</div>
<div class="imagedrop"><div>[[topic:composer.drag_and_drop_images]]</div></div>
</div>

209
templates/flags/detail.tpl Normal file
View file

@ -0,0 +1,209 @@
<!-- IMPORT partials/breadcrumbs.tpl -->

<div class="row">
<div class="col-12">
<h2 class="h4">
{target_readable}
<small><span class="text-muted timeago" title="{datetimeISO}"></span></small>
</h2>

<hr />

<!-- IF type_bool.post -->
<div class="d-flex">
<div class="flex-shrink-0">
<a href="{config.relative_path}/user/{target.user.userslug}">{buildAvatar(target.user, "64px", true, "media-object")}</a>
</div>
<div class="flex-grow-1 ms-3">
<h4 class="media-heading"><a href="{config.relative_path}/user/{target.user.userslug}">{target.user.username}</a></h4>
{target.content}
</div>
</div>
<!-- ENDIF type_bool.post -->

<!-- IF type_bool.user -->
<div class="d-flex">
<div class="flex-shrink-0">
<a href="{config.relative_path}/user/{target.userslug}">{buildAvatar(target, "64px", true, "media-object")}</a>
</div>
<div class="flex-grow-1 ms-3">
<h4 class="media-heading"><a href="{config.relative_path}/user/{target.userslug}">{target.username}</a></h4>
<p class="lead">
<a href="{config.relative_path}/uid/{target.uid}">[[flags:user-view]]</a> |
<a href="{config.relative_path}/uid/{target.uid}/edit">[[flags:user-edit]]</a>
</p>
</div>
</div>
<!-- ENDIF type_bool.user -->

<!-- IF type_bool.empty -->
<div class="alert alert-warning" role="alert">[[flags:target-purged]]</div>
<!-- ENDIF type_bool.empty -->

<hr />

<div class="row">
<div class="col-sm-6">
<form role="form" id="attributes">
<div class="mb-3">
<h2 class="h4">[[flags:reports]]</h2>
<ul class="list-group" component="flag/reports">
{{{ each reports }}}
<li class="list-group-item">
<a href="{config.relative_path}/user/{./reporter.userslug}">{buildAvatar(./reporter, "24px", true)}</a>
&ndash; <span class="timeago" title="{./timestampISO}"></span>
<blockquote><em>{./value}</em></blockquote>
</li>
{{{ end }}}
</ul>
</div>
<div class="mb-3">
<h2 class="h4" for="state">[[flags:state]]</h2>
<select class="form-select" id="state" name="state" disabled>
<option value="open">[[flags:state-open]]</option>
<option value="wip">[[flags:state-wip]]</option>
<option value="resolved">[[flags:state-resolved]]</option>
<option value="rejected">[[flags:state-rejected]]</option>
</select>
</div>
<div class="mb-3">
<h2 class="h4" for="assignee">[[flags:assignee]]</h2>
<select class="form-control" id="assignee" name="assignee" disabled>
<option value="">[[flags:no-assignee]]</option>
{{{each assignees}}}
<option value="{../uid}">{../username}</option>
{{{end}}}
</select>
</div>
<div class="d-grid">
<button type="button" class="btn btn-primary" data-action="update">[[flags:update]]</button>
</div>
</form>

<hr />

<form role="form">
<div class="mb-3">
<h2 class="h4" for="note">[[flags:notes]]</h2>
<textarea id="note" class="form-control"></textarea>
<div class="d-grid">
<button type="button" class="btn btn-block btn-primary" data-action="appendNote">[[flags:add-note]]</button>
</div>
</div>
</form>

<div component="flag/notes">
<!-- IF !notes.length -->
<div class="alert alert-success text-center">[[flags:no-notes]]</div>
<!-- ENDIF !notes.length -->
{{{each notes}}}
<div class="d-flex mb-3">
<div class="flex-shrink-0">
<a href="{config.relative_path}/user/{../user.userslug}">{buildAvatar(notes.user, "32px", true, "media-object")}</a>
</div>
<div class="flex-grow-1 mx-3">
<h2 class="h5">
<a href="{config.relative_path}/user/{../user.userslug}">{../user.username}</a>
<small><span class="timeago" title="{../datetimeISO}"></span></small>
</h4>
{../content}
</div>
<div class="flex-shrink-0">
<a href="#" class="btn btn-sm btn-link" data-action="prepare-edit"><i class="fa fa-pencil"></i></a>
<a href="#" class="btn btn-sm btn-link" data-action="delete-note"><i class="fa fa-trash text-danger"></i></a>
</div>
</div>
{{{end}}}
</div>
</div>
<div class="col-sm-6">
<h2 class="h4">[[flags:quick-actions]]</h2>

<div class="d-grid gap-1">
<a class="btn btn-light" href="{config.relative_path}/{type_path}/{targetId}">
<i class="fa fa-external-link"></i>
[[flags:go-to-target]]
</a>

<a class="btn btn-light" href="#" data-action="assign">
<i class="fa fa-id-card-o"></i>
[[flags:assign-to-me]]
</a>

{{{ if type_bool.post }}}
{{{ if !target.deleted}}}
<a class="btn btn-outline-danger" href="#" data-action="delete-post"><i class="fa fa-trash"></i> [[flags:delete-post]]</a>
{{{ else }}}
<a class="btn btn-danger" href="#" data-action="purge-post"><i class="fa fa-trash"></i> [[flags:purge-post]]</a>
<a class="btn btn-outline-success" href="#" data-action="restore-post"><i class="fa fa-reply"></i><i class="fa fa-trash"></i> [[flags:restore-post]]</a>
{{{ end }}}
{{{ end }}}

{{{ if target.uid }}}
<div class="btn-group" data-uid="{target.uid}">
<button type="button" class="btn btn-light dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-street-view"></i>
[[flags:flagged-user]]
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{config.relative_path}/uid/{target.uid}">[[flags:view-profile]]</a></li>
{{{ if !config.disableChat }}}
<li><a class="dropdown-item" href="#" data-action="chat">[[flags:start-new-chat]]</a></li>
{{{ end }}}
<li class="dropdown-divider"></li>
{{{ if privileges.ban }}}
<li class="{{{ if target.user.banned }}}hidden{{{ end }}}"><a class="dropdown-item" href="#" data-action="ban">[[user:ban_account]]</a></li>
<li class="{{{ if !target.user.banned }}}hidden{{{ end }}}"><a class="dropdown-item" href="#" data-action="unban">[[user:unban_account]]</a></li>
{{{ end }}}
{{{ if privileges.mute}}}
<li class="{{{ if target.user.muted }}}hidden{{{ end }}}"><a class="dropdown-item" href="#" data-action="mute">[[user:mute_account]]</a></li>
<li class="{{{ if !target.user.muted }}}hidden{{{ end }}}"><a class="dropdown-item" href="#" data-action="unmute">[[user:unmute_account]]</a></li>
{{{ end }}}
{{{ if privileges.admin:users }}}
<li><a class="dropdown-item" href="#" data-action="delete-account">[[user:delete_account_as_admin]]</a></li>
<li><a class="dropdown-item" href="#" data-action="delete-content">[[user:delete_content]]</a></li>
<li><a class="dropdown-item" href="#" data-action="delete-all">[[user:delete_all]]</a></li>
{{{ end }}}
</ul>
</div>
{{{ end }}}
</div>

<hr />

<h2 class="h4">[[flags:history]]</h2>
<div component="flag/history">
<!-- IF !history.length -->
<div class="alert alert-success text-center">[[flags:no-history]]</div>
<!-- ENDIF !history.length -->
{{{each history}}}
<div class="d-flex">
<div class="flex-shrink-0">
<a href="{config.relative_path}/user/{../user.userslug}">{buildAvatar(history.user, "32px", true, "media-object")}</a>
</div>
<div class="flex-grow-1 ms-3">
<h4 class="media-heading">
<a href="{config.relative_path}/user/{../user.userslug}">{../user.username}</a>
<small><span class="timeago" title="{../datetimeISO}"></span></small>
</h4>
<ul>
{{{each ./fields}}}
<li>
<span class="badge bg-primary">[[flags:{@key}]]</span><!-- IF @value --> &rarr; <span class="badge bg-light text-dark">{@value}</span><!-- ENDIF @value -->
</li>
{{{end}}}
{{{ each ./meta }}}
<li>
<span class="badge bg-{{./labelClass}}">{{./key}}</span>{{{ if ./value }}} &rarr; <span class="badge bg-light text-dark">{{ ./value }}</span>{{{ end }}}
</li>
{{{ end }}}
</ul>
</div>
</div>
{{{end}}}
</div>
</div>
</div>
</div>
</div>

71
templates/flags/list.tpl Normal file
View file

@ -0,0 +1,71 @@
<!-- IMPORT partials/breadcrumbs.tpl -->

<div class="row">
<div class="col-sm-4 col-md-3">
<!-- IMPORT partials/flags/filters.tpl -->
</div>
<div class="col-sm-8 col-md-9">
<!-- IF hasFilter -->
<div class="alert alert-warning">
<p class="float-end">
<a href="{config.relative_path}/flags">[[flags:filter-reset]]</a>
</p>
[[flags:filter-active]]
</div>
<!-- ENDIF hasFilter -->

<div class="btn-group float-end" component="flags/bulk-actions">
<button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" autocomplete="off" aria-haspopup="true" aria-expanded="false" disabled="disabled">
<i class="fa fa-clone"></i> [[flags:bulk-actions]] <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#" class="dropdown-item" data-action="bulk-assign">[[flags:assign-to-me]]</a></li>
<li><a href="#" class="dropdown-item" data-action="bulk-mark-resolved">[[flags:bulk-resolve]]</a></li>
</ul>
</div>

<table class="table table-striped table-hover" component="flags/list">
<thead>
<tr>
<th>
<input type="checkbox" data-action="toggle-all" autocomplete="off" />
</th>
<th></th>
<th><span class="hidden-xs">[[flags:reports]] </span><i class="fa fa-user-plus"></i></th>
<th><span class="hidden-xs">[[flags:first-reported]] </span><i class="fa fa-clock-o"></i></th>
<th>[[flags:state]]</th>
</tr>
</thead>
<tbody>
<!-- IF !flags.length -->
<tr>
<td colspan="5">
<div class="alert alert-success text-center">
[[flags:no-flags]]
</div>
</td>
</tr>
<!-- ENDIF !flags.length -->
{{{each flags}}}
<tr data-flag-id="{../flagId}">
<td>
<input type="checkbox" autocomplete="off" />
</td>
<td>
<a href="{config.relative_path}/flags/{../flagId}">
<strong>{../target_readable}</strong>
</a>
</td>
<td>
{./heat}
</td>
<td><span class="timeago" title="{../datetimeISO}"></span></td>
<td><span class="badge bg-{../labelClass}">[[flags:state-{../state}]]</span></td>
</tr>
{{{end}}}
</tbody>
</table>

<!-- IMPORT partials/paginator.tpl -->
</div>
</div>

14
templates/footer.tpl Normal file
View file

@ -0,0 +1,14 @@
</div><!-- /.container#content -->
</main>
{{{ if !isSpider }}}
<div component="toaster/tray" class="alert-window">
<div id="reconnect-alert" class="alert alert-dismissible alert-warning clearfix hide" component="toaster/toast">
<button type="button" class="btn-close float-end" data-bs-dismiss="alert" aria-hidden="true"></button>
<p>[[global:reconnecting-message, {config.siteTitle}]]</p>
</div>
</div>
{{{ end }}}

<!-- IMPORT partials/footer/js.tpl -->
</body>
</html>

View file

@ -0,0 +1,77 @@
<div component="groups/container" class="groups details row">
<div component="groups/cover" style="background-image: url({group.cover:url}); background-position: {group.cover:position};">
<!-- IF group.isOwner -->
<div class="controls">
<span class="upload"><i class="fa fa-fw fa-4x fa-upload"></i></span>
<span class="resize"><i class="fa fa-fw fa-4x fa-arrows"></i></span>
<span class="remove"><i class="fa fa-fw fa-4x fa-times"></i></span>
</div>
<div class="save">[[groups:cover-save]] <i class="fa fa-fw fa-floppy-o"></i></div>
<div class="indicator">[[groups:cover-saving]] <i class="fa fa-fw fa-refresh fa-spin"></i></div>
<!-- ENDIF group.isOwner -->
</div>

<div class="col-12">
<!-- IMPORT partials/breadcrumbs.tpl -->
</div>

<div class="col-lg-4 col-12">
<div class="card mb-3">
<div class="card-header">
<span class="fs-5">
<i class="fa fa-list-ul"></i> [[groups:details.title]]
<!-- IF group.private --><span class="badge bg-info text-dark">[[groups:details.private]]</span><!-- ENDIF group.private -->
<!-- IF group.hidden --><span class="badge bg-info text-dark">[[groups:details.hidden]]</span>&nbsp;<!-- ENDIF group.hidden -->
</span>
</div>
<div class="card-body">
<h2>{group.displayName}</h2>
<p>{group.descriptionParsed}</p>
<!-- IF isAdmin -->
<div class="float-end">
<a href="{config.relative_path}/admin/manage/groups/{group.nameEncoded}" target="_blank" class="btn btn-info"><i class="fa fa-gear"></i> [[user:edit]]</a>
</div>
<!-- ENDIF isAdmin -->
<!-- IF loggedIn -->
<div class="float-end">
{function.membershipBtn, group}&nbsp;
</div>
<!-- ENDIF loggedIn -->
</div>
</div>

<div class="card mb-3">
<div class="card-header">
<span class="fs-5">
<i class="fa fa-users"></i> [[groups:details.members]]
</span>
</div>
<div class="card-body">
<!-- IMPORT partials/groups/memberlist.tpl -->
</div>
</div>

<!-- IF group.isOwner -->
<!-- IMPORT partials/groups/admin.tpl -->
<!-- ENDIF group.isOwner -->

<div data-widget-area="left">
{{{each widgets.left}}}
{{widgets.left.html}}
{{{end}}}
</div>
</div>
<div class="col-lg-8 col-12">
<div class="col-lg-11">
<!-- IF !posts.length -->
<div class="alert alert-info">[[groups:details.has_no_posts]]</div>
<!-- ENDIF !posts.length -->
<!-- IMPORT partials/posts_list.tpl -->
</div>
<div data-widget-area="right">
{{{each widgets.right}}}
{{widgets.right.html}}
{{{end}}}
</div>
</div>
</div>

48
templates/groups/list.tpl Normal file
View file

@ -0,0 +1,48 @@
<!-- IMPORT partials/breadcrumbs.tpl -->
<div data-widget-area="header">
{{{each widgets.header}}}
{{widgets.header.html}}
{{{end}}}
</div>
<div class="groups list">
<div class="row justify-content-between">
<div class="col-lg-6">
<!-- IF allowGroupCreation -->
<button class="btn btn-primary" data-action="new"><i class="fa fa-plus"></i> [[groups:new_group]]</button>
<!-- ENDIF allowGroupCreation -->
</div>
<div class="col-lg-6">
<div class="row justify-content-end">
<div class="col-5 col-md-6">
<select class="form-select" id="search-sort">
<option value="alpha">[[groups:details.group_name]]</option>
<option value="count">[[groups:details.member_count]]</option>
<option value="date">[[groups:details.creation_date]]</option>
</select>
</div>
<div class="col-7 col-md-6">
<div class="input-group">
<input type="text" class="form-control" placeholder="[[global:search]]" name="query" value="" id="search-text">
<button id="search-button" class="btn btn-primary">
<i class="fa fa-search"></i>
</button>
</div>
</div>
</div>
</div>
</div>

<hr />

<div component="groups/container" class="row" id="groups-list" data-nextstart={nextStart}>
<!-- IF groups.length -->
<!-- IMPORT partials/groups/list.tpl -->
<!-- ELSE -->
<div class="col-12">
<div class="alert alert-warning">
[[groups:no_groups_found]]
</div>
</div>
<!-- ENDIF groups.length -->
</div>
</div>

View file

@ -0,0 +1,6 @@
<!-- IMPORT partials/breadcrumbs.tpl -->
<div class="users">
<!-- IMPORT partials/users_list.tpl -->

<!-- IMPORT partials/paginator.tpl -->
</div>

47
templates/header.tpl Normal file
View file

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="{function.localeToHTML, userLang, defaultLang}" {{{if languageDirection}}}data-dir="{languageDirection}" style="direction: {languageDirection};"{{{end}}}>
<head>
<title>{browserTitle}</title>
{{{each metaTags}}}{function.buildMetaTag}{{{end}}}
<link rel="stylesheet" type="text/css" href="{relative_path}/assets/client{{{if bootswatchSkin}}}-{bootswatchSkin}{{{end}}}{{{ if (languageDirection=="rtl") }}}-rtl{{{ end }}}.css?{config.cache-buster}" />
{{{each linkTags}}}{function.buildLinkTag}{{{end}}}

<script>
var config = JSON.parse('{{configJSON}}');
var app = {
user: JSON.parse('{{userJSON}}')
};

document.documentElement.style.setProperty('--panel-offset', `${localStorage.getItem('panelOffset') || 0}px`);
</script>

{{{if useCustomHTML}}}
{{customHTML}}
{{{end}}}
{{{if useCustomCSS}}}
<style>{{customCSS}}</style>
{{{end}}}
</head>

<body class="{bodyClass} skin-{{{if bootswatchSkin}}}{bootswatchSkin}{{{else}}}noskin{{{end}}}">
<nav id="menu" class="slideout-menu hidden">
<!-- IMPORT partials/slideout-menu.tpl -->
</nav>
<nav id="chats-menu" class="slideout-menu hidden">
<!-- IMPORT partials/chats-menu.tpl -->
</nav>

<main id="panel" class="slideout-panel">
<nav class="navbar sticky-top navbar-expand-lg bg-light header border-bottom" id="header-menu" component="navbar">
<div class="container justify-content-start flex-nowrap">
<!-- IMPORT partials/menu.tpl -->
</div>
</nav>
<script>
const rect = document.getElementById('header-menu').getBoundingClientRect();
const offset = Math.max(0, rect.bottom);
document.documentElement.style.setProperty('--panel-offset', offset + `px`);
</script>
<div class="container pt-3" id="content">
<!-- IMPORT partials/noscript/warning.tpl -->
<!-- IMPORT partials/noscript/message.tpl -->

96
templates/login.tpl Normal file
View file

@ -0,0 +1,96 @@
<!-- IMPORT partials/breadcrumbs.tpl -->
<div data-widget-area="header">
{{{each widgets.header}}}
{{widgets.header.html}}
{{{end}}}
</div>
<div class="row login">
<div class="{{{ if widgets.sidebar.length }}}col-lg-9 col-sm-12{{{ else }}}col-lg-12{{{ end }}}">
<div class="row">
{{{ if allowLocalLogin }}}
<div class="{{{ if alternate_logins }}}col-md-6{{{ else }}}col-md-12{{{ end }}}">
<div class="login-block">
<div class="alert alert-danger alert-dismissible" id="login-error-notify" {{{ if error }}}style="display:block"{{{ else }}}style="display: none;"{{{ end }}}>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
<strong>[[login:failed_login_attempt]]</strong>
<p>{error}</p>
</div>

<form class="form-horizontal" role="form" method="post" id="login-form">
<div class="row mb-2">
<label for="username" class="col-lg-2 col-form-label">{allowLoginWith}</label>
<div class="col-lg-10">
<input class="form-control" type="text" placeholder="{allowLoginWith}" name="username" id="username" autocorrect="off" autocapitalize="off" value="{username}"/>
</div>
</div>
<div class="row mb-2">
<label for="password" class="col-lg-2 col-form-label">[[user:password]]</label>
<div class="col-lg-10">
<input class="form-control" type="password" placeholder="[[user:password]]" name="password" id="password" {{{ if username }}}autocomplete="off"{{{ end }}}/>
<p id="caps-lock-warning" class="text-danger hidden">
<i class="fa fa-exclamation-triangle"></i> [[login:caps-lock-enabled]]
</p>
</div>
</div>
<div class="row mb-2">
<div class="col-lg-10 offset-lg-2">
<div class="checkbox">
<label>
<input type="checkbox" name="remember" id="remember" checked /> [[login:remember_me]]
</label>
</div>
</div>
</div>
{{{each loginFormEntry}}}
<div class="row mb-2 loginFormEntry">
<label for="login-{loginFormEntry.styleName}" class="col-lg-2 col-form-label">{loginFormEntry.label}</label>
<div id="login-{loginFormEntry.styleName}" class="col-lg-10">{{loginFormEntry.html}}</div>
</div>
{{{end}}}
<input type="hidden" name="_csrf" value="{config.csrf_token}" />
<input type="hidden" name="noscript" id="noscript" value="true" />
<div class="row">
<div class="col-lg-10 offset-lg-2">
<button class="btn btn-primary btn-lg" id="login" type="submit">[[global:login]]</button>
</div>
</div>
<div class="row">
<div class="col-lg-10 offset-lg-2">
{{{ if allowRegistration }}}
<span>[[login:dont_have_account]] <a href="{config.relative_path}/register">[[register:register]]</a></span>
{{{ end }}}
{{{ if allowPasswordReset }}}
<a id="reset-link" href="{config.relative_path}/reset">[[login:forgot_password]]</a>
{{{ end }}}
</div>
</div>
</form>
</div>
</div>
{{{ end }}}

{{{ if alternate_logins }}}
<div class="{{{ if allowLocalLogin }}}col-md-6{{{ else }}}col-md-12{{{ end }}}">
<div class="alt-login-block">
<h4>[[login:alternative_logins]]</h4>
<ul class="alt-logins">
{{{each authentication}}}
<li class="{authentication.name}"><a rel="nofollow noopener noreferrer" target="_top" href="{config.relative_path}{authentication.url}"><i class="fa {authentication.icon} fa-3x"></i></a></li>
{{{end}}}
</ul>
</div>
</div>
{{{ end }}}
</div>
</div>
<div data-widget-area="sidebar" class="col-lg-3 col-sm-12 {{{ if !widgets.sidebar.length }}}hidden{{{ end }}}">
{{{each widgets.sidebar}}}
{{widgets.sidebar.html}}
{{{end}}}
</div>
</div>
<div data-widget-area="footer">
{{{each widgets.footer}}}
{{widgets.footer.html}}
{{{end}}}
</div>

View file

@ -0,0 +1,3 @@
<div id="taskbar" class="taskbar fixed-bottom">
<div class="navbar-inner"><ul class="nav navbar-nav"></ul></div>
</div>

View file

@ -0,0 +1,37 @@
<div class="persona-usercard">
<a href="{config.relative_path}/user/{userslug}">
<!-- IF picture -->
<div class="usercard-picture" style="background-image:url({picture})"></div>
<!-- ELSE -->
<div class="usercard-picture" style="background-color: {icon:bgColor};">{icon:text}</div>
<!-- ENDIF picture -->
</a>
<div class="usercard-body">
<a href="{config.relative_path}/user/{userslug}">
<span class="usercard-name"><!-- IF fullname -->{fullname}<!-- ELSE -->{username}<!-- ENDIF fullname --></span><br />
<span class="usercard-username"><!-- IF !banned -->@{username}<!-- ELSE -->[[user:banned]]<!-- ENDIF !banned --></span>
<!-- IF !banned -->
<i component="user/status" class="fa fa-circle status {status}" title="[[global:{status}]]"></i>
<!-- ENDIF !banned -->
</a>

<div class="row usercard-info">
<div class="col-4">
<small>[[global:posts]]</small>
<span class="human-readable-number">{postcount}</span>
</div>
<div class="col-4">
<small>[[global:reputation]]</small>
<span class="human-readable-number">{reputation}</span>
</div>

<button class="btn-morph persona-fab <!-- IF banned --> hide<!-- ENDIF banned -->">
<span>
<span class="s1"></span>
<span class="s2"></span>
<span class="s3"></span>
</span>
</button>
</div>
</div>
</div>

View file

@ -0,0 +1,66 @@

<div class="notifications">

<!-- IMPORT partials/breadcrumbs.tpl -->
<div class="btn-toolbar justify-content-end" role="toolbar">
<div class="btn-group me-2">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
{{{ if selectedFilter }}}{selectedFilter.name}{{{ end}}} <span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-end" role="menu">
{{{ each filters }}}
{{{ if filters.separator }}}
<li role="separator" class="dropdown-divider"></li>
{{{ else }}}
<li role="presentation" class="category">
<a class="dropdown-item" role="menu-item" href="{config.relative_path}/notifications?filter={filters.filter}"><i class="fa fa-fw {{{ if filters.selected }}}fa-check{{{ end }}}"></i> {filters.name}</a>
</li>
{{{ end }}}
{{{ end }}}
</ul>
</div>

<div class="btn-group">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="true">
<i class="fa fa-eye"></i>
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-end" role="menu" aria-labelledby="dropdownMenu1">
<li role="presentation"><a class="dropdown-item" role="menuitem" tabindex="-1" href="#" component="notifications/mark_all">[[notifications:mark_all_read]]</a></li>
</ul>
</div>
</div>

<hr />

<div class="alert alert-info {{{ if notifications.length }}}hidden{{{ end }}}">
[[notifications:no_notifs]]
</div>

<ul class="notifications-list list-unstyled" data-nextstart="{nextStart}">
{{{each notifications}}}
<li data-nid="{notifications.nid}" class="{notifications.readClass} {{{ if !./read}}}text-bg-warning{{{ end }}} d-flex pointer border p-3 mb-2" component="notifications/item">
<div class="me-2">
{{{ if notifications.from }}}
{buildAvatar(notifications.user, "24px", true)}
{{{ else }}}
{{{ if notifications.image }}}
<img width="24" height="24" src="{notifications.image}" />
{{{ end }}}
{{{ end }}}
</div>
<div>
<p class="mb-1">
<a class="text-reset" component="notifications/item/link" href="{notifications.path}">{notifications.bodyShort}</a>
</p>
<p class="timestamp">
<span class="timeago small text-reset" title="{notifications.datetimeISO}"></span>
</p>
</div>
</li>
{{{end}}}
</ul>
<!-- IMPORT partials/paginator.tpl -->
</div>


View file

@ -0,0 +1,9 @@
<div class="form-group">
<label for="agree-terms">[[register:terms_of_use]]</label>
<div class="tos">{termsOfUse}</div>
<div class="checkbox">
<label>
<input type="checkbox" name="agree-terms" id="agree-terms"> [[register:agree_to_terms_of_use]]
</label>
</div>
</div>

View file

@ -0,0 +1,23 @@
<li component="categories/category" data-cid="{../cid}" data-parent-cid="{../parentCid}" class="row clearfix">
<meta itemprop="name" content="{../name}">

<div class="content col-10 depth-{../depth}">
<div class="float-start">
{buildCategoryIcon(@value, "48px", "rounded-circle")}
</div>

<h2 class="title">
<!-- IMPORT partials/categories/link.tpl -->
</h2>
<div>
<!-- IF ../descriptionParsed -->
<div class="description text-muted">
{../descriptionParsed}
</div>
<!-- ENDIF ../descriptionParsed -->
</div>
</div>
<div class="col-2">
<!-- IMPORT partials/category/watch.tpl -->
</div>
</li>

View file

@ -0,0 +1,46 @@
<!-- IMPORT partials/breadcrumbs.tpl -->

<div data-widget-area="header">
{{{each widgets.header}}}
{{widgets.header.html}}
{{{end}}}
</div>

<div class="cover" component="account/cover" style="background-image: url({cover:url}); background-position: {cover:position};">
<div class="avatar-wrapper" data-uid="{uid}">
<!-- IF picture -->
<img src="{picture}" class="avatar avatar-rounded" style="--avatar-size: 128px;" />
<!-- ELSE -->
<div class="avatar avatar-rounded" style="background-color: {icon:bgColor}; --avatar-size: 128px;" title="{username}">{icon:text}</div>
<!-- ENDIF picture -->
<i component="user/status" class="fa fa-circle status {status}" title="[[global:{status}]]"></i>

<!-- IF loggedIn -->
<!-- IF !isSelf -->
<button class="btn-morph persona-fab <!-- IF isFollowing -->heart<!-- ELSE -->plus<!-- ENDIF isFollowing -->" title="<!-- IF isFollowing -->[[global:unfollow]]<!-- ELSE -->[[global:follow]]<!-- ENDIF isFollowing -->">
<span>
<span class="s1"></span>
<span class="s2"></span>
<span class="s3"></span>
</span>
</button>
<!-- ENDIF !isSelf -->
<!-- ENDIF loggedIn -->
</div>

<div class="container">
<!-- IMPORT partials/account/menu.tpl -->

<!-- IF allowCoverPicture -->
<!-- IF canEdit -->
<div class="controls">
<span class="upload"><i class="fa fa-fw fa-4x fa-upload"></i></span>
<span class="resize"><i class="fa fa-fw fa-4x fa-arrows"></i></span>
<span class="remove"><i class="fa fa-fw fa-4x fa-times"></i></span>
</div>
<div class="save">[[groups:cover-save]] <i class="fa fa-fw fa-floppy-o"></i></div>
<div class="indicator">[[groups:cover-saving]] <i class="fa fa-fw fa-refresh fa-spin"></i></div>
<!-- ENDIF canEdit -->
<!-- ENDIF allowCoverPicture -->
</div>
</div>

View file

@ -0,0 +1,101 @@
<div class="btn-group account-fab bottom-sheet">
<button type="button" class="persona-fab dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-ellipsis-v"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end account-sub-links">
<!-- IF loggedIn -->
<!-- IF !isSelf -->
<!-- IF !banned -->
<!-- IF !config.disableChat -->
<li class="<!-- IF !hasPrivateChat -->hidden<!-- ENDIF !hasPrivateChat -->">
<a class="dropdown-item" component="account/chat" href="#">[[user:chat_with, {username}]]</a>
</li>
<li>
<a class="dropdown-item" component="account/new-chat" href="#">[[user:new_chat_with, {username}]]</a>
</li>
<!-- ENDIF !config.disableChat -->
<li>
<a class="dropdown-item" component="account/flag" href="#">[[user:flag-profile]]</a>
</li>
<li>
<a class="dropdown-item" component="account/block" href="#"><!-- IF !../isBlocked -->[[user:block_user]]<!-- ELSE -->[[user:unblock_user]]<!-- END --></a>
</li>
<li role="separator" class="dropdown-divider"></li>
<!-- ENDIF !banned -->
<!-- ENDIF !isSelf -->
<!-- ENDIF loggedIn -->
<li>
<a class="dropdown-item" href="{config.relative_path}/user/{userslug}" class="d-inline-block" id="profile">[[user:profile]]</a>
</li>
<!-- IF canEdit -->
<li><a class="dropdown-item" href="{config.relative_path}/user/{userslug}/edit">[[user:edit]]</a></li>
<li><a class="dropdown-item" href="{config.relative_path}/user/{userslug}/settings">[[user:settings]]</a></li>
<!-- ENDIF canEdit -->

<!-- IF !isSelf -->
{{{ if (canBan || canMute) }}}
<li role="separator" class="dropdown-divider"></li>
<li class="dropdown-header">[[user:admin_actions_label]]</li>
{{{ end }}}
{{{ if canBan }}}
<li class="<!-- IF banned -->hide<!-- ENDIF banned -->">
<a class="dropdown-item" component="account/ban" href="#">[[user:ban_account]]</a>
</li>
<li class="<!-- IF !banned -->hide<!-- ENDIF !banned -->">
<a class="dropdown-item" component="account/unban" href="#">[[user:unban_account]]</a>
</li>
{{{ end }}}
{{{ if canMute }}}
<li class="<!-- IF muted -->hide<!-- ENDIF muted -->">
<a class="dropdown-item" component="account/mute" href="#">[[user:mute_account]]</a>
</li>
<li class="<!-- IF !muted -->hide<!-- ENDIF !muted -->">
<a class="dropdown-item" component="account/unmute" href="#">[[user:unmute_account]]</a>
</li>
{{{ end }}}
<!-- IF isAdmin -->
<li>
<a component="account/delete-account" href="#" class="dropdown-item">[[user:delete_account_as_admin]]</a>
<a component="account/delete-content" href="#" class="dropdown-item">[[user:delete_content]]</a>
<a component="account/delete-all" href="#" class="dropdown-item">[[user:delete_all]]</a>
</li>
<!-- ENDIF isAdmin -->
<!-- ENDIF !isSelf -->

<li role="separator" class="dropdown-divider"></li>
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/following">[[user:following]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.following}">{counts.following}</span></a></li>
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/followers">[[user:followers]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.followers}">{counts.followers}</span></a></li>
<!-- IF canEdit -->
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/blocks">[[user:blocks]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.blocks}">{counts.blocks}</span></a></li>
<!-- ENDIF canEdit -->
<li role="separator" class="dropdown-divider"></li>
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/topics">[[global:topics]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.topics}">{counts.topics}</span></a></li>
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/posts">[[global:posts]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.posts}">{counts.posts}</span></a></li>
<!-- IF !reputation:disabled -->
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/best">[[global:best]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.best}">{counts.best}</span></a></li>
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/controversial">[[global:controversial]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.controversial}">{counts.controversial}</span></a></li>
<!-- ENDIF !reputation:disabled -->
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/groups">[[global:header.groups]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.groups}">{counts.groups}</span></a></li>

<!-- IF canEdit -->
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/categories">[[user:watched_categories]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.categoriesWatched}">{counts.categoriesWatched}</span></a></li>
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/bookmarks">[[user:bookmarks]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.bookmarks}">{counts.bookmarks}</span></a></li>
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/watched">[[user:watched]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.watched}">{counts.watched}</span></a></li>
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/ignored">[[user:ignored]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.ignored}">{counts.ignored}</span></a></li>
<!-- IF !reputation:disabled -->
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/upvoted">[[global:upvoted]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.upvoted}">{counts.upvoted}</span></a></li>
<!-- IF !downvote:disabled -->
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/downvoted">[[global:downvoted]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.downvoted}">{counts.downvoted}</span></a></li>
<!-- ENDIF !downvote:disabled -->
<!-- ENDIF !reputation:disabled -->
<li><a class="dropdown-item d-flex justify-content-between align-items-center" href="{config.relative_path}/user/{userslug}/uploads">[[global:uploads]] <span class="badge bg-secondary formatted-number rounded-pill ms-2" title="{counts.uploaded}">{counts.uploaded}</span></a></li>
<!-- ENDIF canEdit -->

{{{each profile_links}}}
<!-- IF @first -->
<li role="separator" class="dropdown-divider"></li>
<!-- ENDIF @first -->
<li id="{profile_links.id}" class="plugin-link <!-- IF profile_links.public -->public<!-- ELSE -->private<!-- ENDIF profile_links.public -->"><a class="dropdown-item" href="{config.relative_path}/user/{userslug}/{profile_links.route}"><!-- IF ../icon --><i class="fa fa-fw {profile_links.icon}"></i> <!-- END -->{profile_links.name}</a></li>
{{{end}}}
</ul>
</div>

View file

@ -0,0 +1,18 @@
<!-- IF breadcrumbs.length -->
<ol class="breadcrumb" itemscope="itemscope" itemprop="breadcrumb" itemtype="http://schema.org/BreadcrumbList">
{{{each breadcrumbs}}}
<li<!-- IF @last --> component="breadcrumb/current"<!-- ENDIF @last --> itemscope="itemscope" itemprop="itemListElement" itemtype="http://schema.org/ListItem" class="breadcrumb-item <!-- IF @last -->active<!-- ENDIF @last -->">
<meta itemprop="position" content="{@index}" />
{{{ if ./url }}}<a href="{breadcrumbs.url}" itemprop="item">{{{ end }}}
<span itemprop="name">
{breadcrumbs.text}
<!-- IF @last -->
<!-- IF !feeds:disableRSS -->
<!-- IF rssFeedUrl --><a target="_blank" href="{rssFeedUrl}" itemprop="item"><i class="fa fa-rss-square"></i></a><!-- ENDIF rssFeedUrl --><!-- ENDIF !feeds:disableRSS -->
<!-- ENDIF @last -->
</span>
{{{ if ./url }}}</a>{{{ end }}}
</li>
{{{end}}}
</ol>
<!-- ENDIF breadcrumbs.length -->

View file

@ -0,0 +1,22 @@
<noscript><div class="dropdown" component="category-selector"></noscript>
<button component="category/post" for="category-dropdown-check" class="btn btn-primary text-nowrap" id="new_topic" role="button">
[[category:new_topic_button]]
</button>
<noscript>
<input type="checkbox" class="hidden" id="category-dropdown-check" aria-hidden="true">
<ul component="category/list" class="dropdown-menu category-dropdown-menu" role="menu">
{{{each categories}}}
<li role="presentation" class="category {{{if categories.disabledClass}}}disabled{{{end}}}">
<a role="menu-item" href="{config.relative_path}/compose?cid={categories.cid}">{categories.level}
<span component="category-markup">
<div class="category-item d-inline-block">
{buildCategoryIcon(@value, "24px", "rounded-circle")}
{categories.name}
</div>
</span>
</a>
</li>
{{{end}}}
</ul>
</div>
</noscript>

View file

@ -0,0 +1,60 @@
<li component="categories/category" data-cid="{../cid}" data-numRecentReplies="1" class="row clearfix category-{../cid}">
<meta itemprop="name" content="{../name}">

<div class="content col-12 <!-- IF config.hideCategoryLastPost -->col-md-10 col-sm-12<!-- ELSE -->col-md-7 col-sm-9<!-- ENDIF config.hideCategoryLastPost -->">
<div class="float-start">
{buildCategoryIcon(@value, "48px", "rounded-circle")}
</div>
<h2 class="title">
<!-- IMPORT partials/categories/link.tpl -->
</h2>
<div>
<!-- IF ../descriptionParsed -->
<div class="description text-muted">
{../descriptionParsed}
</div>
<!-- ENDIF ../descriptionParsed -->
<!-- IF !config.hideSubCategories -->
{{{ if ../children.length }}}
<div class="category-children">
{{{ each ../children }}}
{{{ if !../isSection }}}
<span class="category-children-item">
{buildCategoryIcon(@value, "24px", "rounded-circle")}
{{{ if ../link }}}
<a href="{../link}">{../name}</a>
{{{ else }}}
<a href="{config.relative_path}/category/{../slug}">{../name}</a>
{{{ end }}}
</span>
{{{ end }}}
{{{ end }}}
</div>
{{{ end }}}
<!-- ENDIF !config.hideSubCategories -->
</div>
<span class="d-block d-sm-none float-end">
<!-- IF ../teaser.timestampISO -->
<a class="permalink" href="{../teaser.url}">
<small class="timeago" title="{../teaser.timestampISO}"></small>
</a>
<!-- ENDIF ../teaser.timestampISO -->
</span>
</div>

<!-- IF !../link -->
<div class="col-md-1 d-none d-md-block stats text-muted">
<span class="{../unread-class} human-readable-number" title="{../totalTopicCount}">{../totalTopicCount}</span><br />
<small>[[global:topics]]</small>
</div>
<div class="col-md-1 d-none d-md-block stats text-muted">
<span class="{../unread-class} human-readable-number" title="{../totalPostCount}">{../totalPostCount}</span><br />
<small>[[global:posts]]</small>
</div>
<!-- IF !config.hideCategoryLastPost -->
<div class="col-md-3 col-sm-3 teaser d-none d-sm-block" component="topic/teaser">
<!-- IMPORT partials/categories/lastpost.tpl -->
</div>
<!-- ENDIF !config.hideCategoryLastPost -->
<!-- ENDIF !../link -->
</li>

View file

@ -0,0 +1,26 @@
<div class="lastpost background-link-container" style="border-color: {../bgColor}">
{{{each ./posts}}}
<!-- IF @first -->
<div component="category/posts">
<a class="background-link" href="{config.relative_path}/topic/{../topic.slug}<!-- IF ../index -->/{../index}<!-- ENDIF ../index -->"></a>
<p>
<a href="{config.relative_path}/user/{../user.userslug}">{buildAvatar(posts.user, "24px", true)}</a>
<a class="permalink text-muted" href="{config.relative_path}/topic/{../topic.slug}<!-- IF ../index -->/{../index}<!-- ENDIF ../index -->">
<small class="timeago" title="{../timestampISO}"></small>
</a>
</p>
<div class="post-content">
{../content}
</div>
</div>
<!-- ENDIF @first -->
{{{end}}}

<!-- IF !../posts.length -->
<div component="category/posts">
<div class="post-content">
[[category:no_new_posts]]
</div>
</div>
<!-- ENDIF !../posts.length -->
</div>

View file

@ -0,0 +1,11 @@
<!-- IF ../isSection -->
{../name}
<!-- ELSE -->
<!-- IF ../link -->
<a href="{../link}" itemprop="url">
<!-- ELSE -->
<a href="{config.relative_path}/category/{../slug}" itemprop="url">
<!-- ENDIF ../link -->
{../name}
</a>
<!-- ENDIF ../isSection -->

View file

@ -0,0 +1,29 @@
<button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
{{{ if selectedCategory }}}
<span class="category-item">
{buildCategoryIcon(selectedCategory, "24px", "rounded-circle")}
<span class="visible-sm-inline visible-md-inline visible-lg-inline">{selectedCategory.name}</span>
</span>
{{{ else }}}
<span class="visible-sm-inline visible-md-inline visible-lg-inline">[[unread:all_categories]]</span><span class="visible-xs-inline"><i class="fa fa-fw fa-list"></i></span>{{{ end }}} <span class="caret"></span>
</button>
<div component="category-selector-search" class="hidden position-absolute">
<input type="text" class="form-control" autocomplete="off">
</div>
<ul component="category/list" class="dropdown-menu category-dropdown-menu" role="menu">
<li role="presentation" class="category" data-all="all">
<a class="dropdown-item" role="menu-item" href="{config.relative_path}/{allCategoriesUrl}"><i component="category/select/icon" class="fa fa-fw fa-check {{{if selectedCategory}}}invisible{{{end}}}"></i> [[unread:all_categories]]</a>
</li>
{{{each categoryItems}}}
<li role="presentation" class="category {{{ if ../disabledClass }}}disabled{{{ end }}}" data-cid="{../cid}" data-parent-cid="{../parentCid}" data-name="{../name}">
<a class="dropdown-item" role="menu-item" href="#">{../level}<i component="category/select/icon" class="fa fa-fw fa-check {{{ if !../selected }}}invisible{{{ end }}}"></i>
<span component="category-markup" style="{{{ if ../match }}}font-weight: bold;{{{end}}}">
<div class="category-item d-inline-block">
{buildCategoryIcon(@value, "24px", "rounded-circle")}
{./name}
</div>
</span>
</a>
</li>
{{{end}}}
</ul>

View file

@ -0,0 +1,3 @@
<div component="category/dropdown" class="btn-group right category-dropdown-container bottom-sheet">
<!-- IMPORT partials/category-filter-content.tpl -->
</div>

View file

@ -0,0 +1,3 @@
<div component="category/dropdown" class="btn-group category-dropdown-container bottom-sheet">
<!-- IMPORT partials/category-filter-content.tpl -->
</div>

View file

@ -0,0 +1,33 @@
<button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
<span component="category-selector-selected">
{{{ if (selectedCategory && !showCategorySelectLabel) }}}
<span class="category-item">
{buildCategoryIcon(selectedCategory, "24px", "rounded-circle")}
{selectedCategory.name}
</span>
{{{ else }}}
<span class="visible-sm-inline visible-md-inline visible-lg-inline">{{{ if selectCategoryLabel }}}{selectCategoryLabel}{{{ else }}}[[topic:thread_tools.select_category]]{{{ end }}}</span><span class="visible-xs-inline"><i class="fa fa-fw {{{ if selectCategoryIcon }}}{selectCategoryIcon}{{{ else }}}fa-list{{{ end }}}"></i></span>
{{{ end }}}</span> <span class="caret"></span>
</button>
<div component="category-selector-search" class="hidden position-absolute">
<input type="text" class="form-control" autocomplete="off">
</div>
<ul component="category/list" class="dropdown-menu category-dropdown-menu" role="menu">
<li component="category/no-matches" role="presentation" class="category hidden">
<a class="dropdown-item" role="menu-item">[[search:no-matches]]</a>
</li>
{{{each categoryItems}}}
<li role="presentation" class="category {{{ if ../disabledClass }}}disabled {{{ end }}}" data-cid="{../cid}" data-name="{../name}" data-parent-cid="{../parentCid}">
<a class="dropdown-item" role="menu-item">{../level}
<span component="category-markup" style="{{{ if ../match }}}font-weight: bold;{{{end}}}">
<div class="category-item d-inline-block">
{{{ if ./icon }}}
{buildCategoryIcon(@value, "24px", "rounded-circle")}
{{{ end }}}
{./name}
</div>
</span>
</a>
</li>
{{{ end }}}
</ul>

View file

@ -0,0 +1,3 @@
<div component="category-selector" class="btn-group right category-dropdown-container bottom-sheet">
<!-- IMPORT partials/category-selector-content.tpl -->
</div>

View file

@ -0,0 +1,3 @@
<div component="category-selector" class="btn-group bottom-sheet">
<!-- IMPORT partials/category-selector-content.tpl -->
</div>

View file

@ -0,0 +1,15 @@
<div class="btn-group bottom-sheet" component="thread/sort">
<button class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" type="button">
<span class="visible-sm-inline visible-md-inline visible-lg-inline">[[topic:sort_by]]</span>
<span class="visible-xs-inline"><i class="fa fa-fw fa-sort"></i></span>
<span class="caret"></span>
</button>

<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="#" class="newest_to_oldest" data-sort="newest_to_oldest"><i class="fa fa-fw"></i> [[topic:newest_to_oldest]]</a></li>
<li><a class="dropdown-item" href="#" class="oldest_to_newest" data-sort="oldest_to_newest"><i class="fa fa-fw"></i> [[topic:oldest_to_newest]]</a></li>
<li><a class="dropdown-item" href="#" class="most_posts" data-sort="most_posts"><i class="fa fa-fw"></i> [[topic:most_posts]]</a></li>
<li><a class="dropdown-item" href="#" class="most_votes" data-sort="most_votes"><i class="fa fa-fw"></i> [[topic:most_votes]]</a></li>
<li><a class="dropdown-item" href="#" class="most_views" data-sort="most_views"><i class="fa fa-fw"></i> [[topic:most_views]]</a></li>
</ul>
</div>

View file

@ -0,0 +1,18 @@
{{{ if children.length }}}
<div class="subcategory">
{{{ if hasMoreSubCategories }}}
<div class="mb-2"><!-- IMPORT partials/category-selector.tpl --></div>
{{{ else }}}
<p>[[category:subcategories]]</p>
{{{ end }}}

<ul component="category/subcategory/container" class="categories list-unstyled" itemscope itemtype="http://www.schema.org/ItemList">
{{{each children}}}
<!-- IMPORT partials/categories/item.tpl -->
{{{end}}}
</ul>
{{{ if hasMoreSubCategories}}}
<button class="btn btn-outline-secondary mb-2" component="category/load-more-subcategories">[[category:x-more-categories, {subCategoriesLeft}]]</button>
{{{ end }}}
</div>
{{{ end }}}

View file

@ -0,0 +1,3 @@
{{{ each tags }}}
<a href="{config.relative_path}/tags/{topics.tags.value}"><span class="tag-item" data-tag="{topics.tags.value}">{topics.tags.value}</span><span class="tag-topic-count human-readable-number" title="{topics.tags.score}">{topics.tags.score}</span></a>
{{{ end }}}

View file

@ -0,0 +1,81 @@
<!-- IF showTopicTools -->
<div class="btn-group thread-tools bottom-sheet">
<button class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" type="button">
<span class="visible-sm-inline visible-md-inline visible-lg-inline">[[topic:thread_tools.title]]</span>
<span class="visible-xs-inline"><i class="fa fa-fw fa-gear"></i></span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a component="topic/mark-unread-for-all" href="#" class="dropdown-item">
<i class="fa fa-fw fa-inbox"></i> [[topic:thread_tools.markAsUnreadForAll]]
</a>
</li>
<li>
<a component="topic/pin" href="#" class="dropdown-item">
<i class="fa fa-fw fa-thumb-tack"></i> [[topic:thread_tools.pin]]
</a>
</li>
<li>
<a component="topic/unpin" href="#" class="hidden dropdown-item">
<i class="fa fa-fw fa-thumb-tack fa-rotate-90"></i> [[topic:thread_tools.unpin]]
</a>
</li>

<li>
<a component="topic/lock" href="#" class="dropdown-item">
<i class="fa fa-fw fa-lock"></i> [[topic:thread_tools.lock]]
</a>
</li>
<li>
<a component="topic/unlock" href="#" class="hidden dropdown-item" >
<i class="fa fa-fw fa-unlock"></i> [[topic:thread_tools.unlock]]
</a>
</li>

<li class="dropdown-divider"></li>

<li>
<a component="topic/move" href="#" class="dropdown-item">
<i class="fa fa-fw fa-arrows"></i> [[topic:thread_tools.move]]
</a>
</li>
{{{if template.category}}}
<li>
<a component="topic/move-all" href="#" class="dropdown-item">
<i class="fa fa-fw fa-arrows"></i> [[topic:thread_tools.move_all]]
</a>
</li>
{{{end}}}
<li>
<a component="topic/merge" href="#" class="dropdown-item">
<i class="fa fa-fw fa-code-fork"></i> [[topic:thread_tools.merge]]
</a>
</li>

<li class="dropdown-divider"></li>

<li>
<a component="topic/delete" href="#" class="dropdown-item">
<i class="fa fa-fw fa-trash-o"></i> [[topic:thread_tools.delete]]
</a>
</li>
<li>
<a component="topic/restore" href="#" class="hidden dropdown-item">
<i class="fa fa-fw fa-history"></i> [[topic:thread_tools.restore]]
</a>
</li>
<li>
<a component="topic/purge" href="#" class="hidden dropdown-item">
<i class="fa fa-fw fa-eraser"></i> [[topic:thread_tools.purge]]
</a>
</li>

{{{each thread_tools}}}
<li>
<a href="#" class="dropdown-item {thread_tools.class}"><i class="fa fa-fw {thread_tools.icon}"></i> {thread_tools.title}</a>
</li>
{{{end}}}
</ul>
</div>
<!-- ENDIF showTopicTools -->

View file

@ -0,0 +1,21 @@
<!-- IF config.loggedIn -->
<div class="btn-group topic-watch-dropdown bottom-sheet" component="topic/watch">

<button class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" type="button">
<span component="category/watching/menu" <!-- IF !../isWatched -->class="hidden"<!-- ENDIF !../isWatched -->><i class="fa fa-fw fa-inbox"></i><span class="visible-sm-inline visible-md-inline visible-lg-inline"> [[category:watching]]</span></span>

<span component="category/notwatching/menu" <!-- IF !../isNotWatched -->class="hidden"<!-- ENDIF !../isNotWatched -->><i class="fa fa-fw fa-clock-o"></i><span class="visible-sm-inline visible-md-inline visible-lg-inline"> [[category:not-watching]]</span></span>

<span component="category/ignoring/menu" <!-- IF !../isIgnored -->class="hidden"<!-- ENDIF !../isIgnored -->><i class="fa fa-fw fa-eye-slash"></i><span class="visible-sm-inline visible-md-inline visible-lg-inline"> [[category:ignoring]]</span></span>
<span class="caret"></span>
</button>

<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="#" component="category/watching" data-state="watching"><i component="category/watching/check" class="fa fa-fw <!-- IF ../isWatched -->fa-check<!-- ENDIF ../isWatched -->"></i><i class="fa fa-fw fa-inbox"></i> [[category:watching]]<p class="help-text"><small>[[category:watching.description]]</small></p></a></li>

<li><a class="dropdown-item" href="#" component="category/notwatching" data-state="notwatching"><i component="category/notwatching/check" class="fa fa-fw <!-- IF ../isNotWatched -->fa-check<!-- ENDIF ../isNotWatched -->"></i><i class="fa fa-fw fa-clock-o"></i> [[category:not-watching]]<p class="help-text"><small>[[category:not-watching.description]]</small></p></a></li>

<li><a class="dropdown-item" href="#" component="category/ignoring" data-state="ignoring"><i component="category/ignoring/check" class="fa fa-fw <!-- IF ../isIgnored -->fa-check<!-- ENDIF ../isIgnored -->"></i><i class="fa fa-fw fa-eye-slash"></i> [[category:ignoring]]<p class="help-text"><small>[[category:ignoring.description]]</small></p></a></li>
</ul>
</div>
<!-- ENDIF config.loggedIn -->

View file

@ -0,0 +1,41 @@
{{{ if config.loggedIn }}}
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link" href="#" data-bs-target="#notifications" data-bs-toggle="tab"><span class="counter unread-count" component="notifications/icon" data-content="{unreadCount.notification}"></span> <i class="fa fa-fw fa-bell"></i></a>
</li>
{{{ if !config.disableChat }}}
<li class="nav-item">
<a class="nav-link" href="#" data-bs-target="#chats" data-bs-toggle="tab"><i class="counter unread-count" component="chat/icon" data-content="{unreadCount.chat}"></i> <i class="fa fa-fw fa-comment"></i></a>
</li>
{{{ end }}}
<li class="nav-item">
<a class="nav-link active" href="#" data-bs-target="#profile" data-bs-toggle="tab">
{buildAvatar(user, "24px", true, "user-icon")}
<i component="user/status" class="fa fa-fw fa-circle status {user.status}"></i>
</a>
</li>
</ul>

<div class="tab-content">
<div class="tab-pane fade show active" id="profile">
<section class="menu-section" data-section="profile">
<ul class="menu-section-list dropdown-menu show text-bg-dark w-100 border-0" component="header/usercontrol"></ul>
</section>
</div>
<div class="tab-pane fade" id="notifications">
<section class="menu-section text-bg-dark" data-section="notifications">
<ul class="menu-section-list notification-list-mobile" component="notifications/list"></ul>
<p class="menu-section-list"><a href="{relative_path}/notifications">[[notifications:see_all]]</a></p>
</section>
</div>
{{{ if !config.disableChat }}}
<div class="tab-pane fade" id="chats">
<section class="menu-section text-bg-dark" data-section="chats">
<ul class="menu-section-list chat-list" component="chat/list">
<a class="navigation-link" href="{relative_path}/user/{user.userslug}/chats">[[modules:chat.see_all]]</a>
</ul>
</section>
</div>
{{{ end }}}
</div>
{{{ end }}}

View file

@ -0,0 +1,38 @@
<!-- IF rooms.length -->
{{{each rooms}}}
<li class="<!-- IF ../unread -->unread<!-- ENDIF ../unread -->" data-roomid="{rooms.roomId}">
{{{each rooms.users}}}
<!-- IF @first -->
<div class="main-avatar">
<!-- IMPORT partials/chats/user.tpl -->
</div>
<!-- ENDIF @first -->
{{{end}}}

<ul class="members">
{{{each rooms.users}}}
<li>
<!-- IMPORT partials/chats/user.tpl -->
</li>
{{{end}}}
</ul>

<div class="notification-chat-content">
<strong class="room-name">
<!-- IF !rooms.lastUser.uid -->
<span>[[modules:chat.no-users-in-room]]</span>
<!-- ELSE -->
<!-- IF rooms.roomName -->{rooms.roomName}<!-- ELSE -->{rooms.usernames}<!-- ENDIF rooms.roomName -->
<!-- ENDIF !rooms.lastUser.uid -->
</strong>
<span class="teaser-content">
<strong class="teaser-username">{rooms.teaser.user.username}:</strong>
{rooms.teaser.content}
</span>
</div>
<div class="teaser-timestamp notification-chat-controls">{rooms.teaser.timeago}</div>
</li>
{{{end}}}
<!-- ELSE -->
<li class="no_active"><a href="#">[[modules:chat.no_active]]</a></li>
<!-- ENDIF rooms.length -->

View file

@ -0,0 +1,34 @@
<!-- IF roomId -->
<div component="chat/messages" class="expanded-chat" data-roomid="{roomId}">
<div component="chat/header" class="d-flex align-items-center px-3">
<span class="members flex-grow-1">
[[modules:chat.chatting_with]]:
{{{each users}}}
<a href="{config.relative_path}/uid/{../uid}">{../username}</a><!-- IF !@last -->,<!-- END -->
{{{end}}}
</span>

<button type="button" class="btn btn-link d-none d-md-block p-2 text-muted" data-action="pop-out" aria-hidden="true" aria-label="Pop Out"><i class="fa fa-compress"></i></button>
<!-- IMPORT partials/chats/options.tpl -->
<button type="button" class="btn-close" aria-hidden="true" aria-label="Close" data-action="close"></button>
</div>
<div class="position-relative">
<div component="chat/messages/scroll-up-alert" class="position-absolute scroll-up-alert alert alert-info hidden w-100" role="button" style="z-index: 1;">[[modules:chat.scroll-up-alert]]</div>
</div>
<ul class="chat-content">
<!-- IMPORT partials/chats/messages.tpl -->
</ul>
<div component="chat/composer">
<textarea component="chat/input" placeholder="[[modules:chat.placeholder]]" class="form-control chat-input mousetrap" rows="2"></textarea>
<button class="btn btn-primary" type="button" data-action="send"><i class="fa fa-fw fa-2x fa-paper-plane"></i></button>
<span component="chat/message/remaining">{maximumChatMessageLength}</span>
<form component="chat/upload" method="post" enctype="multipart/form-data">
<input type="file" name="files[]" multiple class="hidden"/>
</form>
</div>
</div>
<!-- ELSE -->
<div class="alert alert-info me-3">
[[modules:chat.no-messages]]
</div>
<!-- ENDIF roomId -->

View file

@ -0,0 +1,34 @@
<li component="chat/message" class="chat-message mx-2 pe-2 fw-light clear<!-- IF ../deleted --> deleted<!-- END -->" data-index="{messages.index}" data-mid="{messages.messageId}" data-uid="{messages.fromuid}" data-self="{messages.self}" data-break="{messages.newSet}" data-timestamp="{messages.timestamp}">
<div class="message-header">
<a href="{config.relative_path}/user/{messages.fromUser.userslug}">{buildAvatar(messages.fromUser, "32px", true, "not-responsive")}</a>
<span class="chat-user fw-bold"><a href="{config.relative_path}/user/{messages.fromUser.userslug}">{messages.fromUser.displayname}</a></span>
<!-- IF ../fromUser.banned -->
<span class="badge bg-danger">[[user:banned]]</span>
<!-- END -->
<!-- IF ../fromUser.deleted -->
<span class="badge bg-danger">[[user:deleted]]</span>
<!-- END -->
<small class="chat-timestamp text-muted ms-2 timeago" title="{messages.timestampISO}"></small>
<!-- IF messages.edited -->
<div class="text-muted float-end" title="[[global:edited]] {messages.editedISO}"><i class="fa fa-edit"></i></span></div>
<!-- ENDIF messages.edited -->
</div>
<div class="message-body-wrapper">
<div component="chat/message/body" class="message-body">
{messages.content}
</div>

<!-- IF !config.disableChatMessageEditing -->
<!-- IF messages.self -->
<div class="btn-group controls">
<button class="btn btn-sm btn-link" data-action="edit"><i class="fa fa-pencil"></i></button>
<button class="btn btn-sm btn-link" data-action="delete"><i class="fa fa-times"></i></button>
<button class="btn btn-sm btn-link" data-action="restore"><i class="fa fa-repeat"></i></button>
<!-- IF isAdminOrGlobalMod -->
<button class="btn btn-sm btn-link chat-ip" title="[[modules:chat.show-ip]]"><i class="fa fa-info-circle chat-ip-button"></i></button>
<!-- ENDIF isAdminOrGlobalMod -->
</div>
<!-- ENDIF messages.self -->
<!-- ENDIF !config.disableChatMessageEditing -->
</div>
</li>

View file

@ -0,0 +1,7 @@
{{{each messages}}}
{{{ if !./system }}}
<!-- IMPORT partials/chats/message.tpl -->
{{{ else }}}
<!-- IMPORT partials/chats/system-message.tpl -->
{{{ end }}}
{{{end}}}

View file

@ -0,0 +1,24 @@
<div class="dropdown">
<button class="btn btn-link p-2 text-muted align-text-top" data-bs-toggle="dropdown" component="chat/controlsToggle"><i class="fa fa-gear"></i></button>
<ul class="dropdown-menu dropdown-menu-end" component="chat/controls">
<li class="dropdown-header">[[modules:chat.options]]</li>
<li>
<a class="dropdown-item" href="#" data-action="members"><i class="fa fa-fw fa-cog"></i> [[modules:chat.manage-room]]</a>
</li>
<li>
<a class="dropdown-item" href="#" data-action="rename"><i class="fa fa-fw fa-edit"></i> [[modules:chat.rename-room]]</a>
</li>
<li>
<a class="dropdown-item" href="#" data-action="leave"><i class="fa fa-fw fa-sign-out"></i> [[modules:chat.leave]]</a>
</li>
<!-- IF users.length -->
<li role="separator" class="dropdown-divider"></li>
<li class="dropdown-header">[[modules:chat.in-room]]</li>
{{{each users}}}
<li>
<a class="dropdown-item" href="{config.relative_path}/uid/{../uid}">{buildAvatar(users, "24px", true)} {../username}</a>
</li>
{{{end}}}
<!-- END -->
</ul>
</div>

View file

@ -0,0 +1,31 @@
<li component="chat/recent/room" data-roomid="{rooms.roomId}" class="<!-- IF rooms.unread -->unread<!-- ENDIF rooms.unread -->">
{{{each rooms.users}}}
<!-- IF @first -->
<div class="main-avatar me-2">
<!-- IMPORT partials/chats/user.tpl -->
</div>
<!-- ENDIF @first -->
{{{end}}}

{{{ if rooms.users.length }}}
<ul class="members">
{{{each rooms.users}}}
<li>
<!-- IMPORT partials/chats/user.tpl -->
</li>
{{{end}}}
</ul>
{{{ end }}}

<div class="notification-chat-content flex-grow-1">
<!-- IF !rooms.lastUser.uid -->
<div class="p-3 text-center h-100">
<span>[[modules:chat.no-users-in-room]]</span>
</div>
<!-- ELSE -->
<strong class="room-name">
<span component="chat/title"><!-- IF rooms.roomName -->{rooms.roomName}<!-- ELSE -->{rooms.usernames}<!-- ENDIF rooms.roomName --></span>
</strong>
<!-- ENDIF !rooms.lastUser.uid -->
</div>
</li>

View file

@ -0,0 +1,3 @@
<li component="chat/system-message" class="system-message fs-6 py-3 clear" data-index="{messages.index}" data-mid="{messages.messageId}" data-uid="{messages.fromuid}" data-self="{messages.self}" data-break="0" data-timestamp="{messages.timestamp}">
[[modules:chat.system.{messages.content}, {messages.fromUser.username}]]
</li>

View file

@ -0,0 +1 @@
<a href="{config.relative_path}/user/{rooms.users.userslug}">{buildAvatar(rooms.users, "28px", true)}</a>

View file

@ -0,0 +1,4 @@
<div class="cookie-consent">
<button class="float-end btn btn-primary">{dismiss}</button>
{message} <a target="_blank" rel="noopener" href="{link_url}">{link}</a>
</div>

View file

@ -0,0 +1,97 @@
<div class="text-center">
<div class="card mb-3">
<div class="card-body collapse" id="flags-daily-wrapper" aria-expanded="false">
<div><canvas id="flags:daily" height="150"></canvas></div>
</div>
<div class="card-footer" data-bs-toggle="collapse" data-bs-target="#flags-daily-wrapper" aria-controls="#flags-daily-wrapper"><small>[[flags:graph-label]]</small>&nbsp;<i class="fa fa-sort"></i></div>
</div>
</div>

<div class="card mb-3">
<div class="card-header">
[[flags:quick-filters]]
</div>
<div class="card-body">
<ul>
<li><a href="{config.relative_path}/flags?quick=mine">[[flags:filter-quick-mine]]</a></li>
</ul>
</div>
</div>

<div class="card mb-3">
<div class="card-header">
[[flags:filters]]
</div>
<div class="card-body">
<form role="form" component="flags/filters">
<fieldset>
<div class="mb-3">
<label class="form-label" for="filter-cid">[[flags:filter-cid]]</label>
<div class="input-group">
<!-- IMPORT partials/category-filter.tpl -->
</div>
</div>
<div class="mb-3">
<label class="form-label" for="sort">[[flags:sort]]</label>
<select class="form-control" id="sort" name="sort">
<optgroup label="[[flags:sort-all]]">
<option value="newest">[[flags:sort-newest]]</option>
<option value="oldest">[[flags:sort-oldest]]</option>
<option value="reports">[[flags:sort-reports]]</option>
</optgroup>
<optgroup label="[[flags:sort-posts-only]]">
<option value="downvotes">[[flags:sort-downvotes]]</option>
<option value="upvotes">[[flags:sort-upvotes]]</option>
<option value="replies">[[flags:sort-replies]]</option>
</optgroup>
</select>
</div>
<div class="mb-3">
<label class="form-label" for="filter-state">[[flags:filter-state]]</label>
<select class="form-control" id="filter-state" name="state">
<option value="">[[flags:state-all]]</option>
<option value="open">[[flags:state-open]]</option>
<option value="wip">[[flags:state-wip]]</option>
<option value="resolved">[[flags:state-resolved]]</option>
<option value="rejected">[[flags:state-rejected]]</option>
</select>
</div>

<div class="mb-3">
<label class="form-label" for="filter-type">[[flags:filter-type]]</label>
<select class="form-control" id="filter-type" name="type">
<option value="">[[flags:filter-type-all]]</option>
<option value="post">[[flags:filter-type-post]]</option>
<option value="user">[[flags:filter-type-user]]</option>
</select>
</div>
</fieldset>

<fieldset class="collapse{{{ if expanded }}} show{{{ end }}}" id="more-filters" aria-expanded="{expanded}">
<div class="mb-3">
<label class="form-label" for="filter-assignee">[[flags:filter-assignee]]</label>
<input type="text" class="form-control" id="filter-assignee" name="assignee" />
</div>

<div class="mb-3">
<label class="form-label" for="filter-targetUid">[[flags:filter-targetUid]]</label>
<input type="text" class="form-control" id="filter-targetUid" name="targetUid" />
</div>

<div class="mb-3">
<label class="form-label" for="filter-reporterId">[[flags:filter-reporterId]]</label>
<input type="text" class="form-control" id="filter-reporterId" name="reporterId" />
</div>
</fieldset>

<div class="d-grid gap-2">
{{{ if expanded }}}
<button type="button" class="btn btn-link" data-bs-toggle="collapse" data-bs-target="#more-filters" aria-controls="#more-filters" data-text-variant="[[flags:more-filters]] ">[[flags:fewer-filters]]&nbsp;<i class="fa fa-sort"></i></button>
{{{ else }}}
<button type="button" class="btn btn-link" data-bs-toggle="collapse" data-bs-target="#more-filters" aria-controls="#more-filters" data-text-variant="[[flags:fewer-filters]] ">[[flags:more-filters]]&nbsp;<i class="fa fa-sort"></i></button>
{{{ end }}}
<button type="button" id="apply-filters" class="btn btn-primary">[[flags:apply-filters]]</button>
</div>
</form>
</div>
</div>

View file

@ -0,0 +1,195 @@
<div class="card mb-3">
<div class="card-header">
<div class="fs-5">
<i class="fa fa-clock-o"></i> [[groups:details.pending]]
<!-- IF group.pending.length -->
<div class="btn-group float-end">
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
[[global:more]] <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a class="dropdown-item" href="#" data-ajaxify="false" data-action="acceptAll">[[groups:pending.accept_all]]</a></li>
<li><a class="dropdown-item" href="#" data-ajaxify="false" data-action="rejectAll">[[groups:pending.reject_all]]</a></li>
</ul>
</div>
<!-- ENDIF group.pending.length -->
</div>
</div>
<div class="card-body">
<table component="groups/pending" class="table table-striped table-hover">
<!-- IF !group.pending.length -->
<div class="alert alert-info">[[groups:pending.none]]</div>
<!-- ENDIF !group.pending.length -->
{{{each group.pending}}}
<tr data-uid="{group.pending.uid}">
<td class="p-2">
<a href="{config.relative_path}/user/{group.pending.userslug}">{buildAvatar(group.pending, "24px", true)}</a>
</td>
<td class="member-name p-2">
<a href="{config.relative_path}/user/{group.pending.userslug}">{group.pending.username}</a>
</td>
<td class="p-2">
<div class="btn-group float-end">
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
[[global:more]] <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a class="dropdown-item" href="#" data-ajaxify="false" data-action="accept">[[groups:pending.accept]]</a></li>
<li><a class="dropdown-item" href="#" data-ajaxify="false" data-action="reject">[[groups:pending.reject]]</a></li>
</ul>
</div>
</td>
</tr>
{{{end}}}
</table>
</div>
</div>
<div class="card mb-3">
<div class="card-header">
<span class="fs-5">
<i class="fa fa-gift"></i> [[groups:details.invited]]
</span>
</div>
<div class="card-body">
<div class="input-group mb-2">
<input class="form-control" type="text" component="groups/members/invite" placeholder="[[groups:invited.search]]"/>
<span class="input-group-text search-button"><i class="fa fa-search"></i></span>
</div>

<div class="mb-2">
<textarea class="form-control" component="groups/members/bulk-invite" placeholder="[[groups:bulk-invite-instructions]]"></textarea>
</div>

<div class="mb-2 clearfix">
<button class="btn btn-outline-secondary btn-sm float-end" component="groups/members/bulk-invite-button">[[groups:bulk-invite]]</button>
</div>

<table component="groups/invited" class="table table-striped table-hover">
<!-- IF !group.invited.length -->
<div class="alert alert-info">[[groups:invited.none]]</div>
<!-- ENDIF !group.invited.length -->
{{{each group.invited}}}
<tr data-uid="{group.invited.uid}">
<td class="p-2">
<a href="{config.relative_path}/user/{group.invited.userslug}">{buildAvatar(group.invited, "24px", true)}</a>
</td>
<td class="member-name p-2">
<a href="{config.relative_path}/user/{group.invited.userslug}">{group.invited.username}</a>
</td>
<td class="p-2">
<div class="btn-group float-end">
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
[[global:more]] <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a class="dropdown-item" href="#" data-ajaxify="false" data-action="rescindInvite">[[groups:invited.uninvite]]</a></li>
</ul>
</div>
</td>
</tr>
{{{end}}}
</table>
</div>
</div>

<div class="card mb-3">
<div class="card-header pointer" data-bs-toggle="collapse" data-bs-target=".options">
<span class="fs-5">
<i class="fa fa-caret-down float-end"></i>
<i class="fa fa-cogs"></i> [[groups:details.owner_options]]
</span>
</div>

<div class="card-body options collapse">
<form component="groups/settings" role="form">
<div class="mb-3">
<label class="form-label" for="name">[[groups:details.group_name]]</label>
<input <!-- IF group.system -->readonly<!-- ENDIF group.system --> class="form-control" name="name" id="name" type="text" value="{group.displayName}" />
</div>
<div class="mb-3">
<label class="form-label" for="name">[[groups:details.description]]</label>
<textarea class="form-control" name="description" id="description" type="text" maxlength="255">{group.description}</textarea>
</div>

<hr />
<div class="mb-3">
<label class="form-label" for="memberPostCids">[[groups:details.member-post-cids]]</label>
<div class="row">
<div class="col-md-6">
<input id="memberPostCids" type="text" class="form-control" value="{group.memberPostCids}">
</div>
<div class="col-md-6 member-post-cids-selector">
<!-- IMPORT partials/category-selector.tpl -->
</div>
</div>
</div>

<hr />

<div class="mb-3 user-title-option">
<label class="form-label" for="userTitle">[[groups:details.badge_text]]</label>
<input component="groups/userTitleOption" class="form-control" name="userTitle" id="userTitle" type="text" maxlength="40" value="{group.userTitleEscaped}"<!-- IF !group.userTitleEnabled --> disabled<!-- ENDIF !group.userTitleEnabled --> />
</div>

<div class="mb-3 user-title-option">
<label>[[groups:details.badge_preview]]</label><br />
<span class="badge rounded-1 text-uppercase text-truncate rounded-1 {{{ if !group.userTitleEnabled }}} hide{{{ end }}}" style="max-width:150px; color: {group.textColor}; background-color: {group.labelColor}"><i class="fa{{{ if group.icon }}} {group.icon}{{{ if ./userTitle}}}me-1{{{ end }}}{{{ end }}}"></i><span class="badge-text">{{{ if group.userTitle }}}{group.userTitle}{{{ end }}}</span></span>

<hr/>
<button component="groups/userTitleOption" type="button" class="btn btn-outline-secondary btn-sm" data-action="icon-select"<!-- IF !group.userTitleEnabled --> disabled<!-- ENDIF !group.userTitleEnabled -->>[[groups:details.change_icon]]</button>
<div>
<label class="form-label" for="labelColor" class="badge-color-label">[[groups:details.change_label_colour]]</label>
<input component="groups/userTitleOption" type="color" name="labelColor" value="<!-- IF group.labelColor -->{group.labelColor}<!-- ENDIF group.labelColor -->" />
</div>
<div>
<label class="form-label" for="color" class="badge-color-label">[[groups:details.change_text_colour]]</label>
<input component="groups/userTitleOption" type="color" name="textColor" value="<!-- IF group.textColor -->{group.textColor}<!-- ENDIF group.textColor -->" />
</div>
<input type="hidden" name="icon" value="<!-- IF group.icon -->{group.icon}<!-- ENDIF group.icon -->" />

<div id="icons" class="hidden">
<div class="icon-container">
<div class="row nbb-fa-icons">
<!-- IMPORT partials/fontawesome.tpl -->
</div>
</div>
</div>
</div>
<hr />
<div class="form-check">
<label class="form-check-label">[[groups:details.userTitleEnabled]]</label>
<input class="form-check-input" name="userTitleEnabled" type="checkbox"<!-- IF group.userTitleEnabled --> checked<!-- ENDIF group.userTitleEnabled -->>
</div>
<div class="form-check">
<label class="form-check-label">[[groups:details.private]]</label>
<input class="form-check-input" name="private" type="checkbox"<!-- IF group.private --> checked<!-- ENDIF group.private -->>
<!-- IF !allowPrivateGroups -->
<p class="form-text">
[[groups:details.private_system_help]]
</p>
<!-- ENDIF !allowPrivateGroups -->
<p class="form-text">
[[groups:details.private_help]]
</p>
</div>
<div class="form-check">
<label class="form-check-label">[[groups:details.disableJoinRequests]]</label>
<input class="form-check-input" name="disableJoinRequests" type="checkbox"<!-- IF group.disableJoinRequests --> checked<!-- ENDIF group.disableJoinRequests -->>
</div>
<div class="form-check">
<label class="form-check-label">[[groups:details.disableLeave]]</label>
<input class="form-check-input" name="disableLeave" type="checkbox"{{{if group.disableLeave}}} checked{{{end}}}>
</div>
<div class="form-check">
<label class="form-check-label">[[groups:details.hidden]]</label>
<input class="form-check-input" name="hidden" type="checkbox"<!-- IF group.hidden --> checked<!-- ENDIF group.hidden -->>
<p class="form-text">
[[groups:details.hidden_help]]
</p>
</div>

<button class="btn btn-link text-danger float-end" type="button" data-action="delete">[[groups:details.delete_group]]</button>
<button class="btn btn-primary" type="button" data-action="update">[[global:save_changes]]</button>
</form>
</div>
</div>

View file

@ -0,0 +1 @@
<a href="{config.relative_path}/groups/{./slug}" class="badge rounded-1 text-uppercase text-truncate" style="max-width: 150px;color:{./textColor};background-color: {./labelColor};"><i class="fa {{{ if ./icon }}}{./icon}{{{ if ./userTitle}}} me-1{{{ end }}}{{{else}}}hidden{{{ end }}}"></i><span class="badge-text">{{{ if ./userTitle }}}{./userTitle}{{{ end }}}</span></a>

View file

@ -0,0 +1,21 @@
{{{each groups}}}
<div class="col-lg-4 col-md-6 col-sm-12 mb-3" component="groups/summary" data-slug="{groups.slug}">
<div class="card h-100">
<a href="{config.relative_path}/groups/{groups.slug}" class="card-header list-cover" style="<!-- IF groups.cover:thumb:url -->background-image: url({groups.cover:thumb:url});<!-- ENDIF groups.cover:thumb:url -->">
<h5 class="card-title">{groups.displayName} <small>{groups.memberCount}</small></h5>
</a>
<div class="card-body">
<ul class="members">
{{{each groups.members}}}
<li>
<a href="{config.relative_path}/user/{groups.members.userslug}">{buildAvatar(groups.members, "24px", true)}</a>
</li>
{{{end}}}
<!-- IF groups.truncated -->
<li class="truncated"><i class="fa fa-ellipsis-h"></i></li>
<!-- ENDIF groups.truncated -->
</ul>
</div>
</div>
</div>
{{{end}}}

View file

@ -0,0 +1,41 @@
<div class="d-flex mb-3">
<!-- IF group.isOwner -->
<div class="flex-shrink-0">
<button component="groups/members/add" type="button" class="btn btn-primary me-3" title="[[groups:details.add-member]]"><i class="fa fa-user-plus"></i></button>
</div>
<!-- ENDIF group.isOwner -->
<div class="flex-grow-1">
<div class="input-group">
<input class="form-control" type="text" component="groups/members/search" placeholder="[[global:search]]"/>
<span class="input-group-text search-button"><i class="fa fa-search"></i></span>
</div>
</div>
</div>

<table component="groups/members" class="table table-striped table-hover" data-nextstart="{group.membersNextStart}">
<tbody>
{{{each group.members}}}
<tr data-uid="{group.members.uid}">
<td class="p-2">
<a href="{config.relative_path}/user/{group.members.userslug}">{buildAvatar(group.members, "24px", true)}</a>
</td>
<td class="member-name p-2">
<a class="align-text-top" href="{config.relative_path}/user/{group.members.userslug}">{group.members.username}</a>
<i title="[[groups:owner]]" class="user-owner-icon fa fa-star align-text-top text-warning <!-- IF !group.members.isOwner -->invisible<!-- ENDIF !group.members.isOwner -->"></i>

<!-- IF group.isOwner -->
<div class="owner-controls btn-group float-end">
<a class="btn btn-sm" href="#" data-ajaxify="false" data-action="toggleOwnership" title="[[groups:details.grant]]">
<i class="fa fa-star"></i>
</a>

<a class="btn btn-sm" href="#" data-ajaxify="false" data-action="kick" title="[[groups:details.kick]]">
<i class="fa fa-ban"></i>
</a>
</div>
<!-- ENDIF group.isOwner -->
</td>
</tr>
{{{end}}}
</tbody>
</table>

View file

@ -0,0 +1,85 @@
<li id="user_label" class="nav-item dropdown" title="[[global:header.profile]]">
<label for="user-control-list-check" class="" data-bs-toggle="dropdown" id="user_dropdown" role="button">
{buildAvatar(user, "32px", true)}
<span id="user-header-name" class="d-block d-sm-none">{user.username}</span>
</label>
<input type="checkbox" class="hidden" id="user-control-list-check" aria-hidden="true">
<ul id="user-control-list" component="header/usercontrol" class="dropdown-menu dropdown-menu-end" aria-labelledby="user_dropdown">
<li>
<a class="dropdown-item" component="header/profilelink" href="{relative_path}/user/{user.userslug}">
<i component="user/status" class="fa fa-fw fa-circle status {user.status}"></i> <span component="header/username">{user.username}</span>
</a>
</li>
<li role="presentation" class="dropdown-divider"></li>
<li><h6 class="dropdown-header">[[global:status]]</h6></li>
<li>
<a href="#" class="dropdown-item user-status" data-status="online">
<i class="fa fa-fw fa-circle status online"></i><span <!-- IF user.online -->class="bold"<!-- ENDIF user.online -->> [[global:online]]</span>
</a>
</li>
<li>
<a href="#" class="dropdown-item user-status" data-status="away">
<i class="fa fa-fw fa-circle status away"></i><span <!-- IF user.away -->class="bold"<!-- ENDIF user.away -->> [[global:away]]</span>
</a>
</li>
<li>
<a href="#" class="dropdown-item user-status" data-status="dnd">
<i class="fa fa-fw fa-circle status dnd"></i><span <!-- IF user.dnd -->class="bold"<!-- ENDIF user.dnd -->> [[global:dnd]]</span>
</a>
</li>
<li>
<a href="#" class="dropdown-item user-status" data-status="offline">
<i class="fa fa-fw fa-circle status offline"></i><span <!-- IF user.offline -->class="bold"<!-- ENDIF user.offline -->> [[global:invisible]]</span>
</a>
</li>
<li role="presentation" class="dropdown-divider"></li>
<li>
<a class="dropdown-item" component="header/profilelink/edit" href="{relative_path}/user/{user.userslug}/edit">
<i class="fa fa-fw fa-edit"></i> <span>[[user:edit-profile]]</span>
</a>
</li>
<li>
<a class="dropdown-item" component="header/profilelink/settings" href="{relative_path}/user/{user.userslug}/settings">
<i class="fa fa-fw fa-gear"></i> <span>[[user:settings]]</span>
</a>
</li>
{{{ if showModMenu }}}
<li role="presentation" class="dropdown-divider"></li>
<li><h6 class="dropdown-header">[[pages:moderator-tools]]</h6></li>
<li>
<a class="dropdown-item" href="{relative_path}/flags">
<i class="fa fa-fw fa-flag"></i> <span>[[pages:flagged-content]]</span>
</a>
</li>
<li>
<a class="dropdown-item" href="{relative_path}/post-queue">
<i class="fa fa-fw fa-list-alt"></i> <span>[[pages:post-queue]]</span>
</a>
</li>
<li>
<a class="dropdown-item" href="{relative_path}/ip-blacklist">
<i class="fa fa-fw fa-ban"></i> <span>[[pages:ip-blacklist]]</span>
</a>
</li>
{{{ else }}}
{{{ if postQueueEnabled }}}
<li>
<a class="dropdown-item" href="{relative_path}/post-queue">
<i class="fa fa-fw fa-list-alt"></i> <span>[[pages:post-queue]]</span>
</a>
</li>
{{{ end }}}
{{{ end }}}

<li role="presentation" class="dropdown-divider"></li>
<li component="user/logout">
<form method="post" action="{relative_path}/logout">
<input type="hidden" name="_csrf" value="{config.csrf_token}">
<input type="hidden" name="noscript" value="true">
<button type="submit" class="dropdown-item">
<i class="fa fa-fw fa-sign-out"></i><span> [[global:logout]]</span>
</button>
</form>
</li>
</ul>
</li>

186
templates/partials/menu.tpl Normal file
View file

@ -0,0 +1,186 @@
<div class="d-flex align-items-center me-auto" style="min-width: 0px;">
<button type="button" class="navbar-toggler border-0" id="mobile-menu">
<i class="fa fa-lg fa-fw fa-bars unread-count" ></i>
<span component="unread/icon" class="notification-icon fa fa-fw fa-book unread-count" data-content="{unreadCount.mobileUnread}" data-unread-url="{unreadCount.unreadUrl}"></span>
</button>
<div class="d-inline-flex align-items-center" style="min-width: 0px;">
{{{ if brand:logo }}}
<a class="navbar-brand" href="{{{ if brand:logo:url }}}{brand:logo:url}{{{ else }}}{relative_path}/{{{ end }}}">
<img alt="{brand:logo:alt}" class="{brand:logo:display} forum-logo d-inline-block align-text-bottom" src="{brand:logo}?{config.cache-buster}" />
</a>
{{{ end }}}
{{{ if config.showSiteTitle }}}
<a class="navbar-brand text-truncate" href="{{{ if title:url }}}{title:url}{{{ else }}}{relative_path}/{{{ end }}}">
{config.siteTitle}
</a>
{{{ end }}}
</div>
</div>

{{{ if config.searchEnabled }}}
<div class="navbar-search visible-xs">
<form action="{config.relative_path}/search" method="GET">
<button type="button" class="btn btn-link"><i class="fa fa-lg fa-fw fa-search" title="[[global:header.search]]"></i></button>
<input autocomplete="off" type="text" class="form-control hidden" name="term" placeholder="[[global:search]]"/>
<button class="btn btn-primary hidden" type="submit"></button>
<input type="text" class="hidden" name="in" value="{config.searchDefaultInQuick}" />
</form>
<div class="quick-search-container hidden">
<div class="quick-search-results-container"></div>
</div>
</div>
{{{ end }}}

{{{ if config.loggedIn }}}
<button type="button" class="navbar-toggler border-0" id="mobile-chats">
<span component="notifications/icon" class="notification-icon fa fa-fw fa-bell-o unread-count" data-content="{unreadCount.notification}"></span>
<span component="chat/icon" class="notification-icon fa fa-fw fa-comments unread-count" data-content="{unreadCount.chat}"></span>
{buildAvatar(user, "32px", true)}
</button>
{{{ end }}}

<div component="navbar/title" class="visible-xs hidden">
<span></span>
</div>

<div id="nav-dropdown" class="collapse navbar-collapse d-none d-lg-block">
<ul id="main-nav" class="navbar-nav me-auto mb-2 mb-lg-0">
{{{each navigation}}}
<!-- IF function.displayMenuItem, @index -->
<li class="nav-item {navigation.class}{{{ if navigation.dropdown }}} dropdown{{{ end }}}" title="{navigation.title}">
<a class="nav-link navigation-link {{{ if navigation.dropdown }}}dropdown-toggle{{{ end }}}"
{{{ if navigation.dropdown }}} href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" {{{ else }}} href="{navigation.route}"{{{ end }}} {{{ if navigation.id }}}id="{navigation.id}"{{{ end }}}{{{ if navigation.targetBlank }}} target="_blank"{{{ end }}}>
{{{ if navigation.iconClass }}}
<i class="fa fa-fw {navigation.iconClass}" data-content="{navigation.content}"></i>
{{{ end }}}
{{{ if navigation.text }}}
<span class="{navigation.textClass}">{navigation.text}</span>
{{{ end }}}
{{{ if navigation.dropdown}}}
<i class="fa fa-caret-down"></i>
{{{ end }}}
</a>
{{{ if navigation.dropdown }}}
<ul class="dropdown-menu">
{navigation.dropdownContent}
</ul>
{{{ end }}}
</li>
<!-- ENDIF function.displayMenuItem -->
{{{end}}}
</ul>
<ul class="navbar-nav mb-2 mb-lg-0 hidden-xs">
<li class="nav-item">
<a href="#" id="reconnect" class="nav-link hide" title="[[global:reconnecting-message, {config.siteTitle}]]">
<i class="fa fa-check"></i>
</a>
</li>
</ul>
{{{ if config.searchEnabled }}}
<div class="navbar-nav mb-2 mb-lg-0 position-relative">
<form component="search/form" id="search-form" class="d-flex justify-content-end align-items-center" role="search" method="GET">
<div component="search/fields" class="hidden" id="search-fields">
<div class="input-group flex-nowrap">
<input autocomplete="off" type="text" class="form-control" placeholder="[[global:search]]" name="query" value="">

<button href="#" class="btn btn-outline-secondary">
<i class="fa fa-gears fa-fw advanced-search-link"></i>
</button>
</div>

<div id="quick-search-container" class="quick-search-container dropdown-menu d-block mt-2 hidden">
<div class="form-check filter-category mb-2 ms-4">
<input class="form-check-input" type="checkbox" checked>
<label class="form-check-label name"></label>
</div>

<div class="text-center loading-indicator"><i class="fa fa-spinner fa-spin"></i></div>
<div class="quick-search-results-container"></div>
</div>
<button type="submit" class="btn btn-outline-secondary hide">[[global:search]]</button>
</div>

<li id="" class="nav-item"><a component="search/button" id="search-button" href="#" class="nav-link"><i class="fa fa-search fa-fw" title="Search"></i></a></li>
</form>

</div>
{{{ end }}}

{{{ if !maintenanceHeader }}}
{{{ if config.loggedIn }}}
<ul id="logged-in-menu" class="navbar-nav me-0 mb-2 mb-lg-0 align-items-center">
<li class="nav-item notifications dropdown d-none d-sm-block" component="notifications" title="[[global:header.notifications]]">
<a href="{relative_path}/notifications" class="nav-link" data-bs-toggle="dropdown" id="notif_dropdown" data-ajaxify="false" role="button">
<i component="notifications/icon" class="fa fa-fw {{{ if unreadCount.notification}}}fa-bell{{{ else }}}fa-bell-o{{{ end }}} unread-count" data-content="{unreadCount.notification}"></i>
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="notif_dropdown">
<li>
<ul component="notifications/list" class="notification-list">
<li class="loading-text">
<a href="#"><i class="fa fa-refresh fa-spin"></i> [[global:notifications.loading]]</a>
</li>
</ul>
</li>
<li class="notif-dropdown-link">
<div class="btn-group d-flex justify-content-center">
<a role="button" href="#" class="btn btn-light mark-all-read"><i class="fa fa-check-double"></i> [[notifications:mark_all_read]]</a>
<a class="btn btn-light" href="{relative_path}/notifications"><i class="fa fa-list"></i> [[notifications:see_all]]</a>
</div>
</li>
</ul>
</li>

<!-- IF canChat -->
<li class="nav-item chats dropdown" title="[[global:header.chats]]">
<a class="nav-link" data-bs-toggle="dropdown" href="{relative_path}/user/{user.userslug}/chats" id="chat_dropdown" component="chat/dropdown" data-ajaxify="false" role="button">
<i component="chat/icon" class="fa fa-comment-o fa-fw unread-count" data-content="{unreadCount.chat}"></i> <span class="d-inline d-sm-none">[[global:header.chats]]</span>
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="chat_dropdown">
<li>
<ul component="chat/list" class="chat-list chats-list">
<li class="loading-text">
<a href="#"><i class="fa fa-refresh fa-spin"></i> [[global:chats.loading]]</a>
</li>
</ul>
</li>
<li class="notif-dropdown-link">
<div class="btn-group d-flex justify-content-center">
<a class="btn btn-light mark-all-read" href="#" component="chats/mark-all-read"><i class="fa fa-check-double"></i> [[modules:chat.mark_all_read]]</a>
<a class="btn btn-light" href="{relative_path}/user/{user.userslug}/chats"><i class="fa fa-comments"></i> [[modules:chat.see_all]]</a>
</div>
</li>
</ul>
</li>
<!-- ENDIF canChat -->

<!-- IMPORT partials/header/user-menu.tpl -->
</ul>
{{{ else }}}
<ul id="logged-out-menu" class="navbar-nav me-0 mb-2 mb-lg-0 align-items-center">
{{{ if allowRegistration }}}
<li class="nav-item">
<a class="nav-link" href="{relative_path}/register">
<i class="fa fa-pencil fa-fw d-inline-block d-sm-none"></i>
<span>[[global:register]]</span>
</a>
</li>
{{{ end }}}
<li class="nav-item">
<a class="nav-link" href="{relative_path}/login">
<i class="fa fa-sign-in fa-fw d-inline-block d-sm-none"></i>
<span>[[global:login]]</span>
</a>
</li>
</ul>
{{{ end }}}
{{{ else }}}
<ul class="navbar-nav me-0 mb-2 mb-lg-0"></ul>
<li class="nav-item">
<a href="{relative_path}/login">
<i class="fa fa-sign-in fa-fw d-block d-sm-none"></i>
<span>[[global:login]]</span>
</a>
</li>
</ul>
{{{ end }}}
</div>

View file

@ -0,0 +1,30 @@

<!-- IF !notifications.length -->
<li class="no-notifs"><a href="#">[[notifications:no_notifs]]</a></li>
<!-- ENDIF !notifications.length -->

{{{each notifications}}}
<li class="{notifications.readClass}" data-nid="{notifications.nid}" data-path="{notifications.path}" <!-- IF notifications.pid --> data-pid="{notifications.pid}"<!-- ENDIF notifications.pid --><!-- IF notifications.tid --> data-tid="{notifications.tid}"<!-- ENDIF notifications.tid -->>
<!-- IF notifications.image -->
<!-- IF notifications.from -->
<a href="{config.relative_path}/user/{notifications.user.userslug}"><img class="float-start user-img avatar avatar-rounded" style="--avatar-size: 32px;" src="{notifications.image}" /></a>
<!-- ENDIF notifications.from -->
<!-- ELSE -->
<a href="{config.relative_path}/user/{notifications.user.userslug}"><div class="float-start avatar avatar-rounded" style="--avatar-size: 32px; background-color: {notifications.user.icon:bgColor};">{notifications.user.icon:text}</div></a>
<!-- ENDIF notifications.image -->

<a href="{notifications.path}" class="notification-chat-content deco-none">
<span class="text">{notifications.bodyShort}</span>
</a>

<div class="notification-chat-controls">
{{{ if ./nid }}}
<div class="mark-read" aria-label="Mark Read">
<i class="unread fa fa-circle"></i>
<i class="read fa fa-circle-o"></i>
</div>
{{{ end }}}
<span class="relTime">{notifications.timeago}</span>
</div>
</li>
{{{end}}}

View file

@ -0,0 +1,45 @@
<div component="pagination" class="pagination-container<!-- IF !pagination.pages.length --> hidden<!-- ENDIF !pagination.pages.length -->">
<ul class="pagination hidden-xs justify-content-center">
<li class="page-item previous float-start<!-- IF !pagination.prev.active --> disabled<!-- ENDIF !pagination.prev.active -->">
<a class="page-link" href="?{pagination.prev.qs}" data-page="{pagination.prev.page}"><i class="fa fa-chevron-left"></i> </a>
</li>

{{{each pagination.pages}}}
<!-- IF pagination.pages.separator -->
<li component="pagination/select-page" class="page-item page select-page">
<a class="page-link" href="#"><i class="fa fa-ellipsis-h"></i></a>
</li>
<!-- ELSE -->
<li class="page-item page<!-- IF pagination.pages.active --> active<!-- ENDIF pagination.pages.active -->" >
<a class="page-link" href="?{pagination.pages.qs}" data-page="{pagination.pages.page}">{pagination.pages.page}</a>
</li>
<!-- ENDIF pagination.pages.separator -->
{{{end}}}

<li class="page-item next float-end<!-- IF !pagination.next.active --> disabled<!-- ENDIF !pagination.next.active -->">
<a class="page-link" href="?{pagination.next.qs}" data-page="{pagination.next.page}"> <i class="fa fa-chevron-right"></i></a>
</li>
</ul>

<ul class="pagination hidden-sm hidden-md hidden-lg justify-content-center">
<li class="page-item first<!-- IF !pagination.prev.active --> disabled<!-- ENDIF !pagination.prev.active -->">
<a class="page-link" href="?{pagination.first.qs}" data-page="1"><i class="fa fa-fast-backward"></i> </a>
</li>

<li class="page-item previous<!-- IF !pagination.prev.active --> disabled<!-- ENDIF !pagination.prev.active -->">
<a class="page-link" href="?{pagination.prev.qs}" data-page="{pagination.prev.page}"><i class="fa fa-chevron-left"></i> </a>
</li>

<li component="pagination/select-page" class="page-item page select-page">
<a class="page-link" href="#">{pagination.currentPage} / {pagination.pageCount}</a>
</li>

<li class="page-item next<!-- IF !pagination.next.active --> disabled<!-- ENDIF !pagination.next.active -->">
<a class="page-link" href="?{pagination.next.qs}" data-page="{pagination.next.page}"> <i class="fa fa-chevron-right"></i></a>
</li>

<li class="page-item last<!-- IF !pagination.next.active --> disabled<!-- ENDIF !pagination.next.active -->">
<a class="page-link" href="?{pagination.last.qs}" data-page="{pagination.pageCount}"><i class="fa fa-fast-forward"></i> </a>
</li>
</ul>
</div>

View file

@ -0,0 +1,20 @@
<div class="topic-main-buttons float-end d-inline-block">
<span class="loading-indicator btn float-start hidden" done="0">
<span class="hidden-xs">[[topic:loading_more_posts]]</span> <i class="fa fa-refresh fa-spin"></i>
</span>

<!-- IF loggedIn -->
<button component="topic/mark-unread" class="btn btn-sm btn-outline-secondary" title="[[topic:mark_unread]]">
<i class="fa fa-fw fa-inbox"></i><span class="visible-sm-inline visible-md-inline visible-lg-inline"></span>
</button>
<!-- ENDIF loggedIn -->

<!-- IMPORT partials/topic/watch.tpl -->

<!-- IMPORT partials/topic/sort.tpl -->

<div class="d-inline-block">
<!-- IMPORT partials/thread_tools.tpl -->
</div>
<!-- IMPORT partials/topic/reply-button.tpl -->
</div>

View file

@ -0,0 +1,8 @@
<ul component="posts" class="posts-list list-unstyled" data-nextstart="{nextStart}">
{{{each posts}}}
<!-- IMPORT partials/posts_list_item.tpl -->
{{{end}}}
</ul>
<div component="posts/loading" class="loading-indicator text-center hidden">
<i class="fa fa-refresh fa-spin"></i>
</div>

View file

@ -0,0 +1,32 @@
<li component="post" class="posts-list-item row<!-- IF ../deleted --> deleted<!-- ELSE --><!-- IF ../topic.deleted --> deleted<!-- ENDIF --><!-- ENDIF -->{{{ if ../topic.scheduled }}} scheduled{{{ end }}}" data-pid="{../pid}" data-uid="{../uid}">
<div class="col-lg-11 col-sm-10 col-9 post-body">
<a class="topic-title" href="{config.relative_path}/post/{../pid}">
<!-- IF !../isMainPost -->RE: <!-- ENDIF -->{../topic.title}
</a>

<div component="post/content" class="content">
{../content}
</div>

<small class="topic-category"><a href="{config.relative_path}/category/{../category.slug}">[[global:posted_in, {../category.name}]]</a></small>

{{{ if ../isMainPost }}}
{{{ if ../topic.tags.length }}}
<span class="tag-list">
{{{ each ../topic.tags }}}
<a href="{config.relative_path}/tags/{topic.tags.valueEncoded}"><span class="tag tag-item tag-class-{topic.tags.class}">{topic.tags.valueEscaped}</span></a>
{{{ end }}}
</span>
{{{ end }}}
{{{ end }}}

<div class="post-info">
<a href="{config.relative_path}/user/{../user.userslug}">{buildAvatar(../user, "28px", true, "user-img not-responsive")}</a>

<div class="post-author">
<a href="{config.relative_path}/user/{../user.userslug}">{../user.displayname}</a><br />
<span class="timeago" title="{../timestampISO}"></span>
</div>
</div>
</div>
</li>

View file

@ -0,0 +1,34 @@
<ul id="quick-search-results" class="quick-search-results list-unstyled mb-0 p-0 overflow-auto" style="max-width:400px; max-height: 500px;">
{{{each posts}}}
<li data-tid="{posts.topic.tid}" data-pid="{posts.pid}">
<a href="{config.relative_path}/post/{posts.pid}"
class="text-decoration-none text-reset clearfix d-block text-truncate px-3 py-1">
{buildAvatar(posts.user, "24px", true)}
<span class="quick-search-title fw-bold">{posts.topic.title}</span>
<br/>
<p class="snippet text-break text-wrap">
{posts.snippet}
</p>
<small class="post-info float-end">
<div class="category-item d-inline-block">
{buildCategoryIcon(./category, "24px", "rounded-circle")} {posts.category.name}
&bull; <span class="timeago" title="{posts.timestampISO}"></span>
</div>
</small>
</a>
</li>
<!-- IF !@last -->
<li role="separator" class="dropdown-divider"></li>
<!-- ENDIF -->
{{{end}}}
</ul>
<!-- IF multiplePages -->
<div class="text-center mt-2">
<a href="{url}">
[[search:see-more-results, {matchCount}]]
</a>
</div>
<!-- ENDIF multiplePages -->
{{{if !posts.length}}}
<div class="text-center no-results">[[search:no-matches]]</li>
{{{end}}}

View file

@ -0,0 +1,55 @@
<div id="results" class="search-results col-md-12" data-search-query="{search_query}">
{{{ if matchCount }}}
<div class="alert alert-info">[[search:results_matching, {matchCount}, {search_query}, {time}]] </div>
{{{ else }}}
{{{ if search_query }}}
<div class="alert alert-warning">[[search:no-matches]]</div>
{{{ end }}}
{{{ end }}}

{{{each posts}}}
<div class="topic-row card clearfix mb-3">
<div class="card-body">
<div>
<a href="{config.relative_path}/user/{./user.userslug}">{buildAvatar(./user, "24px", true)}</a>
<span class="search-result-text search-result-title"><a href="{config.relative_path}/post/{posts.pid}">{./topic.title}</a></span>
</div>

{{{ if showAsPosts }}}
<div class="search-result-text">
{./content}
<p class="fade-out"></p>
</div>
{{{ end }}}

<small class="post-info float-end">
<a href="{config.relative_path}/category/{./category.slug}">
<div class="category-item d-inline-block">
{buildCategoryIcon(./category, "24px", "rounded-circle")}
{./category.name}
</div>
</a> &bull;
<span class="timeago" title="{./timestampISO}"></span>
</small>
</div>
</div>
{{{end}}}

{{{ if users.length }}}
<!-- IMPORT partials/users_list.tpl -->
{{{ end }}}

{{{ if tags.length }}}
<!-- IMPORT partials/tags_list.tpl -->
{{{ end }}}

{{{ if categories.length }}}
<ul class="categories">
{{{each categories}}}
<!-- IMPORT partials/categories/item.tpl -->
{{{end}}}
</ul>
{{{ end }}}

<!-- IMPORT partials/paginator.tpl -->
</div>

View file

@ -0,0 +1,4 @@
<section class="menu-section" data-section="navigation">
<ul class="menu-section-list text-bg-dark"></ul>
</section>

View file

@ -0,0 +1,5 @@
{{{each tags}}}
<h5 class="float-start tag-container me-4 mb-4 fw-bold">
<a href="{config.relative_path}/tags/{tags.valueEncoded}" data-tag="{tags.valueEscaped}"><span class="tag-item text-muted text-uppercase text-nowrap tag-class-{tags.class} me-2" data-tag="{tags.valueEscaped}">{tags.valueEscaped}</span><span class="tag-topic-count text-primary text-nowrap human-readable-number" title="{tags.score}">{tags.score}</span></a>
</h5>
{{{end}}}

View file

@ -0,0 +1,8 @@
{{{ if privileges.view_thread_tools }}}
<div title="[[topic:thread_tools.title]]" class="btn-group thread-tools bottom-sheet">
<button class="btn btn-sm btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" type="button">
<i class="fa fa-fw fa-gear"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end"></ul>
</div>
{{{ end }}}

View file

@ -0,0 +1,14 @@
<div id="{alert_id}" role="alert" class="alert alert-dismissible alert-{type}" component="toaster/toast">
<button type="button" class="btn-close float-end" data-bs-dismiss="alert" aria-label="close"></button>
<!-- IF image -->
<img src="{image}">
<!-- ENDIF image -->

<!-- IF title -->
<strong>{title}</strong>
<!-- ENDIF title -->

<!-- IF message -->
<p>{message}</p>
<!-- ENDIF message -->
</div>

Some files were not shown because too many files have changed in this diff Show more