Merge commit 'db07ab150875613c798b8b193bbd1fb59d65398a' into v3.x

isekai-main
Misty Release Bot 1 year ago
commit 7936609f05

@ -5,12 +5,10 @@ on:
branches:
- master
- develop
- bootstrap5
pull_request:
branches:
- master
- develop
- bootstrap5
defaults:
run:
@ -29,19 +27,19 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
node: [16, 18]
node: [16, 18, 20]
database: [mongo-dev, mongo, redis, postgres]
include:
# only run coverage once
- os: ubuntu-latest
node: 16
node: 18
coverage: true
# test under development once
- database: mongo-dev
test_env: development
# only run eslint once
- os: ubuntu-latest
node: 16
node: 18
database: mongo-dev
lint: true
runs-on: ${{ matrix.os }}

@ -1,3 +1,253 @@
#### v3.3.0 (2023-08-16)
##### Chores
* **deps:**
* update dependency smtp-server to v3.13.0 (#11913) (7f6865cc)
* update dependency lint-staged to v14 (#11909) (ee78b418)
* update dependency lint-staged to v13.3.0 (#11908) (6f3b7bc8)
* update dependency eslint to v8.47.0 (#11904) (af3c5e54)
* update dependency @commitlint/cli to v17.7.1 (#11896) (1d44b004)
* update commitlint monorepo to v17.7.0 (#11892) (d1d38097)
* update dependency sass-embedded to v1.64.2 (#11858) (c9c23513)
* update dependency eslint to v8.46.0 (#11852) (8a761ff7)
* update dependency sass-embedded to v1.64.1 (#11833) (1cca1760)
* update commitlint monorepo to v17.6.7 (#11817) (ac65ab42)
* update dependency sass-embedded to v1.64.0 (#11821) (560bb29c)
* update actions/setup-node action to v3 (#10347) (c8387828)
* update mongo docker tag to v6 (#10889) (f2715979)
* update dependency eslint to v8.45.0 (#11800) (b53da688)
* update coverallsapp/github-action action to v2.2.1 (#11795) (adfde1d4)
* update redis docker tag to v7.0.12 (#11789) (40477c85)
* update dependency eslint to v8.44.0 (#11771) (0bad8578)
* up emoji (ffa8b729)
* up harmony (c83a7023)
* up composer (c9663718)
* up harmony/composer (5d030a77)
* up composer (e151ec86)
* up composer-default (8d2ac658)
* up harmony (dfc155e4)
* up themes (9501d855)
* up harmony (89968048)
* harmony (a282f701)
* up themes (35c97bcb)
* up themes (8b31815f)
* up themes (ba2f6031)
* harmony (e8fb02f3)
* up harmony (3b125ba2)
* up harmony (58968353)
* up themes (8444af1c)
* up harmony (6faec937)
* up composer (7bfe327c)
* up mentions (0495b863)
* up themes (472fbd85)
* up harmony (f3776501)
* up theme (05c1e1f1)
* up harmony (ee0128d7)
* up harmony (ba03e223)
* up themes (6fc80f9f)
* incrementing version number - v3.2.3 (b06d3e63)
* update changelog for v3.2.3 (afb38c71)
* up mentions (4b92df75)
* up composer (48a04eb7)
* up harmony (efc250f2)
* up themes (6b017eb1)
* up harmony (08491053)
* up harmony (3b7b0d41)
* remove test log (b93cc788)
* up plugins (d1132ac4)
* up themes (35ac434c)
* up themes (98e0d141)
* up harmony (ac063fe5)
* up mentions (d545c143)
* up themes (b36bec95)
* up themes (62429252)
* remove log (954db1ee)
* up themes (9fda8dce)
* incrementing version number - v3.2.2 (758ecfcd)
* incrementing version number - v3.2.1 (20145074)
* up markdown (f23cda10)
* incrementing version number - v3.2.0 (9ecac38e)
* incrementing version number - v3.1.7 (0b4e81ab)
* incrementing version number - v3.1.6 (b3a3b130)
* incrementing version number - v3.1.5 (ec19343a)
* incrementing version number - v3.1.4 (2452783c)
* incrementing version number - v3.1.3 (3b4e9d3f)
* incrementing version number - v3.1.2 (40fa3489)
* incrementing version number - v3.1.1 (40250733)
* incrementing version number - v3.1.0 (0cb386bd)
* incrementing version number - v3.0.1 (26f6ea49)
* incrementing version number - v3.0.0 (224e08cd)
* **i18n:**
* fallback strings for new resources: nodebb.admin-dashboard (80ea4eb0)
* fallback strings for new resources: nodebb.admin-settings-chat, nodebb.error (e27d1743)
* fallback strings for new resources: nodebb.modules (ab668617)
* fallback strings for new resources: nodebb.global (bee5a9b4)
* fallback strings for new resources: nodebb.error (eca28302)
* fallback strings for new resources: nodebb.admin-dashboard (ac355acd)
* fallback strings for new resources: nodebb.admin-dashboard (62b7be1c)
* fallback strings for new resources: nodebb.admin-menu (ad9d8f77)
* fallback strings for new resources: nodebb.admin-admin, nodebb.admin-manage-categories (f075e12a)
* fallback strings for new resources: nodebb.modules (2c54e362)
* fallback strings for new resources: nodebb.admin-manage-users (9284b7e4)
* fallback strings for new resources: nodebb.modules (4ed0ed45)
* fallback strings for new resources: nodebb.topic (9c0b98df)
* fallback strings for new resources: nodebb.email (3613d1e6)
* fallback strings for new resources: nodebb.modules (a7dae508)
* fallback strings for new resources: nodebb.admin-settings-guest (640e32d4)
* fallback strings for new resources: nodebb.error, nodebb.modules (2710037c)
* fallback strings for new resources: nodebb.admin-admin (58bdedaf)
##### New Features
* closes #11902, ability to clear search history (7a79fed8)
* #11897, show guest handles in post queue and after using POST /compose (76fde8ef)
* #11881, limit room names (9349cb63)
* add toMid to chat messages (0316f324)
* add category selector to analytics page (acef5e33)
* middleware.handleMultipart, applies on API routes — invokes multipart middleware based on content-type header (bcc4b82c)
* #11868 apply blacklist to routes (#11873) (23404ad1)
* add icon to invite/accept notifs (78c5dfdc)
* add flags link to acp manage menu, closes #11867 (99c22942)
* move to npm fontawesome dependency and support fa pro (#11820) (b709ed9e)
* password check hook (#11853) (8ac34f8e)
* #11850, chat msg search (11bfeaf1)
* #11814, dont check content len for admins (f359a767)
* closes #11843, refresh search after changing filters (907c5fa6)
* #11842, ability to change reputation of users (ac027387)
* closes #11812, add unread public rooms into digest (eb0fcd32)
* allow multiple room owners, closes #6503 (91642cb3)
* show online users at the top of userlist and update (911ef058)
* simplified api module handler logic, content-type detection/parsing (2d016af8)
* allow FormData object to be passed in to the API module (ed99ea20)
##### Bug Fixes
* **deps:**
* update dependency postcss to v8.4.28 (#11915) (eafa03ab)
* update dependency sharp to v0.32.5 (#11916) (a013b161)
* update dependency ace-builds to v1.24.1 (#11914) (4a97ee0f)
* update dependency cron to v2.4.1 (#11911) (6476c4b8)
* update dependency esbuild to v0.19.2 (#11910) (d9e08e05)
* update dependency autoprefixer to v10.4.15 (#11907) (2a1e33dd)
* update dependency nodebb-plugin-2factor to v7.2.1 (#11898) (762658d4)
* update dependency nodebb-plugin-ntfy to v1.4.0 (#11905) (b8d926f9)
* update dependency lru-cache to v10.0.1 (#11899) (fd385647)
* update dependency esbuild to v0.19.1 (#11903) (12771b70)
* update dependency sass to v1.65.1 (#11895) (4b04b41e)
* update dependency ace-builds to v1.24.0 (#11893) (8f6feb0b)
* update dependency compare-versions to v6.1.0 (#11883) (9608b124)
* update dependency nodebb-plugin-ntfy to v1.3.0 (#11889) (53006408)
* update dependency esbuild to v0.19.0 (#11884) (454a968e)
* update dependency nodebb-theme-persona to v13.2.17 (#11888) (70f83075)
* update dependency nodebb-theme-harmony to v1.1.33 (#11887) (31ed1a40)
* update fontsource monorepo to v5.0.8 (#11880) (4cb0b738)
* update dependency esbuild to v0.18.19 (#11882) (2c8fd3b8)
* update dependency esbuild to v0.18.18 (#11879) (9a07cdbd)
* update dependency nodebb-plugin-ntfy to v1.2.5 (#11876) (7710a5e6)
* update dependency @fortawesome/fontawesome-free to v6.4.2 (#11870) (7d5a8666)
* update fontsource monorepo to v5.0.7 (#11869) (2c8bf84c)
* update dependency validator to v13.11.0 (#11877) (4594cd67)
* update dependency nodebb-plugin-ntfy to v1.2.4 (ae8f5398)
* update socket.io packages to v4.7.2 (#11871) (d4d339f9)
* update dependency pg to v8.11.2 (#11859) (5582fe92)
* update dependency pg-cursor to v2.10.2 (#11860) (c79c1e5f)
* update dependency sass to v1.64.2 (#11861) (da852139)
* update dependency esbuild to v0.18.17 (#11844) (ba379836)
* update dependency cron to v2.4.0 (#11839) (b1db67ce)
* update dependency postcss to v8.4.27 (#11827) (8d8930f8)
* update dependency sharp to v0.32.4 (#11828) (ef9fd345)
* update dependency sass to v1.64.1 (#11834) (878fe217)
* update dependency esbuild to v0.18.16 (#11838) (ace36434)
* update dependency esbuild to v0.18.15 (#11823) (8ab9c72c)
* update dependency nodemailer to v6.9.4 (#11819) (fcb99af1)
* update dependency sass to v1.64.0 (#11822) (e7626d90)
* update dependency nodebb-plugin-ntfy to v1.1.0 (#11815) (465b3e09)
* update dependency webpack to v5.88.2 (#11811) (4095cda6)
* update dependency esbuild to v0.18.14 (#11813) (de13aae6)
* update dependency esbuild to v0.18.13 (#11801) (212f90bb)
* update dependency sharp to v0.32.3 (#11799) (406ced79)
* update dependency postcss to v8.4.26 (#11798) (8e295464)
* update dependency esbuild to v0.18.12 (#11794) (f15265ff)
* update dependency winston to v3.10.0 (#11792) (786fff6f)
* update dependency sharp to v0.32.2 (#11791) (d156e67e)
* update dependency nodebb-plugin-ntfy to v1.0.16 (#11790) (3c5e5d3e)
* update fontsource monorepo to v5.0.5 (#11785) (efd784fa)
* update dependency compare-versions to v6 (#11784) (50fd242b)
* update dependency semver to v7.5.4 (#11783) (3bf10941)
* update dependency mongodb to v5.7.0 (#11781) (833a1ba7)
* update dependency postcss to v8.4.25 (#11780) (7fb8e414)
* update dependency jsonwebtoken to v9.0.1 (#11778) (bb89a12a)
* update dependency ace-builds to v1.23.4 (#11782) (b2cabd43)
* update fontsource monorepo to v5.0.4 (#11776) (5eedd8eb)
* update dependency nodebb-plugin-emoji to v5.1.3 (#11777) (1932a31d)
* update dependency nodebb-theme-persona to v13.1.7 (#11769) (d2e6062b)
* update dependency @isaacs/ttlcache to v1.4.1 (#11774) (63bbb366)
* update dependency esbuild to v0.18.11 (#11772) (335a3619)
* update dependency webpack to v5.88.1 (#11764) (a3111e4d)
* if you send message scrollToBottom (223c85e4)
* clicking on email consent form label checks the wrong box (2fe93361)
* clicking on email consent form label checks the wrong box (514af5d6)
* copy FA fonts to build directory instead of serving them directly (#11891) (ac4623ee)
* use config.undoTimeout instead of hardcoded value (850cfb33)
* add missing id/for to all checkboxes (ae747875)
* #11875, simplify alerts and show progress (881a28eb)
* bug where api module would throw if response contained no content-type header (007d735f)
* chat mark read regression (f4e2e617)
* fix escaped characters in tooltips, fix priv checks in tooltips (82562bec)
* #11855, remove superfluous password challenge on admin email update (unless they're updating their own) (4ca71f63)
* register abort can error on weird session state (#11854) (c9511915)
* #11847, typo in markNotification (5a8b4125)
* #11837, use userslug for check (e2fa8cf3)
* #11841, use unique nid per user who flagged (17783440)
* reset_code.tpl card block expanding to bottom of container (2792d771)
* closes #11825, user icons in global privileges (dae4f9f7)
* fallback for room timestamp (77550a50)
* dont error if timestamp is missing (6cc86b6e)
* topic postercount field if owner is changed (00be053e)
* updateOwner (16fe1eb9)
* unread notif filter (3e7ca4f2)
* closes #11806, fix code blocks (165b0f85)
* allow escape to close chat modals (4c311502)
* on leave/kick remove matching sockets from rooms (a766f74f)
* spec (79fae26d)
* lint (1e38a16b)
* client side js crash (adb3a5e6)
* #11797, update title on chat switch (840792ae)
* always return empty array if its not set (5c208610)
* mobile back button (9149a9a2)
* clear cache on sort (e03fdcd6)
* #11787 (bf2c429a)
* fetch handler not passing back errors or success payloads (8a531826)
* group membership methods for guests/spiders (2791eb8a)
##### Other Changes
* new filter to filter skins (c1361ee5)
* //github.com/NodeBB/NodeBB/issues/11818 (f7ae8963)
* //github.com/NodeBB/NodeBB/issues/11818 (0dce4c46)
##### Performance Improvements
* faster upgrade script (43060f3c)
##### Refactors
* ip blacklist.test (38c0c8de)
* dont load all tokens in verify token (2fe193d6)
* dont sleep if iteration is done on first one (0136e924)
* use fetch() throughout, instead of jQuery .ajax() (7415b16d)
##### Tests
* fix test if ip is invalid (38377982)
* fix tests to use regular uid (d615273d)
* fix tests since nid format changed (e41042ee)
* fix test (9e574e07)
* log e11000 errors (934df69e)
* fix spec (b63cd548)
* fix (e31f5c42)
#### v3.2.3 (2023-07-19)
##### Chores

@ -102,10 +102,10 @@
"nodebb-plugin-ntfy": "1.4.0",
"nodebb-plugin-spam-be-gone": "2.1.1",
"nodebb-rewards-essentials": "0.2.3",
"nodebb-theme-harmony": "1.1.35",
"nodebb-theme-harmony": "1.1.38",
"nodebb-theme-lavender": "7.1.3",
"nodebb-theme-peace": "2.1.10",
"nodebb-theme-persona": "13.2.17",
"nodebb-theme-peace": "2.1.12",
"nodebb-theme-persona": "13.2.19",
"nodebb-widget-essentials": "7.0.13",
"nodemailer": "6.9.4",
"nprogress": "0.2.0",

@ -2,18 +2,21 @@
@use "@fontsource/poppins/scss/mixins" as Poppins;
$weights: $font-weight-light, $font-weight-normal, $font-weight-semibold, $font-weight-bold;
$subsets: (latin, latin-ext);
@include Inter.faces(
$weights: $weights,
$subsets: $subsets,
$display: fallback,
$directory: "./plugins/core/inter"
);
@include Poppins.faces(
$weights: $weights,
$display: fallback,
$directory: "./plugins/core/poppins"
$weights: $weights,
$subsets: $subsets,
$display: fallback,
$directory: "./plugins/core/poppins"
);
.ff-base { font-family: $font-family-base !important; }
.ff-sans { font-family: $font-family-sans-serif !important; }
.ff-secondary { font-family: $font-family-secondary; }
.ff-secondary { font-family: $font-family-secondary; }

@ -2,8 +2,8 @@
define('admin/appearance/skins', [
'translator', 'alerts', 'settings',
], function (translator, alerts, settings) {
'translator', 'alerts', 'settings', 'hooks',
], function (translator, alerts, settings, hooks) {
const Skins = {};
Skins.init = function () {
@ -11,9 +11,19 @@ define('admin/appearance/skins', [
$.ajax({
method: 'get',
url: 'https://bootswatch.com/api/5.json',
}).done(Skins.render);
}).done((bsData) => {
hooks.on('action:settings.sorted-list.loaded', (data) => {
if (data.hash === 'custom-skins') {
// lower case all custom-skin ids after load
$('.custom-skin-settings [data-type="list"] [data-theme]').each((i, el) => {
$(el).attr('data-theme', $(el).attr('data-theme').toLowerCase());
});
Skins.render(bsData);
}
});
settings.load('custom-skins', $('.custom-skin-settings'));
});
settings.load('custom-skins', $('.custom-skin-settings'));
$('#save-custom-skins').on('click', function () {
settings.save('custom-skins', $('.custom-skin-settings'), function () {
alerts.success('[[admin/appearance/skins:save-custom-skins-success]]');
@ -33,13 +43,12 @@ define('admin/appearance/skins', [
if (action && action === 'use') {
const parentEl = target.parents('[data-theme]');
const themeType = parentEl.attr('data-type');
const cssSrc = parentEl.attr('data-css');
const themeId = parentEl.attr('data-theme');
const themeName = parentEl.attr('data-theme-name');
socket.emit('admin.themes.set', {
type: themeType,
type: 'bootswatch',
id: themeId,
src: cssSrc,
}, function (err) {
@ -52,7 +61,7 @@ define('admin/appearance/skins', [
alert_id: 'admin:theme',
type: 'info',
title: '[[admin/appearance/skins:skin-updated]]',
message: themeId ? ('[[admin/appearance/skins:applied-success, ' + themeId + ']]') : '[[admin/appearance/skins:revert-success]]',
message: themeId ? ('[[admin/appearance/skins:applied-success, ' + themeName + ']]') : '[[admin/appearance/skins:revert-success]]',
timeout: 5000,
});
});
@ -67,7 +76,7 @@ define('admin/appearance/skins', [
themes: bootswatch.themes.map(function (theme) {
return {
type: 'bootswatch',
id: theme.name,
id: theme.name.toLowerCase(),
name: theme.name,
description: theme.description,
screenshot_url: theme.thumbnail,
@ -82,9 +91,7 @@ define('admin/appearance/skins', [
if (app.config.bootswatchSkin) {
const skin = app.config.bootswatchSkin;
highlightSelectedTheme(
skin.charAt(0).toUpperCase() + skin.slice(1)
);
highlightSelectedTheme(skin);
}
});
};

@ -104,7 +104,16 @@ helpers.getUserDataByUserSlug = async function (userslug, callerUID, query = {})
canViewInfo: canViewInfo,
});
userData.sso = results.sso.associations;
userData.sso = results.sso.associations.map((association) => {
if (!isSelf) {
delete association.deauthUrl;
if (!association.associated) {
delete association.url;
}
}
return association;
});
userData.banned = Boolean(userData.banned);
userData.muted = parseInt(userData.mutedUntil, 10) > Date.now();
userData.website = escape(userData.website);

@ -95,8 +95,9 @@ function printStartupInfo() {
}
function addProcessHandlers() {
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
['SIGTERM', 'SIGINT', 'SIGQUIT'].forEach((signal) => {
process.on(signal, () => shutdown());
});
process.on('SIGHUP', restart);
process.on('uncaughtException', (err) => {
winston.error(err.stack);
@ -130,7 +131,7 @@ function restart() {
}
async function shutdown(code) {
winston.info('[app] Shutdown (SIGTERM/SIGINT) Initialised.');
winston.info('[app] Shutdown (SIGTERM/SIGINT/SIGQUIT) Initialised.');
try {
await require('./webserver').destroy();
winston.info('[app] Web server closed to connections.');
@ -142,6 +143,7 @@ async function shutdown(code) {
process.exit(code || 0);
} catch (err) {
winston.error(err.stack);
return process.exit(code || 0);
}
}

@ -1,5 +1,6 @@
'use strict';
/* eslint-disable no-await-in-loop */
'use strict';
const db = require('../../database');
const batch = require('../../batch');
@ -13,7 +14,7 @@ module.exports = {
progress.total = await db.sortedSetCard(`chat:rooms`);
await batch.processSortedSet(`chat:rooms`, async (roomIds) => {
progress.incr(roomIds.length);
await Promise.all(roomIds.map(async (roomId) => {
for (const roomId of roomIds) {
await batch.processSortedSet(`chat:room:${roomId}:mids`, async (mids) => {
let messageData = await db.getObjects(mids.map(mid => `message:${mid}`));
messageData.forEach((m, idx) => {
@ -36,7 +37,7 @@ module.exports = {
}, {
batch: 500,
});
}));
}
}, {
batch: 500,
});

@ -26,7 +26,7 @@ module.exports = {
});
await db.sortedSetAddBulk(bulkAdd);
}, {
batch: 500,
batch: 100,
});
},
};

@ -1,12 +1,10 @@
'use strict';
/* eslint-disable no-await-in-loop */
const _ = require('lodash');
'use strict';
const db = require('../../database');
const batch = require('../../batch');
module.exports = {
name: 'Update chat messages to add roomId field',
timestamp: Date.UTC(2023, 6, 2),
@ -18,47 +16,66 @@ module.exports = {
for (let i = 1; i <= nextChatRoomId; i++) {
allRoomIds.push(i);
}
progress.total = allRoomIds.length;
progress.total = 0;
// calculate user count and set progress.total
await batch.processArray(allRoomIds, async (roomIds) => {
progress.incr(roomIds.length);
const [arrayOfUids, arrayOfRoomData] = await Promise.all([
db.getSortedSetsMembers(roomIds.map(roomId => `chat:room:${roomId}:uids`)),
db.getObjects(roomIds.map(roomId => `chat:room:${roomId}`)),
]);
await Promise.all(roomIds.map(async (roomId) => {
const userCount = await db.sortedSetCard(`chat:room:${roomId}:uids`);
await db.setObjectField(`chat:room:${roomId}`, 'userCount', userCount);
progress.total += userCount;
}));
}, {
batch: 500,
});
await Promise.all(roomIds.map(async (roomId, index) => {
const uids = arrayOfUids[index];
const roomData = arrayOfRoomData[index];
if (!uids.length && !roomData) {
return;
}
if (roomData && roomData.owner && !uids.includes(String(roomData.owner))) {
uids.push(roomData.owner);
}
const userKeys = uids.map(uid => `uid:${uid}:chat:room:${roomId}:mids`);
const mids = await db.getSortedSetsMembers(userKeys);
const uniqMids = _.uniq(_.flatten(mids));
let messageData = await db.getObjects(uniqMids.map(mid => `message:${mid}`));
messageData.forEach((m, idx) => {
if (m) {
m.mid = parseInt(uniqMids[idx], 10);
}
});
messageData = messageData.filter(Boolean);
await batch.processArray(allRoomIds, async (roomIds) => {
const arrayOfRoomData = await db.getObjects(roomIds.map(roomId => `chat:room:${roomId}`));
for (const roomData of arrayOfRoomData) {
if (roomData) {
const midsSeen = Object.create(null);
const { roomId } = roomData;
await batch.processSortedSet(`chat:room:${roomId}:uids`, async (uids) => {
for (const uid of uids) {
await batch.processSortedSet(`uid:${uid}:chat:room:${roomId}:mids`, async (mids) => {
const uniqMids = mids.filter(mid => !midsSeen.hasOwnProperty(mid));
if (!uniqMids.length) {
return;
}
const bulkSet = messageData.map(
msg => [`message:${msg.mid}`, { roomId: roomId }]
);
let messageData = await db.getObjects(uniqMids.map(mid => `message:${mid}`));
messageData.forEach((m, idx) => {
if (m) {
m.mid = parseInt(uniqMids[idx], 10);
}
});
messageData = messageData.filter(Boolean);
await db.setObjectBulk(bulkSet);
await db.setObjectField(`chat:room:${roomId}`, 'userCount', uids.length);
await db.sortedSetAdd(
`chat:room:${roomId}:mids`,
messageData.map(m => m.timestamp),
messageData.map(m => m.mid),
);
await db.deleteAll(userKeys);
}));
const bulkSet = messageData.map(
msg => [`message:${msg.mid}`, { roomId: roomId }]
);
await db.setObjectBulk(bulkSet);
await db.sortedSetAdd(
`chat:room:${roomId}:mids`,
messageData.map(m => m.timestamp),
messageData.map(m => m.mid),
);
uniqMids.forEach((mid) => {
midsSeen[mid] = 1;
});
}, {
batch: 500,
});
// eslint-disable-next-line no-await-in-loop
await db.deleteAll(`uid:${uid}:chat:room:${roomId}:mids`);
}
progress.incr(uids.length);
}, {
batch: 500,
});
}
}
}, {
batch: 500,
});

@ -1,9 +1,10 @@
<li data-type="item" class="list-group-item">
<li data-type="item" class="list-group-item" data-theme-name="{custom-skin-name}" data-theme="{custom-skin-name}">
<div class="d-flex justify-content-between align-items-center">
<div class="">
<strong>{custom-skin-name}</strong>
</div>
<div class="">
<button type="button" data-action="use" class="btn btn-sm btn-primary">[[admin/appearance/skins:select-skin]]</button>
<button type="button" data-type="edit" class="btn btn-sm btn-light"><i class="fa fa-edit text-primary"></i></button>
<button type="button" data-type="remove" class="btn btn-sm btn-light"><i class="fa fa-trash-o text-danger"></i></button>
</div>

@ -1,5 +1,5 @@
{{{ each themes }}}
<div class="col-lg-4 col-md-6 col-12 mb-4" data-type="{./type}" data-theme="{./id}"{{{ if ./css }}} data-css="{./css}" {{{ end }}}>
<div class="col-lg-4 col-md-6 col-12 mb-4" data-type="{./type}" data-theme-name="{./name}" data-theme="{./id}"{{{ if ./css }}} data-css="{./css}" {{{ end }}}>
<div class="card h-100">
<img src="{./screenshot_url}" class="card-img-top">
<div class="card-body">

Loading…
Cancel
Save