nodebb-plugin-moving-topics 1.0.0 → 1.1.0

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/README.md CHANGED
@@ -9,6 +9,7 @@ Allow topic owners to move their own topics between categories, without granting
9
9
  - Moves are blocked for **locked** or **deleted** topics.
10
10
  - Target categories are filtered by `topics:create` + `topics:read`.
11
11
  - Admins/moderators retain existing move permissions.
12
+ - Optional limit by number of posts in the topic (default: 5).
12
13
 
13
14
  ## Requirements
14
15
 
@@ -39,7 +40,11 @@ Then rebuild and restart:
39
40
 
40
41
  ## Configuration
41
42
 
42
- No settings are required. The plugin respects existing category permissions.
43
+ Admin control panel: `/admin/settings/post`
44
+
45
+ - **Maximum posts to allow owner moves** (`movingTopicsMaxPosts`)
46
+ - Default: `5`
47
+ - Set to `0` for no limit
43
48
 
44
49
  ## Usage
45
50
 
@@ -0,0 +1,6 @@
1
+ {
2
+ "admin.settings.title": "Topic move (owners)",
3
+ "admin.settings.max-posts": "Maximum posts to allow owner moves",
4
+ "admin.settings.max-posts-help": "Set to 0 for no limit. Default: 5.",
5
+ "error.max-posts": "Cannot move a topic with more than %1 posts"
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "admin.settings.title": "העברת נושאים (בעלים)",
3
+ "admin.settings.max-posts": "מספר פוסטים מקסימלי להעברת נושא",
4
+ "admin.settings.max-posts-help": "קבעו 0 ללא הגבלה. ברירת מחדל: 5.",
5
+ "error.max-posts": "לא ניתן להעביר נושא שיש בו מעל %1 פוסטים"
6
+ }
package/library.js CHANGED
@@ -9,6 +9,14 @@ function getCore(path) {
9
9
  return require.main.require(path);
10
10
  }
11
11
 
