@okjavis/nodebb-theme-javis 5.0.8 → 6.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/plugin.json +4 -1
- package/public/src/client/account/categories.js +57 -0
- package/public/src/client/category.js +148 -0
- package/public/src/client/world.js +112 -0
- package/templates/account/categories.tpl +40 -0
- package/templates/account/posts.tpl +32 -0
- package/templates/account/topics.tpl +46 -0
- package/templates/admin/settings/user.tpl +369 -0
- package/templates/feed.tpl +19 -11
- package/templates/partials/account/sidebar-left.tpl +0 -14
- package/templates/partials/category/watch.tpl +24 -0
- package/templates/partials/notifications_list.tpl +45 -0
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -19,7 +19,10 @@
|
|
|
19
19
|
],
|
|
20
20
|
"modules": {
|
|
21
21
|
"../admin/plugins/javis.js": "public/admin.js",
|
|
22
|
-
"forum/search": "public/src/client/search.js"
|
|
22
|
+
"forum/search": "public/src/client/search.js",
|
|
23
|
+
"forum/category": "public/src/client/category.js",
|
|
24
|
+
"forum/world": "public/src/client/world.js",
|
|
25
|
+
"forum/account/categories": "public/src/client/account/categories.js"
|
|
23
26
|
},
|
|
24
27
|
"templates": "templates",
|
|
25
28
|
"screenshot": "screenshot.png",
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
define('forum/account/categories', ['forum/account/header', 'alerts', 'api'], function (header, alerts, api) {
|
|
5
|
+
const Categories = {};
|
|
6
|
+
|
|
7
|
+
Categories.init = function () {
|
|
8
|
+
header.init();
|
|
9
|
+
|
|
10
|
+
ajaxify.data.categories.forEach(function (category) {
|
|
11
|
+
handleIgnoreWatch(category.cid);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
$('[component="category/watch/all"]').find(
|
|
15
|
+
'[component="category/watching"], [component="category/tracking"]'
|
|
16
|
+
).on('click', async (e) => {
|
|
17
|
+
const cids = [];
|
|
18
|
+
const state = e.currentTarget.getAttribute('data-state');
|
|
19
|
+
const { uid } = ajaxify.data;
|
|
20
|
+
$('[data-parent-cid="0"]').each(function (index, el) {
|
|
21
|
+
cids.push($(el).attr('data-cid'));
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
let modified_cids = await Promise.all(cids.map(async cid => api.put(`/categories/${cid}/watch`, { state, uid })));
|
|
25
|
+
modified_cids = modified_cids
|
|
26
|
+
.reduce((memo, cur) => memo.concat(cur.modified), [])
|
|
27
|
+
.filter((cid, idx, arr) => arr.indexOf(cid) === idx);
|
|
28
|
+
|
|
29
|
+
updateDropdowns(modified_cids, state);
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function handleIgnoreWatch(cid) {
|
|
34
|
+
const category = $('[data-cid="' + cid + '"]');
|
|
35
|
+
category.find('[component="category/watching"]').on('click', async (e) => {
|
|
36
|
+
const isWatching = category.find('[component="category/watching/check"]').hasClass('fa-check');
|
|
37
|
+
const state = isWatching ? 'tracking' : 'watching';
|
|
38
|
+
const { uid } = ajaxify.data;
|
|
39
|
+
|
|
40
|
+
const { modified } = await api.put(`/categories/${cid}/watch`, { state, uid });
|
|
41
|
+
updateDropdowns(modified, state);
|
|
42
|
+
alerts.success('[[category:' + state + '.message]]');
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function updateDropdowns(modified_cids, state) {
|
|
47
|
+
modified_cids.forEach(function (cid) {
|
|
48
|
+
const category = $('[data-cid="' + cid + '"]');
|
|
49
|
+
const nowWatching = state === 'watching';
|
|
50
|
+
category.find('[component="category/watching/menu"]').toggleClass('hidden', !nowWatching);
|
|
51
|
+
category.find('[component="category/watching/check"]').toggleClass('fa-check', nowWatching);
|
|
52
|
+
category.find('[component="category/notwatching/menu"]').toggleClass('hidden', nowWatching);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return Categories;
|
|
57
|
+
});
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
define('forum/category', [
|
|
4
|
+
'forum/infinitescroll',
|
|
5
|
+
'navigator',
|
|
6
|
+
'topicList',
|
|
7
|
+
'sort',
|
|
8
|
+
'categorySelector',
|
|
9
|
+
'hooks',
|
|
10
|
+
'alerts',
|
|
11
|
+
'api',
|
|
12
|
+
'clipboard',
|
|
13
|
+
], function (infinitescroll, navigator, topicList, sort, categorySelector, hooks, alerts, api, clipboard) {
|
|
14
|
+
const Category = {};
|
|
15
|
+
|
|
16
|
+
$(window).on('action:ajaxify.start', function (ev, data) {
|
|
17
|
+
if (!String(data.url).startsWith('category/')) {
|
|
18
|
+
navigator.disable();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
Category.init = function () {
|
|
23
|
+
const cid = ajaxify.data.cid;
|
|
24
|
+
|
|
25
|
+
app.enterRoom('category_' + cid);
|
|
26
|
+
|
|
27
|
+
topicList.init('category', loadTopicsAfter);
|
|
28
|
+
|
|
29
|
+
sort.handleSort('categoryTopicSort', 'category/' + ajaxify.data.slug);
|
|
30
|
+
|
|
31
|
+
if (!config.usePagination) {
|
|
32
|
+
navigator.init('[component="category/topic"]', ajaxify.data.topic_count, Category.toTop, Category.toBottom);
|
|
33
|
+
} else {
|
|
34
|
+
navigator.disable();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
handleScrollToTopicIndex();
|
|
38
|
+
|
|
39
|
+
handleIgnoreWatch(cid);
|
|
40
|
+
|
|
41
|
+
handleLoadMoreSubcategories();
|
|
42
|
+
|
|
43
|
+
handleDescription();
|
|
44
|
+
|
|
45
|
+
categorySelector.init($('[component="category-selector"]'), {
|
|
46
|
+
privilege: 'find',
|
|
47
|
+
parentCid: ajaxify.data.cid,
|
|
48
|
+
onSelect: function (category) {
|
|
49
|
+
ajaxify.go('/category/' + category.cid);
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
new clipboard('[data-clipboard-text]');
|
|
54
|
+
|
|
55
|
+
hooks.fire('action:topics.loaded', { topics: ajaxify.data.topics });
|
|
56
|
+
hooks.fire('action:category.loaded', { cid: ajaxify.data.cid });
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function handleScrollToTopicIndex() {
|
|
60
|
+
let topicIndex = ajaxify.data.topicIndex;
|
|
61
|
+
if (topicIndex && utils.isNumber(topicIndex)) {
|
|
62
|
+
topicIndex = Math.max(0, parseInt(topicIndex, 10));
|
|
63
|
+
if (topicIndex && window.location.search.indexOf('page=') === -1) {
|
|
64
|
+
navigator.scrollToElement($('[component="category/topic"][data-index="' + topicIndex + '"]'), true, 0);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function handleIgnoreWatch(cid) {
|
|
70
|
+
$('[component="category/watching"]').on('click', function () {
|
|
71
|
+
const isWatching = $('[component="category/watching/check"]').hasClass('fa-check');
|
|
72
|
+
const state = isWatching ? 'tracking' : 'watching';
|
|
73
|
+
|
|
74
|
+
api.put(`/categories/${encodeURIComponent(cid)}/watch`, { state }, (err) => {
|
|
75
|
+
if (err) {
|
|
76
|
+
return alerts.error(err);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const nowWatching = state === 'watching';
|
|
80
|
+
$('[component="category/watching/menu"]').toggleClass('hidden', !nowWatching);
|
|
81
|
+
$('[component="category/watching/check"]').toggleClass('fa-check', nowWatching);
|
|
82
|
+
$('[component="category/notwatching/menu"]').toggleClass('hidden', nowWatching);
|
|
83
|
+
|
|
84
|
+
alerts.success('[[category:' + state + '.message]]');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function handleLoadMoreSubcategories() {
|
|
90
|
+
$('[component="category/load-more-subcategories"]').on('click', async function () {
|
|
91
|
+
const btn = $(this);
|
|
92
|
+
const { categories: data } = await api.get(`/categories/${ajaxify.data.cid}/children?start=${ajaxify.data.nextSubCategoryStart}`);
|
|
93
|
+
btn.toggleClass('hidden', !data.length || data.length < ajaxify.data.subCategoriesPerPage);
|
|
94
|
+
if (!data.length) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
app.parseAndTranslate('category', 'children', { children: data }, function (html) {
|
|
98
|
+
html.find('.timeago').timeago();
|
|
99
|
+
$('[component="category/subcategory/container"]').append(html);
|
|
100
|
+
ajaxify.data.nextSubCategoryStart += ajaxify.data.subCategoriesPerPage;
|
|
101
|
+
ajaxify.data.subCategoriesLeft -= data.length;
|
|
102
|
+
btn.toggleClass('hidden', ajaxify.data.subCategoriesLeft <= 0)
|
|
103
|
+
.translateText('[[category:x-more-categories, ' + ajaxify.data.subCategoriesLeft + ']]');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return false;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function handleDescription() {
|
|
111
|
+
const fadeEl = document.querySelector('.description.clamp-fade-4');
|
|
112
|
+
if (!fadeEl) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fadeEl.addEventListener('click', () => {
|
|
117
|
+
const state = fadeEl.classList.contains('line-clamp-4');
|
|
118
|
+
fadeEl.classList.toggle('line-clamp-4', !state);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
Category.toTop = function () {
|
|
123
|
+
navigator.scrollTop(0);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
Category.toBottom = async () => {
|
|
127
|
+
const { count } = await api.get(`/categories/${encodeURIComponent(ajaxify.data.category.cid)}/count`);
|
|
128
|
+
navigator.scrollBottom(count - 1);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
function loadTopicsAfter(after, direction, callback) {
|
|
132
|
+
callback = callback || function () {};
|
|
133
|
+
|
|
134
|
+
hooks.fire('action:topics.loading');
|
|
135
|
+
const params = utils.params();
|
|
136
|
+
infinitescroll.loadMore(`/categories/${encodeURIComponent(ajaxify.data.cid)}/topics`, {
|
|
137
|
+
after: after,
|
|
138
|
+
direction: direction,
|
|
139
|
+
query: params,
|
|
140
|
+
categoryTopicSort: params.sort || config.categoryTopicSort,
|
|
141
|
+
}, function (data, done) {
|
|
142
|
+
hooks.fire('action:topics.loaded', { topics: data.topics });
|
|
143
|
+
callback(data, done);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return Category;
|
|
148
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
define('forum/world', ['topicList', 'search', 'sort', 'hooks', 'alerts', 'api', 'bootbox'], function (topicList, search, sort, hooks, alerts, api, bootbox) {
|
|
4
|
+
const World = {};
|
|
5
|
+
|
|
6
|
+
World.init = function () {
|
|
7
|
+
app.enterRoom('world');
|
|
8
|
+
topicList.init('world');
|
|
9
|
+
|
|
10
|
+
sort.handleSort('categoryTopicSort', 'world');
|
|
11
|
+
|
|
12
|
+
handleIgnoreWatch(-1);
|
|
13
|
+
handleHelp();
|
|
14
|
+
handleCategories();
|
|
15
|
+
|
|
16
|
+
search.enableQuickSearch({
|
|
17
|
+
searchElements: {
|
|
18
|
+
inputEl: $('[component="category-search"]'),
|
|
19
|
+
resultEl: $('.world .quick-search-container'),
|
|
20
|
+
},
|
|
21
|
+
searchOptions: {
|
|
22
|
+
in: 'categories',
|
|
23
|
+
},
|
|
24
|
+
dropdown: {
|
|
25
|
+
maxWidth: '400px',
|
|
26
|
+
maxHeight: '350px',
|
|
27
|
+
},
|
|
28
|
+
hideOnNoMatches: false,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
hooks.fire('action:topics.loaded', { topics: ajaxify.data.topics });
|
|
32
|
+
hooks.fire('action:category.loaded', { cid: ajaxify.data.cid });
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function handleIgnoreWatch(cid) {
|
|
36
|
+
$('[component="category/watching"]').on('click', function () {
|
|
37
|
+
const isWatching = $('[component="category/watching/check"]').hasClass('fa-check');
|
|
38
|
+
const state = isWatching ? 'tracking' : 'watching';
|
|
39
|
+
|
|
40
|
+
api.put(`/categories/${cid}/watch`, { state }, (err) => {
|
|
41
|
+
if (err) {
|
|
42
|
+
return alerts.error(err);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const nowWatching = state === 'watching';
|
|
46
|
+
$('[component="category/watching/menu"]').toggleClass('hidden', !nowWatching);
|
|
47
|
+
$('[component="category/watching/check"]').toggleClass('fa-check', nowWatching);
|
|
48
|
+
$('[component="category/notwatching/menu"]').toggleClass('hidden', nowWatching);
|
|
49
|
+
|
|
50
|
+
alerts.success('[[category:' + state + '.message]]');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function handleHelp() {
|
|
56
|
+
const trigger = document.getElementById('world-help');
|
|
57
|
+
if (!trigger) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const content = [
|
|
62
|
+
'<p class="lead">[[world:help.intro]]</p>',
|
|
63
|
+
'<p>[[world:help.fediverse]]</p>',
|
|
64
|
+
'<p>[[world:help.build]]</p>',
|
|
65
|
+
'<p>[[world:help.federating]]</p>',
|
|
66
|
+
'<p>[[world:help.next-generation]]</p>',
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
trigger.addEventListener('click', () => {
|
|
70
|
+
bootbox.dialog({
|
|
71
|
+
title: '[[world:help.title]]',
|
|
72
|
+
message: content.join('\n'),
|
|
73
|
+
size: 'large',
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function handleCategories() {
|
|
79
|
+
// const optionsEl = document.getElementById('category-options');
|
|
80
|
+
// const dropdownEl = optionsEl.querySelector('ul');
|
|
81
|
+
const showEl = document.getElementById('show-categories');
|
|
82
|
+
const hideEl = document.getElementById('hide-categories');
|
|
83
|
+
const categoriesEl = document.querySelector('.categories-list');
|
|
84
|
+
if (![showEl, hideEl, categoriesEl].every(Boolean)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const update = () => {
|
|
89
|
+
showEl.classList.toggle('hidden', visibility);
|
|
90
|
+
hideEl.classList.toggle('hidden', !visibility);
|
|
91
|
+
categoriesEl.classList.toggle('hidden', !visibility);
|
|
92
|
+
localStorage.setItem('world:show-categories', visibility);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
let visibility = localStorage.getItem('world:show-categories');
|
|
96
|
+
console.log('got value', visibility);
|
|
97
|
+
visibility = visibility ? visibility === 'true' : true; // localStorage values are strings
|
|
98
|
+
update();
|
|
99
|
+
|
|
100
|
+
showEl.addEventListener('click', () => {
|
|
101
|
+
visibility = true;
|
|
102
|
+
update();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
hideEl.addEventListener('click', () => {
|
|
106
|
+
visibility = false;
|
|
107
|
+
update();
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return World;
|
|
112
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<!-- IMPORT partials/account/header.tpl -->
|
|
2
|
+
|
|
3
|
+
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
4
|
+
<div class="d-flex gap-1">
|
|
5
|
+
<h3 class="fw-semibold fs-5 mb-0">{title}</h3>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div class="d-flex gap-1">
|
|
9
|
+
<div class="btn-group bottom-sheet" component="category/watch/all">
|
|
10
|
+
<button class="btn btn-ghost btn-sm ff-secondary fw-semibold dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" type="button">
|
|
11
|
+
<span>[[user:change-all]]</span>
|
|
12
|
+
</button>
|
|
13
|
+
<ul class="dropdown-menu p-1 text-sm dropdown-menu-end" role="menu">
|
|
14
|
+
<li>
|
|
15
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2 p-2" href="#" component="category/watching" data-state="watching" role="menuitem">
|
|
16
|
+
<i class="flex-shrink-0 fa fa-fw fa-bell text-secondary"></i>
|
|
17
|
+
<span class="fw-semibold">[[category:watching]]</span>
|
|
18
|
+
</a>
|
|
19
|
+
</li>
|
|
20
|
+
<li>
|
|
21
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2 p-2" href="#" component="category/tracking" data-state="tracking" role="menuitem">
|
|
22
|
+
<i class="flex-shrink-0 fa fa-fw fa-bell-o text-secondary"></i>
|
|
23
|
+
<span class="fw-semibold">[[category:not-watching]]</span>
|
|
24
|
+
</a>
|
|
25
|
+
</li>
|
|
26
|
+
</ul>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div>
|
|
32
|
+
<ul class="categories list-unstyled" itemscope itemtype="http://www.schema.org/ItemList">
|
|
33
|
+
{{{each categories}}}
|
|
34
|
+
<!-- IMPORT partials/account/category-item.tpl -->
|
|
35
|
+
{{{end}}}
|
|
36
|
+
</ul>
|
|
37
|
+
<!-- IMPORT partials/paginator.tpl -->
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- IMPORT partials/account/footer.tpl -->
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<!-- IMPORT partials/account/header.tpl -->
|
|
2
|
+
|
|
3
|
+
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-3">
|
|
4
|
+
<h3 class="fw-semibold fs-5 mb-0">[[global:posts]]</h3>
|
|
5
|
+
<div class="d-flex flex-wrap gap-1">
|
|
6
|
+
<a href="{config.relative_path}/user/{userslug}/posts" class="btn btn-ghost btn-sm ff-secondary fw-semibold {{{ if template.account/posts }}}active{{{ end }}}">[[global:header.recent]]</a>
|
|
7
|
+
{{{ if !reputation:disabled }}}
|
|
8
|
+
<a href="{config.relative_path}/user/{userslug}/best"class="btn btn-ghost btn-sm ff-secondary fw-semibold {{{ if template.account/best }}}active{{{ end }}}">[[global:best]]</a>
|
|
9
|
+
<a href="{config.relative_path}/user/{userslug}/controversial" class="btn btn-ghost btn-sm ff-secondary fw-semibold {{{ if template.account/controversial }}}active{{{ end }}}">[[global:controversial]]</a>
|
|
10
|
+
{{{ if canEdit }}}
|
|
11
|
+
<a href="{config.relative_path}/user/{userslug}/upvoted" class="btn btn-ghost btn-sm ff-secondary fw-semibold {{{ if template.account/upvoted }}}active{{{ end }}}">[[global:upvoted]]</a>
|
|
12
|
+
{{{ if !downvote:disabled }}}
|
|
13
|
+
<a href="{config.relative_path}/user/{userslug}/downvoted" class="btn btn-ghost btn-sm ff-secondary fw-semibold {{{ if template.account/downvoted }}}active{{{ end }}}">[[global:downvoted]]</a>
|
|
14
|
+
{{{ end }}}
|
|
15
|
+
{{{ end }}}
|
|
16
|
+
{{{ end }}}
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
{{{ if !posts.length }}}
|
|
21
|
+
<div class="alert alert-warning text-center">{noItemsFoundKey}</div>
|
|
22
|
+
{{{ end }}}
|
|
23
|
+
|
|
24
|
+
<div>
|
|
25
|
+
<!-- IMPORT partials/posts_list.tpl -->
|
|
26
|
+
|
|
27
|
+
{{{ if config.usePagination }}}
|
|
28
|
+
<!-- IMPORT partials/paginator.tpl -->
|
|
29
|
+
{{{ end }}}
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<!-- IMPORT partials/account/footer.tpl -->
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<!-- IMPORT partials/account/header.tpl -->
|
|
2
|
+
|
|
3
|
+
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
4
|
+
<div class="d-flex gap-1">
|
|
5
|
+
<h3 class="fw-semibold fs-5 mb-0 align-self-center">[[global:topics]]</h3>
|
|
6
|
+
{{{ if showSort }}}
|
|
7
|
+
<div class="btn-group bottom-sheet" component="thread/sort">
|
|
8
|
+
<button title="[[global:sort]]" class="btn btn-ghost btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" type="button"><i class="fa-solid fa-arrow-up-wide-short"></i></button>
|
|
9
|
+
<ul class="dropdown-menu p-1 text-sm" role="menu">
|
|
10
|
+
{{{each sortOptions }}}
|
|
11
|
+
<li>
|
|
12
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="{config.relative_path}{./url}" role="menuitem">
|
|
13
|
+
<div class="flex-grow-1">{./name}</div>
|
|
14
|
+
<i class="flex-shrink-0 fa fa-fw {{{if ./selected}}}fa-check{{{end}}}"></i>
|
|
15
|
+
</a>
|
|
16
|
+
</li>
|
|
17
|
+
{{{end}}}
|
|
18
|
+
</ul>
|
|
19
|
+
</div>
|
|
20
|
+
{{{ end }}}
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="d-flex gap-1">
|
|
24
|
+
{{{ if canEdit }}}
|
|
25
|
+
<a href="{config.relative_path}/user/{userslug}/topics" class="btn btn-ghost btn-sm ff-secondary fw-semibold {{{ if template.account/topics }}}active{{{ end }}}">[[global:header.recent]]</a>
|
|
26
|
+
<a href="{config.relative_path}/user/{userslug}/watched" class="btn btn-ghost btn-sm ff-secondary fw-semibold {{{ if template.account/watched }}}active{{{ end }}}">[[user:watched]]</a>
|
|
27
|
+
{{{ if !reputation:disabled }}}
|
|
28
|
+
<a href="{config.relative_path}/user/{userslug}/upvoted" class="btn btn-ghost btn-sm ff-secondary fw-semibold {{{ if template.account/upvoted }}}active{{{ end }}}">[[global:upvoted]]</a>
|
|
29
|
+
{{{ end }}}
|
|
30
|
+
{{{ end }}}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
{{{ if !topics.length }}}
|
|
36
|
+
<div class="alert alert-warning text-center">{noItemsFoundKey}</div>
|
|
37
|
+
{{{ end }}}
|
|
38
|
+
|
|
39
|
+
<div class="category">
|
|
40
|
+
<!-- IMPORT partials/topics_list.tpl -->
|
|
41
|
+
{{{ if config.usePagination }}}
|
|
42
|
+
<!-- IMPORT partials/paginator.tpl -->
|
|
43
|
+
{{{ end }}}
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<!-- IMPORT partials/account/footer.tpl -->
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
<div class="acp-page-container">
|
|
2
|
+
<!-- IMPORT admin/partials/settings/header.tpl -->
|
|
3
|
+
|
|
4
|
+
<div class="row settings m-0">
|
|
5
|
+
<div id="spy-container" class="col-12 col-md-8 px-0 mb-4" tabindex="0">
|
|
6
|
+
<div id="account-settings" class="mb-4">
|
|
7
|
+
<h5 class="fw-bold tracking-tight settings-header">[[admin/settings/user:account-settings]]</h5>
|
|
8
|
+
<div class="mb-3">
|
|
9
|
+
<label class="form-label" for="allowLoginWith">[[admin/settings/user:allow-login-with]]</label>
|
|
10
|
+
<select id="allowLoginWith" class="form-select" data-field="allowLoginWith">
|
|
11
|
+
<option value="username-email">[[admin/settings/user:allow-login-with.username-email]]</option>
|
|
12
|
+
<option value="username">[[admin/settings/user:allow-login-with.username]]</option>
|
|
13
|
+
</select>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="form-check form-switch mb-3">
|
|
17
|
+
<input class="form-check-input" type="checkbox" id="gdpr_enabled" data-field="gdpr_enabled">
|
|
18
|
+
<label for="gdpr_enabled" class="form-check-label">[[admin/settings/user:gdpr-enabled]]</label>
|
|
19
|
+
<p class="form-text">[[admin/settings/user:gdpr-enabled-help]]</p>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="form-check form-switch mb-3">
|
|
22
|
+
<input class="form-check-input" type="checkbox" id="username:disableEdit" data-field="username:disableEdit">
|
|
23
|
+
<label for="username:disableEdit" class="form-check-label">[[admin/settings/user:disable-username-changes]]</label>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="form-check form-switch mb-3">
|
|
26
|
+
<input class="form-check-input" type="checkbox" id="email:disableEdit" data-field="email:disableEdit">
|
|
27
|
+
<label for="email:disableEdit" class="form-check-label">[[admin/settings/user:disable-email-changes]]</label>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="form-check form-switch mb-3">
|
|
30
|
+
<input class="form-check-input" type="checkbox" id="password:disableEdit" data-field="password:disableEdit">
|
|
31
|
+
<label for="password:disableEdit" class="form-check-label">[[admin/settings/user:disable-password-changes]]</label>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="form-check form-switch mb-3">
|
|
34
|
+
<input class="form-check-input" type="checkbox" id="allowAccountDelete" data-field="allowAccountDelete" checked>
|
|
35
|
+
<label for="allowAccountDelete" class="form-check-label">[[admin/settings/user:allow-account-deletion]]</label>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="form-check form-switch mb-3">
|
|
38
|
+
<input class="form-check-input" type="checkbox" id="hideFullname" data-field="hideFullname">
|
|
39
|
+
<label for="hideFullname" class="form-check-label">[[admin/settings/user:hide-fullname]]</label>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="form-check form-switch mb-3">
|
|
42
|
+
<input class="form-check-input" type="checkbox" id="hideEmail" data-field="hideEmail">
|
|
43
|
+
<label for="hideEmail" class="form-check-label">[[admin/settings/user:hide-email]]</label>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="form-check form-switch mb-3">
|
|
46
|
+
<input class="form-check-input" type="checkbox" id="showFullnameAsDisplayName" data-field="showFullnameAsDisplayName">
|
|
47
|
+
<label for="showFullnameAsDisplayName" class="form-check-label">[[admin/settings/user:show-fullname-as-displayname]]</label>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="form-check form-switch mb-3">
|
|
50
|
+
<input class="form-check-input" type="checkbox" id="disableCustomUserSkins" data-field="disableCustomUserSkins">
|
|
51
|
+
<label for="disableCustomUserSkins" class="form-check-label">[[admin/settings/user:disable-user-skins]]</label>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<hr/>
|
|
56
|
+
|
|
57
|
+
<div id="account-protection" class="mb-4">
|
|
58
|
+
<h5 class="fw-bold tracking-tight settings-header">[[admin/settings/user:account-protection]]</h5>
|
|
59
|
+
<div class="mb-3">
|
|
60
|
+
<label class="form-label" for="adminReloginDuration">[[admin/settings/user:admin-relogin-duration]]</label>
|
|
61
|
+
<input id="adminReloginDuration" type="text" class="form-control" data-field="adminReloginDuration" placeholder="60" />
|
|
62
|
+
<p class="form-text">
|
|
63
|
+
[[admin/settings/user:admin-relogin-duration-help]]
|
|
64
|
+
</p>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="mb-3">
|
|
67
|
+
<label class="form-label" for="loginAttempts">[[admin/settings/user:login-attempts]]</label>
|
|
68
|
+
<input id="loginAttempts" type="text" class="form-control" data-field="loginAttempts" placeholder="5" />
|
|
69
|
+
<p class="form-text">
|
|
70
|
+
[[admin/settings/user:login-attempts-help]]
|
|
71
|
+
</p>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="mb-3">
|
|
74
|
+
<label class="form-label" for="lockoutDuration">[[admin/settings/user:lockout-duration]]</label>
|
|
75
|
+
<input id="lockoutDuration" type="text" class="form-control" data-field="lockoutDuration" placeholder="60" />
|
|
76
|
+
</div>
|
|
77
|
+
<div class="mb-3">
|
|
78
|
+
<label class="form-label" for="passwordExpiryDays">[[admin/settings/user:password-expiry-days]]</label>
|
|
79
|
+
<input id="passwordExpiryDays" type="text" class="form-control" data-field="passwordExpiryDays" placeholder="0" />
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<hr/>
|
|
84
|
+
|
|
85
|
+
<div id="session-time" class="mb-4">
|
|
86
|
+
<h5 class="fw-bold tracking-tight settings-header">[[admin/settings/user:session-time]]</h5>
|
|
87
|
+
<div class="row">
|
|
88
|
+
<div class="col-sm-6">
|
|
89
|
+
<div class="form-group mb-3">
|
|
90
|
+
<label class="form-label" for="loginDays">[[admin/settings/user:session-time-days]]</label>
|
|
91
|
+
<input id="loginDays" type="number" min="0" class="form-control" data-field="loginDays" placeholder="[[admin/settings/user:session-time-days]]" />
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="col-sm-6">
|
|
95
|
+
<div class="form-group mb-3">
|
|
96
|
+
<label class="form-label" for="loginSeconds">[[admin/settings/user:session-time-seconds]]</label>
|
|
97
|
+
<input id="loginSeconds" type="number" min="0" step="60" class="form-control" data-field="loginSeconds" placeholder="[[admin/settings/user:session-time-seconds]]" />
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
<div class="row">
|
|
102
|
+
<div class="col-12">
|
|
103
|
+
<p class="form-text">
|
|
104
|
+
[[admin/settings/user:session-time-help]]
|
|
105
|
+
</p>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="form-group mb-3">
|
|
110
|
+
<label class="form-label" for="sessionDuration">[[admin/settings/user:session-duration]]</label>
|
|
111
|
+
<input id="sessionDuration" type="number" step="60" min="0" class="form-control" data-field="sessionDuration">
|
|
112
|
+
<p class="form-text">[[admin/settings/user:session-duration-help]]</p>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div class="form-group mb-3">
|
|
116
|
+
<label class="form-label" for="onlineCutoff">[[admin/settings/user:online-cutoff]]</label>
|
|
117
|
+
<input id="onlineCutoff" type="number" min="0" class="form-control" data-field="onlineCutoff">
|
|
118
|
+
<p class="form-text">[[admin/settings/user:online-cutoff-help]]</p>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<hr/>
|
|
123
|
+
|
|
124
|
+
<div id="user-registration" class="mb-4">
|
|
125
|
+
<h5 class="fw-bold tracking-tight settings-header">[[admin/settings/user:registration]]</h5>
|
|
126
|
+
<div class="mb-3">
|
|
127
|
+
<label class="form-label" for="registrationType">[[admin/settings/user:registration-type]]</label>
|
|
128
|
+
<select id="registrationType" class="form-select" data-field="registrationType">
|
|
129
|
+
<option value="normal">[[admin/settings/user:registration-type.normal]]</option>
|
|
130
|
+
<option value="invite-only">[[admin/settings/user:registration-type.invite-only]]</option>
|
|
131
|
+
<option value="admin-invite-only">[[admin/settings/user:registration-type.admin-invite-only]]</option>
|
|
132
|
+
<option value="disabled">[[admin/settings/user:registration-type.disabled]]</option>
|
|
133
|
+
</select>
|
|
134
|
+
<p class="form-text">
|
|
135
|
+
[[admin/settings/user:registration-type.help, {config.relative_path}]]
|
|
136
|
+
</p>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="mb-3">
|
|
139
|
+
<label class="form-label" for="registrationApprovalType">[[admin/settings/user:registration-approval-type]]</label>
|
|
140
|
+
<select id="registrationApprovalType" class="form-select" data-field="registrationApprovalType">
|
|
141
|
+
<option value="normal">[[admin/settings/user:registration-type.normal]]</option>
|
|
142
|
+
<option value="admin-approval">[[admin/settings/user:registration-type.admin-approval]]</option>
|
|
143
|
+
<option value="admin-approval-ip">[[admin/settings/user:registration-type.admin-approval-ip]]</option>
|
|
144
|
+
</select>
|
|
145
|
+
<p class="form-text">
|
|
146
|
+
[[admin/settings/user:registration-approval-type.help, {config.relative_path}]]
|
|
147
|
+
</p>
|
|
148
|
+
</div>
|
|
149
|
+
<div class="mb-3">
|
|
150
|
+
<label class="form-label" for="autoApproveTime">[[admin/settings/user:registration-queue-auto-approve-time]]</label>
|
|
151
|
+
<input id="autoApproveTime" type="number" class="form-control" data-field="autoApproveTime" placeholder="0">
|
|
152
|
+
<p class="form-text">
|
|
153
|
+
[[admin/settings/user:registration-queue-auto-approve-time-help]]
|
|
154
|
+
</p>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div class="form-check form-switch mb-3">
|
|
158
|
+
<input class="form-check-input" type="checkbox" id="showAverageApprovalTime" data-field="showAverageApprovalTime">
|
|
159
|
+
<label for="showAverageApprovalTime" class="form-check-label">[[admin/settings/user:registration-queue-show-average-time]]</label>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<div class="form-check form-switch mb-3">
|
|
163
|
+
<input class="form-check-input" type="checkbox" id="requireEmailAddress" data-field="requireEmailAddress" name="requireEmailAddress" />
|
|
164
|
+
<label for="requireEmailAddress" class="form-check-label">[[admin/settings/email:require-email-address]]</label>
|
|
165
|
+
</div>
|
|
166
|
+
<p class="form-text">[[admin/settings/email:require-email-address-warning]]</p>
|
|
167
|
+
|
|
168
|
+
<div class="mb-3">
|
|
169
|
+
<label class="form-label" for="maximumInvites">[[admin/settings/user:max-invites]]</label>
|
|
170
|
+
<input id="maximumInvites" type="number" class="form-control" data-field="maximumInvites" placeholder="0">
|
|
171
|
+
<p class="form-text">
|
|
172
|
+
[[admin/settings/user:max-invites-help]]
|
|
173
|
+
</p>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="mb-3">
|
|
176
|
+
<label class="form-label" for="inviteExpiration">[[admin/settings/user:invite-expiration]]</label>
|
|
177
|
+
<input id="inviteExpiration" type="number" class="form-control" data-field="inviteExpiration" placeholder="7">
|
|
178
|
+
<p class="form-text">
|
|
179
|
+
[[admin/settings/user:invite-expiration-help]]
|
|
180
|
+
</p>
|
|
181
|
+
</div>
|
|
182
|
+
<div class="mb-3">
|
|
183
|
+
<label class="form-label" for="minimumUsernameLength">[[admin/settings/user:min-username-length]]</label>
|
|
184
|
+
<input id="minimumUsernameLength" type="text" class="form-control" value="2" data-field="minimumUsernameLength">
|
|
185
|
+
</div>
|
|
186
|
+
<div class="mb-3">
|
|
187
|
+
<label class="form-label" for="maximumUsernameLength">[[admin/settings/user:max-username-length]]</label>
|
|
188
|
+
<input id="maximumUsernameLength" type="text" class="form-control" value="16" data-field="maximumUsernameLength">
|
|
189
|
+
</div>
|
|
190
|
+
<div class="mb-3">
|
|
191
|
+
<label class="form-label" for="minimumPasswordLength">[[admin/settings/user:min-password-length]]</label>
|
|
192
|
+
<input id="minimumPasswordLength" type="text" class="form-control" value="6" data-field="minimumPasswordLength">
|
|
193
|
+
</div>
|
|
194
|
+
<div class="mb-3">
|
|
195
|
+
<label class="form-label" for="minimumPasswordStrength">[[admin/settings/user:min-password-strength]]</label>
|
|
196
|
+
<select id="minimumPasswordStrength" class="form-select" data-field="minimumPasswordStrength">
|
|
197
|
+
<option value="0">0 - too guessable: risky password</option>
|
|
198
|
+
<option value="1">1 - very guessable</option>
|
|
199
|
+
<option value="2">2 - somewhat guessable</option>
|
|
200
|
+
<option value="3">3 - safely unguessable</option>
|
|
201
|
+
<option value="4">4 - very unguessable</option>
|
|
202
|
+
</select>
|
|
203
|
+
</div>
|
|
204
|
+
<div class="mb-3">
|
|
205
|
+
<label class="form-label" for="maximumAboutMeLength">[[admin/settings/user:max-about-me-length]]</label>
|
|
206
|
+
<input id="maximumAboutMeLength" type="text" class="form-control" value="500" data-field="maximumAboutMeLength">
|
|
207
|
+
</div>
|
|
208
|
+
<div class="mb-3">
|
|
209
|
+
<label class="form-label" for="termsOfUse">[[admin/settings/user:terms-of-use]]</label>
|
|
210
|
+
<textarea id="termsOfUse" class="form-control" data-field="termsOfUse"></textarea>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
<hr/>
|
|
215
|
+
|
|
216
|
+
<!-- new user restrictions -->
|
|
217
|
+
<div id="new-user-restrictions" class="mb-4">
|
|
218
|
+
<h5 class="fw-bold tracking-tight settings-header">[[admin/settings/user:restrictions-new]]</h5>
|
|
219
|
+
|
|
220
|
+
<div class="mb-3">
|
|
221
|
+
<label class="form-label" for="newbieReputationThreshold">[[admin/settings/user:restrictions.rep-threshold]]</label>
|
|
222
|
+
<input id="newbieReputationThreshold" type="text" class="form-control" value="3" data-field="newbieReputationThreshold">
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
<div class="mb-3">
|
|
226
|
+
<label class="form-label" for="newbiePostDelay">[[admin/settings/user:restrictions.seconds-between-new]]</label>
|
|
227
|
+
<input id="newbiePostDelay" type="text" class="form-control" value="120" data-field="newbiePostDelay">
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<div class="mb-3">
|
|
231
|
+
<label class="form-label" for="initialPostDelay">[[admin/settings/user:restrictions.seconds-before-new]]</label>
|
|
232
|
+
<input id="initialPostDelay" type="text" class="form-control" value="10" data-field="initialPostDelay">
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<div class="mb-3">
|
|
236
|
+
<label class="form-label" for="newbiePostEditDuration">[[admin/settings/user:restrictions.seconds-edit-after-new]]</label>
|
|
237
|
+
<input id="newbiePostEditDuration" type="text" class="form-control" value="120" data-field="newbiePostEditDuration">
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
<div class="mb-3">
|
|
241
|
+
<label class="form-label" for="newbieChatMessageDelay">[[admin/settings/user:restrictions.milliseconds-between-messages]]</label>
|
|
242
|
+
<input id="newbieChatMessageDelay" type="text" class="form-control" data-field="newbieChatMessageDelay">
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
<div class="mb-3">
|
|
246
|
+
<label class="form-label" for="groupsExemptFromNewUserRestrictions">[[admin/settings/user:restrictions.groups-exempt-from-new-user-restrictions]]</label>
|
|
247
|
+
<select id="groupsExemptFromNewUserRestrictions" class="form-select" multiple data-field="groupsExemptFromNewUserRestrictions">
|
|
248
|
+
{{{ each groupsExemptFromNewUserRestrictions }}}
|
|
249
|
+
<option value="{groupsExemptFromNewUserRestrictions.displayName}">{groupsExemptFromNewUserRestrictions.displayName}</option>
|
|
250
|
+
{{{ end }}}
|
|
251
|
+
</select>
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
</div>
|
|
255
|
+
|
|
256
|
+
<hr/>
|
|
257
|
+
|
|
258
|
+
<div id="guest-settings" class="mb-4">
|
|
259
|
+
<h5 class="fw-bold tracking-tight settings-header">[[admin/settings/user:guest-settings]]</h5>
|
|
260
|
+
|
|
261
|
+
<div class="mb-3">
|
|
262
|
+
<div class="form-check form-switch mb-3">
|
|
263
|
+
<input class="form-check-input" type="checkbox" id="allowGuestHandles" data-field="allowGuestHandles">
|
|
264
|
+
<label for="allowGuestHandles" class="form-check-label">[[admin/settings/user:handles.enabled]]</label>
|
|
265
|
+
</div>
|
|
266
|
+
<p class="form-text">
|
|
267
|
+
[[admin/settings/user:handles.enabled-help]]
|
|
268
|
+
</p>
|
|
269
|
+
</div>
|
|
270
|
+
<div class="mb-3">
|
|
271
|
+
<div class="form-check form-switch mb-3">
|
|
272
|
+
<input class="form-check-input" type="checkbox" id="guestsIncrementTopicViews" data-field="guestsIncrementTopicViews">
|
|
273
|
+
<label for="guestsIncrementTopicViews" class="form-check-label">[[admin/settings/user:topic-views.enabled]]</label>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
<div class="mb-3">
|
|
277
|
+
<div class="form-check form-switch mb-3">
|
|
278
|
+
<input class="form-check-input" type="checkbox" id="allowGuestReplyNotifications" data-field="allowGuestReplyNotifications">
|
|
279
|
+
<label for="allowGuestReplyNotifications" class="form-check-label">[[admin/settings/user:reply-notifications.enabled]]</label>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
|
|
284
|
+
<hr/>
|
|
285
|
+
|
|
286
|
+
<div id="default-user-settings" class="mb-4">
|
|
287
|
+
<h5 class="fw-bold tracking-tight settings-header">[[admin/settings/user:default-user-settings]]</h5>
|
|
288
|
+
<div class="form-check form-switch mb-3">
|
|
289
|
+
<input class="form-check-input" type="checkbox" id="showemail" data-field="showemail">
|
|
290
|
+
<label for="showemail" class="form-check-label">[[admin/settings/user:show-email]]</label>
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<div class="form-check form-switch mb-3">
|
|
294
|
+
<input class="form-check-input" type="checkbox" id="showfullname" data-field="showfullname">
|
|
295
|
+
<label for="showfullname" class="form-check-label">[[admin/settings/user:show-fullname]]</label>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<div class="form-check form-switch mb-3">
|
|
299
|
+
<input class="form-check-input" type="checkbox" id="disableIncomingChats" data-field="disableIncomingChats">
|
|
300
|
+
<label for="disableIncomingChats" class="form-check-label">[[admin/settings/user:disable-incoming-chats]]</label>
|
|
301
|
+
</div>
|
|
302
|
+
|
|
303
|
+
<div class="form-check form-switch mb-3">
|
|
304
|
+
<input class="form-check-input" type="checkbox" id="openOutgoingLinksInNewTab" data-field="openOutgoingLinksInNewTab">
|
|
305
|
+
<label for="openOutgoingLinksInNewTab" class="form-check-label">[[admin/settings/user:outgoing-new-tab]]</label>
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
<div class="form-check form-switch mb-3">
|
|
309
|
+
<input class="form-check-input" type="checkbox" id="topicSearchEnabled" data-field="topicSearchEnabled">
|
|
310
|
+
<label for="topicSearchEnabled" class="form-check-label">[[admin/settings/user:topic-search]]</label>
|
|
311
|
+
</div>
|
|
312
|
+
|
|
313
|
+
<div class="form-check form-switch mb-3">
|
|
314
|
+
<input class="form-check-input" type="checkbox" id="updateUrlWithPostIndex" data-field="updateUrlWithPostIndex">
|
|
315
|
+
<label for="updateUrlWithPostIndex" class="form-check-label">[[admin/settings/user:update-url-with-post-index]]</label>
|
|
316
|
+
</div>
|
|
317
|
+
|
|
318
|
+
<div class="mb-3">
|
|
319
|
+
<label class="form-label" for="dailyDigestFreq">[[admin/settings/user:digest-freq]]</label>
|
|
320
|
+
<select id="dailyDigestFreq" class="form-select" data-field="dailyDigestFreq">
|
|
321
|
+
<option value="off">[[admin/settings/user:digest-freq.off]]</option>
|
|
322
|
+
<option value="day">[[admin/settings/user:digest-freq.daily]]</option>
|
|
323
|
+
<option value="week">[[admin/settings/user:digest-freq.weekly]]</option>
|
|
324
|
+
<option value="biweek">[[admin/settings/user:digest-freq.biweekly]]</option>
|
|
325
|
+
<option value="month">[[admin/settings/user:digest-freq.monthly]]</option>
|
|
326
|
+
</select>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<div class="form-check form-switch mb-3">
|
|
330
|
+
<input class="form-check-input" type="checkbox" id="followTopicsOnCreate" data-field="followTopicsOnCreate">
|
|
331
|
+
<label for="followTopicsOnCreate" class="form-check-label">[[admin/settings/user:follow-created-topics]]</label>
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
<div class="form-check form-switch mb-3">
|
|
335
|
+
<input class="form-check-input" type="checkbox" id="followTopicsOnReply" data-field="followTopicsOnReply">
|
|
336
|
+
<label for="followTopicsOnReply" class="form-check-label">[[admin/settings/user:follow-replied-topics]]</label>
|
|
337
|
+
</div>
|
|
338
|
+
|
|
339
|
+
<div class="mb-3">
|
|
340
|
+
<label class="form-label" for="categoryWatchState">[[admin/settings/user:categoryWatchState]]</label>
|
|
341
|
+
<select id="categoryWatchState" class="form-select" data-field="categoryWatchState">
|
|
342
|
+
<option value="tracking" selected>[[admin/settings/user:categoryWatchState.tracking]]</option>
|
|
343
|
+
<option value="watching">[[admin/settings/user:categoryWatchState.watching]]</option>
|
|
344
|
+
</select>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
<label class="form-label mb-2">[[admin/settings/user:default-notification-settings]]</label>
|
|
348
|
+
|
|
349
|
+
{{{ each notificationSettings }}}
|
|
350
|
+
<div class="row">
|
|
351
|
+
<div class="mb-3 col-7">
|
|
352
|
+
<label class="form-label">{./label}</label>
|
|
353
|
+
</div>
|
|
354
|
+
<div class="mb-3 col-5">
|
|
355
|
+
<select class="form-select" data-field="{./name}">
|
|
356
|
+
<option value="none">[[notifications:none]]</option>
|
|
357
|
+
<option value="notification">[[notifications:notification-only]]</option>
|
|
358
|
+
<option value="email">[[notifications:email-only]]</option>
|
|
359
|
+
<option value="notificationemail">[[notifications:notification-and-email]]</option>
|
|
360
|
+
</select>
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
{{{ end }}}
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
|
|
367
|
+
<!-- IMPORT admin/partials/settings/toc.tpl -->
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
package/templates/feed.tpl
CHANGED
|
@@ -26,20 +26,28 @@
|
|
|
26
26
|
<div class="col-lg-6 col-sm-12 ms-auto">
|
|
27
27
|
{{{ end }}}
|
|
28
28
|
|
|
29
|
-
<!--
|
|
30
|
-
<div class="d-flex justify-content-between py-2 mb-0 gap-1">
|
|
31
|
-
{{{ if
|
|
32
|
-
<
|
|
29
|
+
<!-- For You / Following tabs + filter -->
|
|
30
|
+
<div class="d-flex justify-content-between align-items-center py-2 mb-0 gap-1">
|
|
31
|
+
{{{ if loggedIn }}}
|
|
32
|
+
<div class="d-flex gap-1">
|
|
33
|
+
<a href="{config.relative_path}/feed" class="btn btn-ghost btn-sm ff-secondary fw-semibold {{{ if !showFollowed }}}active{{{ end }}}">For You</a>
|
|
34
|
+
<a href="{config.relative_path}/feed?users=followed" class="btn btn-ghost btn-sm ff-secondary fw-semibold {{{ if showFollowed }}}active{{{ end }}}">Following</a>
|
|
35
|
+
</div>
|
|
33
36
|
{{{ end }}}
|
|
37
|
+
<div class="d-flex align-items-center gap-1 ms-auto">
|
|
38
|
+
<!-- IMPORT partials/category/filter-dropdown-right.tpl -->
|
|
39
|
+
{{{ if canPost }}}
|
|
40
|
+
<button id="new_topic" class="btn btn-primary btn-sm d-none">[[category:new-topic-button]]</button>
|
|
41
|
+
{{{ end }}}
|
|
42
|
+
</div>
|
|
34
43
|
</div>
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
{{{
|
|
42
|
-
<div class="alert alert-warning text-center">[[feed:no-posts-found]] {{{ if !following.length }}}[[feed:are-you-following-anyone]] {{{ end }}}</div>
|
|
45
|
+
{{{ if !posts.length }}}
|
|
46
|
+
{{{ if showFollowed }}}
|
|
47
|
+
<div class="alert alert-info text-center">Follow some members to see their posts here.</div>
|
|
48
|
+
{{{ else }}}
|
|
49
|
+
<div class="alert alert-warning text-center">[[feed:no-posts-found]]</div>
|
|
50
|
+
{{{ end }}}
|
|
43
51
|
{{{ end }}}
|
|
44
52
|
|
|
45
53
|
<ul component="posts" class="list-unstyled" data-nextstart="{nextStart}">
|
|
@@ -29,12 +29,6 @@
|
|
|
29
29
|
<span class="flex-shrink-0 text-xs" title="{counts.shares}">{humanReadableNumber(counts.shares)}</span>
|
|
30
30
|
</a>
|
|
31
31
|
|
|
32
|
-
<a href="{config.relative_path}/user/{userslug}/groups" class="btn btn-ghost btn-sm text-start ff-secondary fw-semibold d-flex gap-2 align-items-center
|
|
33
|
-
{{{ if template.account/groups }}}active{{{ end }}}">
|
|
34
|
-
<div class="flex-grow-1">[[global:header.groups]]</div>
|
|
35
|
-
<span class="flex-shrink-0 text-xs" title="{counts.groups}">{humanReadableNumber(counts.groups)}</span>
|
|
36
|
-
</a>
|
|
37
|
-
|
|
38
32
|
<a href="{config.relative_path}/user/{userslug}/followers" class="btn btn-ghost btn-sm text-start ff-secondary fw-semibold d-flex gap-2 align-items-center
|
|
39
33
|
{{{ if template.account/followers }}}active{{{ end }}}">
|
|
40
34
|
<div class="flex-grow-1">[[user:followers]]</div>
|
|
@@ -53,14 +47,6 @@
|
|
|
53
47
|
<div class="flex-grow-1">[[user:watched-categories]]</div>
|
|
54
48
|
<span class="flex-shrink-0 text-xs" title="{counts.categoriesWatched}">{counts.categoriesWatched}</span>
|
|
55
49
|
</a>
|
|
56
|
-
{{{ if isSelf }}}
|
|
57
|
-
<a href="{config.relative_path}/user/{userslug}/tags" class="btn btn-ghost btn-sm text-start ff-secondary fw-semibold d-flex gap-2 align-items-center
|
|
58
|
-
{{{ if template.account/tags }}}active{{{ end }}}">
|
|
59
|
-
<div class="flex-grow-1">[[user:watched-tags]]</div>
|
|
60
|
-
<span class="flex-shrink-0 text-xs" title="{counts.tagsWatched}">{counts.tagsWatched}</span>
|
|
61
|
-
</a>
|
|
62
|
-
{{{ end }}}
|
|
63
|
-
|
|
64
50
|
<a href="{config.relative_path}/user/{userslug}/blocks" class="btn btn-ghost btn-sm text-start ff-secondary fw-semibold d-flex gap-2 align-items-center
|
|
65
51
|
{{{ if template.account/blocks }}}active{{{ end }}}">
|
|
66
52
|
<div class="flex-grow-1">[[user:blocked-users]]</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{{{ if config.loggedIn }}}
|
|
2
|
+
<div class="btn-group bottom-sheet" component="topic/watch">
|
|
3
|
+
<button class="btn btn-ghost btn-sm ff-secondary dropdown-toggle" data-bs-toggle="dropdown" type="button" aria-haspopup="true" aria-expanded="false">
|
|
4
|
+
<span component="category/watching/menu" class="d-flex gap-2 align-items-center {{{ if !./isWatched }}} hidden{{{ end }}}"><i class="fa fa-fw fa-bell text-primary"></i><span class="visible-md-inline visible-lg-inline fw-semibold">[[category:watching]]</span></span>
|
|
5
|
+
|
|
6
|
+
<span component="category/notwatching/menu" class="d-flex gap-2 align-items-center {{{ if ./isWatched }}} hidden{{{ end }}}"><i class="fa fa-fw fa-bell-o text-primary"></i><span class="visible-md-inline visible-lg-inline fw-semibold">[[category:watch]]</span></span>
|
|
7
|
+
</button>
|
|
8
|
+
|
|
9
|
+
<ul class="dropdown-menu p-1 text-sm {{{ if template.account/categories }}}dropdown-menu-end{{{ end }}}" role="menu">
|
|
10
|
+
<li>
|
|
11
|
+
<a class="dropdown-item rounded-1 d-flex align-items-center gap-2 p-2" href="#" component="category/watching" data-state="watching" role="menuitem">
|
|
12
|
+
<div class="flex-grow-1 d-flex flex-column">
|
|
13
|
+
<span class="d-flex align-items-center gap-2">
|
|
14
|
+
<i class="flex-shrink-0 fa fa-fw fa-bell text-secondary"></i>
|
|
15
|
+
<span class="flex-grow-1 fw-semibold">[[category:watching]]</span>
|
|
16
|
+
</span>
|
|
17
|
+
<div class="help-text text-secondary text-xs">[[category:watching.description]]</div>
|
|
18
|
+
</div>
|
|
19
|
+
<span class="flex-shrink-0"><i component="category/watching/check" class="fa fa-fw {{{ if ./isWatched }}}fa-check{{{ end }}}"></i></span>
|
|
20
|
+
</a>
|
|
21
|
+
</li>
|
|
22
|
+
</ul>
|
|
23
|
+
</div>
|
|
24
|
+
{{{ end }}}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{{{ if !notifications.length }}}
|
|
2
|
+
<div class="no-notifs text-center p-4 d-flex flex-column">
|
|
3
|
+
<div class="p-4"><i class="fa-solid fa-wind fs-2 text-muted"></i></div>
|
|
4
|
+
<div class="text-xs fw-semibold text-muted">[[notifications:no-notifs]]</div>
|
|
5
|
+
</div>
|
|
6
|
+
{{{ end }}}
|
|
7
|
+
|
|
8
|
+
{{{ each notifications }}}
|
|
9
|
+
<div class="{./readClass}" data-nid="{./nid}" data-path="{./path}" {{{ if ./pid }}}data-pid="{./pid}"{{{ end }}}{{{ if ./tid }}}data-tid="{./tid}"{{{ end }}}>
|
|
10
|
+
<div class="d-flex gap-1 justify-content-between">
|
|
11
|
+
<div class="btn btn-ghost btn-sm d-flex gap-2 flex-grow-1 text-start align-items-start">
|
|
12
|
+
<a class="flex-grow-0 flex-shrink-0" href="{{{ if ./user.userslug}}}{config.relative_path}/user/{./user.userslug}{{{ else }}}#{{{ end }}}">
|
|
13
|
+
{{{ if (./image && ./from) }}}
|
|
14
|
+
<img class="avatar avatar-rounded" style="--avatar-size: 32px;" src="{./image}" onerror="this.onerror=null;this.style.display='none';this.nextElementSibling.classList.remove('hidden');" />
|
|
15
|
+
<div class="avatar avatar-rounded hidden" style="--avatar-size: 32px; background-color: {./user.icon:bgColor};">{./user.icon:text}</div>
|
|
16
|
+
{{{ else }}}
|
|
17
|
+
{{{ if ./icon }}}
|
|
18
|
+
<div class="avatar avatar-rounded" style="--avatar-size: 32px;"><i class="text-secondary fa {./icon}"></i></div>
|
|
19
|
+
{{{ else }}}
|
|
20
|
+
<div class="avatar avatar-rounded" style="--avatar-size: 32px; background-color: {./user.icon:bgColor};">{./user.icon:text}</div>
|
|
21
|
+
{{{ end }}}
|
|
22
|
+
{{{ end }}}
|
|
23
|
+
</a>
|
|
24
|
+
|
|
25
|
+
<div class="d-flex flex-grow-1 flex-column align-items-start position-relative">
|
|
26
|
+
<a href="{./path}" class="text-decoration-none d-inline-block text-reset text-break text-sm ff-sans stretched-link" component="notifications/item/link">
|
|
27
|
+
{./bodyShort}
|
|
28
|
+
</a>
|
|
29
|
+
<div class="text-xs text-muted">{{{ if ./timeagoLong }}}{./timeagoLong}{{{ else }}}<span class="timeago" title="{./datetimeISO}"></span>{{{ end }}}</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<div>
|
|
33
|
+
{{{ if ./nid }}}
|
|
34
|
+
<button class="mark-read btn btn-ghost btn-sm d-flex align-items-center justify-content-center flex-grow-0 flex-shrink-0 p-1" style="width: 1.5rem; height: 1.5rem;">
|
|
35
|
+
<i class="unread fa fa-2xs fa-circle text-primary {{{ if ./read }}}hidden{{{ end }}}" aria-label="[[unread:mark-as-read]]"></i>
|
|
36
|
+
<i class="read fa fa-2xs fa-circle-o text-secondary {{{ if !./read }}}hidden{{{ end }}}" aria-label="[[unread:mark-as-unread]]"></i>
|
|
37
|
+
</button>
|
|
38
|
+
{{{ end }}}
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
{{{ if !@last }}}
|
|
43
|
+
<hr class="my-1" />
|
|
44
|
+
{{{ end }}}
|
|
45
|
+
{{{ end }}}
|