Bootstrap5 (#10894)

* chore: up deps

* chore: up composer

* fix(deps): bump 2factor to v7

* chore: up harmony

* chore: up harmony

* fix: missing await

* feat: allow middlewares to pass in template values via res.locals

* feat: buildAccountData middleware automatically added ot all account routes

* fix: properly allow values in res.locals.templateValues to be added to the template data

* refactor: user/blocks

* refactor(accounts): categories and consent

* feat: automatically 404 if exposeUid or exposeGroupName come up empty

* refactor: remove calls to getUserDataByUserSlug for most account routes, since it is populated via middleware now

* fix: allow exposeUid and exposeGroupName to work with slugs with mixed capitalization

* fix: move reputation removal check to accountHelpers method

* test: skip i18n tests if ref branch when present is not develop

* fix(deps): bump theme versions

* fix(deps): bump ntfy and 2factor

* chore: up harmony

* fix: add missing return

* fix: #11191, only focus on search input on md environments and up

* feat: allow file uploads on mobile chat

closes https://github.com/NodeBB/NodeBB/issues/11217

* chore: up themes

* chore: add lang string

* fix(deps): bump ntfy to 1.0.15

* refactor: use new if/each syntax

* chore: up composer

* fix: regression from user helper refactor

* chore: up harmony

* chore: up composer

* chore: up harmony

* chore: up harmony

* chore: up harmony

* chore: fix composer version

* feat: add increment helper

* chore: up harmony

* fix: #11228 no timestamps in future 

* chore: up harmony

* check config.theme as well

fire action:posts.loaded after processing dom

* chore: up harmony

* chore: up harmony

* chore: up harmony

* chore: up themes

* chore: up harmony

* remove extra class

* refactor: move these to core from harmony

* chore: up widgets

* chore: up widgets

* height auto

* fix: closes #11238

* dont focus inputs, annoying on mobile

* fix: dont focus twice, only focus on chat input on desktop

dont wrap widget footer in row

* chore: up harmony

* chore: up harmony

* update chat window

* chore: up themes

* fix cache buster for skins

* chat fixes

* chore: up harmony

* chore: up composer

* refactor: change hook logs to debug

* fix: scroll to post right after adding to dom

* fix: hash scrolling and highlighting correct post

* test: re-enable read API schema tests

* fix: add back schema changes for 179faa2270 and c3920ccb10

* fix: schema changes from 488f0978a4

* fix: schema changes for f4cf482a87

* fix: schema update for be6bbabd0e

* fix: schema changes for 69c96078ea

* fix: schema changes for d1364c3130

* fix: schema changes for 84ff1152f7

* fix: schema changes for b860c2605c

* fix: schema changes for 23cb67a112

* fix: schema changes for b916e42f40

* fix: schema change for a9bbb586fc

* fix: schema changes for 4b738c8cd3

* fix: schema changes for 58b5781cea

* fix: schema changes for 794bf01b21

* fix: schema changes for 80ea12c1c1, e368feef51, and 52ead114be

* fix: composer-default object in config?

* fix: schema changes for 9acdc6808c and 0930934200

* fix: schema changes for c0a52924f1

* fix: schema change for aba420a3f3, move loggedInUser to optional props

* fix: schema changes for 8c67031609

* fix: schema changes for 27e53b42f3

* fix: schema changes for 2835966518

* fix: breaking test for email confirmation API call

* fix: schema changes for refactored search page

* fix: schema changes for user object

* fix: schema changes for 9f531f957e

* fix: schema changes for c4042c70de and 23175110a2

* fix: schema changes for 9b3616b103

* fix: schema changes for 5afd5de07d

* fix: schema change for 1d7baf1217

* fix: schema changes for 57bfb37c55 and be6bbabd0e

* fix: schema changes for 6e86b4afa2 and 3efad2e13b and 68f66223e7

* fix: allowing optional qs prop in pagination keys (not sure why this didn't break before)

* fix: re-login on email change

* fix: schema changes for c926358d73

* fix: schema changes for 388a8270c9

* fix: schema change for 2658bcc821

* fix: no need to call account middlewares for chats routes

* fix: schema changes for 71743affc3

* fix: final schema changes

* test: support for anyOf and oneOf

* fix: check thumb

* dont scroll to top on back press

* remove group log

* fix: add top margin to merged and deleted alerts

* chore: up widgets

* fix: improve fix-lists mixin

* chore: up harmony/composer

* feat: allow hiding quicksearch results during search

* dont record searches made by composer

* chore: up 54

* chore: up spam be gone

* feat: add prev/next page and page count into mobile paginator

* chore: up harmony

* chore: up harmony

* use old style for IS

* fix: hide entire toolbar row if no posts or not singlePost

* fix: updated messaging for post-queue template, #11206

* fix: btn-sm on post queue back button

* fix: bump harmony, closes #11206

* fix: remove unused alert module import

* fix: bump harmony

* fix: bump harmony

* chore: up harmony

* refactor: IS scrolltop

* fix: update users:search-user-for-chat source string

* feat: support for mark-read toggle on chats dropdown and recent chats list

* feat: api v3 calls to mark chat read/unread

* feat: send event:chats.mark socket event on mark read or unread

* refactor: allow frontend to mark chats as unread, use new API v3 routes instead of socket calls, better frontend event handling

* docs: openapi schema updates for chat marking

* fix: allow unread state toggling in chats dropdown too

* fix: issue where repeated openings of the chats dropdown would continually add events for mark-read/unread

* fix: debug log

* refactor: move userSearch filter to a module

* feat(routes): allow remounting /categories (#11230)

* feat: send flags count to frontend on flags list page

* refactor: filter form client-side js to extract out some logic

* fix: applyFilters to not take any arguments, update selectedCids in updateButton instead of onHidden

* fix: use userFilter module for assignee, reporterId, targetUid

* fix(openapi): schema changes for updated flags page

* fix: dont allow adding duplicates to userFilter

* use same var

* remove log

* fix: closes #11282

* feat: lang key for x-topics

* chore: up harmony

* chore: up emoji

* chore: up harmony

* fix: update userFilter to allow new option `selectedBlock`

* fix: wrong block name passed to userFilter

* fix: https://github.com/NodeBB/NodeBB/issues/11283

* fix: chats, allow multiple dropdowns like in harmony

* chore: up harmony

* refactor: flag note adding/editing, closes #11285

* fix: remove old prepareEdit logic

* chore: add caveat about hacky code block in userFilter module

* fix: placeholders for userFilter module

* refactor: navigator so it works with multiple thumbs/navigators

* chore: up harmony

* fix: closes #11287, destroy quick reply autocomplete

on navigation

* fix: filter disabled categories on user categories page count

* chore: up harmony

* docs: update openapi spec to include info about passing in timestamps for topic creation, removing timestamp as valid request param for topic replying

* fix: send back null values on ACP search dashboard for startDate and endDate if not expicitly passed in, fix tests

* fix: tweak table order in ACP dash searches

* fix: only invoke navigator click drag on left mouse button

* feat: add back unread indicator to navigator

* clear bookmark on mark unread

* fix: navigator crash on ajaxify

* better thumb top calculation

* fix: reset user bookmark when topic is marked unread

* Revert "fix: reset user bookmark when topic is marked unread"

This reverts commit 9bcd85c2c6.

* fix: update unread indicator on scroll, add unread count

* chore: bump harmony

* fix: crash on navigator unread update when backing out of a topic

* fix: closes #11183

* fix: update topics:recent zset when rescheduling a topic

* fix: dupe quote button, increase delay, hide immediately on empty selection

* fix: navigator not showing up on first load

* refactor: remove glance

assorted fixes to navigator
dont reduce remaning count if user scrolls down and up quickly
only call topic.navigatorCallback when index changes

* more sanity checks for bookmark

dont allow setting bookmark higher than topic postcount

* closes #11218, 🚋

* Revert "fix: update topics:recent zset when rescheduling a topic"

This reverts commit 737973cca9.

* fix: #11306, show proper error if queued post doesn't exist

was showing no-privileges if someone else accepted the post

* https://github.com/NodeBB/NodeBB/issues/11307

dont use li

* chore: up harmony

* chore: bump version string

* fix: copy paste fail

* feat: closes #7382, tag filtering

add client side support for filtering by tags on /category, /recent and /unread

* chore: up harmony

* chore: up harmony

* Revert "fix: add back req.query fallback for backwards compatibility" [breaking]

This reverts commit cf6cc2c454.
This commit is no longer required as passing in a CSRF token via query parameter is no longer supported as of NodeBB v3.x

This is a breaking change.

* fix: pass csrf token in form data, re: NodeBB/NodeBB#11309

* chore: up deps

* fix: tests, use x-csrf-token query param removed

* test: fix csrf_token

* lint: remove unused

* feat: add itemprop="image" to avatar helper

* fix: get chat upload button in chat modal

* breaking: remove deprecated socket.io methods

* test: update messaging tests to not use sockets

* fix: parent post links

* fix: prevent post tooltip if mouse leaves before data/tpl is loaded

* chore: up harmony

* chore: up harmony

* chore: up harmony

* chore: up harmony

* fix: nested replies indices

* fix(deps): bump 2factor

* feat: add loggedIn user to all api routes

* chore: up themes

* refactor: audit admin v3 write api routes as per #11321

* refactor: audit category v3 write api routes as per #11321 [breaking]

docs: fix open api spec for #11321

* refactor: audit chat v3 write api routes as per #11321

* refactor: audit files v3 write api routes as per #11321

* refactor: audit flags v3 write api routes as per #11321

* refactor: audit posts v3 write api routes as per #11321

* refactor: audit topics v3 write api routes as per #11321

* refactor: audit users v3 write api routes as per #11321

* fix: lang string

* remove min height

* fix: empty topic/labels taking up space

* fix: tag filtering when changing filter to watched topics

or changing popular time limit to month

* chore: up harmony

* fix: closes #11354, show no post error if queued post already accepted/rejected

* test: #11354

* test: #11354

* fix(deps): bump 2factor

* fix: #11357 clear cache on thumb remove

* fix: thumb remove on windows, closes #11357

* test: openapi for thumbs

* test: fix openapi

---------

Co-authored-by: Julian Lam <julian@nodebb.org>
Co-authored-by: Opliko <opliko.reg@protonmail.com>
This commit is contained in:
Barış Soner Uşaklı
2023-03-17 11:58:31 -04:00
committed by GitHub
parent 1e7f32b1c4
commit 7ba70d1561
564 changed files with 19068 additions and 36796 deletions

View File

@@ -3,6 +3,7 @@
module.exports = function (utils, Benchpress, relative_path) {
Benchpress.setGlobal('true', true);
Benchpress.setGlobal('false', false);
const oneDayInMs = 24 * 60 * 60 * 1000;
const helpers = {
displayMenuItem,
@@ -11,6 +12,8 @@ module.exports = function (utils, Benchpress, relative_path) {
stringify,
escape,
stripTags,
buildCategoryIcon,
buildCategoryLabel,
generateCategoryBackground,
generateChildrenCategories,
generateTopicClass,
@@ -18,11 +21,12 @@ module.exports = function (utils, Benchpress, relative_path) {
spawnPrivilegeStates,
localeToHTML,
renderTopicImage,
renderTopicEvents,
renderEvents,
renderDigestAvatar,
userAgentIcons,
buildAvatar,
increment,
generateRepliedTo,
generateWrote,
register,
__escape: identity,
};
@@ -81,6 +85,25 @@ module.exports = function (utils, Benchpress, relative_path) {
return utils.stripHTMLTags(str);
}
function buildCategoryIcon(category, size, rounded) {
if (!category) {
return '';
}
return `<span class="icon d-inline-flex justify-content-center align-items-center align-middle ${rounded}" style="${generateCategoryBackground(category)} width:${size}; height: ${size}; font-size: ${parseInt(size, 10) / 2}px;">${category.icon ? `<i class="fa fa-fw ${category.icon}"></i>` : ''}</span>`;
}
function buildCategoryLabel(category, className = '') {
if (!category) {
return '';
}
return `<span class="badge px-1 ${className}" style="${generateCategoryBackground(category)}">
${category.icon ? `<i class="fa fa-fw ${category.icon}"></i>` : ''}
${category.name}
</span>`;
}
function generateCategoryBackground(category) {
if (!category) {
return '';
@@ -89,6 +112,7 @@ module.exports = function (utils, Benchpress, relative_path) {
if (category.bgColor) {
style.push('background-color: ' + category.bgColor);
style.push(`border-color: ${category.bgColor}!important`);
}
if (category.color) {
@@ -113,8 +137,8 @@ module.exports = function (utils, Benchpress, relative_path) {
category.children.forEach(function (child) {
if (child && !child.isSection) {
const link = child.link ? child.link : (relative_path + '/category/' + child.slug);
html += '<span class="category-children-item pull-left">' +
'<div role="presentation" class="icon pull-left" style="' + generateCategoryBackground(child) + '">' +
html += '<span class="category-children-item float-start">' +
'<div role="presentation" class="icon float-start" style="' + generateCategoryBackground(child) + '">' +
'<i class="fa fa-fw ' + child.icon + '"></i>' +
'</div>' +
'<a href="' + link + '"><small>' + child.name + '</small></a></span>';
@@ -130,17 +154,17 @@ module.exports = function (utils, Benchpress, relative_path) {
}
// Groups helpers
function membershipBtn(groupObj) {
function membershipBtn(groupObj, btnClass = '') {
if (groupObj.isMember && groupObj.name !== 'administrators') {
return '<button class="btn btn-danger" data-action="leave" data-group="' + groupObj.displayName + '"' + (groupObj.disableLeave ? ' disabled' : '') + '><i class="fa fa-times"></i> [[groups:membership.leave-group]]</button>';
return `<button class="btn btn-danger ${btnClass}" data-action="leave" data-group="${groupObj.displayName}" ${(groupObj.disableLeave ? ' disabled' : '')}><i class="fa fa-times"></i> [[groups:membership.leave-group]]</button>`;
}
if (groupObj.isPending && groupObj.name !== 'administrators') {
return '<button class="btn btn-warning disabled"><i class="fa fa-clock-o"></i> [[groups:membership.invitation-pending]]</button>';
return `<button class="btn btn-warning disabled ${btnClass}"><i class="fa fa-clock-o"></i> [[groups:membership.invitation-pending]]</button>`;
} else if (groupObj.isInvited) {
return '<button class="btn btn-link" data-action="rejectInvite" data-group="' + groupObj.displayName + '">[[groups:membership.reject]]</button><button class="btn btn-success" data-action="acceptInvite" data-group="' + groupObj.name + '"><i class="fa fa-plus"></i> [[groups:membership.accept-invitation]]</button>';
return `<button class="btn btn-link" data-action="rejectInvite" data-group="${groupObj.displayName}">[[groups:membership.reject]]</button><button class="btn btn-success" data-action="acceptInvite" data-group="${groupObj.name}"><i class="fa fa-plus"></i> [[groups:membership.accept-invitation]]</button>`;
} else if (!groupObj.disableJoinRequests && groupObj.name !== 'administrators') {
return '<button class="btn btn-success" data-action="join" data-group="' + groupObj.displayName + '"><i class="fa fa-plus"></i> [[groups:membership.join-group]]</button>';
return `<button class="btn btn-success" data-action="join" data-group="${groupObj.displayName}"><i class="fa fa-plus"></i> [[groups:membership.join-group]]</button>`;
}
return '';
}
@@ -164,7 +188,13 @@ module.exports = function (utils, Benchpress, relative_path) {
(member === 'spiders' && !spidersEnabled.includes(priv.name)) ||
(member === 'Global Moderators' && globalModDisabled.includes(priv.name));
return '<td class="text-center" data-privilege="' + priv.name + '" data-value="' + priv.state + '"><input autocomplete="off" type="checkbox"' + (priv.state ? ' checked' : '') + (disabled ? ' disabled="disabled"' : '') + ' /></td>';
return `
<td data-privilege="${priv.name}" data-value="${priv.state}">
<div class="form-check text-center">
<input class="form-check-input float-none" autocomplete="off" type="checkbox"${(priv.state ? ' checked' : '')}${(disabled ? ' disabled="disabled"' : '')} />
</div>
</td>
`;
}).join('');
}
@@ -180,49 +210,6 @@ module.exports = function (utils, Benchpress, relative_path) {
return '<img component="user/picture" data-uid="' + topicObj.user.uid + '" src="' + topicObj.user.picture + '" class="user-img" title="' + topicObj.user.username + '" />';
}
function renderTopicEvents(index, sort) {
if (sort === 'most_votes') {
return '';
}
const start = this.posts[index].eventStart;
const end = this.posts[index].eventEnd;
const events = this.events.filter(event => event.timestamp >= start && event.timestamp < end);
if (!events.length) {
return '';
}
return renderEvents.call(this, events);
}
function renderEvents(events) {
return events.reduce((html, event) => {
html += `<li component="topic/event" class="timeline-event" data-topic-event-id="${event.id}" data-topic-event-type="${event.type}">
<div class="timeline-badge">
<i class="fa ${event.icon || 'fa-circle'}"></i>
</div>
<span class="timeline-text">
${event.href ? `<a href="${relative_path}${event.href}">${event.text}</a>` : event.text}&nbsp;
</span>
`;
if (event.user) {
if (!event.user.system) {
html += `<span><a href="${relative_path}/user/${event.user.userslug}">${buildAvatar(event.user, 'xs', true)}&nbsp;${event.user.username}</a></span>&nbsp;`;
} else {
html += `<span class="timeline-text">[[global:system-user]]</span>&nbsp;`;
}
}
html += `<span class="timeago timeline-text" title="${event.timestampISO}"></span>`;
if (this.privileges.isAdminOrMod) {
html += `&nbsp;<span component="topic/event/delete" data-topic-event-id="${event.id}" data-topic-event-type="${event.type} class="timeline-text pointer" title="[[topic:delete-event]]"><i class="fa fa-trash"></i></span>`;
}
return html;
}, '');
}
function renderDigestAvatar(block) {
if (block.teaser) {
if (block.teaser.user.picture) {
@@ -292,7 +279,7 @@ module.exports = function (utils, Benchpress, relative_path) {
/**
* userObj requires:
* - uid, picture, icon:bgColor, icon:text (getUserField w/ "picture" should return all 4), username
* size: one of "xs", "sm", "md", "lg", or "xl" (required), or an integer
* size: a picture size in the form of a value with units (e.g. 64px, 4rem, etc.)
* rounded: true or false (optional, default false)
* classNames: additional class names to prepend (optional, default none)
* component: overrides the default component (optional, default none)
@@ -303,38 +290,51 @@ module.exports = function (utils, Benchpress, relative_path) {
userObj = this;
}
const attributes = [
'alt="' + userObj.username + '"',
'title="' + userObj.username + '"',
'data-uid="' + userObj.uid + '"',
'loading="lazy"',
];
const styles = [];
const attributes = new Map([
['alt', userObj.username],
['title', userObj.username],
['data-uid', userObj.uid],
['loading', 'lazy'],
]);
const styles = [`--avatar-size: ${size};`];
const attr2String = attributes => Array.from(attributes).reduce((output, [prop, value]) => {
output += ` ${prop}="${value}"`;
return output;
}, '');
classNames = classNames || '';
// Validate sizes, handle integers, otherwise fall back to `avatar-sm`
if (['xs', 'sm', 'sm2x', 'md', 'lg', 'xl'].includes(size)) {
classNames += ' avatar-' + size;
} else if (!isNaN(parseInt(size, 10))) {
styles.push('width: ' + size + 'px;', 'height: ' + size + 'px;', 'line-height: ' + size + 'px;', 'font-size: ' + (parseInt(size, 10) / 16) + 'rem;');
} else {
classNames += ' avatar-sm';
}
attributes.unshift('class="avatar ' + classNames + (rounded ? ' avatar-rounded' : '') + '"');
attributes.set('class', `avatar ${classNames}${rounded ? ' avatar-rounded' : ''}`);
// Component override
if (component) {
attributes.push('component="' + component + '"');
} else {
attributes.push('component="avatar/' + (userObj.picture ? 'picture' : 'icon') + '"');
}
let output = '';
if (userObj.picture) {
return '<img ' + attributes.join(' ') + ' src="' + userObj.picture + '" style="' + styles.join(' ') + '" />';
attributes.set('component', component || 'avatar/picture');
output += '<img ' + attr2String(attributes) + ' src="' + userObj.picture + '" style="' + styles.join(' ') + '" onError="this.remove();" itemprop="image" />';
}
attributes.set('component', component || 'avatar/icon');
styles.push('background-color: ' + userObj['icon:bgColor'] + ';');
return '<span ' + attributes.join(' ') + ' style="' + styles.join(' ') + '">' + userObj['icon:text'] + '</span>';
output += '<span ' + attr2String(attributes) + ' style="' + styles.join(' ') + '">' + userObj['icon:text'] + '</span>';
return output;
}
function increment(value, inc) {
return value + parseInt(inc, 10);
}
function generateRepliedTo(post, timeagoCutoff) {
const username = post.parent && post.parent.username ?
post.parent.username : '[[global:guest]]';
const isBeforeCutoff = post.timestamp < (Date.now() - (timeagoCutoff * oneDayInMs));
const langSuffix = isBeforeCutoff ? 'on' : 'ago';
return `[[topic:replied-to-user-${langSuffix}, ${post.toPid}, ${relative_path}/post/${post.toPid}, ${username}, ${post.timestampISO}]]`;
}
function generateWrote(post, timeagoCutoff) {
const isBeforeCutoff = post.timestamp < (Date.now() - (timeagoCutoff * oneDayInMs));
const langSuffix = isBeforeCutoff ? 'on' : 'ago';
return `[[topic:wrote-${langSuffix}, ${post.timestampISO}]]`;
}
function register() {