12
+ function getMaxMovablePosts(meta) {
13
+ const value = parseInt(meta.config.movingTopicsMaxPosts, 10);
14
+ if (Number.isNaN(value)) {
15
+ return 5;
16
+ }
17
+ return value;
18
+ }
19
+
12
20
  plugin.init = async function () {
13
21
  if (patchedMove) {
14
22
  return;
@@ -25,6 +33,7 @@ plugin.init = async function () {
25
33
  const activitypubApi = getCore('./src/api/activitypub');
26
34
  const activitypub = getCore('./src/activitypub');
27
35
  const user = getCore('./src/user');
36
+ const meta = getCore('./src/meta');
28
37
 
29
38
  originalMove = topicsApi.move;
30
39
 
@@ -48,6 +57,7 @@ plugin.init = async function () {
48
57
 
49
58
  const uids = await user.getUidsFromSet('users:online', 0, -1);
50
59
  const cids = [targetCid];
60
+ const maxPosts = getMaxMovablePosts(meta);
51
61
 
52
62
  await batch.processArray(tids, async (batchTids) => {
53
63
  await Promise.all(batchTids.map(async (tid) => {
@@ -59,6 +69,7 @@ plugin.init = async function () {
59
69
  'slug',
60
70
  'deleted',
61
71
  'locked',
72
+ 'postcount',
62
73
  ]);
63
74
 
64
75
  if (!topicData) {
@@ -66,6 +77,10 @@ plugin.init = async function () {
66
77
  }
67
78
 
68
79
  const isOwner = parseInt(topicData.uid, 10) === parseInt(caller.uid, 10);
80
+ const exceedsMax = maxPosts > 0 && topicData.postcount > maxPosts;
81
+ if (exceedsMax) {
82
+ throw new Error(`[[moving-topics:error.max-posts, ${maxPosts}]]`);
83
+ }
69
84
  if (!isOwner || topicData.locked || topicData.deleted) {
70
85
  throw new Error('[[error:no-privileges]]');
71
86
  }
@@ -106,13 +121,22 @@ plugin.init = async function () {
106
121
 
107
122
  plugin.filterTopicPrivileges = async function (data) {
108
123
  const topics = getCore('./src/topics');
109
- const topicData = await topics.getTopicFields(data.tid, ['uid', 'locked', 'deleted']);
124
+ const meta = getCore('./src/meta');
125
+ const topicData = await topics.getTopicFields(data.tid, ['uid', 'locked', 'deleted', 'postcount']);
110
126
  if (!topicData) {
111
127
  return data;
112
128
  }
113
129
 
130
+ const maxPosts = getMaxMovablePosts(meta);
131
+ const exceedsMax = maxPosts > 0 && topicData.postcount > maxPosts;
114
132
  const isOwner = parseInt(topicData.uid, 10) === parseInt(data.uid, 10);
115
- data.canMoveOwnTopic = !!(isOwner && !data.isAdminOrMod && !topicData.locked && !topicData.deleted);
133
+ data.canMoveOwnTopic = !!(
134
+ isOwner &&
135
+ !data.isAdminOrMod &&
136
+ !topicData.locked &&
137
+ !topicData.deleted &&
138
+ !exceedsMax
139
+ );
116
140
 
117
141
  if (data.canMoveOwnTopic && !data.view_thread_tools) {
118
142
  data.view_thread_tools = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-moving-topics",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Allow topic owners to move their own topics.",
5
5
  "main": "library.js",
6
6
  "license": "MIT"
package/plugin.json CHANGED
@@ -8,7 +8,12 @@
8
8
  { "hook": "static:app.load", "method": "init" },
9
9
  { "hook": "filter:privileges.topics.get", "method": "filterTopicPrivileges" }
10
10
  ],
11
+ "acpScripts": [
12
+ "static/lib/admin.js"
13
+ ],
11
14
  "scripts": [
12
15
  "static/lib/client.js"
13
- ]
16
+ ],
17
+ "languages": "languages",
18
+ "defaultLang": "en-GB"
14
19
  }
@@ -0,0 +1,50 @@
1
+ 'use strict';
2
+
3
+ /* global ajaxify, app */
4
+
5
+ let inserted = false;
6
+
7
+ $(window).on('action:app.load', function () {
8
+ require(['hooks', 'translator', 'admin/settings'], function (hooks, translator, Settings) {
9
+ hooks.on('action:ajaxify.end', function () {
10
+ if (!ajaxify.data || !ajaxify.data.template || !ajaxify.data.template['admin/settings/post']) {
11
+ inserted = false;
12
+ return;
13
+ }
14
+
15
+ const container = $('#spy-container');
16
+ if (!container.length) {
17
+ return;
18
+ }
19
+
20
+ if (inserted) {
21
+ return;
22
+ }
23
+
24
+ const sectionHtml = [
25
+ '<hr/>',
26
+ '<div id="moving-topics-settings" class="mb-4">',
27
+ '<h5 class="fw-bold tracking-tight settings-header">[[moving-topics:admin.settings.title]]</h5>',
28
+ '<div class="mb-3">',
29
+ '<label class="form-label" for="movingTopicsMaxPosts">[[moving-topics:admin.settings.max-posts]]</label>',
30
+ '<input id="movingTopicsMaxPosts" type="number" min="0" class="form-control" data-field="movingTopicsMaxPosts" placeholder="5">',
31
+ '<p class="form-text">[[moving-topics:admin.settings.max-posts-help]]</p>',
32
+ '</div>',
33
+ '</div>',
34
+ ].join('');
35
+
36
+ translator.translate(sectionHtml, function (translated) {
37
+ container.append(translated);
38
+ inserted = true;
39
+
40
+ if (!app.config.hasOwnProperty('movingTopicsMaxPosts')) {
41
+ $('#movingTopicsMaxPosts').val(5);
42
+ } else {
43
+ $('#movingTopicsMaxPosts').val(app.config.movingTopicsMaxPosts);
44
+ }
45
+
46
+ Settings.prepare();
47
+ });
48
+ });
49
+ });
50
+ });